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