xref: /csrg-svn/bin/rm/rm.c (revision 68969)
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*68969Sbostic static char sccsid[] = "@(#)rm.c	8.8 (Berkeley) 04/27/95";
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 
3067575Spendry 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
main(argc,argv)471083Sbill main(argc, argv)
4834044Sbostic 	int argc;
4959515Sbostic 	char *argv[];
501083Sbill {
5145769Sbostic 	int ch, rflag;
521083Sbill 
5366860Sbostic 	Pflag = rflag = 0;
54*68969Sbostic 	while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
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;
7467575Spendry 		case 'W':
7567575Spendry 			Wflag = 1;
7667575Spendry 			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 
89*68969Sbostic 	if (*argv) {
90*68969Sbostic 		stdin_ok = isatty(STDIN_FILENO);
9145769Sbostic 
92*68969Sbostic 		if (rflag)
93*68969Sbostic 			rm_tree(argv);
94*68969Sbostic 		else
95*68969Sbostic 			rm_file(argv);
96*68969Sbostic 	}
97*68969Sbostic 
9859515Sbostic 	exit (eval);
991083Sbill }
1001083Sbill 
10159515Sbostic void
rm_tree(argv)10266860Sbostic rm_tree(argv)
10345769Sbostic 	char **argv;
1041083Sbill {
10566561Spendry 	FTS *fts;
10666561Spendry 	FTSENT *p;
10766561Spendry 	int needstat;
10867575Spendry 	int flags;
1091083Sbill 
11045769Sbostic 	/*
11145769Sbostic 	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
11245769Sbostic 	 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
11345769Sbostic 	 */
11445769Sbostic 	needstat = !fflag && !iflag && stdin_ok;
11545769Sbostic 
11645769Sbostic 	/*
11745769Sbostic 	 * If the -i option is specified, the user can skip on the pre-order
11845769Sbostic 	 * visit.  The fts_number field flags skipped directories.
11945769Sbostic 	 */
12045769Sbostic #define	SKIPPED	1
12145769Sbostic 
12267575Spendry 	flags = FTS_PHYSICAL;
12367575Spendry 	if (!needstat)
12467575Spendry 		flags |= FTS_NOSTAT;
12567575Spendry 	if (Wflag)
12667575Spendry 		flags |= FTS_WHITEOUT;
12767575Spendry 	if (!(fts = fts_open(argv, flags, (int (*)())NULL)))
12859515Sbostic 		err(1, NULL);
12966605Sbostic 	while ((p = fts_read(fts)) != NULL) {
13059515Sbostic 		switch (p->fts_info) {
13145769Sbostic 		case FTS_DNR:
13266605Sbostic 			if (!fflag || p->fts_errno != ENOENT) {
13366605Sbostic 				warnx("%s: %s",
13466605Sbostic 				    p->fts_path, strerror(p->fts_errno));
13561007Sbostic 				eval = 1;
13661007Sbostic 			}
13761007Sbostic 			continue;
13845769Sbostic 		case FTS_ERR:
13966605Sbostic 			errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
14045769Sbostic 		case FTS_NS:
14159515Sbostic 			/*
14259515Sbostic 			 * FTS_NS: assume that if can't stat the file, it
14359515Sbostic 			 * can't be unlinked.
14459515Sbostic 			 */
14545769Sbostic 			if (!needstat)
14645769Sbostic 				break;
14766605Sbostic 			if (!fflag || p->fts_errno != ENOENT) {
14866605Sbostic 				warnx("%s: %s",
14966605Sbostic 				    p->fts_path, strerror(p->fts_errno));
15059515Sbostic 				eval = 1;
15159515Sbostic 			}
15245769Sbostic 			continue;
15345769Sbostic 		case FTS_D:
15459515Sbostic 			/* Pre-order: give user chance to skip. */
155*68969Sbostic 			if (!fflag && !check(p->fts_path, p->fts_accpath,
15652238Sbostic 			    p->fts_statp)) {
15745769Sbostic 				(void)fts_set(fts, p, FTS_SKIP);
15845769Sbostic 				p->fts_number = SKIPPED;
15918327Sralph 			}
16045769Sbostic 			continue;
16145769Sbostic 		case FTS_DP:
16259515Sbostic 			/* Post-order: see if user skipped. */
16345769Sbostic 			if (p->fts_number == SKIPPED)
16445769Sbostic 				continue;
16545769Sbostic 			break;
166*68969Sbostic 		default:
167*68969Sbostic 			if (!fflag &&
168*68969Sbostic 			    !check(p->fts_path, p->fts_accpath, p->fts_statp))
169*68969Sbostic 				continue;
17018277Sralph 		}
17145769Sbostic 
17245769Sbostic 		/*
17345769Sbostic 		 * If we can't read or search the directory, may still be
17445769Sbostic 		 * able to remove it.  Don't print out the un{read,search}able
17545769Sbostic 		 * message unless the remove fails.
17645769Sbostic 		 */
17767575Spendry 		switch (p->fts_info) {
17867575Spendry 		case FTS_DP:
17967575Spendry 		case FTS_DNR:
180*68969Sbostic 			if (!rmdir(p->fts_accpath) || fflag && errno == ENOENT)
18118277Sralph 				continue;
18267575Spendry 			break;
18367575Spendry 
18467575Spendry 		case FTS_W:
18567835Smckusick 			if (!undelete(p->fts_accpath) ||
18667575Spendry 			    fflag && errno == ENOENT)
18767575Spendry 				continue;
18867575Spendry 			break;
18967575Spendry 
19067575Spendry 		default:
19166860Sbostic 			if (Pflag)
19266860Sbostic 				rm_overwrite(p->fts_accpath, NULL);
19366860Sbostic 			if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
19466860Sbostic 				continue;
19566860Sbostic 		}
19659515Sbostic 		warn("%s", p->fts_path);
19759515Sbostic 		eval = 1;
1981083Sbill 	}
19966605Sbostic 	if (errno)
20066605Sbostic 		err(1, "fts_read");
20145769Sbostic }
2021083Sbill 
20359515Sbostic void
rm_file(argv)20466860Sbostic rm_file(argv)
20545769Sbostic 	char **argv;
20645769Sbostic {
20766860Sbostic 	struct stat sb;
208*68969Sbostic 	int rval;
20966561Spendry 	char *f;
21045769Sbostic 
21145769Sbostic 	/*
21245769Sbostic 	 * Remove a file.  POSIX 1003.2 states that, by default, attempting
21345769Sbostic 	 * to remove a directory is an error, so must always stat the file.
21445769Sbostic 	 */
21566605Sbostic 	while ((f = *argv++) != NULL) {
21645769Sbostic 		/* Assume if can't stat the file, can't unlink it. */
21745769Sbostic 		if (lstat(f, &sb)) {
21867575Spendry 			if (Wflag) {
21967575Spendry 				sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
22067575Spendry 			} else {
22167575Spendry 				if (!fflag || errno != ENOENT) {
22267575Spendry 					warn("%s", f);
22367575Spendry 					eval = 1;
22467575Spendry 				}
22567575Spendry 				continue;
22659515Sbostic 			}
22767575Spendry 		} else if (Wflag) {
22867575Spendry 			warnx("%s: %s", f, strerror(EEXIST));
22967575Spendry 			eval = 1;
23045769Sbostic 			continue;
2311083Sbill 		}
23267575Spendry 
233*68969Sbostic 		if (S_ISDIR(sb.st_mode) && !dflag) {
23459515Sbostic 			warnx("%s: is a directory", f);
23559515Sbostic 			eval = 1;
23645769Sbostic 			continue;
23718327Sralph 		}
23867575Spendry 		if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
23945769Sbostic 			continue;
24067575Spendry 		if (S_ISWHT(sb.st_mode))
24167835Smckusick 			rval = undelete(f);
24267575Spendry 		else if (S_ISDIR(sb.st_mode))
24366860Sbostic 			rval = rmdir(f);
24466860Sbostic 		else {
24566860Sbostic 			if (Pflag)
24666860Sbostic 				rm_overwrite(f, &sb);
24766860Sbostic 			rval = unlink(f);
24866860Sbostic 		}
24966860Sbostic 		if (rval && (!fflag || errno != ENOENT)) {
25059515Sbostic 			warn("%s", f);
25159515Sbostic 			eval = 1;
25259515Sbostic 		}
2531083Sbill 	}
2541083Sbill }
2551083Sbill 
25666860Sbostic /*
25766860Sbostic  * rm_overwrite --
25866860Sbostic  *	Overwrite the file 3 times with varying bit patterns.
25966860Sbostic  *
26066860Sbostic  * XXX
26166860Sbostic  * This is a cheap way to *really* delete files.  Note that only regular
26266860Sbostic  * files are deleted, directories (and therefore names) will remain.
26366860Sbostic  * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
26466860Sbostic  * System V file system).  In a logging file system, you'll have to have
26566860Sbostic  * kernel support.
26666860Sbostic  */
26766860Sbostic void
rm_overwrite(file,sbp)26866860Sbostic rm_overwrite(file, sbp)
26966860Sbostic 	char *file;
27066860Sbostic 	struct stat *sbp;
27166860Sbostic {
27266860Sbostic 	struct stat sb;
27366860Sbostic 	off_t len;
27466860Sbostic 	int fd, wlen;
27566860Sbostic 	char buf[8 * 1024];
27666860Sbostic 
27766860Sbostic 	fd = -1;
27866860Sbostic 	if (sbp == NULL) {
27966860Sbostic 		if (lstat(file, &sb))
28066860Sbostic 			goto err;
28166860Sbostic 		sbp = &sb;
28266860Sbostic 	}
28366860Sbostic 	if (!S_ISREG(sbp->st_mode))
28466860Sbostic 		return;
28566860Sbostic 	if ((fd = open(file, O_WRONLY, 0)) == -1)
28666860Sbostic 		goto err;
28766860Sbostic 
28866860Sbostic #define	PASS(byte) {							\
28966860Sbostic 	memset(buf, byte, sizeof(buf));					\
29066860Sbostic 	for (len = sbp->st_size; len > 0; len -= wlen) {		\
29166860Sbostic 		wlen = len < sizeof(buf) ? len : sizeof(buf);		\
29266860Sbostic 		if (write(fd, buf, wlen) != wlen)			\
29366860Sbostic 			goto err;					\
29466860Sbostic 	}								\
29566860Sbostic }
29666860Sbostic 	PASS(0xff);
29766860Sbostic 	if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
29866860Sbostic 		goto err;
29966860Sbostic 	PASS(0x00);
30066860Sbostic 	if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
30166860Sbostic 		goto err;
30266860Sbostic 	PASS(0xff);
30366860Sbostic 	if (!fsync(fd) && !close(fd))
30466860Sbostic 		return;
30566860Sbostic 
30666860Sbostic err:	eval = 1;
30766860Sbostic 	warn("%s", file);
30866860Sbostic }
30966860Sbostic 
31066860Sbostic 
31159515Sbostic int
check(path,name,sp)31245769Sbostic check(path, name, sp)
31345769Sbostic 	char *path, *name;
31445769Sbostic 	struct stat *sp;
3151083Sbill {
31666561Spendry 	int ch, first;
31759515Sbostic 	char modep[15];
3181083Sbill 
31945769Sbostic 	/* Check -i first. */
32045769Sbostic 	if (iflag)
32145769Sbostic 		(void)fprintf(stderr, "remove %s? ", path);
32245769Sbostic 	else {
32345769Sbostic 		/*
32445769Sbostic 		 * If it's not a symbolic link and it's unwritable and we're
32545769Sbostic 		 * talking to a terminal, ask.  Symbolic links are excluded
32665344Sbostic 		 * because their permissions are meaningless.  Check stdin_ok
32765344Sbostic 		 * first because we may not have stat'ed the file.
32845769Sbostic 		 */
32965344Sbostic 		if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK))
33059515Sbostic 			return (1);
33145769Sbostic 		strmode(sp->st_mode, modep);
33245769Sbostic 		(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
33345769Sbostic 		    modep + 1, modep[9] == ' ' ? "" : " ",
33445769Sbostic 		    user_from_uid(sp->st_uid, 0),
33545769Sbostic 		    group_from_gid(sp->st_gid, 0), path);
33645769Sbostic 	}
33745769Sbostic 	(void)fflush(stderr);
33845769Sbostic 
33945769Sbostic 	first = ch = getchar();
34045769Sbostic 	while (ch != '\n' && ch != EOF)
34145769Sbostic 		ch = getchar();
34259515Sbostic 	return (first == 'y');
3431083Sbill }
34418277Sralph 
34545769Sbostic #define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
34659515Sbostic void
checkdot(argv)34745769Sbostic checkdot(argv)
34845769Sbostic 	char **argv;
34918277Sralph {
35066561Spendry 	char *p, **save, **t;
35145769Sbostic 	int complained;
35218277Sralph 
35345769Sbostic 	complained = 0;
35445769Sbostic 	for (t = argv; *t;) {
35566605Sbostic 		if ((p = strrchr(*t, '/')) != NULL)
35645769Sbostic 			++p;
35745769Sbostic 		else
35845769Sbostic 			p = *t;
35945769Sbostic 		if (ISDOT(p)) {
36045769Sbostic 			if (!complained++)
36159515Sbostic 				warnx("\".\" and \"..\" may not be removed");
36259515Sbostic 			eval = 1;
363*68969Sbostic 			for (save = t; (t[0] = t[1]) != NULL; ++t)
364*68969Sbostic 				continue;
36545769Sbostic 			t = save;
36645769Sbostic 		} else
36745769Sbostic 			++t;
36845769Sbostic 	}
36918277Sralph }
37034044Sbostic 
37159515Sbostic void
usage()37234044Sbostic usage()
37334044Sbostic {
37466561Spendry 
375*68969Sbostic 	(void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n");
37634044Sbostic 	exit(1);
37734044Sbostic }
378