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 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1990, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 43 #else 44 static const char rcsid[] = 45 "$FreeBSD$"; 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/stat.h> 50 #include <sys/param.h> 51 #include <sys/mount.h> 52 53 #include <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <fts.h> 57 #include <grp.h> 58 #include <pwd.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <sysexits.h> 63 #include <unistd.h> 64 65 int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 66 uid_t uid; 67 68 int check(char *, char *, struct stat *); 69 void checkdot(char **); 70 void rm_file(char **); 71 void rm_overwrite(char *, struct stat *); 72 void rm_tree(char **); 73 void usage(void); 74 75 /* 76 * rm -- 77 * This rm is different from historic rm's, but is expected to match 78 * POSIX 1003.2 behavior. The most visible difference is that -f 79 * has two specific effects now, ignore non-existent files and force 80 * file removal. 81 */ 82 int 83 main(int argc, char *argv[]) 84 { 85 int ch, rflag; 86 char *p; 87 88 /* 89 * Test for the special case where the utility is called as 90 * "unlink", for which the functionality provided is greatly 91 * simplified. 92 */ 93 if ((p = rindex(argv[0], '/')) == NULL) 94 p = argv[0]; 95 else 96 ++p; 97 if (strcmp(p, "unlink") == 0) { 98 if (argc == 2) { 99 rm_file(&argv[1]); 100 exit(eval); 101 } else 102 usage(); 103 } 104 105 Pflag = rflag = 0; 106 while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) 107 switch(ch) { 108 case 'd': 109 dflag = 1; 110 break; 111 case 'f': 112 fflag = 1; 113 iflag = 0; 114 break; 115 case 'i': 116 fflag = 0; 117 iflag = 1; 118 break; 119 case 'P': 120 Pflag = 1; 121 break; 122 case 'R': 123 case 'r': /* Compatibility. */ 124 rflag = 1; 125 break; 126 case 'v': 127 vflag = 1; 128 break; 129 case 'W': 130 Wflag = 1; 131 break; 132 default: 133 usage(); 134 } 135 argc -= optind; 136 argv += optind; 137 138 if (argc < 1) { 139 if (fflag) 140 return 0; 141 usage(); 142 } 143 144 checkdot(argv); 145 uid = geteuid(); 146 147 if (*argv) { 148 stdin_ok = isatty(STDIN_FILENO); 149 150 if (rflag) 151 rm_tree(argv); 152 else 153 rm_file(argv); 154 } 155 156 exit (eval); 157 } 158 159 void 160 rm_tree(char **argv) 161 { 162 FTS *fts; 163 FTSENT *p; 164 int needstat; 165 int flags; 166 int rval; 167 168 /* 169 * Remove a file hierarchy. If forcing removal (-f), or interactive 170 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 171 */ 172 needstat = !uid || (!fflag && !iflag && stdin_ok); 173 174 /* 175 * If the -i option is specified, the user can skip on the pre-order 176 * visit. The fts_number field flags skipped directories. 177 */ 178 #define SKIPPED 1 179 180 flags = FTS_PHYSICAL; 181 if (!needstat) 182 flags |= FTS_NOSTAT; 183 if (Wflag) 184 flags |= FTS_WHITEOUT; 185 if (!(fts = fts_open(argv, flags, NULL))) 186 err(1, NULL); 187 while ((p = fts_read(fts)) != NULL) { 188 switch (p->fts_info) { 189 case FTS_DNR: 190 if (!fflag || p->fts_errno != ENOENT) { 191 warnx("%s: %s", 192 p->fts_path, strerror(p->fts_errno)); 193 eval = 1; 194 } 195 continue; 196 case FTS_ERR: 197 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 198 case FTS_NS: 199 /* 200 * FTS_NS: assume that if can't stat the file, it 201 * can't be unlinked. 202 */ 203 if (!needstat) 204 break; 205 if (!fflag || p->fts_errno != ENOENT) { 206 warnx("%s: %s", 207 p->fts_path, strerror(p->fts_errno)); 208 eval = 1; 209 } 210 continue; 211 case FTS_D: 212 /* Pre-order: give user chance to skip. */ 213 if (!fflag && !check(p->fts_path, p->fts_accpath, 214 p->fts_statp)) { 215 (void)fts_set(fts, p, FTS_SKIP); 216 p->fts_number = SKIPPED; 217 } 218 else if (!uid && 219 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 220 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 221 chflags(p->fts_accpath, 222 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 223 goto err; 224 continue; 225 case FTS_DP: 226 /* Post-order: see if user skipped. */ 227 if (p->fts_number == SKIPPED) 228 continue; 229 break; 230 default: 231 if (!fflag && 232 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 233 continue; 234 } 235 236 rval = 0; 237 if (!uid && 238 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 239 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 240 rval = chflags(p->fts_accpath, 241 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 242 if (rval == 0) { 243 /* 244 * If we can't read or search the directory, may still be 245 * able to remove it. Don't print out the un{read,search}able 246 * message unless the remove fails. 247 */ 248 switch (p->fts_info) { 249 case FTS_DP: 250 case FTS_DNR: 251 rval = rmdir(p->fts_accpath); 252 if (rval == 0 || (fflag && errno == ENOENT)) { 253 if (rval == 0 && vflag) 254 (void)printf("%s\n", 255 p->fts_path); 256 continue; 257 } 258 break; 259 260 case FTS_W: 261 rval = undelete(p->fts_accpath); 262 if (rval == 0 && (fflag && errno == ENOENT)) { 263 if (vflag) 264 (void)printf("%s\n", 265 p->fts_path); 266 continue; 267 } 268 break; 269 270 default: 271 if (Pflag) 272 rm_overwrite(p->fts_accpath, NULL); 273 rval = unlink(p->fts_accpath); 274 if (rval == 0 || (fflag && errno == ENOENT)) { 275 if (rval == 0 && vflag) 276 (void)printf("%s\n", 277 p->fts_path); 278 continue; 279 } 280 } 281 } 282 err: 283 warn("%s", p->fts_path); 284 eval = 1; 285 } 286 if (errno) 287 err(1, "fts_read"); 288 } 289 290 void 291 rm_file(char **argv) 292 { 293 struct stat sb; 294 int rval; 295 char *f; 296 297 /* 298 * Remove a file. POSIX 1003.2 states that, by default, attempting 299 * to remove a directory is an error, so must always stat the file. 300 */ 301 while ((f = *argv++) != NULL) { 302 /* Assume if can't stat the file, can't unlink it. */ 303 if (lstat(f, &sb)) { 304 if (Wflag) { 305 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 306 } else { 307 if (!fflag || errno != ENOENT) { 308 warn("%s", f); 309 eval = 1; 310 } 311 continue; 312 } 313 } else if (Wflag) { 314 warnx("%s: %s", f, strerror(EEXIST)); 315 eval = 1; 316 continue; 317 } 318 319 if (S_ISDIR(sb.st_mode) && !dflag) { 320 warnx("%s: is a directory", f); 321 eval = 1; 322 continue; 323 } 324 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 325 continue; 326 rval = 0; 327 if (!uid && 328 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 329 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 330 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 331 if (rval == 0) { 332 if (S_ISWHT(sb.st_mode)) 333 rval = undelete(f); 334 else if (S_ISDIR(sb.st_mode)) 335 rval = rmdir(f); 336 else { 337 if (Pflag) 338 rm_overwrite(f, &sb); 339 rval = unlink(f); 340 } 341 } 342 if (rval && (!fflag || errno != ENOENT)) { 343 warn("%s", f); 344 eval = 1; 345 } 346 if (vflag && rval == 0) 347 (void)printf("%s\n", f); 348 } 349 } 350 351 /* 352 * rm_overwrite -- 353 * Overwrite the file 3 times with varying bit patterns. 354 * 355 * XXX 356 * This is a cheap way to *really* delete files. Note that only regular 357 * files are deleted, directories (and therefore names) will remain. 358 * Also, this assumes a fixed-block filesystem (like FFS, or a V7 or a 359 * System V filesystem). In a logging filesystem, you'll have to have 360 * kernel support. 361 */ 362 void 363 rm_overwrite(char *file, struct stat *sbp) 364 { 365 struct stat sb; 366 struct statfs fsb; 367 off_t len; 368 int bsize, fd, wlen; 369 char *buf = NULL; 370 371 fd = -1; 372 if (sbp == NULL) { 373 if (lstat(file, &sb)) 374 goto err; 375 sbp = &sb; 376 } 377 if (!S_ISREG(sbp->st_mode)) 378 return; 379 if ((fd = open(file, O_WRONLY, 0)) == -1) 380 goto err; 381 if (fstatfs(fd, &fsb) == -1) 382 goto err; 383 bsize = MAX(fsb.f_iosize, 1024); 384 if ((buf = malloc(bsize)) == NULL) 385 err(1, "malloc"); 386 387 #define PASS(byte) { \ 388 memset(buf, byte, bsize); \ 389 for (len = sbp->st_size; len > 0; len -= wlen) { \ 390 wlen = len < bsize ? len : bsize; \ 391 if (write(fd, buf, wlen) != wlen) \ 392 goto err; \ 393 } \ 394 } 395 PASS(0xff); 396 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 397 goto err; 398 PASS(0x00); 399 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 400 goto err; 401 PASS(0xff); 402 if (!fsync(fd) && !close(fd)) { 403 free(buf); 404 return; 405 } 406 407 err: eval = 1; 408 if (buf) 409 free(buf); 410 warn("%s", file); 411 } 412 413 414 int 415 check(char *path, char *name, struct stat *sp) 416 { 417 int ch, first; 418 char modep[15], *flagsp; 419 420 /* Check -i first. */ 421 if (iflag) 422 (void)fprintf(stderr, "remove %s? ", path); 423 else { 424 /* 425 * If it's not a symbolic link and it's unwritable and we're 426 * talking to a terminal, ask. Symbolic links are excluded 427 * because their permissions are meaningless. Check stdin_ok 428 * first because we may not have stat'ed the file. 429 */ 430 if (!stdin_ok || S_ISLNK(sp->st_mode) || 431 (!access(name, W_OK) && 432 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 433 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 434 return (1); 435 strmode(sp->st_mode, modep); 436 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 437 err(1, NULL); 438 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 439 modep + 1, modep[9] == ' ' ? "" : " ", 440 user_from_uid(sp->st_uid, 0), 441 group_from_gid(sp->st_gid, 0), 442 *flagsp ? flagsp : "", *flagsp ? " " : "", 443 path); 444 free(flagsp); 445 } 446 (void)fflush(stderr); 447 448 first = ch = getchar(); 449 while (ch != '\n' && ch != EOF) 450 ch = getchar(); 451 return (first == 'y' || first == 'Y'); 452 } 453 454 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 455 void 456 checkdot(char **argv) 457 { 458 char *p, **save, **t; 459 int complained; 460 461 complained = 0; 462 for (t = argv; *t;) { 463 if ((p = strrchr(*t, '/')) != NULL) 464 ++p; 465 else 466 p = *t; 467 if (ISDOT(p)) { 468 if (!complained++) 469 warnx("\".\" and \"..\" may not be removed"); 470 eval = 1; 471 for (save = t; (t[0] = t[1]) != NULL; ++t) 472 continue; 473 t = save; 474 } else 475 ++t; 476 } 477 } 478 479 void 480 usage(void) 481 { 482 483 (void)fprintf(stderr, "%s\n%s\n", 484 "usage: rm [-f | -i] [-dPRrvW] file ...", 485 " unlink file"); 486 exit(EX_USAGE); 487 } 488