1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $Id: rm.c,v 1.5 1994/09/24 02:57:02 davidg Exp $ 34 */ 35 36 #ifndef lint 37 static char copyright[] = 38 "@(#) Copyright (c) 1990, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 42 #ifndef lint 43 static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 44 #endif /* not lint */ 45 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <fts.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 58 extern char *flags_to_string __P((u_long, char *)); 59 60 int dflag, eval, fflag, iflag, Pflag, stdin_ok; 61 uid_t uid; 62 63 int check __P((char *, char *, struct stat *)); 64 void checkdot __P((char **)); 65 void rm_file __P((char **)); 66 void rm_overwrite __P((char *, struct stat *)); 67 void rm_tree __P((char **)); 68 void usage __P((void)); 69 70 /* 71 * rm -- 72 * This rm is different from historic rm's, but is expected to match 73 * POSIX 1003.2 behavior. The most visible difference is that -f 74 * has two specific effects now, ignore non-existent files and force 75 * file removal. 76 */ 77 int 78 main(argc, argv) 79 int argc; 80 char *argv[]; 81 { 82 int ch, rflag; 83 84 Pflag = rflag = 0; 85 while ((ch = getopt(argc, argv, "dfiPRr")) != EOF) 86 switch(ch) { 87 case 'd': 88 dflag = 1; 89 break; 90 case 'f': 91 fflag = 1; 92 iflag = 0; 93 break; 94 case 'i': 95 fflag = 0; 96 iflag = 1; 97 break; 98 case 'P': 99 Pflag = 1; 100 break; 101 case 'R': 102 case 'r': /* Compatibility. */ 103 rflag = 1; 104 break; 105 case '?': 106 default: 107 usage(); 108 } 109 argc -= optind; 110 argv += optind; 111 112 if (argc < 1) 113 usage(); 114 115 checkdot(argv); 116 if (!*argv) 117 exit (eval); 118 119 stdin_ok = isatty(STDIN_FILENO); 120 uid = geteuid(); 121 122 if (rflag) 123 rm_tree(argv); 124 else 125 rm_file(argv); 126 exit (eval); 127 } 128 129 void 130 rm_tree(argv) 131 char **argv; 132 { 133 FTS *fts; 134 FTSENT *p; 135 int needstat; 136 int rval; 137 138 /* 139 * Remove a file hierarchy. If forcing removal (-f), or interactive 140 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 141 */ 142 needstat = !uid || !fflag && !iflag && stdin_ok; 143 144 /* 145 * If the -i option is specified, the user can skip on the pre-order 146 * visit. The fts_number field flags skipped directories. 147 */ 148 #define SKIPPED 1 149 150 if (!(fts = fts_open(argv, 151 needstat ? FTS_PHYSICAL|FTS_NOCHDIR : 152 FTS_PHYSICAL|FTS_NOSTAT|FTS_NOCHDIR, (int (*)())NULL))) 153 err(1, NULL); 154 while ((p = fts_read(fts)) != NULL) { 155 switch (p->fts_info) { 156 case FTS_DNR: 157 if (!fflag || p->fts_errno != ENOENT) { 158 warnx("%s: %s", 159 p->fts_path, strerror(p->fts_errno)); 160 eval = 1; 161 } 162 continue; 163 case FTS_ERR: 164 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 165 case FTS_NS: 166 /* 167 * FTS_NS: assume that if can't stat the file, it 168 * can't be unlinked. 169 */ 170 if (!needstat) 171 break; 172 if (!fflag || p->fts_errno != ENOENT) { 173 warnx("%s: %s", 174 p->fts_path, strerror(p->fts_errno)); 175 eval = 1; 176 } 177 continue; 178 case FTS_D: 179 /* Pre-order: give user chance to skip. */ 180 if (iflag && !check(p->fts_path, p->fts_accpath, 181 p->fts_statp)) { 182 (void)fts_set(fts, p, FTS_SKIP); 183 p->fts_number = SKIPPED; 184 } 185 else if (!uid && 186 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 187 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 188 chflags(p->fts_accpath, 189 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 190 goto err; 191 continue; 192 case FTS_DP: 193 /* Post-order: see if user skipped. */ 194 if (p->fts_number == SKIPPED) 195 continue; 196 break; 197 } 198 if (!fflag && 199 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 200 continue; 201 202 rval = 0; 203 if (!uid && 204 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 205 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 206 rval = chflags(p->fts_accpath, 207 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 208 if (!rval) { 209 /* 210 * If we can't read or search the directory, may still be 211 * able to remove it. Don't print out the un{read,search}able 212 * message unless the remove fails. 213 */ 214 if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) { 215 if (!rmdir(p->fts_accpath)) 216 continue; 217 if (errno == ENOENT) { 218 if (fflag) 219 continue; 220 } else if (p->fts_info != FTS_DP) 221 warnx("%s: unable to read", p->fts_path); 222 } else { 223 if (Pflag) 224 rm_overwrite(p->fts_accpath, NULL); 225 if (!unlink(p->fts_accpath) || (fflag && errno == ENOENT)) 226 continue; 227 } 228 } 229 err: 230 warn("%s", p->fts_path); 231 eval = 1; 232 } 233 if (errno) 234 err(1, "fts_read"); 235 } 236 237 void 238 rm_file(argv) 239 char **argv; 240 { 241 struct stat sb; 242 int df, rval; 243 char *f; 244 245 df = dflag; 246 /* 247 * Remove a file. POSIX 1003.2 states that, by default, attempting 248 * to remove a directory is an error, so must always stat the file. 249 */ 250 while ((f = *argv++) != NULL) { 251 /* Assume if can't stat the file, can't unlink it. */ 252 if (lstat(f, &sb)) { 253 if (!fflag || errno != ENOENT) { 254 warn("%s", f); 255 eval = 1; 256 } 257 continue; 258 } 259 if (S_ISDIR(sb.st_mode) && !df) { 260 warnx("%s: is a directory", f); 261 eval = 1; 262 continue; 263 } 264 if (!fflag && !check(f, f, &sb)) 265 continue; 266 rval = 0; 267 if (!uid && 268 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 269 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 270 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 271 if (!rval) { 272 if (S_ISDIR(sb.st_mode)) 273 rval = rmdir(f); 274 else { 275 if (Pflag) 276 rm_overwrite(f, &sb); 277 rval = unlink(f); 278 } 279 } 280 if (rval && (!fflag || errno != ENOENT)) { 281 warn("%s", f); 282 eval = 1; 283 } 284 } 285 } 286 287 /* 288 * rm_overwrite -- 289 * Overwrite the file 3 times with varying bit patterns. 290 * 291 * XXX 292 * This is a cheap way to *really* delete files. Note that only regular 293 * files are deleted, directories (and therefore names) will remain. 294 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 295 * System V file system). In a logging file system, you'll have to have 296 * kernel support. 297 */ 298 void 299 rm_overwrite(file, sbp) 300 char *file; 301 struct stat *sbp; 302 { 303 struct stat sb; 304 off_t len; 305 int fd, wlen; 306 char buf[8 * 1024]; 307 308 fd = -1; 309 if (sbp == NULL) { 310 if (lstat(file, &sb)) 311 goto err; 312 sbp = &sb; 313 } 314 if (!S_ISREG(sbp->st_mode)) 315 return; 316 if ((fd = open(file, O_WRONLY, 0)) == -1) 317 goto err; 318 319 #define PASS(byte) { \ 320 memset(buf, byte, sizeof(buf)); \ 321 for (len = sbp->st_size; len > 0; len -= wlen) { \ 322 wlen = len < sizeof(buf) ? len : sizeof(buf); \ 323 if (write(fd, buf, wlen) != wlen) \ 324 goto err; \ 325 } \ 326 } 327 PASS(0xff); 328 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 329 goto err; 330 PASS(0x00); 331 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 332 goto err; 333 PASS(0xff); 334 if (!fsync(fd) && !close(fd)) 335 return; 336 337 err: eval = 1; 338 warn("%s", file); 339 } 340 341 342 int 343 check(path, name, sp) 344 char *path, *name; 345 struct stat *sp; 346 { 347 int ch, first; 348 char modep[15], flagsp[128]; 349 350 /* Check -i first. */ 351 if (iflag) 352 (void)fprintf(stderr, "remove %s? ", path); 353 else { 354 /* 355 * If it's not a symbolic link and it's unwritable and we're 356 * talking to a terminal, ask. Symbolic links are excluded 357 * because their permissions are meaningless. Check stdin_ok 358 * first because we may not have stat'ed the file. 359 */ 360 if (!stdin_ok || S_ISLNK(sp->st_mode) || 361 !access(name, W_OK) && 362 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 363 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid)) 364 return (1); 365 strmode(sp->st_mode, modep); 366 strcpy(flagsp, flags_to_string(sp->st_flags, NULL)); 367 if (*flagsp) 368 strcat(flagsp, " "); 369 (void)fprintf(stderr, "override %s%s%s/%s %sfor %s? ", 370 modep + 1, modep[9] == ' ' ? "" : " ", 371 user_from_uid(sp->st_uid, 0), 372 group_from_gid(sp->st_gid, 0), 373 *flagsp ? flagsp : "", 374 path); 375 } 376 (void)fflush(stderr); 377 378 first = ch = getchar(); 379 while (ch != '\n' && ch != EOF) 380 ch = getchar(); 381 return (first == 'y'); 382 } 383 384 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 385 void 386 checkdot(argv) 387 char **argv; 388 { 389 char *p, **save, **t; 390 int complained; 391 392 complained = 0; 393 for (t = argv; *t;) { 394 if ((p = strrchr(*t, '/')) != NULL) 395 ++p; 396 else 397 p = *t; 398 if (ISDOT(p)) { 399 if (!complained++) 400 warnx("\".\" and \"..\" may not be removed"); 401 eval = 1; 402 for (save = t; (t[0] = t[1]) != NULL; ++t); 403 t = save; 404 } else 405 ++t; 406 } 407 } 408 409 void 410 usage() 411 { 412 413 (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 414 exit(1); 415 } 416