Home | History | Annotate | Line # | Download | only in lib
remove.c revision 1.1.1.1.4.2.2.1
      1  1.1.1.1.4.2.2.1  matt /*	$NetBSD: remove.c,v 1.1.1.1.4.2.2.1 2010/04/21 05:23:10 matt Exp $	*/
      2      1.1.1.1.4.2   snj 
      3      1.1.1.1.4.2   snj /*-
      4      1.1.1.1.4.2   snj  * Copyright (c) 2008 Joerg Sonnenberger <joerg (at) NetBSD.org>.
      5      1.1.1.1.4.2   snj  * All rights reserved.
      6      1.1.1.1.4.2   snj  *
      7      1.1.1.1.4.2   snj  * Redistribution and use in source and binary forms, with or without
      8      1.1.1.1.4.2   snj  * modification, are permitted provided that the following conditions
      9      1.1.1.1.4.2   snj  * are met:
     10      1.1.1.1.4.2   snj  *
     11      1.1.1.1.4.2   snj  * 1. Redistributions of source code must retain the above copyright
     12      1.1.1.1.4.2   snj  *    notice, this list of conditions and the following disclaimer.
     13      1.1.1.1.4.2   snj  * 2. Redistributions in binary form must reproduce the above copyright
     14      1.1.1.1.4.2   snj  *    notice, this list of conditions and the following disclaimer in
     15      1.1.1.1.4.2   snj  *    the documentation and/or other materials provided with the
     16      1.1.1.1.4.2   snj  *    distribution.
     17      1.1.1.1.4.2   snj  *
     18      1.1.1.1.4.2   snj  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19      1.1.1.1.4.2   snj  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20      1.1.1.1.4.2   snj  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     21      1.1.1.1.4.2   snj  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
     22      1.1.1.1.4.2   snj  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     23      1.1.1.1.4.2   snj  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24      1.1.1.1.4.2   snj  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25      1.1.1.1.4.2   snj  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26      1.1.1.1.4.2   snj  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27      1.1.1.1.4.2   snj  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     28      1.1.1.1.4.2   snj  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29      1.1.1.1.4.2   snj  * SUCH DAMAGE.
     30      1.1.1.1.4.2   snj  */
     31      1.1.1.1.4.2   snj 
     32      1.1.1.1.4.2   snj #if HAVE_CONFIG_H
     33      1.1.1.1.4.2   snj #include "config.h"
     34      1.1.1.1.4.2   snj #endif
     35      1.1.1.1.4.2   snj 
     36      1.1.1.1.4.2   snj #include <nbcompat.h>
     37      1.1.1.1.4.2   snj 
     38      1.1.1.1.4.2   snj #if HAVE_SYS_CDEFS_H
     39      1.1.1.1.4.2   snj #include <sys/cdefs.h>
     40      1.1.1.1.4.2   snj #endif
     41      1.1.1.1.4.2   snj 
     42  1.1.1.1.4.2.2.1  matt __RCSID("$NetBSD: remove.c,v 1.1.1.1.4.2.2.1 2010/04/21 05:23:10 matt Exp $");
     43      1.1.1.1.4.2   snj 
     44      1.1.1.1.4.2   snj #if HAVE_DIRENT_H
     45      1.1.1.1.4.2   snj #include <dirent.h>
     46      1.1.1.1.4.2   snj #endif
     47      1.1.1.1.4.2   snj #if HAVE_ERR_H
     48      1.1.1.1.4.2   snj #include <err.h>
     49      1.1.1.1.4.2   snj #endif
     50      1.1.1.1.4.2   snj #include <errno.h>
     51      1.1.1.1.4.2   snj #if HAVE_FCNTL_H
     52      1.1.1.1.4.2   snj #include <fcntl.h>
     53      1.1.1.1.4.2   snj #endif
     54      1.1.1.1.4.2   snj #include <limits.h>
     55      1.1.1.1.4.2   snj #include <stdio.h>
     56      1.1.1.1.4.2   snj #include <stdlib.h>
     57      1.1.1.1.4.2   snj #include <string.h>
     58      1.1.1.1.4.2   snj #include <unistd.h>
     59      1.1.1.1.4.2   snj 
     60      1.1.1.1.4.2   snj #include "lib.h"
     61      1.1.1.1.4.2   snj 
     62      1.1.1.1.4.2   snj static int
     63      1.1.1.1.4.2   snj safe_fchdir(int cwd)
     64      1.1.1.1.4.2   snj {
     65      1.1.1.1.4.2   snj 	int tmp_errno, rv;
     66      1.1.1.1.4.2   snj 
     67      1.1.1.1.4.2   snj 	tmp_errno = errno;
     68      1.1.1.1.4.2   snj 	rv = fchdir(cwd);
     69      1.1.1.1.4.2   snj 	errno = tmp_errno;
     70      1.1.1.1.4.2   snj 
     71      1.1.1.1.4.2   snj 	return rv;
     72      1.1.1.1.4.2   snj }
     73      1.1.1.1.4.2   snj 
     74      1.1.1.1.4.2   snj static int
     75      1.1.1.1.4.2   snj long_remove(const char **path_ptr, int missing_ok, int *did_chdir)
     76      1.1.1.1.4.2   snj {
     77      1.1.1.1.4.2   snj 	char tmp_path[PATH_MAX + 1];
     78  1.1.1.1.4.2.2.1  matt 	const char *path;
     79      1.1.1.1.4.2   snj 	size_t i, len;
     80      1.1.1.1.4.2   snj 	int rv;
     81      1.1.1.1.4.2   snj 
     82      1.1.1.1.4.2   snj 	path = *path_ptr;
     83      1.1.1.1.4.2   snj 	len = strlen(path);
     84      1.1.1.1.4.2   snj 	*did_chdir = 0;
     85      1.1.1.1.4.2   snj 
     86      1.1.1.1.4.2   snj 	while (len >= PATH_MAX) {
     87      1.1.1.1.4.2   snj 		for (i = PATH_MAX - 1; i > 0; --i) {
     88      1.1.1.1.4.2   snj 			if (path[i] == '/')
     89      1.1.1.1.4.2   snj 				break;
     90      1.1.1.1.4.2   snj 		}
     91      1.1.1.1.4.2   snj 		if (i == 0) {
     92      1.1.1.1.4.2   snj 			errno = ENAMETOOLONG;
     93      1.1.1.1.4.2   snj 			return -1; /* Assumes PATH_MAX > NAME_MAX */
     94      1.1.1.1.4.2   snj 		}
     95      1.1.1.1.4.2   snj 		memcpy(tmp_path, path, i);
     96      1.1.1.1.4.2   snj 		tmp_path[i] = '\0';
     97      1.1.1.1.4.2   snj 		if (chdir(tmp_path))
     98      1.1.1.1.4.2   snj 			return -1;
     99      1.1.1.1.4.2   snj 		*did_chdir = 1;
    100      1.1.1.1.4.2   snj 		path += i + 1;
    101      1.1.1.1.4.2   snj 		len -= i + 1;
    102      1.1.1.1.4.2   snj 	}
    103      1.1.1.1.4.2   snj 
    104      1.1.1.1.4.2   snj 	if (remove(path) == 0 || (errno == ENOENT && missing_ok))
    105      1.1.1.1.4.2   snj 		rv = 0;
    106      1.1.1.1.4.2   snj 	else
    107      1.1.1.1.4.2   snj 		rv = -1;
    108      1.1.1.1.4.2   snj 
    109      1.1.1.1.4.2   snj 	*path_ptr = path;
    110      1.1.1.1.4.2   snj 
    111      1.1.1.1.4.2   snj 	return rv;
    112      1.1.1.1.4.2   snj }
    113      1.1.1.1.4.2   snj 
    114      1.1.1.1.4.2   snj static int
    115      1.1.1.1.4.2   snj recursive_remove_internal(const char *path, int missing_ok, int cwd)
    116      1.1.1.1.4.2   snj {
    117      1.1.1.1.4.2   snj 	DIR *dir;
    118      1.1.1.1.4.2   snj 	struct dirent *de;
    119      1.1.1.1.4.2   snj 	const char *sub_path;
    120      1.1.1.1.4.2   snj 	char *subdir;
    121      1.1.1.1.4.2   snj 	int did_chdir, rv;
    122      1.1.1.1.4.2   snj 
    123      1.1.1.1.4.2   snj 	/*
    124      1.1.1.1.4.2   snj 	 * If the argument is longer than PATH_MAX, long_remove
    125      1.1.1.1.4.2   snj 	 * will try to shorten it using chdir.  So before returning,
    126      1.1.1.1.4.2   snj 	 * make sure to fchdir back to the original cwd.
    127      1.1.1.1.4.2   snj 	 */
    128      1.1.1.1.4.2   snj 	sub_path = path;
    129      1.1.1.1.4.2   snj 	if (long_remove(&sub_path, missing_ok, &did_chdir) == 0)
    130      1.1.1.1.4.2   snj 		rv = 0;
    131      1.1.1.1.4.2   snj 	else if (errno != ENOTEMPTY) /* Other errors are terminal. */
    132      1.1.1.1.4.2   snj 		rv = -1;
    133      1.1.1.1.4.2   snj 	else
    134      1.1.1.1.4.2   snj 		rv = 1;
    135      1.1.1.1.4.2   snj 
    136      1.1.1.1.4.2   snj 	if (rv != 1) {
    137      1.1.1.1.4.2   snj 		if (did_chdir && safe_fchdir(cwd) == -1 && rv == 0)
    138      1.1.1.1.4.2   snj 			rv = -1;
    139      1.1.1.1.4.2   snj 		return rv;
    140      1.1.1.1.4.2   snj 	}
    141      1.1.1.1.4.2   snj 
    142      1.1.1.1.4.2   snj 	if ((dir = opendir(sub_path)) == NULL) {
    143      1.1.1.1.4.2   snj 		if (errno == EMFILE)
    144      1.1.1.1.4.2   snj 			warn("opendir failed");
    145      1.1.1.1.4.2   snj 		return -1;
    146      1.1.1.1.4.2   snj 	}
    147      1.1.1.1.4.2   snj 
    148      1.1.1.1.4.2   snj 	if (did_chdir && fchdir(cwd) == -1)
    149      1.1.1.1.4.2   snj 		return -1;
    150      1.1.1.1.4.2   snj 
    151      1.1.1.1.4.2   snj 	rv = 0;
    152      1.1.1.1.4.2   snj 
    153      1.1.1.1.4.2   snj 	while ((de = readdir(dir)) != NULL) {
    154      1.1.1.1.4.2   snj 		if (strcmp(de->d_name, ".") == 0)
    155      1.1.1.1.4.2   snj 			continue;
    156      1.1.1.1.4.2   snj 		if (strcmp(de->d_name, "..") == 0)
    157      1.1.1.1.4.2   snj 			continue;
    158      1.1.1.1.4.2   snj 		subdir = xasprintf("%s/%s", path, de->d_name);
    159      1.1.1.1.4.2   snj 		rv = recursive_remove_internal(subdir, 1, cwd);
    160      1.1.1.1.4.2   snj 		free(subdir);
    161      1.1.1.1.4.2   snj 	}
    162      1.1.1.1.4.2   snj 
    163      1.1.1.1.4.2   snj 	closedir(dir);
    164      1.1.1.1.4.2   snj 
    165      1.1.1.1.4.2   snj 	safe_fchdir(cwd);
    166      1.1.1.1.4.2   snj 
    167      1.1.1.1.4.2   snj 	rv |= long_remove(&path, missing_ok, &did_chdir);
    168      1.1.1.1.4.2   snj 
    169      1.1.1.1.4.2   snj 	if (did_chdir && safe_fchdir(cwd) == -1 && rv == 0)
    170      1.1.1.1.4.2   snj 		rv = -1;
    171      1.1.1.1.4.2   snj 
    172      1.1.1.1.4.2   snj 	return rv;
    173      1.1.1.1.4.2   snj }
    174      1.1.1.1.4.2   snj 
    175      1.1.1.1.4.2   snj int
    176      1.1.1.1.4.2   snj recursive_remove(const char *path, int missing_ok)
    177      1.1.1.1.4.2   snj {
    178      1.1.1.1.4.2   snj 	int orig_cwd, rv;
    179      1.1.1.1.4.2   snj 
    180      1.1.1.1.4.2   snj 	/* First try the easy case of regular file or empty directory. */
    181      1.1.1.1.4.2   snj 	if (remove(path) == 0 || (errno == ENOENT && missing_ok))
    182      1.1.1.1.4.2   snj 		return 0;
    183      1.1.1.1.4.2   snj 
    184      1.1.1.1.4.2   snj 	/*
    185      1.1.1.1.4.2   snj 	 * If the path is too long, long_remove will use chdir to shorten it,
    186      1.1.1.1.4.2   snj 	 * so remember the current directory first.
    187      1.1.1.1.4.2   snj 	 */
    188      1.1.1.1.4.2   snj 	if ((orig_cwd = open(".", O_RDONLY)) == -1)
    189      1.1.1.1.4.2   snj 		return -1;
    190      1.1.1.1.4.2   snj 
    191      1.1.1.1.4.2   snj 	rv = recursive_remove_internal(path, missing_ok, orig_cwd);
    192      1.1.1.1.4.2   snj 
    193      1.1.1.1.4.2   snj 	close(orig_cwd);
    194      1.1.1.1.4.2   snj 	return rv;
    195      1.1.1.1.4.2   snj }
    196