1 /* $NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $ */
4 * Copyright (c) 1990, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/cdefs.h>
38 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
44 static char sccsid
[] = "@(#)rm.c 8.8 (Berkeley) 4/27/95";
46 __RCSID("$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $");
50 #include <sys/types.h>
65 int dflag
, eval
, fflag
, iflag
, Pflag
, Wflag
, stdin_ok
;
67 int check
__P((char *, char *, struct stat
*));
68 void checkdot
__P((char **));
69 void rm_file
__P((char **));
70 void rm_overwrite
__P((char *, struct stat
*));
71 void rm_tree
__P((char **));
72 void usage
__P((void));
73 int main
__P((int, char *[]));
75 #ifdef __APPLE__ /* We're missing this prototype */
76 int undelete
__P((char *));
80 * For the sake of the `-f' flag, check whether an error number indicates the
81 * failure of an operation due to an non-existent file, either per se (ENOENT)
82 * or because its filename argument was illegal (ENAMETOOLONG, ENOTDIR).
84 #define NONEXISTENT(x) \
85 ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR)
89 * This rm is different from historic rm's, but is expected to match
90 * POSIX 1003.2 behavior. The most visible difference is that -f
91 * has two specific effects now, ignore non-existent files and force
101 (void)setlocale(LC_ALL
, "");
104 while ((ch
= getopt(argc
, argv
, "dfiPRrW")) != -1)
121 case 'r': /* Compatibility. */
140 stdin_ok
= isatty(STDIN_FILENO
);
162 * Remove a file hierarchy. If forcing removal (-f), or interactive
163 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
165 needstat
= !fflag
&& !iflag
&& stdin_ok
;
168 * If the -i option is specified, the user can skip on the pre-order
169 * visit. The fts_number field flags skipped directories.
173 flags
= FTS_PHYSICAL
;
177 flags
|= FTS_WHITEOUT
;
178 if (!(fts
= fts_open(argv
, flags
,
179 (int (*) __P((const FTSENT
**, const FTSENT
**)))NULL
)))
181 while ((p
= fts_read(fts
)) != NULL
) {
182 switch (p
->fts_info
) {
184 if (!fflag
|| p
->fts_errno
!= ENOENT
) {
186 p
->fts_path
, strerror(p
->fts_errno
));
191 errx(1, "%s: %s", p
->fts_path
, strerror(p
->fts_errno
));
195 * FTS_NS: assume that if can't stat the file, it
200 if (!fflag
|| !NONEXISTENT(p
->fts_errno
)) {
202 p
->fts_path
, strerror(p
->fts_errno
));
207 /* Pre-order: give user chance to skip. */
208 if (!fflag
&& !check(p
->fts_path
, p
->fts_accpath
,
210 (void)fts_set(fts
, p
, FTS_SKIP
);
211 p
->fts_number
= SKIPPED
;
215 /* Post-order: see if user skipped. */
216 if (p
->fts_number
== SKIPPED
)
221 !check(p
->fts_path
, p
->fts_accpath
, p
->fts_statp
))
226 * If we can't read or search the directory, may still be
227 * able to remove it. Don't print out the un{read,search}able
228 * message unless the remove fails.
230 switch (p
->fts_info
) {
233 if (!rmdir(p
->fts_accpath
) ||
234 (fflag
&& errno
== ENOENT
))
239 if (!undelete(p
->fts_accpath
) ||
240 (fflag
&& errno
== ENOENT
))
246 rm_overwrite(p
->fts_accpath
, NULL
);
247 if (!unlink(p
->fts_accpath
) ||
248 (fflag
&& NONEXISTENT(errno
)))
251 warn("%s", p
->fts_path
);
267 * Remove a file. POSIX 1003.2 states that, by default, attempting
268 * to remove a directory is an error, so must always stat the file.
270 while ((f
= *argv
++) != NULL
) {
271 /* Assume if can't stat the file, can't unlink it. */
274 sb
.st_mode
= S_IFWHT
|S_IWUSR
|S_IRUSR
;
276 if (!fflag
|| !NONEXISTENT(errno
)) {
283 warnx("%s: %s", f
, strerror(EEXIST
));
288 if (S_ISDIR(sb
.st_mode
) && !dflag
) {
289 warnx("%s: is a directory", f
);
293 if (!fflag
&& !S_ISWHT(sb
.st_mode
) && !check(f
, f
, &sb
))
295 if (S_ISWHT(sb
.st_mode
))
297 else if (S_ISDIR(sb
.st_mode
))
301 rm_overwrite(f
, &sb
);
304 if (rval
&& (!fflag
|| !NONEXISTENT(errno
))) {
313 * Overwrite the file 3 times with varying bit patterns.
316 * This is a cheap way to *really* delete files. Note that only regular
317 * files are deleted, directories (and therefore names) will remain.
318 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
319 * System V file system). In a logging file system, you'll have to have
323 rm_overwrite(file
, sbp
)
334 if (lstat(file
, &sb
))
338 if (!S_ISREG(sbp
->st_mode
))
340 if ((fd
= open(file
, O_WRONLY
, 0)) == -1)
343 #define PASS(byte) { \
344 memset(buf, byte, sizeof(buf)); \
345 for (len = sbp->st_size; len > 0; len -= wlen) { \
346 wlen = len < sizeof(buf) ? len : sizeof(buf); \
347 if (write(fd, buf, wlen) != wlen) \
352 if (fsync(fd
) || lseek(fd
, (off_t
)0, SEEK_SET
))
355 if (fsync(fd
) || lseek(fd
, (off_t
)0, SEEK_SET
))
358 if (!fsync(fd
) && !close(fd
))
367 check(path
, name
, sp
)
374 /* Check -i first. */
376 (void)fprintf(stderr
, "remove %s? ", path
);
379 * If it's not a symbolic link and it's unwritable and we're
380 * talking to a terminal, ask. Symbolic links are excluded
381 * because their permissions are meaningless. Check stdin_ok
382 * first because we may not have stat'ed the file.
384 if (!stdin_ok
|| S_ISLNK(sp
->st_mode
) || !access(name
, W_OK
))
386 strmode(sp
->st_mode
, modep
);
387 (void)fprintf(stderr
, "override %s%s%s/%s for %s? ",
388 modep
+ 1, modep
[9] == ' ' ?
"" : " ",
389 user_from_uid(sp
->st_uid
, 0),
390 group_from_gid(sp
->st_gid
, 0), path
);
392 (void)fflush(stderr
);
394 first
= ch
= getchar();
395 while (ch
!= '\n' && ch
!= EOF
)
397 return (first
== 'y' || first
== 'Y');
401 * POSIX.2 requires that if "." or ".." are specified as the basename
402 * portion of an operand, a diagnostic message be written to standard
403 * error and nothing more be done with such operands.
405 * Since POSIX.2 defines basename as the final portion of a path after
406 * trailing slashes have been removed, we'll remove them here.
408 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
413 char *p
, **save
, **t
;
417 for (t
= argv
; *t
;) {
418 /* strip trailing slashes */
419 p
= strrchr (*t
, '\0');
420 while (--p
> *t
&& *p
== '/')
423 /* extract basename */
424 if ((p
= strrchr(*t
, '/')) != NULL
)
431 warnx("\".\" and \"..\" may not be removed");
433 for (save
= t
; (t
[0] = t
[1]) != NULL
; ++t
)
445 (void)fprintf(stderr
, "usage: rm [-dfiPRrW] file ...\n");