1 /* $OpenBSD: rm.c,v 1.7 2015/11/27 17:32:16 tedu Exp $ */ 2 /* $NetBSD: rm.c,v 1.19 1995/09/07 06:48:50 jtc Exp $ */ 3 4 /*- 5 * Copyright (c) 1990, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 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 the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/mount.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <fts.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <limits.h> 46 #include <pwd.h> 47 #include <grp.h> 48 49 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 50 51 extern char *__progname; 52 53 static int eval, stdin_ok; 54 55 static int check(char *, char *, struct stat *); 56 static void checkdot(char **); 57 static void rm_file(char **); 58 static void rm_tree(char **); 59 60 static void __dead 61 usage(void) 62 { 63 (void)fprintf(stderr, "usage: %s [-dfiPRr] file ...\n", __progname); 64 exit(1); 65 } 66 67 /* 68 * rm -- 69 * This rm is different from historic rm's, but is expected to match 70 * POSIX 1003.2 behavior. The most visible difference is that -f 71 * has two specific effects now, ignore non-existent files and force 72 * file removal. 73 */ 74 int 75 rmmain(int argc, char *argv[]) 76 { 77 78 checkdot(argv); 79 80 if (*argv) { 81 stdin_ok = isatty(STDIN_FILENO); 82 83 rm_tree(argv); 84 } 85 86 return (eval); 87 } 88 89 static void 90 rm_tree(char **argv) 91 { 92 FTS *fts; 93 FTSENT *p; 94 int flags; 95 96 /* 97 * If the -i option is specified, the user can skip on the pre-order 98 * visit. The fts_number field flags skipped directories. 99 */ 100 #define SKIPPED 1 101 102 flags = FTS_PHYSICAL; 103 flags |= FTS_NOSTAT; 104 if (!(fts = fts_open(argv, flags, NULL))) 105 err(1, NULL); 106 while ((p = fts_read(fts)) != NULL) { 107 switch (p->fts_info) { 108 case FTS_DNR: 109 if (p->fts_errno != ENOENT) { 110 warnx("%s: %s", 111 p->fts_path, strerror(p->fts_errno)); 112 eval = 1; 113 } 114 continue; 115 case FTS_ERR: 116 errc(1, p->fts_errno, "%s", p->fts_path); 117 case FTS_NS: 118 /* 119 * FTS_NS: assume that if can't stat the file, it 120 * can't be unlinked. 121 */ 122 break; 123 case FTS_D: 124 /* Pre-order: give user chance to skip. */ 125 continue; 126 case FTS_DP: 127 /* Post-order: see if user skipped. */ 128 if (p->fts_number == SKIPPED) 129 continue; 130 break; 131 default: 132 break; 133 } 134 135 /* 136 * If we can't read or search the directory, may still be 137 * able to remove it. Don't print out the un{read,search}able 138 * message unless the remove fails. 139 */ 140 switch (p->fts_info) { 141 case FTS_DP: 142 case FTS_DNR: 143 if (!rmdir(p->fts_accpath) || 144 (errno == ENOENT)) 145 continue; 146 break; 147 148 case FTS_F: 149 case FTS_NSOK: 150 default: 151 if (!unlink(p->fts_accpath) || 152 (errno == ENOENT)) 153 continue; 154 } 155 warn("%s", p->fts_path); 156 eval = 1; 157 } 158 if (errno) 159 err(1, "fts_read"); 160 fts_close(fts); 161 } 162 163 static void 164 rm_file(char **argv) 165 { 166 struct stat sb; 167 int rval; 168 char *f; 169 170 /* 171 * Remove a file. POSIX 1003.2 states that, by default, attempting 172 * to remove a directory is an error, so must always stat the file. 173 */ 174 while ((f = *argv++) != NULL) { 175 /* Assume if can't stat the file, can't unlink it. */ 176 if (lstat(f, &sb)) { 177 if (errno != ENOENT) { 178 warn("%s", f); 179 eval = 1; 180 } 181 continue; 182 } 183 184 if (S_ISDIR(sb.st_mode)) { 185 warnx("%s: is a directory", f); 186 eval = 1; 187 continue; 188 } 189 if (S_ISDIR(sb.st_mode)) 190 rval = rmdir(f); 191 else { 192 rval = unlink(f); 193 } 194 if (rval && (errno != ENOENT)) { 195 warn("%s", f); 196 eval = 1; 197 } 198 } 199 } 200 201 static int 202 check(char *path, char *name, struct stat *sp) 203 { 204 int ch, first; 205 char modep[15]; 206 207 /* 208 * If it's not a symbolic link and it's unwritable and we're 209 * talking to a terminal, ask. Symbolic links are excluded 210 * because their permissions are meaningless. Check stdin_ok 211 * first because we may not have stat'ed the file. 212 */ 213 if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK) || 214 errno != EACCES) 215 return (1); 216 strmode(sp->st_mode, modep); 217 (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 218 modep + 1, modep[9] == ' ' ? "" : " ", 219 user_from_uid(sp->st_uid, 0), 220 group_from_gid(sp->st_gid, 0), path); 221 (void)fflush(stderr); 222 223 first = ch = getchar(); 224 while (ch != '\n' && ch != EOF) 225 ch = getchar(); 226 return (first == 'y' || first == 'Y'); 227 } 228 229 /* 230 * POSIX.2 requires that if "." or ".." are specified as the basename 231 * portion of an operand, a diagnostic message be written to standard 232 * error and nothing more be done with such operands. 233 * 234 * Since POSIX.2 defines basename as the final portion of a path after 235 * trailing slashes have been removed, we'll remove them here. 236 */ 237 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 238 static void 239 checkdot(char **argv) 240 { 241 char *p, **save, **t; 242 int complained; 243 244 complained = 0; 245 for (t = argv; *t;) { 246 /* strip trailing slashes */ 247 p = strrchr (*t, '\0'); 248 while (--p > *t && *p == '/') 249 *p = '\0'; 250 251 /* extract basename */ 252 if ((p = strrchr(*t, '/')) != NULL) 253 ++p; 254 else 255 p = *t; 256 257 if (ISDOT(p)) { 258 if (!complained++) 259 warnx("\".\" and \"..\" may not be removed"); 260 eval = 1; 261 for (save = t; (t[0] = t[1]) != NULL; ++t) 262 continue; 263 t = save; 264 } else 265 ++t; 266 } 267 } 268