VirtualBox

source: kBuild/trunk/src/gmakenew/kmkbuiltin/rm.c@ 942

Last change on this file since 942 was 942, checked in by bird, 18 years ago

Always use the GNU make getopt stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 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#ifdef __EMX__
75#undef S_IFWHT
76#undef S_ISWHT
77#endif
78#ifndef S_IFWHT
79#define S_IFWHT 0
80#define S_ISWHT(s) 0
81#define undelete(s) (-1)
82#endif
83
84#if !defined(__FreeBSD__) && !defined(__APPLE__)
85extern void strmode(mode_t mode, char *p);
86#endif
87
88static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
89static uid_t uid;
90
91static char *argv0;
92
93static int check(char *, char *, struct stat *);
94static void checkdot(char **);
95static void rm_file(char **);
96static int rm_overwrite(char *, struct stat *);
97#ifdef DO_RMTREE
98static void rm_tree(char **);
99#endif
100static int usage(void);
101
102/*
103 * rm --
104 * This rm is different from historic rm's, but is expected to match
105 * POSIX 1003.2 behavior. The most visible difference is that -f
106 * has two specific effects now, ignore non-existent files and force
107 * file removal.
108 */
109int
110kmk_builtin_rm(int argc, char *argv[])
111{
112 int ch, rflag;
113 char *p;
114
115 /* reinitialize globals */
116 argv0 = argv[0];
117 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0;
118 uid = 0;
119
120 /* kmk: reset getopt and set program name. */
121 g_progname = argv[0];
122 opterr = 1;
123 optarg = NULL;
124 optopt = 0;
125 optind = 0; /* init */
126
127#if 0 /* kmk: we don't need this */
128 /*
129 * Test for the special case where the utility is called as
130 * "unlink", for which the functionality provided is greatly
131 * simplified.
132 */
133 if ((p = rindex(argv[0], '/')) == NULL)
134 p = argv[0];
135 else
136 ++p;
137 if (strcmp(p, "unlink") == 0) {
138 while (getopt(argc, argv, "") != -1)
139 return usage();
140 argc -= optind;
141 argv += optind;
142 if (argc != 1)
143 return usage();
144 rm_file(&argv[0]);
145 return eval;
146 }
147#else
148 (void)p;
149#endif
150 Pflag = rflag = 0;
151 while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1)
152 switch(ch) {
153 case 'd':
154 dflag = 1;
155 break;
156 case 'f':
157 fflag = 1;
158 iflag = 0;
159 break;
160 case 'i':
161 fflag = 0;
162 iflag = 1;
163 break;
164 case 'P':
165 Pflag = 1;
166 break;
167 case 'R':
168 case 'r': /* Compatibility. */
169#ifdef DO_RMTREE
170 rflag = 1;
171 break;
172#else
173 errno = EINVAL;
174 return err(1, "Recursion is not supported!");
175#endif
176 case 'v':
177 vflag = 1;
178 break;
179#ifdef FTS_WHITEOUT
180 case 'W':
181 Wflag = 1;
182 break;
183#endif
184 default:
185 return usage();
186 }
187 argc -= optind;
188 argv += optind;
189
190 if (argc < 1) {
191 if (fflag)
192 return (0);
193 return usage();
194 }
195
196 checkdot(argv);
197 uid = geteuid();
198
199 if (*argv) {
200 stdin_ok = isatty(STDIN_FILENO);
201#ifdef DO_RMTREE
202 if (rflag)
203 rm_tree(argv);
204 else
205#endif
206 rm_file(argv);
207 }
208
209 return eval;
210}
211
212#ifdef DO_RMTREE
213static void
214rm_tree(char **argv)
215{
216 FTS *fts;
217 FTSENT *p;
218 int needstat;
219 int flags;
220 int rval;
221
222 /*
223 * Remove a file hierarchy. If forcing removal (-f), or interactive
224 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
225 */
226 needstat = !uid || (!fflag && !iflag && stdin_ok);
227
228 /*
229 * If the -i option is specified, the user can skip on the pre-order
230 * visit. The fts_number field flags skipped directories.
231 */
232#define SKIPPED 1
233
234 flags = FTS_PHYSICAL;
235 if (!needstat)
236 flags |= FTS_NOSTAT;
237#ifdef FTS_WHITEOUT
238 if (Wflag)
239 flags |= FTS_WHITEOUT;
240#endif
241 if (!(fts = fts_open(argv, flags, NULL))) {
242 eval = err(1, "fts_open");
243 return;
244 }
245 while ((p = fts_read(fts)) != NULL) {
246 switch (p->fts_info) {
247 case FTS_DNR:
248 if (!fflag || p->fts_errno != ENOENT) {
249 fprintf(stderr, "%s: %s: %s\n",
250 argv0, p->fts_path, strerror(p->fts_errno));
251 eval = 1;
252 }
253 continue;
254 case FTS_ERR:
255 eval = errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
256 return;
257 case FTS_NS:
258 /*
259 * Assume that since fts_read() couldn't stat the
260 * file, it can't be unlinked.
261 */
262 if (!needstat)
263 break;
264 if (!fflag || p->fts_errno != ENOENT) {
265 fprintf(stderr, "%s: %s: %s\n",
266 argv0, p->fts_path, strerror(p->fts_errno));
267 eval = 1;
268 }
269 continue;
270 case FTS_D:
271 /* Pre-order: give user chance to skip. */
272 if (!fflag && !check(p->fts_path, p->fts_accpath,
273 p->fts_statp)) {
274 (void)fts_set(fts, p, FTS_SKIP);
275 p->fts_number = SKIPPED;
276 }
277#ifdef UF_APPEND
278 else if (!uid &&
279 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
280 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
281 chflags(p->fts_accpath,
282 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
283 goto err;
284#endif
285 continue;
286 case FTS_DP:
287 /* Post-order: see if user skipped. */
288 if (p->fts_number == SKIPPED)
289 continue;
290 break;
291 default:
292 if (!fflag &&
293 !check(p->fts_path, p->fts_accpath, p->fts_statp))
294 continue;
295 }
296
297 rval = 0;
298#ifdef UF_APPEND
299 if (!uid &&
300 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
301 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
302 rval = chflags(p->fts_accpath,
303 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
304#endif
305 if (rval == 0) {
306 /*
307 * If we can't read or search the directory, may still be
308 * able to remove it. Don't print out the un{read,search}able
309 * message unless the remove fails.
310 */
311 switch (p->fts_info) {
312 case FTS_DP:
313 case FTS_DNR:
314 rval = rmdir(p->fts_accpath);
315 if (rval == 0 || (fflag && errno == ENOENT)) {
316 if (rval == 0 && vflag)
317 (void)printf("%s\n",
318 p->fts_path);
319 continue;
320 }
321 break;
322
323#ifdef FTS_W
324 case FTS_W:
325 rval = undelete(p->fts_accpath);
326 if (rval == 0 && (fflag && errno == ENOENT)) {
327 if (vflag)
328 (void)printf("%s\n",
329 p->fts_path);
330 continue;
331 }
332 break;
333#endif
334
335 case FTS_NS:
336 /*
337 * Assume that since fts_read() couldn't stat
338 * the file, it can't be unlinked.
339 */
340 if (fflag)
341 continue;
342 /* FALLTHROUGH */
343 default:
344 if (Pflag)
345 if (!rm_overwrite(p->fts_accpath, NULL))
346 continue;
347 rval = unlink(p->fts_accpath);
348#ifdef _MSC_VER
349 if (rval != 0) {
350 chmod(p->fts_accpath, 0777);
351 rval = unlink(p->fts_accpath);
352 }
353#endif
354
355 if (rval == 0 || (fflag && errno == ENOENT)) {
356 if (rval == 0 && vflag)
357 (void)printf("%s\n",
358 p->fts_path);
359 continue;
360 }
361 }
362 }
363err:
364 fprintf(stderr, "%s: %s: %s\n", argv0, p->fts_path, strerror(errno));
365 eval = 1;
366 }
367 if (errno) {
368 fprintf(stderr, "%s: fts_read: %s\n", argv0, strerror(errno));
369 eval = 1;
370 }
371}
372#endif /* DO_RMTREE */
373
374static void
375rm_file(char **argv)
376{
377 struct stat sb;
378 int rval;
379 char *f;
380
381 /*
382 * Remove a file. POSIX 1003.2 states that, by default, attempting
383 * to remove a directory is an error, so must always stat the file.
384 */
385 while ((f = *argv++) != NULL) {
386 /* Assume if can't stat the file, can't unlink it. */
387 if (lstat(f, &sb)) {
388#ifdef FTS_WHITEOUT
389 if (Wflag) {
390 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
391 } else {
392#else
393 {
394#endif
395 if (!fflag || errno != ENOENT) {
396 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
397 eval = 1;
398 }
399 continue;
400 }
401#ifdef FTS_WHITEOUT
402 } else if (Wflag) {
403 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(EEXIST));
404 eval = 1;
405 continue;
406#endif
407 }
408
409 if (S_ISDIR(sb.st_mode) && !dflag) {
410 fprintf(stderr, "%s: %s: is a directory\n", argv0, f);
411 eval = 1;
412 continue;
413 }
414 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
415 continue;
416 rval = 0;
417#ifdef UF_APPEND
418 if (!uid &&
419 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
420 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
421 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
422#endif
423 if (rval == 0) {
424 if (S_ISWHT(sb.st_mode))
425 rval = undelete(f);
426 else if (S_ISDIR(sb.st_mode))
427 rval = rmdir(f);
428 else {
429 if (Pflag)
430 if (!rm_overwrite(f, &sb))
431 continue;
432 rval = unlink(f);
433#ifdef _MSC_VER
434 if (rval != 0) {
435 chmod(f, 0777);
436 rval = unlink(f);
437 }
438#endif
439 }
440 }
441 if (rval && (!fflag || errno != ENOENT)) {
442 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
443 eval = 1;
444 }
445 if (vflag && rval == 0)
446 (void)printf("%s\n", f);
447 }
448}
449
450/*
451 * rm_overwrite --
452 * Overwrite the file 3 times with varying bit patterns.
453 *
454 * XXX
455 * This is a cheap way to *really* delete files. Note that only regular
456 * files are deleted, directories (and therefore names) will remain.
457 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
458 * System V file system). In a logging file system, you'll have to have
459 * kernel support.
460 */
461static int
462rm_overwrite(char *file, struct stat *sbp)
463{
464 struct stat sb;
465#ifdef HAVE_FSTATFS
466 struct statfs fsb;
467#endif
468 off_t len;
469 int bsize, fd, wlen;
470 char *buf = NULL;
471
472 fd = -1;
473 if (sbp == NULL) {
474 if (lstat(file, &sb))
475 goto err;
476 sbp = &sb;
477 }
478 if (!S_ISREG(sbp->st_mode))
479 return (1);
480 if ((fd = open(file, O_WRONLY, 0)) == -1)
481 goto err;
482#ifdef HAVE_FSTATFS
483 if (fstatfs(fd, &fsb) == -1)
484 goto err;
485 bsize = MAX(fsb.f_iosize, 1024);
486#elif defined(HAVE_ST_BLKSIZE)
487 bsize = MAX(sb.st_blksize, 1024);
488#else
489 bsize = 1024;
490#endif
491 if ((buf = malloc(bsize)) == NULL)
492 exit(err(1, "%s: malloc", file));
493
494#define PASS(byte) { \
495 memset(buf, byte, bsize); \
496 for (len = sbp->st_size; len > 0; len -= wlen) { \
497 wlen = len < bsize ? len : bsize; \
498 if (write(fd, buf, wlen) != wlen) \
499 goto err; \
500 } \
501}
502 PASS(0xff);
503 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
504 goto err;
505 PASS(0x00);
506 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
507 goto err;
508 PASS(0xff);
509 if (!fsync(fd) && !close(fd)) {
510 free(buf);
511 return (1);
512 }
513
514err: eval = 1;
515 if (buf)
516 free(buf);
517 if (fd != -1)
518 close(fd);
519 fprintf(stderr, "%s: %s: %s\n", argv0, file, strerror(errno));
520 return (0);
521}
522
523
524static int
525check(char *path, char *name, struct stat *sp)
526{
527 int ch, first;
528 char modep[15], *flagsp;
529
530 /* Check -i first. */
531 if (iflag)
532 (void)fprintf(stderr, "remove %s? ", path);
533 else {
534 /*
535 * If it's not a symbolic link and it's unwritable and we're
536 * talking to a terminal, ask. Symbolic links are excluded
537 * because their permissions are meaningless. Check stdin_ok
538 * first because we may not have stat'ed the file.
539 * Also skip this check if the -P option was specified because
540 * we will not be able to overwrite file contents and will
541 * barf later.
542 */
543 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
544 (!access(name, W_OK) &&
545#ifdef SF_APPEND
546 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
547 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))
548#else
549 1)
550#endif
551 )
552 return (1);
553 strmode(sp->st_mode, modep);
554#ifdef SF_APPEND
555 if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
556 exit(err(1, "fflagstostr"));
557 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
558 modep + 1, modep[9] == ' ' ? "" : " ",
559 user_from_uid(sp->st_uid, 0),
560 group_from_gid(sp->st_gid, 0),
561 *flagsp ? flagsp : "", *flagsp ? " " : "",
562 path);
563 free(flagsp);
564#else
565 (void)flagsp;
566 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
567 modep + 1, modep[9] == ' ' ? "" : " ",
568 sp->st_uid, sp->st_gid, path);
569#endif
570 }
571 (void)fflush(stderr);
572
573 first = ch = getchar();
574 while (ch != '\n' && ch != EOF)
575 ch = getchar();
576 return (first == 'y' || first == 'Y');
577}
578
579#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
580static void
581checkdot(char **argv)
582{
583 char *p, **save, **t;
584 int complained;
585
586 complained = 0;
587 for (t = argv; *t;) {
588 if ((p = strrchr(*t, '/')) != NULL)
589 ++p;
590 else
591 p = *t;
592 if (ISDOT(p)) {
593 if (!complained++)
594 fprintf(stderr, "%s: \".\" and \"..\" may not be removed\n", argv0);
595 eval = 1;
596 for (save = t; (t[0] = t[1]) != NULL; ++t)
597 continue;
598 t = save;
599 } else
600 ++t;
601 }
602}
603
604static int
605usage(void)
606{
607
608 (void)fprintf(stderr, "%s\n%s\n",
609 "usage: rm [-f | -i] [-dPRrvW] file ...\n",
610 " unlink file");
611 return EX_USAGE;
612}
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