prettiness police
[unix-history] / usr / src / bin / rm / rm.c
CommitLineData
7f9ef9cc 1/*-
5fa310b5 2 * Copyright (c) 1990, 1993, 1994
51d86845 3 * The Regents of the University of California. All rights reserved.
7f9ef9cc
KB
4 *
5 * %sccs.include.redist.c%
d1e4967d 6 */
ae80f4a2 7
7f9ef9cc 8#ifndef lint
51d86845 9static char copyright[] =
5fa310b5 10"@(#) Copyright (c) 1990, 1993, 1994\n\
51d86845 11 The Regents of the University of California. All rights reserved.\n";
7f9ef9cc 12#endif /* not lint */
d1e4967d 13
7f9ef9cc 14#ifndef lint
815897e8 15static char sccsid[] = "@(#)rm.c 8.8 (Berkeley) %G%";
7f9ef9cc 16#endif /* not lint */
d1e4967d 17
7f9ef9cc
KB
18#include <sys/types.h>
19#include <sys/stat.h>
411a0820
KB
20
21#include <err.h>
706c0358 22#include <errno.h>
60f2fa5e 23#include <fcntl.h>
7f9ef9cc 24#include <fts.h>
7f9ef9cc 25#include <stdio.h>
7f9ef9cc 26#include <stdlib.h>
411a0820
KB
27#include <string.h>
28#include <unistd.h>
29
88edbcd4 30int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok;
ae80f4a2 31
411a0820
KB
32int check __P((char *, char *, struct stat *));
33void checkdot __P((char **));
60f2fa5e
KB
34void rm_file __P((char **));
35void rm_overwrite __P((char *, struct stat *));
36void rm_tree __P((char **));
411a0820 37void usage __P((void));
7f9ef9cc
KB
38
39/*
40 * rm --
41 * This rm is different from historic rm's, but is expected to match
42 * POSIX 1003.2 behavior. The most visible difference is that -f
43 * has two specific effects now, ignore non-existent files and force
44 * file removal.
45 */
411a0820 46int
ae80f4a2 47main(argc, argv)
8161a03a 48 int argc;
411a0820 49 char *argv[];
ae80f4a2 50{
7f9ef9cc 51 int ch, rflag;
ae80f4a2 52
60f2fa5e 53 Pflag = rflag = 0;
815897e8 54 while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
7f9ef9cc
KB
55 switch(ch) {
56 case 'd':
57 dflag = 1;
58 break;
8161a03a 59 case 'f':
7f9ef9cc
KB
60 fflag = 1;
61 iflag = 0;
d1e4967d 62 break;
8161a03a 63 case 'i':
7f9ef9cc
KB
64 fflag = 0;
65 iflag = 1;
8161a03a 66 break;
60f2fa5e
KB
67 case 'P':
68 Pflag = 1;
69 break;
8161a03a 70 case 'R':
60f2fa5e 71 case 'r': /* Compatibility. */
7f9ef9cc 72 rflag = 1;
8161a03a 73 break;
88edbcd4
JSP
74 case 'W':
75 Wflag = 1;
76 break;
8161a03a
KB
77 case '?':
78 default:
79 usage();
80 }
7f9ef9cc
KB
81 argc -= optind;
82 argv += optind;
ca02483b 83
7f9ef9cc 84 if (argc < 1)
8161a03a 85 usage();
ae80f4a2 86
7f9ef9cc 87 checkdot(argv);
d1e4967d 88
815897e8
KB
89 if (*argv) {
90 stdin_ok = isatty(STDIN_FILENO);
91
92 if (rflag)
93 rm_tree(argv);
94 else
95 rm_file(argv);
96 }
7f9ef9cc 97
411a0820 98 exit (eval);
7f9ef9cc
KB
99}
100
411a0820 101void
60f2fa5e 102rm_tree(argv)
7f9ef9cc
KB
103 char **argv;
104{
706c0358
JSP
105 FTS *fts;
106 FTSENT *p;
107 int needstat;
88edbcd4 108 int flags;
7f9ef9cc
KB
109
110 /*
111 * Remove a file hierarchy. If forcing removal (-f), or interactive
112 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
113 */
114 needstat = !fflag && !iflag && stdin_ok;
115
116 /*
117 * If the -i option is specified, the user can skip on the pre-order
118 * visit. The fts_number field flags skipped directories.
119 */
120#define SKIPPED 1
121
88edbcd4
JSP
122 flags = FTS_PHYSICAL;
123 if (!needstat)
124 flags |= FTS_NOSTAT;
125 if (Wflag)
126 flags |= FTS_WHITEOUT;
127 if (!(fts = fts_open(argv, flags, (int (*)())NULL)))
411a0820 128 err(1, NULL);
5fa310b5 129 while ((p = fts_read(fts)) != NULL) {
411a0820 130 switch (p->fts_info) {
7f9ef9cc 131 case FTS_DNR:
5fa310b5
KB
132 if (!fflag || p->fts_errno != ENOENT) {
133 warnx("%s: %s",
134 p->fts_path, strerror(p->fts_errno));
d6a1ced2
KB
135 eval = 1;
136 }
137 continue;
7f9ef9cc 138 case FTS_ERR:
5fa310b5 139 errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
7f9ef9cc 140 case FTS_NS:
411a0820
KB
141 /*
142 * FTS_NS: assume that if can't stat the file, it
143 * can't be unlinked.
144 */
7f9ef9cc 145 if (!needstat)
d1e4967d 146 break;
5fa310b5
KB
147 if (!fflag || p->fts_errno != ENOENT) {
148 warnx("%s: %s",
149 p->fts_path, strerror(p->fts_errno));
411a0820
KB
150 eval = 1;
151 }
7f9ef9cc 152 continue;
7f9ef9cc 153 case FTS_D:
411a0820 154 /* Pre-order: give user chance to skip. */
815897e8 155 if (!fflag && !check(p->fts_path, p->fts_accpath,
834beb60 156 p->fts_statp)) {
7f9ef9cc
KB
157 (void)fts_set(fts, p, FTS_SKIP);
158 p->fts_number = SKIPPED;
d1e4967d 159 }
7f9ef9cc 160 continue;
7f9ef9cc 161 case FTS_DP:
411a0820 162 /* Post-order: see if user skipped. */
7f9ef9cc
KB
163 if (p->fts_number == SKIPPED)
164 continue;
165 break;
815897e8
KB
166 default:
167 if (!fflag &&
168 !check(p->fts_path, p->fts_accpath, p->fts_statp))
169 continue;
d1e4967d 170 }
7f9ef9cc
KB
171
172 /*
173 * If we can't read or search the directory, may still be
174 * able to remove it. Don't print out the un{read,search}able
175 * message unless the remove fails.
176 */
88edbcd4
JSP
177 switch (p->fts_info) {
178 case FTS_DP:
179 case FTS_DNR:
815897e8 180 if (!rmdir(p->fts_accpath) || fflag && errno == ENOENT)
7f9ef9cc 181 continue;
88edbcd4
JSP
182 break;
183
184 case FTS_W:
607ed6e9 185 if (!undelete(p->fts_accpath) ||
88edbcd4
JSP
186 fflag && errno == ENOENT)
187 continue;
188 break;
189
190 default:
60f2fa5e
KB
191 if (Pflag)
192 rm_overwrite(p->fts_accpath, NULL);
193 if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
194 continue;
195 }
411a0820
KB
196 warn("%s", p->fts_path);
197 eval = 1;
ae80f4a2 198 }
5fa310b5
KB
199 if (errno)
200 err(1, "fts_read");
7f9ef9cc
KB
201}
202
411a0820 203void
60f2fa5e 204rm_file(argv)
7f9ef9cc
KB
205 char **argv;
206{
7f9ef9cc 207 struct stat sb;
815897e8 208 int rval;
60f2fa5e 209 char *f;
ae80f4a2 210
7f9ef9cc
KB
211 /*
212 * Remove a file. POSIX 1003.2 states that, by default, attempting
213 * to remove a directory is an error, so must always stat the file.
214 */
5fa310b5 215 while ((f = *argv++) != NULL) {
7f9ef9cc
KB
216 /* Assume if can't stat the file, can't unlink it. */
217 if (lstat(f, &sb)) {
88edbcd4
JSP
218 if (Wflag) {
219 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
220 } else {
221 if (!fflag || errno != ENOENT) {
222 warn("%s", f);
223 eval = 1;
224 }
225 continue;
411a0820 226 }
88edbcd4
JSP
227 } else if (Wflag) {
228 warnx("%s: %s", f, strerror(EEXIST));
229 eval = 1;
7f9ef9cc 230 continue;
ae80f4a2 231 }
88edbcd4 232
815897e8 233 if (S_ISDIR(sb.st_mode) && !dflag) {
411a0820
KB
234 warnx("%s: is a directory", f);
235 eval = 1;
7f9ef9cc 236 continue;
b2704a87 237 }
88edbcd4 238 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
7f9ef9cc 239 continue;
88edbcd4 240 if (S_ISWHT(sb.st_mode))
607ed6e9 241 rval = undelete(f);
88edbcd4 242 else if (S_ISDIR(sb.st_mode))
60f2fa5e
KB
243 rval = rmdir(f);
244 else {
245 if (Pflag)
246 rm_overwrite(f, &sb);
247 rval = unlink(f);
248 }
249 if (rval && (!fflag || errno != ENOENT)) {
411a0820
KB
250 warn("%s", f);
251 eval = 1;
252 }
ae80f4a2
BJ
253 }
254}
255
60f2fa5e
KB
256/*
257 * rm_overwrite --
258 * Overwrite the file 3 times with varying bit patterns.
259 *
260 * XXX
261 * This is a cheap way to *really* delete files. Note that only regular
262 * files are deleted, directories (and therefore names) will remain.
263 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
264 * System V file system). In a logging file system, you'll have to have
265 * kernel support.
266 */
267void
268rm_overwrite(file, sbp)
269 char *file;
270 struct stat *sbp;
271{
272 struct stat sb;
273 off_t len;
274 int fd, wlen;
275 char buf[8 * 1024];
276
277 fd = -1;
278 if (sbp == NULL) {
279 if (lstat(file, &sb))
280 goto err;
281 sbp = &sb;
282 }
283 if (!S_ISREG(sbp->st_mode))
284 return;
285 if ((fd = open(file, O_WRONLY, 0)) == -1)
286 goto err;
287
288#define PASS(byte) { \
289 memset(buf, byte, sizeof(buf)); \
290 for (len = sbp->st_size; len > 0; len -= wlen) { \
291 wlen = len < sizeof(buf) ? len : sizeof(buf); \
292 if (write(fd, buf, wlen) != wlen) \
293 goto err; \
294 } \
295}
296 PASS(0xff);
297 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
298 goto err;
299 PASS(0x00);
300 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
301 goto err;
302 PASS(0xff);
303 if (!fsync(fd) && !close(fd))
304 return;
305
306err: eval = 1;
307 warn("%s", file);
308}
309
310
411a0820 311int
7f9ef9cc
KB
312check(path, name, sp)
313 char *path, *name;
314 struct stat *sp;
ae80f4a2 315{
706c0358 316 int ch, first;
411a0820 317 char modep[15];
ae80f4a2 318
7f9ef9cc
KB
319 /* Check -i first. */
320 if (iflag)
321 (void)fprintf(stderr, "remove %s? ", path);
322 else {
323 /*
324 * If it's not a symbolic link and it's unwritable and we're
325 * talking to a terminal, ask. Symbolic links are excluded
91c2220f
KB
326 * because their permissions are meaningless. Check stdin_ok
327 * first because we may not have stat'ed the file.
7f9ef9cc 328 */
91c2220f 329 if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK))
411a0820 330 return (1);
7f9ef9cc
KB
331 strmode(sp->st_mode, modep);
332 (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
333 modep + 1, modep[9] == ' ' ? "" : " ",
334 user_from_uid(sp->st_uid, 0),
335 group_from_gid(sp->st_gid, 0), path);
336 }
337 (void)fflush(stderr);
338
339 first = ch = getchar();
340 while (ch != '\n' && ch != EOF)
341 ch = getchar();
411a0820 342 return (first == 'y');
d1e4967d
RC
343}
344
7f9ef9cc 345#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
411a0820 346void
7f9ef9cc
KB
347checkdot(argv)
348 char **argv;
349{
706c0358 350 char *p, **save, **t;
7f9ef9cc
KB
351 int complained;
352
353 complained = 0;
354 for (t = argv; *t;) {
5fa310b5 355 if ((p = strrchr(*t, '/')) != NULL)
7f9ef9cc
KB
356 ++p;
357 else
358 p = *t;
359 if (ISDOT(p)) {
360 if (!complained++)
411a0820
KB
361 warnx("\".\" and \"..\" may not be removed");
362 eval = 1;
815897e8
KB
363 for (save = t; (t[0] = t[1]) != NULL; ++t)
364 continue;
7f9ef9cc
KB
365 t = save;
366 } else
367 ++t;
368 }
369}
370
411a0820 371void
8161a03a
KB
372usage()
373{
706c0358 374
815897e8 375 (void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n");
8161a03a
KB
376 exit(1);
377}