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 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #if 0 31 #ifndef lint 32 static const char copyright[] = 33 "@(#) Copyright (c) 1990, 1993, 1994\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35 #endif /* not lint */ 36 37 #ifndef lint 38 static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 39 #endif /* not lint */ 40 #endif 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 #include <sys/stat.h> 45 #include <sys/param.h> 46 #include <sys/mount.h> 47 48 #include <err.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <fts.h> 52 #include <grp.h> 53 #include <pwd.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <sysexits.h> 58 #include <unistd.h> 59 60 int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 61 int rflag, Iflag; 62 uid_t uid; 63 64 int check(char *, char *, struct stat *); 65 int check2(char **); 66 void checkdot(char **); 67 void checkslash(char **); 68 void rm_file(char **); 69 int rm_overwrite(char *, struct stat *); 70 void rm_tree(char **); 71 void usage(void); 72 73 /* 74 * rm -- 75 * This rm is different from historic rm's, but is expected to match 76 * POSIX 1003.2 behavior. The most visible difference is that -f 77 * has two specific effects now, ignore non-existent files and force 78 * file removal. 79 */ 80 int 81 main(int argc, char *argv[]) 82 { 83 int ch; 84 char *p; 85 86 /* 87 * Test for the special case where the utility is called as 88 * "unlink", for which the functionality provided is greatly 89 * simplified. 90 */ 91 if ((p = rindex(argv[0], '/')) == NULL) 92 p = argv[0]; 93 else 94 ++p; 95 if (strcmp(p, "unlink") == 0) { 96 while (getopt(argc, argv, "") != -1) 97 usage(); 98 argc -= optind; 99 argv += optind; 100 if (argc != 1) 101 usage(); 102 rm_file(&argv[0]); 103 exit(eval); 104 } 105 106 Pflag = rflag = 0; 107 while ((ch = getopt(argc, argv, "dfiIPRrvW")) != -1) 108 switch(ch) { 109 case 'd': 110 dflag = 1; 111 break; 112 case 'f': 113 fflag = 1; 114 iflag = 0; 115 break; 116 case 'i': 117 fflag = 0; 118 iflag = 1; 119 break; 120 case 'I': 121 Iflag = 1; 122 break; 123 case 'P': 124 Pflag = 1; 125 break; 126 case 'R': 127 case 'r': /* Compatibility. */ 128 rflag = 1; 129 break; 130 case 'v': 131 vflag = 1; 132 break; 133 case 'W': 134 Wflag = 1; 135 break; 136 default: 137 usage(); 138 } 139 argc -= optind; 140 argv += optind; 141 142 if (argc < 1) { 143 if (fflag) 144 return (0); 145 usage(); 146 } 147 148 checkdot(argv); 149 if (getenv("POSIXLY_CORRECT") == NULL) 150 checkslash(argv); 151 uid = geteuid(); 152 153 if (*argv) { 154 stdin_ok = isatty(STDIN_FILENO); 155 156 if (Iflag) { 157 if (check2(argv) == 0) 158 exit (1); 159 } 160 if (rflag) 161 rm_tree(argv); 162 else 163 rm_file(argv); 164 } 165 166 exit (eval); 167 } 168 169 void 170 rm_tree(char **argv) 171 { 172 FTS *fts; 173 FTSENT *p; 174 int needstat; 175 int flags; 176 int rval; 177 178 /* 179 * Remove a file hierarchy. If forcing removal (-f), or interactive 180 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 181 */ 182 needstat = !uid || (!fflag && !iflag && stdin_ok); 183 184 /* 185 * If the -i option is specified, the user can skip on the pre-order 186 * visit. The fts_number field flags skipped directories. 187 */ 188 #define SKIPPED 1 189 190 flags = FTS_PHYSICAL; 191 if (!needstat) 192 flags |= FTS_NOSTAT; 193 if (Wflag) 194 flags |= FTS_WHITEOUT; 195 if (!(fts = fts_open(argv, flags, NULL))) 196 err(1, "fts_open"); 197 while ((p = fts_read(fts)) != NULL) { 198 switch (p->fts_info) { 199 case FTS_DNR: 200 if (!fflag || p->fts_errno != ENOENT) { 201 warnx("%s: %s", 202 p->fts_path, strerror(p->fts_errno)); 203 eval = 1; 204 } 205 continue; 206 case FTS_ERR: 207 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 208 case FTS_NS: 209 /* 210 * Assume that since fts_read() couldn't stat the 211 * file, it can't be unlinked. 212 */ 213 if (!needstat) 214 break; 215 if (!fflag || p->fts_errno != ENOENT) { 216 warnx("%s: %s", 217 p->fts_path, strerror(p->fts_errno)); 218 eval = 1; 219 } 220 continue; 221 case FTS_D: 222 /* Pre-order: give user chance to skip. */ 223 if (!fflag && !check(p->fts_path, p->fts_accpath, 224 p->fts_statp)) { 225 (void)fts_set(fts, p, FTS_SKIP); 226 p->fts_number = SKIPPED; 227 } 228 else if (!uid && 229 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 230 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 231 chflags(p->fts_accpath, 232 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 233 goto err; 234 continue; 235 case FTS_DP: 236 /* Post-order: see if user skipped. */ 237 if (p->fts_number == SKIPPED) 238 continue; 239 break; 240 default: 241 if (!fflag && 242 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 243 continue; 244 } 245 246 rval = 0; 247 if (!uid && 248 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 249 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 250 rval = chflags(p->fts_accpath, 251 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 252 if (rval == 0) { 253 /* 254 * If we can't read or search the directory, may still be 255 * able to remove it. Don't print out the un{read,search}able 256 * message unless the remove fails. 257 */ 258 switch (p->fts_info) { 259 case FTS_DP: 260 case FTS_DNR: 261 rval = rmdir(p->fts_accpath); 262 if (rval == 0 || (fflag && errno == ENOENT)) { 263 if (rval == 0 && vflag) 264 (void)printf("%s\n", 265 p->fts_path); 266 continue; 267 } 268 break; 269 270 case FTS_W: 271 rval = undelete(p->fts_accpath); 272 if (rval == 0 && (fflag && errno == ENOENT)) { 273 if (vflag) 274 (void)printf("%s\n", 275 p->fts_path); 276 continue; 277 } 278 break; 279 280 case FTS_NS: 281 /* 282 * Assume that since fts_read() couldn't stat 283 * the file, it can't be unlinked. 284 */ 285 if (fflag) 286 continue; 287 /* FALLTHROUGH */ 288 default: 289 if (Pflag) 290 if (!rm_overwrite(p->fts_accpath, NULL)) 291 continue; 292 rval = unlink(p->fts_accpath); 293 if (rval == 0 || (fflag && errno == ENOENT)) { 294 if (rval == 0 && vflag) 295 (void)printf("%s\n", 296 p->fts_path); 297 continue; 298 } 299 } 300 } 301 err: 302 warn("%s", p->fts_path); 303 eval = 1; 304 } 305 if (errno) 306 err(1, "fts_read"); 307 } 308 309 void 310 rm_file(char **argv) 311 { 312 struct stat sb; 313 int rval; 314 char *f; 315 316 /* 317 * Remove a file. POSIX 1003.2 states that, by default, attempting 318 * to remove a directory is an error, so must always stat the file. 319 */ 320 while ((f = *argv++) != NULL) { 321 /* Assume if can't stat the file, can't unlink it. */ 322 if (lstat(f, &sb)) { 323 if (Wflag) { 324 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 325 } else { 326 if (!fflag || errno != ENOENT) { 327 warn("%s", f); 328 eval = 1; 329 } 330 continue; 331 } 332 } else if (Wflag) { 333 warnx("%s: %s", f, strerror(EEXIST)); 334 eval = 1; 335 continue; 336 } 337 338 if (S_ISDIR(sb.st_mode) && !dflag) { 339 warnx("%s: is a directory", f); 340 eval = 1; 341 continue; 342 } 343 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 344 continue; 345 rval = 0; 346 if (!uid && 347 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 348 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 349 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 350 if (rval == 0) { 351 if (S_ISWHT(sb.st_mode)) 352 rval = undelete(f); 353 else if (S_ISDIR(sb.st_mode)) 354 rval = rmdir(f); 355 else { 356 if (Pflag) 357 if (!rm_overwrite(f, &sb)) 358 continue; 359 rval = unlink(f); 360 } 361 } 362 if (rval && (!fflag || errno != ENOENT)) { 363 warn("%s", f); 364 eval = 1; 365 } 366 if (vflag && rval == 0) 367 (void)printf("%s\n", f); 368 } 369 } 370 371 /* 372 * rm_overwrite -- 373 * Overwrite the file 3 times with varying bit patterns. 374 * 375 * XXX 376 * This is a cheap way to *really* delete files. Note that only regular 377 * files are deleted, directories (and therefore names) will remain. 378 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 379 * System V file system). In a logging file system, you'll have to have 380 * kernel support. 381 */ 382 int 383 rm_overwrite(char *file, struct stat *sbp) 384 { 385 struct stat sb; 386 struct statfs fsb; 387 off_t len; 388 int bsize, fd, wlen; 389 char *buf = NULL; 390 391 fd = -1; 392 if (sbp == NULL) { 393 if (lstat(file, &sb)) 394 goto err; 395 sbp = &sb; 396 } 397 if (!S_ISREG(sbp->st_mode)) 398 return (1); 399 if ((fd = open(file, O_WRONLY, 0)) == -1) 400 goto err; 401 if (fstatfs(fd, &fsb) == -1) 402 goto err; 403 bsize = MAX(fsb.f_iosize, 1024); 404 if ((buf = malloc(bsize)) == NULL) 405 err(1, "%s: malloc", file); 406 407 #define PASS(byte) { \ 408 memset(buf, byte, bsize); \ 409 for (len = sbp->st_size; len > 0; len -= wlen) { \ 410 wlen = len < bsize ? len : bsize; \ 411 if (write(fd, buf, wlen) != wlen) \ 412 goto err; \ 413 } \ 414 } 415 PASS(0xff); 416 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 417 goto err; 418 PASS(0x00); 419 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 420 goto err; 421 PASS(0xff); 422 if (!fsync(fd) && !close(fd)) { 423 free(buf); 424 return (1); 425 } 426 427 err: eval = 1; 428 if (buf) 429 free(buf); 430 if (fd != -1) 431 close(fd); 432 warn("%s", file); 433 return (0); 434 } 435 436 437 int 438 check(char *path, char *name, struct stat *sp) 439 { 440 int ch, first; 441 char modep[15], *flagsp; 442 443 /* Check -i first. */ 444 if (iflag) 445 (void)fprintf(stderr, "remove %s? ", path); 446 else { 447 /* 448 * If it's not a symbolic link and it's unwritable and we're 449 * talking to a terminal, ask. Symbolic links are excluded 450 * because their permissions are meaningless. Check stdin_ok 451 * first because we may not have stat'ed the file. 452 * Also skip this check if the -P option was specified because 453 * we will not be able to overwrite file contents and will 454 * barf later. 455 */ 456 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag || 457 (!access(name, W_OK) && 458 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 459 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 460 return (1); 461 strmode(sp->st_mode, modep); 462 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 463 err(1, "fflagstostr"); 464 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 465 modep + 1, modep[9] == ' ' ? "" : " ", 466 user_from_uid(sp->st_uid, 0), 467 group_from_gid(sp->st_gid, 0), 468 *flagsp ? flagsp : "", *flagsp ? " " : "", 469 path); 470 free(flagsp); 471 } 472 (void)fflush(stderr); 473 474 first = ch = getchar(); 475 while (ch != '\n' && ch != EOF) 476 ch = getchar(); 477 return (first == 'y' || first == 'Y'); 478 } 479 480 #define ISSLASH(a) ((a)[0] == '/' && (a)[1] == '\0') 481 void 482 checkslash(char **argv) 483 { 484 char **t, **u; 485 int complained; 486 487 complained = 0; 488 for (t = argv; *t;) { 489 if (ISSLASH(*t)) { 490 if (!complained++) 491 warnx("\"/\" may not be removed"); 492 eval = 1; 493 for (u = t; u[0] != NULL; ++u) 494 u[0] = u[1]; 495 } else { 496 ++t; 497 } 498 } 499 } 500 501 int 502 check2(char **argv) 503 { 504 struct stat st; 505 int first; 506 int ch; 507 int fcount = 0; 508 int dcount = 0; 509 int i; 510 const char *dname = NULL; 511 512 for (i = 0; argv[i]; ++i) { 513 if (lstat(argv[i], &st) == 0) { 514 if (S_ISDIR(st.st_mode)) { 515 ++dcount; 516 dname = argv[i]; /* only used if 1 dir */ 517 } else { 518 ++fcount; 519 } 520 } 521 } 522 first = 0; 523 while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') { 524 if (dcount && rflag) { 525 fprintf(stderr, "recursively remove"); 526 if (dcount == 1) 527 fprintf(stderr, " %s", dname); 528 else 529 fprintf(stderr, " %d dirs", dcount); 530 if (fcount == 1) 531 fprintf(stderr, " and 1 file"); 532 else if (fcount > 1) 533 fprintf(stderr, " and %d files", fcount); 534 } else if (dcount + fcount > 3) { 535 fprintf(stderr, "remove %d files", dcount + fcount); 536 } else { 537 return(1); 538 } 539 fprintf(stderr, "? "); 540 fflush(stderr); 541 542 first = ch = getchar(); 543 while (ch != '\n' && ch != EOF) 544 ch = getchar(); 545 if (ch == EOF) 546 break; 547 } 548 return (first == 'y' || first == 'Y'); 549 } 550 551 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 552 void 553 checkdot(char **argv) 554 { 555 char *p, **save, **t; 556 int complained; 557 558 complained = 0; 559 for (t = argv; *t;) { 560 if ((p = strrchr(*t, '/')) != NULL) 561 ++p; 562 else 563 p = *t; 564 if (ISDOT(p)) { 565 if (!complained++) 566 warnx("\".\" and \"..\" may not be removed"); 567 eval = 1; 568 for (save = t; (t[0] = t[1]) != NULL; ++t) 569 continue; 570 t = save; 571 } else 572 ++t; 573 } 574 } 575 576 void 577 usage(void) 578 { 579 580 (void)fprintf(stderr, "%s\n%s\n", 581 "usage: rm [-f | -i] [-dIPRrvW] file ...", 582 " unlink file"); 583 exit(EX_USAGE); 584 } 585