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