145769Sbostic /*- 266605Sbostic * Copyright (c) 1990, 1993, 1994 361008Sbostic * The Regents of the University of California. All rights reserved. 445769Sbostic * 545769Sbostic * %sccs.include.redist.c% 618277Sralph */ 718277Sralph 845769Sbostic #ifndef lint 961008Sbostic static char copyright[] = 1066605Sbostic "@(#) Copyright (c) 1990, 1993, 1994\n\ 1161008Sbostic The Regents of the University of California. All rights reserved.\n"; 1245769Sbostic #endif /* not lint */ 1345769Sbostic 1445769Sbostic #ifndef lint 15*67575Spendry static char sccsid[] = "@(#)rm.c 8.6 (Berkeley) 07/28/94"; 1645769Sbostic #endif /* not lint */ 1745769Sbostic 1845769Sbostic #include <sys/types.h> 1945769Sbostic #include <sys/stat.h> 2059515Sbostic 2159515Sbostic #include <err.h> 2266561Spendry #include <errno.h> 2366860Sbostic #include <fcntl.h> 2445769Sbostic #include <fts.h> 251083Sbill #include <stdio.h> 2659515Sbostic #include <stdlib.h> 2745769Sbostic #include <string.h> 2859515Sbostic #include <unistd.h> 291083Sbill 30*67575Spendry int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok; 311083Sbill 3259515Sbostic int check __P((char *, char *, struct stat *)); 3359515Sbostic void checkdot __P((char **)); 3466860Sbostic void rm_file __P((char **)); 3566860Sbostic void rm_overwrite __P((char *, struct stat *)); 3666860Sbostic void rm_tree __P((char **)); 3759515Sbostic void usage __P((void)); 3859515Sbostic 3945769Sbostic /* 4045769Sbostic * rm -- 4145769Sbostic * This rm is different from historic rm's, but is expected to match 4245769Sbostic * POSIX 1003.2 behavior. The most visible difference is that -f 4345769Sbostic * has two specific effects now, ignore non-existent files and force 4445769Sbostic * file removal. 4545769Sbostic */ 4659515Sbostic int 471083Sbill main(argc, argv) 4834044Sbostic int argc; 4959515Sbostic char *argv[]; 501083Sbill { 5145769Sbostic int ch, rflag; 521083Sbill 5366860Sbostic Pflag = rflag = 0; 54*67575Spendry while ((ch = getopt(argc, argv, "dfiPRrW")) != EOF) 5545769Sbostic switch(ch) { 5645769Sbostic case 'd': 5745769Sbostic dflag = 1; 5845769Sbostic break; 5934044Sbostic case 'f': 6045769Sbostic fflag = 1; 6145769Sbostic iflag = 0; 6218277Sralph break; 6334044Sbostic case 'i': 6445769Sbostic fflag = 0; 6545769Sbostic iflag = 1; 6634044Sbostic break; 6766860Sbostic case 'P': 6866860Sbostic Pflag = 1; 6966860Sbostic break; 7034044Sbostic case 'R': 7166860Sbostic case 'r': /* Compatibility. */ 7245769Sbostic rflag = 1; 7334044Sbostic break; 74*67575Spendry case 'W': 75*67575Spendry Wflag = 1; 76*67575Spendry break; 7734044Sbostic case '?': 7834044Sbostic default: 7934044Sbostic usage(); 8034044Sbostic } 8145769Sbostic argc -= optind; 8245769Sbostic argv += optind; 831932Sroot 8445769Sbostic if (argc < 1) 8534044Sbostic usage(); 8645769Sbostic 8745769Sbostic checkdot(argv); 8845769Sbostic if (!*argv) 8959515Sbostic exit (eval); 9045769Sbostic 9145769Sbostic stdin_ok = isatty(STDIN_FILENO); 9245769Sbostic 9345769Sbostic if (rflag) 9466860Sbostic rm_tree(argv); 9545769Sbostic else 9666860Sbostic rm_file(argv); 9759515Sbostic exit (eval); 981083Sbill } 991083Sbill 10059515Sbostic void 10166860Sbostic rm_tree(argv) 10245769Sbostic char **argv; 1031083Sbill { 10466561Spendry FTS *fts; 10566561Spendry FTSENT *p; 10666561Spendry int needstat; 107*67575Spendry int flags; 1081083Sbill 10945769Sbostic /* 11045769Sbostic * Remove a file hierarchy. If forcing removal (-f), or interactive 11145769Sbostic * (-i) or can't ask anyway (stdin_ok), don't stat the file. 11245769Sbostic */ 11345769Sbostic needstat = !fflag && !iflag && stdin_ok; 11445769Sbostic 11545769Sbostic /* 11645769Sbostic * If the -i option is specified, the user can skip on the pre-order 11745769Sbostic * visit. The fts_number field flags skipped directories. 11845769Sbostic */ 11945769Sbostic #define SKIPPED 1 12045769Sbostic 121*67575Spendry flags = FTS_PHYSICAL; 122*67575Spendry if (!needstat) 123*67575Spendry flags |= FTS_NOSTAT; 124*67575Spendry if (Wflag) 125*67575Spendry flags |= FTS_WHITEOUT; 126*67575Spendry if (!(fts = fts_open(argv, flags, (int (*)())NULL))) 12759515Sbostic err(1, NULL); 12866605Sbostic while ((p = fts_read(fts)) != NULL) { 12959515Sbostic switch (p->fts_info) { 13045769Sbostic case FTS_DNR: 13166605Sbostic if (!fflag || p->fts_errno != ENOENT) { 13266605Sbostic warnx("%s: %s", 13366605Sbostic p->fts_path, strerror(p->fts_errno)); 13461007Sbostic eval = 1; 13561007Sbostic } 13661007Sbostic continue; 13745769Sbostic case FTS_ERR: 13866605Sbostic errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 13945769Sbostic case FTS_NS: 14059515Sbostic /* 14159515Sbostic * FTS_NS: assume that if can't stat the file, it 14259515Sbostic * can't be unlinked. 14359515Sbostic */ 14445769Sbostic if (!needstat) 14545769Sbostic break; 14666605Sbostic if (!fflag || p->fts_errno != ENOENT) { 14766605Sbostic warnx("%s: %s", 14866605Sbostic p->fts_path, strerror(p->fts_errno)); 14959515Sbostic eval = 1; 15059515Sbostic } 15145769Sbostic continue; 15245769Sbostic case FTS_D: 15359515Sbostic /* Pre-order: give user chance to skip. */ 15445769Sbostic if (iflag && !check(p->fts_path, p->fts_accpath, 15552238Sbostic p->fts_statp)) { 15645769Sbostic (void)fts_set(fts, p, FTS_SKIP); 15745769Sbostic p->fts_number = SKIPPED; 15818327Sralph } 15945769Sbostic continue; 16045769Sbostic case FTS_DP: 16159515Sbostic /* Post-order: see if user skipped. */ 16245769Sbostic if (p->fts_number == SKIPPED) 16345769Sbostic continue; 16445769Sbostic break; 16518277Sralph } 16645769Sbostic if (!fflag && 16752238Sbostic !check(p->fts_path, p->fts_accpath, p->fts_statp)) 16845769Sbostic continue; 16945769Sbostic 17045769Sbostic /* 17145769Sbostic * If we can't read or search the directory, may still be 17245769Sbostic * able to remove it. Don't print out the un{read,search}able 17345769Sbostic * message unless the remove fails. 17445769Sbostic */ 175*67575Spendry switch (p->fts_info) { 176*67575Spendry case FTS_DP: 177*67575Spendry case FTS_DNR: 17845769Sbostic if (!rmdir(p->fts_accpath)) 17918277Sralph continue; 18045769Sbostic if (errno == ENOENT) { 18145769Sbostic if (fflag) 18245769Sbostic continue; 18345769Sbostic } else if (p->fts_info != FTS_DP) 18459515Sbostic warnx("%s: unable to read", p->fts_path); 185*67575Spendry break; 186*67575Spendry 187*67575Spendry case FTS_W: 188*67575Spendry if (!unwhiteout(p->fts_accpath) || 189*67575Spendry fflag && errno == ENOENT) 190*67575Spendry continue; 191*67575Spendry break; 192*67575Spendry 193*67575Spendry default: 19466860Sbostic if (Pflag) 19566860Sbostic rm_overwrite(p->fts_accpath, NULL); 19666860Sbostic if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 19766860Sbostic continue; 19866860Sbostic } 19959515Sbostic warn("%s", p->fts_path); 20059515Sbostic eval = 1; 2011083Sbill } 20266605Sbostic if (errno) 20366605Sbostic err(1, "fts_read"); 20445769Sbostic } 2051083Sbill 20659515Sbostic void 20766860Sbostic rm_file(argv) 20845769Sbostic char **argv; 20945769Sbostic { 21066860Sbostic struct stat sb; 21166860Sbostic int df, rval; 21266561Spendry char *f; 21345769Sbostic 21445769Sbostic df = dflag; 21545769Sbostic /* 21645769Sbostic * Remove a file. POSIX 1003.2 states that, by default, attempting 21745769Sbostic * to remove a directory is an error, so must always stat the file. 21845769Sbostic */ 21966605Sbostic while ((f = *argv++) != NULL) { 22045769Sbostic /* Assume if can't stat the file, can't unlink it. */ 22145769Sbostic if (lstat(f, &sb)) { 222*67575Spendry if (Wflag) { 223*67575Spendry sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 224*67575Spendry } else { 225*67575Spendry if (!fflag || errno != ENOENT) { 226*67575Spendry warn("%s", f); 227*67575Spendry eval = 1; 228*67575Spendry } 229*67575Spendry continue; 23059515Sbostic } 231*67575Spendry } else if (Wflag) { 232*67575Spendry warnx("%s: %s", f, strerror(EEXIST)); 233*67575Spendry eval = 1; 23445769Sbostic continue; 2351083Sbill } 236*67575Spendry 23745769Sbostic if (S_ISDIR(sb.st_mode) && !df) { 23859515Sbostic warnx("%s: is a directory", f); 23959515Sbostic eval = 1; 24045769Sbostic continue; 24118327Sralph } 242*67575Spendry if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 24345769Sbostic continue; 244*67575Spendry if (S_ISWHT(sb.st_mode)) 245*67575Spendry rval = unwhiteout(f); 246*67575Spendry else if (S_ISDIR(sb.st_mode)) 24766860Sbostic rval = rmdir(f); 24866860Sbostic else { 24966860Sbostic if (Pflag) 25066860Sbostic rm_overwrite(f, &sb); 25166860Sbostic rval = unlink(f); 25266860Sbostic } 25366860Sbostic if (rval && (!fflag || errno != ENOENT)) { 25459515Sbostic warn("%s", f); 25559515Sbostic eval = 1; 25659515Sbostic } 2571083Sbill } 2581083Sbill } 2591083Sbill 26066860Sbostic /* 26166860Sbostic * rm_overwrite -- 26266860Sbostic * Overwrite the file 3 times with varying bit patterns. 26366860Sbostic * 26466860Sbostic * XXX 26566860Sbostic * This is a cheap way to *really* delete files. Note that only regular 26666860Sbostic * files are deleted, directories (and therefore names) will remain. 26766860Sbostic * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 26866860Sbostic * System V file system). In a logging file system, you'll have to have 26966860Sbostic * kernel support. 27066860Sbostic */ 27166860Sbostic void 27266860Sbostic rm_overwrite(file, sbp) 27366860Sbostic char *file; 27466860Sbostic struct stat *sbp; 27566860Sbostic { 27666860Sbostic struct stat sb; 27766860Sbostic off_t len; 27866860Sbostic int fd, wlen; 27966860Sbostic char buf[8 * 1024]; 28066860Sbostic 28166860Sbostic fd = -1; 28266860Sbostic if (sbp == NULL) { 28366860Sbostic if (lstat(file, &sb)) 28466860Sbostic goto err; 28566860Sbostic sbp = &sb; 28666860Sbostic } 28766860Sbostic if (!S_ISREG(sbp->st_mode)) 28866860Sbostic return; 28966860Sbostic if ((fd = open(file, O_WRONLY, 0)) == -1) 29066860Sbostic goto err; 29166860Sbostic 29266860Sbostic #define PASS(byte) { \ 29366860Sbostic memset(buf, byte, sizeof(buf)); \ 29466860Sbostic for (len = sbp->st_size; len > 0; len -= wlen) { \ 29566860Sbostic wlen = len < sizeof(buf) ? len : sizeof(buf); \ 29666860Sbostic if (write(fd, buf, wlen) != wlen) \ 29766860Sbostic goto err; \ 29866860Sbostic } \ 29966860Sbostic } 30066860Sbostic PASS(0xff); 30166860Sbostic if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 30266860Sbostic goto err; 30366860Sbostic PASS(0x00); 30466860Sbostic if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 30566860Sbostic goto err; 30666860Sbostic PASS(0xff); 30766860Sbostic if (!fsync(fd) && !close(fd)) 30866860Sbostic return; 30966860Sbostic 31066860Sbostic err: eval = 1; 31166860Sbostic warn("%s", file); 31266860Sbostic } 31366860Sbostic 31466860Sbostic 31559515Sbostic int 31645769Sbostic check(path, name, sp) 31745769Sbostic char *path, *name; 31845769Sbostic struct stat *sp; 3191083Sbill { 32066561Spendry int ch, first; 32159515Sbostic char modep[15]; 3221083Sbill 32345769Sbostic /* Check -i first. */ 32445769Sbostic if (iflag) 32545769Sbostic (void)fprintf(stderr, "remove %s? ", path); 32645769Sbostic else { 32745769Sbostic /* 32845769Sbostic * If it's not a symbolic link and it's unwritable and we're 32945769Sbostic * talking to a terminal, ask. Symbolic links are excluded 33065344Sbostic * because their permissions are meaningless. Check stdin_ok 33165344Sbostic * first because we may not have stat'ed the file. 33245769Sbostic */ 33365344Sbostic if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) 33459515Sbostic return (1); 33545769Sbostic strmode(sp->st_mode, modep); 33645769Sbostic (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 33745769Sbostic modep + 1, modep[9] == ' ' ? "" : " ", 33845769Sbostic user_from_uid(sp->st_uid, 0), 33945769Sbostic group_from_gid(sp->st_gid, 0), path); 34045769Sbostic } 34145769Sbostic (void)fflush(stderr); 34245769Sbostic 34345769Sbostic first = ch = getchar(); 34445769Sbostic while (ch != '\n' && ch != EOF) 34545769Sbostic ch = getchar(); 34659515Sbostic return (first == 'y'); 3471083Sbill } 34818277Sralph 34945769Sbostic #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 35059515Sbostic void 35145769Sbostic checkdot(argv) 35245769Sbostic char **argv; 35318277Sralph { 35466561Spendry char *p, **save, **t; 35545769Sbostic int complained; 35618277Sralph 35745769Sbostic complained = 0; 35845769Sbostic for (t = argv; *t;) { 35966605Sbostic if ((p = strrchr(*t, '/')) != NULL) 36045769Sbostic ++p; 36145769Sbostic else 36245769Sbostic p = *t; 36345769Sbostic if (ISDOT(p)) { 36445769Sbostic if (!complained++) 36559515Sbostic warnx("\".\" and \"..\" may not be removed"); 36659515Sbostic eval = 1; 36766605Sbostic for (save = t; (t[0] = t[1]) != NULL; ++t); 36845769Sbostic t = save; 36945769Sbostic } else 37045769Sbostic ++t; 37145769Sbostic } 37218277Sralph } 37334044Sbostic 37459515Sbostic void 37534044Sbostic usage() 37634044Sbostic { 37766561Spendry 37845769Sbostic (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 37934044Sbostic exit(1); 38034044Sbostic } 381