rm.c revision 122304
1132332Smarcel/*- 2132360Smarcel * Copyright (c) 1990, 1993, 1994 3132332Smarcel * The Regents of the University of California. All rights reserved. 4132332Smarcel * 5132332Smarcel * Redistribution and use in source and binary forms, with or without 6132332Smarcel * modification, are permitted provided that the following conditions 7132332Smarcel * are met: 8132332Smarcel * 1. Redistributions of source code must retain the above copyright 9132332Smarcel * notice, this list of conditions and the following disclaimer. 10132332Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11132332Smarcel * notice, this list of conditions and the following disclaimer in the 12132332Smarcel * documentation and/or other materials provided with the distribution. 13132332Smarcel * 3. All advertising materials mentioning features or use of this software 14132332Smarcel * must display the following acknowledgement: 15132332Smarcel * This product includes software developed by the University of 16132332Smarcel * California, Berkeley and its contributors. 17132332Smarcel * 4. Neither the name of the University nor the names of its contributors 18132332Smarcel * may be used to endorse or promote products derived from this software 19132332Smarcel * without specific prior written permission. 20132332Smarcel * 21132332Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22132332Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23132332Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24132332Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25132332Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26132332Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27132332Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28132332Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29132332Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30132332Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31132332Smarcel * SUCH DAMAGE. 32132332Smarcel */ 33132332Smarcel 34132332Smarcel#if 0 35132332Smarcel#ifndef lint 36132332Smarcelstatic const char copyright[] = 37132332Smarcel"@(#) Copyright (c) 1990, 1993, 1994\n\ 38132332Smarcel The Regents of the University of California. All rights reserved.\n"; 39132332Smarcel#endif /* not lint */ 40132332Smarcel 41132332Smarcel#ifndef lint 42132332Smarcelstatic char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 43132332Smarcel#endif /* not lint */ 44132332Smarcel#endif 45132332Smarcel#include <sys/cdefs.h> 46132332Smarcel__FBSDID("$FreeBSD: head/bin/rm/rm.c 122304 2003-11-08 09:55:16Z bde $"); 47132332Smarcel 48132332Smarcel#include <sys/stat.h> 49132332Smarcel#include <sys/param.h> 50132332Smarcel#include <sys/mount.h> 51132332Smarcel 52132332Smarcel#include <err.h> 53132332Smarcel#include <errno.h> 54132332Smarcel#include <fcntl.h> 55132332Smarcel#include <fts.h> 56132332Smarcel#include <grp.h> 57132332Smarcel#include <pwd.h> 58132332Smarcel#include <stdio.h> 59132332Smarcel#include <stdlib.h> 60144922Sdavidxu#include <string.h> 61132332Smarcel#include <sysexits.h> 62132332Smarcel#include <unistd.h> 63132332Smarcel 64132332Smarcelint dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 65132332Smarceluid_t uid; 66132332Smarcel 67132332Smarcelint check(char *, char *, struct stat *); 68132332Smarcelvoid checkdot(char **); 69132332Smarcelvoid rm_file(char **); 70132332Smarcelvoid rm_overwrite(char *, struct stat *); 71132332Smarcelvoid rm_tree(char **); 72132332Smarcelvoid usage(void); 73132332Smarcel 74132332Smarcel/* 75132332Smarcel * rm -- 76132360Smarcel * This rm is different from historic rm's, but is expected to match 77132360Smarcel * POSIX 1003.2 behavior. The most visible difference is that -f 78132360Smarcel * has two specific effects now, ignore non-existent files and force 79132360Smarcel * file removal. 80132360Smarcel */ 81132360Smarcelint 82132360Smarcelmain(int argc, char *argv[]) 83132360Smarcel{ 84132360Smarcel int ch, rflag; 85132360Smarcel char *p; 86132360Smarcel 87132360Smarcel /* 88132360Smarcel * Test for the special case where the utility is called as 89132360Smarcel * "unlink", for which the functionality provided is greatly 90132332Smarcel * simplified. 91132332Smarcel */ 92132332Smarcel if ((p = rindex(argv[0], '/')) == NULL) 93132332Smarcel p = argv[0]; 94132332Smarcel else 95132332Smarcel ++p; 96132332Smarcel if (strcmp(p, "unlink") == 0) { 97132332Smarcel while (getopt(argc, argv, "") != -1) 98132332Smarcel usage(); 99132332Smarcel argc -= optind; 100132332Smarcel argv += optind; 101132332Smarcel if (argc != 1) 102132332Smarcel usage(); 103132332Smarcel rm_file(&argv[0]); 104132360Smarcel exit(eval); 105132360Smarcel } 106132360Smarcel 107132360Smarcel Pflag = rflag = 0; 108132360Smarcel while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) 109132360Smarcel switch(ch) { 110132332Smarcel case 'd': 111132360Smarcel dflag = 1; 112132332Smarcel break; 113132332Smarcel case 'f': 114132360Smarcel fflag = 1; 115132332Smarcel iflag = 0; 116132332Smarcel break; 117132332Smarcel case 'i': 118132332Smarcel fflag = 0; 119132332Smarcel iflag = 1; 120132332Smarcel break; 121132332Smarcel case 'P': 122132332Smarcel Pflag = 1; 123132332Smarcel break; 124132332Smarcel case 'R': 125132332Smarcel case 'r': /* Compatibility. */ 126132332Smarcel rflag = 1; 127132332Smarcel break; 128132332Smarcel case 'v': 129132332Smarcel vflag = 1; 130132332Smarcel break; 131132332Smarcel case 'W': 132132332Smarcel Wflag = 1; 133132332Smarcel break; 134132332Smarcel default: 135132332Smarcel usage(); 136132332Smarcel } 137132332Smarcel argc -= optind; 138132332Smarcel argv += optind; 139132332Smarcel 140132332Smarcel if (argc < 1) { 141132332Smarcel if (fflag) 142132332Smarcel return 0; 143132332Smarcel usage(); 144132332Smarcel } 145132332Smarcel 146132332Smarcel checkdot(argv); 147132332Smarcel uid = geteuid(); 148132332Smarcel 149132332Smarcel if (*argv) { 150132332Smarcel stdin_ok = isatty(STDIN_FILENO); 151132332Smarcel 152132332Smarcel if (rflag) 153132332Smarcel rm_tree(argv); 154132332Smarcel else 155132332Smarcel rm_file(argv); 156132332Smarcel } 157132332Smarcel 158132332Smarcel exit (eval); 159132332Smarcel} 160132332Smarcel 161132332Smarcelvoid 162132332Smarcelrm_tree(char **argv) 163132332Smarcel{ 164132332Smarcel FTS *fts; 165132332Smarcel FTSENT *p; 166132332Smarcel int needstat; 167132332Smarcel int flags; 168132332Smarcel int rval; 169132332Smarcel 170132332Smarcel /* 171132332Smarcel * Remove a file hierarchy. If forcing removal (-f), or interactive 172132332Smarcel * (-i) or can't ask anyway (stdin_ok), don't stat the file. 173132332Smarcel */ 174132332Smarcel needstat = !uid || (!fflag && !iflag && stdin_ok); 175132360Smarcel 176132332Smarcel /* 177132332Smarcel * If the -i option is specified, the user can skip on the pre-order 178132332Smarcel * visit. The fts_number field flags skipped directories. 179132332Smarcel */ 180144663Sdavidxu#define SKIPPED 1 181132332Smarcel 182132332Smarcel flags = FTS_PHYSICAL; 183132332Smarcel if (!needstat) 184132332Smarcel flags |= FTS_NOSTAT; 185132332Smarcel if (Wflag) 186132332Smarcel flags |= FTS_WHITEOUT; 187132332Smarcel if (!(fts = fts_open(argv, flags, NULL))) 188132332Smarcel err(1, "fts_open"); 189132332Smarcel while ((p = fts_read(fts)) != NULL) { 190132332Smarcel switch (p->fts_info) { 191132332Smarcel case FTS_DNR: 192132332Smarcel if (!fflag || p->fts_errno != ENOENT) { 193132332Smarcel warnx("%s: %s", 194132332Smarcel p->fts_path, strerror(p->fts_errno)); 195132332Smarcel eval = 1; 196132332Smarcel } 197132332Smarcel continue; 198132332Smarcel case FTS_ERR: 199132332Smarcel errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 200132332Smarcel case FTS_NS: 201132332Smarcel /* 202132332Smarcel * FTS_NS: assume that if can't stat the file, it 203132332Smarcel * can't be unlinked. 204132332Smarcel */ 205132332Smarcel if (!needstat) 206132332Smarcel break; 207132332Smarcel if (!fflag || p->fts_errno != ENOENT) { 208132332Smarcel warnx("%s: %s", 209132332Smarcel p->fts_path, strerror(p->fts_errno)); 210132332Smarcel eval = 1; 211132332Smarcel } 212132332Smarcel continue; 213132332Smarcel case FTS_D: 214132332Smarcel /* Pre-order: give user chance to skip. */ 215132332Smarcel if (!fflag && !check(p->fts_path, p->fts_accpath, 216132332Smarcel p->fts_statp)) { 217132332Smarcel (void)fts_set(fts, p, FTS_SKIP); 218132332Smarcel p->fts_number = SKIPPED; 219132332Smarcel } 220132332Smarcel else if (!uid && 221132332Smarcel (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 222132332Smarcel !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 223132332Smarcel chflags(p->fts_accpath, 224132332Smarcel p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 225132332Smarcel goto err; 226132332Smarcel continue; 227132332Smarcel case FTS_DP: 228132332Smarcel /* Post-order: see if user skipped. */ 229132332Smarcel if (p->fts_number == SKIPPED) 230146818Sdfr continue; 231146818Sdfr break; 232146818Sdfr default: 233132332Smarcel if (!fflag && 234132332Smarcel !check(p->fts_path, p->fts_accpath, p->fts_statp)) 235132332Smarcel continue; 236146818Sdfr } 237146818Sdfr 238146818Sdfr rval = 0; 239132332Smarcel if (!uid && 240132332Smarcel (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 241132332Smarcel !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 242180982Smarcel rval = chflags(p->fts_accpath, 243180982Smarcel p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 244132332Smarcel if (rval == 0) { 245132332Smarcel /* 246132332Smarcel * If we can't read or search the directory, may still be 247132332Smarcel * able to remove it. Don't print out the un{read,search}able 248132332Smarcel * message unless the remove fails. 249132332Smarcel */ 250 switch (p->fts_info) { 251 case FTS_DP: 252 case FTS_DNR: 253 rval = rmdir(p->fts_accpath); 254 if (rval == 0 || (fflag && errno == ENOENT)) { 255 if (rval == 0 && vflag) 256 (void)printf("%s\n", 257 p->fts_path); 258 continue; 259 } 260 break; 261 262 case FTS_W: 263 rval = undelete(p->fts_accpath); 264 if (rval == 0 && (fflag && errno == ENOENT)) { 265 if (vflag) 266 (void)printf("%s\n", 267 p->fts_path); 268 continue; 269 } 270 break; 271 272 default: 273 if (Pflag) 274 rm_overwrite(p->fts_accpath, NULL); 275 rval = unlink(p->fts_accpath); 276 if (rval == 0 || (fflag && errno == ENOENT)) { 277 if (rval == 0 && vflag) 278 (void)printf("%s\n", 279 p->fts_path); 280 continue; 281 } 282 } 283 } 284err: 285 warn("%s", p->fts_path); 286 eval = 1; 287 } 288 if (errno) 289 err(1, "fts_read"); 290} 291 292void 293rm_file(char **argv) 294{ 295 struct stat sb; 296 int rval; 297 char *f; 298 299 /* 300 * Remove a file. POSIX 1003.2 states that, by default, attempting 301 * to remove a directory is an error, so must always stat the file. 302 */ 303 while ((f = *argv++) != NULL) { 304 /* Assume if can't stat the file, can't unlink it. */ 305 if (lstat(f, &sb)) { 306 if (Wflag) { 307 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 308 } else { 309 if (!fflag || errno != ENOENT) { 310 warn("%s", f); 311 eval = 1; 312 } 313 continue; 314 } 315 } else if (Wflag) { 316 warnx("%s: %s", f, strerror(EEXIST)); 317 eval = 1; 318 continue; 319 } 320 321 if (S_ISDIR(sb.st_mode) && !dflag) { 322 warnx("%s: is a directory", f); 323 eval = 1; 324 continue; 325 } 326 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 327 continue; 328 rval = 0; 329 if (!uid && 330 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 331 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 332 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 333 if (rval == 0) { 334 if (S_ISWHT(sb.st_mode)) 335 rval = undelete(f); 336 else if (S_ISDIR(sb.st_mode)) 337 rval = rmdir(f); 338 else { 339 if (Pflag) 340 rm_overwrite(f, &sb); 341 rval = unlink(f); 342 } 343 } 344 if (rval && (!fflag || errno != ENOENT)) { 345 warn("%s", f); 346 eval = 1; 347 } 348 if (vflag && rval == 0) 349 (void)printf("%s\n", f); 350 } 351} 352 353/* 354 * rm_overwrite -- 355 * Overwrite the file 3 times with varying bit patterns. 356 * 357 * XXX 358 * This is a cheap way to *really* delete files. Note that only regular 359 * files are deleted, directories (and therefore names) will remain. 360 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 361 * System V file system). In a logging file system, you'll have to have 362 * kernel support. 363 */ 364void 365rm_overwrite(char *file, struct stat *sbp) 366{ 367 struct stat sb; 368 struct statfs fsb; 369 off_t len; 370 int bsize, fd, wlen; 371 char *buf = NULL; 372 373 fd = -1; 374 if (sbp == NULL) { 375 if (lstat(file, &sb)) 376 goto err; 377 sbp = &sb; 378 } 379 if (!S_ISREG(sbp->st_mode)) 380 return; 381 if ((fd = open(file, O_WRONLY, 0)) == -1) 382 goto err; 383 if (fstatfs(fd, &fsb) == -1) 384 goto err; 385 bsize = MAX(fsb.f_iosize, 1024); 386 if ((buf = malloc(bsize)) == NULL) 387 err(1, "%s: malloc", file); 388 389#define PASS(byte) { \ 390 memset(buf, byte, bsize); \ 391 for (len = sbp->st_size; len > 0; len -= wlen) { \ 392 wlen = len < bsize ? len : bsize; \ 393 if (write(fd, buf, wlen) != wlen) \ 394 goto err; \ 395 } \ 396} 397 PASS(0xff); 398 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 399 goto err; 400 PASS(0x00); 401 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 402 goto err; 403 PASS(0xff); 404 if (!fsync(fd) && !close(fd)) { 405 free(buf); 406 return; 407 } 408 409err: eval = 1; 410 if (buf) 411 free(buf); 412 if (fd != -1) 413 close(fd); 414 warn("%s", file); 415} 416 417 418int 419check(char *path, char *name, struct stat *sp) 420{ 421 int ch, first; 422 char modep[15], *flagsp; 423 424 /* Check -i first. */ 425 if (iflag) 426 (void)fprintf(stderr, "remove %s? ", path); 427 else { 428 /* 429 * If it's not a symbolic link and it's unwritable and we're 430 * talking to a terminal, ask. Symbolic links are excluded 431 * because their permissions are meaningless. Check stdin_ok 432 * first because we may not have stat'ed the file. 433 */ 434 if (!stdin_ok || S_ISLNK(sp->st_mode) || 435 (!access(name, W_OK) && 436 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 437 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 438 return (1); 439 strmode(sp->st_mode, modep); 440 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 441 err(1, "fflagstostr"); 442 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 443 modep + 1, modep[9] == ' ' ? "" : " ", 444 user_from_uid(sp->st_uid, 0), 445 group_from_gid(sp->st_gid, 0), 446 *flagsp ? flagsp : "", *flagsp ? " " : "", 447 path); 448 free(flagsp); 449 } 450 (void)fflush(stderr); 451 452 first = ch = getchar(); 453 while (ch != '\n' && ch != EOF) 454 ch = getchar(); 455 return (first == 'y' || first == 'Y'); 456} 457 458#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 459void 460checkdot(char **argv) 461{ 462 char *p, **save, **t; 463 int complained; 464 465 complained = 0; 466 for (t = argv; *t;) { 467 if ((p = strrchr(*t, '/')) != NULL) 468 ++p; 469 else 470 p = *t; 471 if (ISDOT(p)) { 472 if (!complained++) 473 warnx("\".\" and \"..\" may not be removed"); 474 eval = 1; 475 for (save = t; (t[0] = t[1]) != NULL; ++t) 476 continue; 477 t = save; 478 } else 479 ++t; 480 } 481} 482 483void 484usage(void) 485{ 486 487 (void)fprintf(stderr, "%s\n%s\n", 488 "usage: rm [-f | -i] [-dPRrvW] file ...", 489 " unlink file"); 490 exit(EX_USAGE); 491} 492