VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/rm.c@ 1183

Last change on this file since 1183 was 1183, checked in by bird, 17 years ago

Added --version and --help to all builtins.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/*-
2 * Copyright (c) 1990, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char copyright[] =
33"@(#) Copyright (c) 1990, 1993, 1994\n\
34 The Regents of the University of California. All rights reserved.\n";
35#endif /* not lint */
36
37#ifndef lint
38static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94";
39#endif /* not lint */
40#include <sys/cdefs.h>
41//__FBSDID("$FreeBSD: src/bin/rm/rm.c,v 1.47 2004/04/06 20:06:50 markm Exp $");
42#endif
43#ifdef __EMX__
44# define DO_RMTREE
45#endif
46
47#include <sys/stat.h>
48#ifndef _MSC_VER
49# include <sys/param.h>
50# include <sys/mount.h>
51#endif
52
53#include "err.h"
54#include <errno.h>
55#include <fcntl.h>
56#ifdef DO_RMTREE
57# include <fts.h>
58#endif
59#include <grp.h>
60#include <pwd.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <sysexits.h>
65#include <unistd.h>
66
67#ifdef __sun__
68# include "getopt.h"
69#endif
70#ifdef _MSC_VER
71# include "mscfakes.h"
72#endif
73
74#include "kmkbuiltin.h"
75
76
77#ifdef __EMX__
78#undef S_IFWHT
79#undef S_ISWHT
80#endif
81#ifndef S_IFWHT
82#define S_IFWHT 0
83#define S_ISWHT(s) 0
84#define undelete(s) (-1)
85#endif
86
87#if !defined(__FreeBSD__) && !defined(__APPLE__)
88extern void strmode(mode_t mode, char *p);
89#endif
90
91static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
92static uid_t uid;
93
94static char *argv0;
95
96static struct option long_options[] =
97{
98 { "help", no_argument, 0, 261 },
99 { "version", no_argument, 0, 262 },
100 { 0, 0, 0, 0 },
101};
102
103
104static int check(char *, char *, struct stat *);
105static void checkdot(char **);
106static void rm_file(char **);
107static int rm_overwrite(char *, struct stat *);
108#ifdef DO_RMTREE
109static void rm_tree(char **);
110#endif
111static int usage(FILE *);
112
113
114
115/*
116 * rm --
117 * This rm is different from historic rm's, but is expected to match
118 * POSIX 1003.2 behavior. The most visible difference is that -f
119 * has two specific effects now, ignore non-existent files and force
120 * file removal.
121 */
122int
123kmk_builtin_rm(int argc, char *argv[], char **envp)
124{
125 int ch, rflag;
126
127 /* reinitialize globals */
128 argv0 = argv[0];
129 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0;
130 uid = 0;
131
132 /* kmk: reset getopt and set program name. */
133 g_progname = argv[0];
134 opterr = 1;
135 optarg = NULL;
136 optopt = 0;
137 optind = 0; /* init */
138
139 Pflag = rflag = 0;
140 while ((ch = getopt_long(argc, argv, "dfiPRrvW", long_options, NULL)) != -1)
141 switch(ch) {
142 case 'd':
143 dflag = 1;
144 break;
145 case 'f':
146 fflag = 1;
147 iflag = 0;
148 break;
149 case 'i':
150 fflag = 0;
151 iflag = 1;
152 break;
153 case 'P':
154 Pflag = 1;
155 break;
156 case 'R':
157 case 'r': /* Compatibility. */
158#ifdef DO_RMTREE
159 rflag = 1;
160 break;
161#else
162 errno = EINVAL;
163 return err(1, "Recursion is not supported!");
164#endif
165 case 'v':
166 vflag = 1;
167 break;
168#ifdef FTS_WHITEOUT
169 case 'W':
170 Wflag = 1;
171 break;
172#endif
173 case 261:
174 usage(stdout);
175 return 0;
176 case 262:
177 return kbuild_version(argv[0]);
178 case '?':
179 default:
180 return usage(stderr);
181 }
182 argc -= optind;
183 argv += optind;
184
185 if (argc < 1) {
186 if (fflag)
187 return (0);
188 return usage(stderr);
189 }
190
191 checkdot(argv);
192 uid = geteuid();
193
194 if (*argv) {
195 stdin_ok = isatty(STDIN_FILENO);
196#ifdef DO_RMTREE
197 if (rflag)
198 rm_tree(argv);
199 else
200#endif
201 rm_file(argv);
202 }
203
204 return eval;
205}
206
207#ifdef DO_RMTREE
208static void
209rm_tree(char **argv)
210{
211 FTS *fts;
212 FTSENT *p;
213 int needstat;
214 int flags;
215 int rval;
216
217 /*
218 * Remove a file hierarchy. If forcing removal (-f), or interactive
219 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
220 */
221 needstat = !uid || (!fflag && !iflag && stdin_ok);
222
223 /*
224 * If the -i option is specified, the user can skip on the pre-order
225 * visit. The fts_number field flags skipped directories.
226 */
227#define SKIPPED 1
228
229 flags = FTS_PHYSICAL;
230 if (!needstat)
231 flags |= FTS_NOSTAT;
232#ifdef FTS_WHITEOUT
233 if (Wflag)
234 flags |= FTS_WHITEOUT;
235#endif
236 if (!(fts = fts_open(argv, flags, NULL))) {
237 eval = err(1, "fts_open");
238 return;
239 }
240 while ((p = fts_read(fts)) != NULL) {
241 switch (p->fts_info) {
242 case FTS_DNR:
243 if (!fflag || p->fts_errno != ENOENT) {
244 fprintf(stderr, "%s: %s: %s\n",
245 argv0, p->fts_path, strerror(p->fts_errno));
246 eval = 1;
247 }
248 continue;
249 case FTS_ERR:
250 eval = errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
251 return;
252 case FTS_NS:
253 /*
254 * Assume that since fts_read() couldn't stat the
255 * file, it can't be unlinked.
256 */
257 if (!needstat)
258 break;
259 if (!fflag || p->fts_errno != ENOENT) {
260 fprintf(stderr, "%s: %s: %s\n",
261 argv0, p->fts_path, strerror(p->fts_errno));
262 eval = 1;
263 }
264 continue;
265 case FTS_D:
266 /* Pre-order: give user chance to skip. */
267 if (!fflag && !check(p->fts_path, p->fts_accpath,
268 p->fts_statp)) {
269 (void)fts_set(fts, p, FTS_SKIP);
270 p->fts_number = SKIPPED;
271 }
272#ifdef UF_APPEND
273 else if (!uid &&
274 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
275 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
276 chflags(p->fts_accpath,
277 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
278 goto err;
279#endif
280 continue;
281 case FTS_DP:
282 /* Post-order: see if user skipped. */
283 if (p->fts_number == SKIPPED)
284 continue;
285 break;
286 default:
287 if (!fflag &&
288 !check(p->fts_path, p->fts_accpath, p->fts_statp))
289 continue;
290 }
291
292 rval = 0;
293#ifdef UF_APPEND
294 if (!uid &&
295 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
296 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
297 rval = chflags(p->fts_accpath,
298 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
299#endif
300 if (rval == 0) {
301 /*
302 * If we can't read or search the directory, may still be
303 * able to remove it. Don't print out the un{read,search}able
304 * message unless the remove fails.
305 */
306 switch (p->fts_info) {
307 case FTS_DP:
308 case FTS_DNR:
309 rval = rmdir(p->fts_accpath);
310 if (rval == 0 || (fflag && errno == ENOENT)) {
311 if (rval == 0 && vflag)
312 (void)printf("%s\n",
313 p->fts_path);
314 continue;
315 }
316 break;
317
318#ifdef FTS_W
319 case FTS_W:
320 rval = undelete(p->fts_accpath);
321 if (rval == 0 && (fflag && errno == ENOENT)) {
322 if (vflag)
323 (void)printf("%s\n",
324 p->fts_path);
325 continue;
326 }
327 break;
328#endif
329
330 case FTS_NS:
331 /*
332 * Assume that since fts_read() couldn't stat
333 * the file, it can't be unlinked.
334 */
335 if (fflag)
336 continue;
337 /* FALLTHROUGH */
338 default:
339 if (Pflag)
340 if (!rm_overwrite(p->fts_accpath, NULL))
341 continue;
342 rval = unlink(p->fts_accpath);
343#ifdef _MSC_VER
344 if (rval != 0) {
345 chmod(p->fts_accpath, 0777);
346 rval = unlink(p->fts_accpath);
347 }
348#endif
349
350 if (rval == 0 || (fflag && errno == ENOENT)) {
351 if (rval == 0 && vflag)
352 (void)printf("%s\n",
353 p->fts_path);
354 continue;
355 }
356 }
357 }
358err:
359 fprintf(stderr, "%s: %s: %s\n", argv0, p->fts_path, strerror(errno));
360 eval = 1;
361 }
362 if (errno) {
363 fprintf(stderr, "%s: fts_read: %s\n", argv0, strerror(errno));
364 eval = 1;
365 }
366}
367#endif /* DO_RMTREE */
368
369static void
370rm_file(char **argv)
371{
372 struct stat sb;
373 int rval;
374 char *f;
375
376 /*
377 * Remove a file. POSIX 1003.2 states that, by default, attempting
378 * to remove a directory is an error, so must always stat the file.
379 */
380 while ((f = *argv++) != NULL) {
381 /* Assume if can't stat the file, can't unlink it. */
382 if (lstat(f, &sb)) {
383#ifdef FTS_WHITEOUT
384 if (Wflag) {
385 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
386 } else {
387#else
388 {
389#endif
390 if (!fflag || errno != ENOENT) {
391 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
392 eval = 1;
393 }
394 continue;
395 }
396#ifdef FTS_WHITEOUT
397 } else if (Wflag) {
398 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(EEXIST));
399 eval = 1;
400 continue;
401#endif
402 }
403
404 if (S_ISDIR(sb.st_mode) && !dflag) {
405 fprintf(stderr, "%s: %s: is a directory\n", argv0, f);
406 eval = 1;
407 continue;
408 }
409 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
410 continue;
411 rval = 0;
412#ifdef UF_APPEND
413 if (!uid &&
414 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
415 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
416 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
417#endif
418 if (rval == 0) {
419 if (S_ISWHT(sb.st_mode))
420 rval = undelete(f);
421 else if (S_ISDIR(sb.st_mode))
422 rval = rmdir(f);
423 else {
424 if (Pflag)
425 if (!rm_overwrite(f, &sb))
426 continue;
427 rval = unlink(f);
428#ifdef _MSC_VER
429 if (rval != 0) {
430 chmod(f, 0777);
431 rval = unlink(f);
432 }
433#endif
434 }
435 }
436 if (rval && (!fflag || errno != ENOENT)) {
437 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
438 eval = 1;
439 }
440 if (vflag && rval == 0)
441 (void)printf("%s\n", f);
442 }
443}
444
445/*
446 * rm_overwrite --
447 * Overwrite the file 3 times with varying bit patterns.
448 *
449 * XXX
450 * This is a cheap way to *really* delete files. Note that only regular
451 * files are deleted, directories (and therefore names) will remain.
452 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
453 * System V file system). In a logging file system, you'll have to have
454 * kernel support.
455 */
456static int
457rm_overwrite(char *file, struct stat *sbp)
458{
459 struct stat sb;
460#ifdef HAVE_FSTATFS
461 struct statfs fsb;
462#endif
463 off_t len;
464 int bsize, fd, wlen;
465 char *buf = NULL;
466
467 fd = -1;
468 if (sbp == NULL) {
469 if (lstat(file, &sb))
470 goto err;
471 sbp = &sb;
472 }
473 if (!S_ISREG(sbp->st_mode))
474 return (1);
475 if ((fd = open(file, O_WRONLY, 0)) == -1)
476 goto err;
477#ifdef HAVE_FSTATFS
478 if (fstatfs(fd, &fsb) == -1)
479 goto err;
480 bsize = MAX(fsb.f_iosize, 1024);
481#elif defined(HAVE_ST_BLKSIZE)
482 bsize = MAX(sb.st_blksize, 1024);
483#else
484 bsize = 1024;
485#endif
486 if ((buf = malloc(bsize)) == NULL)
487 exit(err(1, "%s: malloc", file));
488
489#define PASS(byte) { \
490 memset(buf, byte, bsize); \
491 for (len = sbp->st_size; len > 0; len -= wlen) { \
492 wlen = len < bsize ? len : bsize; \
493 if (write(fd, buf, wlen) != wlen) \
494 goto err; \
495 } \
496}
497 PASS(0xff);
498 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
499 goto err;
500 PASS(0x00);
501 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
502 goto err;
503 PASS(0xff);
504 if (!fsync(fd) && !close(fd)) {
505 free(buf);
506 return (1);
507 }
508
509err: eval = 1;
510 if (buf)
511 free(buf);
512 if (fd != -1)
513 close(fd);
514 fprintf(stderr, "%s: %s: %s\n", argv0, file, strerror(errno));
515 return (0);
516}
517
518
519static int
520check(char *path, char *name, struct stat *sp)
521{
522 int ch, first;
523 char modep[15], *flagsp;
524
525 /* Check -i first. */
526 if (iflag)
527 (void)fprintf(stderr, "remove %s? ", path);
528 else {
529 /*
530 * If it's not a symbolic link and it's unwritable and we're
531 * talking to a terminal, ask. Symbolic links are excluded
532 * because their permissions are meaningless. Check stdin_ok
533 * first because we may not have stat'ed the file.
534 * Also skip this check if the -P option was specified because
535 * we will not be able to overwrite file contents and will
536 * barf later.
537 */
538 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
539 (!access(name, W_OK) &&
540#ifdef SF_APPEND
541 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
542 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))
543#else
544 1)
545#endif
546 )
547 return (1);
548 strmode(sp->st_mode, modep);
549#ifdef SF_APPEND
550 if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
551 exit(err(1, "fflagstostr"));
552 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
553 modep + 1, modep[9] == ' ' ? "" : " ",
554 user_from_uid(sp->st_uid, 0),
555 group_from_gid(sp->st_gid, 0),
556 *flagsp ? flagsp : "", *flagsp ? " " : "",
557 path);
558 free(flagsp);
559#else
560 (void)flagsp;
561 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
562 modep + 1, modep[9] == ' ' ? "" : " ",
563 sp->st_uid, sp->st_gid, path);
564#endif
565 }
566 (void)fflush(stderr);
567
568 first = ch = getchar();
569 while (ch != '\n' && ch != EOF)
570 ch = getchar();
571 return (first == 'y' || first == 'Y');
572}
573
574#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
575static void
576checkdot(char **argv)
577{
578 char *p, **save, **t;
579 int complained;
580
581 complained = 0;
582 for (t = argv; *t;) {
583 if ((p = strrchr(*t, '/')) != NULL)
584 ++p;
585 else
586 p = *t;
587 if (ISDOT(p)) {
588 if (!complained++)
589 fprintf(stderr, "%s: \".\" and \"..\" may not be removed\n", argv0);
590 eval = 1;
591 for (save = t; (t[0] = t[1]) != NULL; ++t)
592 continue;
593 t = save;
594 } else
595 ++t;
596 }
597}
598
599static int
600usage(FILE *pf)
601{
602 fprintf(pf, "usage: %s [-f | -i] [-dPRrvW] file ...\n"
603 " or: %s --help\n"
604 " or: %s --version\n",
605 g_progname, g_progname, g_progname);
606 return EX_USAGE;
607}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette