VirtualBox

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

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

Full protection, depth argument and environment overrides.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.2 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#include <fts.h>
57#include <grp.h>
58#include <pwd.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <sysexits.h>
63#include <unistd.h>
64#include "getopt.h"
65#ifdef _MSC_VER
66# include "mscfakes.h"
67#endif
68#include "kmkbuiltin.h"
69
70#if defined(__EMX__) || defined(_MSC_VER)
71# define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' )
72# define HAVE_DOS_PATHS 1
73# define DEFAULT_PROTECTION_DEPTH 1
74#else
75# define IS_SLASH(ch) ( (ch) == '/' )
76# undef HAVE_DOS_PATHS
77# define DEFAULT_PROTECTION_DEPTH 2
78#endif
79
80#ifdef __EMX__
81#undef S_IFWHT
82#undef S_ISWHT
83#endif
84#ifndef S_IFWHT
85#define S_IFWHT 0
86#define S_ISWHT(s) 0
87#define undelete(s) (-1)
88#endif
89
90#if !defined(__FreeBSD__) && !defined(__APPLE__)
91extern void strmode(mode_t mode, char *p);
92#endif
93
94static int protectionflag, fullprotectionflag, protectiondepth;
95static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
96static uid_t uid;
97
98static char *argv0;
99
100static struct option long_options[] =
101{
102 { "help", no_argument, 0, 261 },
103 { "version", no_argument, 0, 262 },
104 { "disable-protection", no_argument, 0, 263 },
105 { "enable-protection", no_argument, 0, 264 },
106 { "enable-full-protection", no_argument, 0, 265 },
107 { "disable-full-protection", no_argument, 0, 266 },
108 { "protection-depth", required_argument, 0, 267 },
109 { 0, 0, 0, 0 },
110};
111
112
113static int check(char *, char *, struct stat *);
114static void checkdot(char **);
115static void rm_file(char **);
116static int rm_overwrite(char *, struct stat *);
117static void rm_tree(char **);
118static int count_path_components(const char *);
119static int set_protection_depth(const char *);
120static int usage(FILE *);
121
122
123
124/*
125 * rm --
126 * This rm is different from historic rm's, but is expected to match
127 * POSIX 1003.2 behavior. The most visible difference is that -f
128 * has two specific effects now, ignore non-existent files and force
129 * file removal.
130 */
131int
132kmk_builtin_rm(int argc, char *argv[], char **envp)
133{
134 int ch, rflag;
135 int i;
136
137 /* reinitialize globals */
138 argv0 = argv[0];
139 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0;
140 fullprotectionflag = 0;
141 protectionflag = 1;
142 protectiondepth = DEFAULT_PROTECTION_DEPTH;
143 uid = 0;
144
145 /* kmk: reset getopt and set program name. */
146 g_progname = argv[0];
147 opterr = 1;
148 optarg = NULL;
149 optopt = 0;
150 optind = 0; /* init */
151
152 Pflag = rflag = 0;
153 while ((ch = getopt_long(argc, argv, "dfiPRvW", long_options, NULL)) != -1)
154 switch(ch) {
155 case 'd':
156 dflag = 1;
157 break;
158 case 'f':
159 fflag = 1;
160 iflag = 0;
161 break;
162 case 'i':
163 fflag = 0;
164 iflag = 1;
165 break;
166 case 'P':
167 Pflag = 1;
168 break;
169 case 'R':
170#if 0
171 case 'r': /* Compatibility. */
172#endif
173 rflag = 1;
174 break;
175 case 'v':
176 vflag = 1;
177 break;
178#ifdef FTS_WHITEOUT
179 case 'W':
180 Wflag = 1;
181 break;
182#endif
183 case 261:
184 usage(stdout);
185 return 0;
186 case 262:
187 return kbuild_version(argv[0]);
188 case 263:
189 protectionflag = 0;
190 break;
191 case 264:
192 protectionflag = 1;
193 break;
194 case 265:
195 fullprotectionflag = 1;
196 break;
197 case 266:
198 fullprotectionflag = 0;
199 break;
200 case 267:
201 set_protection_depth(optarg);
202 break;
203 case '?':
204 default:
205 return usage(stderr);
206 }
207 argc -= optind;
208 argv += optind;
209
210 if (argc < 1) {
211 if (fflag)
212 return (0);
213 return usage(stderr);
214 }
215
216 /* Search the environment for option overrides (protection). */
217#define STR_SIZE_PAIR(str) str, sizeof(str) - 1
218 for (i = 0; envp[i]; i++) {
219 if (!strncmp(envp[i], "KMK_RM_", sizeof("KMK_RM_") - 1)) {
220 if (!strncmp(envp[i], STR_SIZE_PAIR("KMK_RM_PROTECTION_DEPTH="))) {
221 const char *val = envp[i] + sizeof("KMK_RM_PROTECTION_DEPTH=") - 1;
222 if (set_protection_depth(val))
223 return eval;
224 } else if (!strncmp(envp[i], STR_SIZE_PAIR("KMK_RM_DISABLE_PROTECTION="))) {
225 if (protectionflag >= 0)
226 protectionflag = 0;
227 } else if (!strncmp(envp[i], STR_SIZE_PAIR("KMK_RM_ENABLE_PROTECTION="))) {
228 protectionflag = -1;
229 } else if (!strncmp(envp[i], STR_SIZE_PAIR("KMK_RM_DISABLE_FULL_PROTECTION="))) {
230 if (fullprotectionflag >= 0)
231 fullprotectionflag = 0;
232 } else if (!strncmp(envp[i], STR_SIZE_PAIR("KMK_RM_ENABLE_FULL_PROTECTION="))) {
233 fullprotectionflag = protectionflag = -1;
234 }
235 }
236 }
237 if (fullprotectionflag)
238 protectionflag = 1;
239#undef STR_SIZE_PAIR
240
241 checkdot(argv);
242 uid = geteuid();
243
244 if (*argv) {
245 stdin_ok = isatty(STDIN_FILENO);
246 if (rflag)
247 rm_tree(argv);
248 else
249 rm_file(argv);
250 }
251
252 return eval;
253}
254
255/**
256 * Sets protectiondepth according to the option argument.
257 *
258 * @returns eval, that is 0 on success and non-zero on failure
259 *
260 * @param val The value.
261 */
262static int
263set_protection_depth(const char *val)
264{
265 /* skip leading blanks, they don't count either way. */
266 while (isspace(*val))
267 val++;
268
269 /* number or path? */
270 if (!isdigit(*val) || strpbrk(val, ":/\\")) {
271 protectiondepth = count_path_components(val);
272 } else {
273 char *more = 0;
274 protectiondepth = strtol(val, &more, 0);
275 if (protectiondepth != 0 && more) {
276 /* trailing space is harmless. */
277 while (isspace(*more))
278 more++;
279 }
280 if (!protectiondepth || val == more || *more)
281 return eval = errx(1, "bogus protection depth: %s", val);
282 }
283
284 if (protectiondepth < 1)
285 return eval = errx(1, "bogus protection depth: %s", val);
286 return eval;
287}
288
289/**
290 * Counts the components in the specified sub path.
291 * This is a helper for count_path_components.
292 *
293 * etc = 1
294 * etc/ = 1
295 * etc/x11 = 2
296 * and so and and so forth.
297 */
298static int
299count_sub_path_components(const char *path, int depth)
300{
301 for (;;) {
302 const char *end;
303 size_t len;
304
305 /* skip slashes. */
306 while (IS_SLASH(*path))
307 path++;
308 if (!*path)
309 break;
310
311 /* find end of component. */
312 end = path;
313 while (!IS_SLASH(*end) && *end)
314 end++;
315
316 /* count it, checking for '..' and '.'. */
317 len = end - path;
318 if (len == 2 && path[0] == '.' && path[1] == '.') {
319 if (depth > 0)
320 depth--;
321 } else if (len != 1 || path[0] != '.') {
322 depth++;
323 }
324
325 /* advance */
326 if (!*end)
327 break;
328 path = end + 1;
329 }
330 return depth;
331}
332
333/**
334 * Parses the specified path counting the number of components
335 * relative to root.
336 *
337 * We don't check symbolic links and such, just some simple and cheap
338 * path parsing.
339 *
340 * @param path The path to process.
341 *
342 * @returns 0 or higher on success.
343 * On failure an error is printed, eval is set and -1 is returned.
344 */
345static int
346count_path_components(const char *path)
347{
348 int components = 0;
349
350 /*
351 * Deal with root, UNC, drive letter.
352 */
353#if defined(_MSC_VER) || defined(__OS2__)
354 if (IS_SLASH(path[0]) && IS_SLASH(path[1]) && !IS_SLASH(path[2])) {
355 /* skip the root - UNC */
356 path += 3;
357 while (!IS_SLASH(*path) && *path) /* server name */
358 path++;
359 while (IS_SLASH(*path))
360 path++;
361 while (!IS_SLASH(*path) && *path) /* share name */
362 path++;
363 while (IS_SLASH(*path))
364 path++;
365 } else {
366 unsigned drive_letter = (unsigned)toupper(path[0]) - (unsigned)'A';
367 if (drive_letter <= (unsigned)('Z' - 'A') && path[1] == ':') {
368 drive_letter++; /* A == 1 */
369 } else {
370 drive_letter = 0; /* 0 == default */
371 }
372
373 if (IS_SLASH(path[drive_letter ? 2 : 0])) {
374 /*
375 * Relative path, must count cwd depth first.
376 */
377 char *tmp = _getdcwd(drive_letter, NULL, 32);
378 if (!tmp) {
379 eval = err(1, "_getdcwd");
380 return -1;
381 }
382
383 if (IS_SLASH(cwd[0]) && IS_SLASH(cwd[1])) {
384 /* skip the root - UNC */
385 tmp = &cwd[2];
386 while (!IS_SLASH(*tmp) && *tmp) /* server name */
387 tmp++;
388 while (IS_SLASH(*tmp))
389 tmp++;
390 while (!IS_SLASH(*tmp) && *tmp) /* share name */
391 tmp++;
392 } else {
393 /* skip the drive letter and while we're at it, the root slash too. */
394 tmp = &cwd[1 + (cwd[1] == ':')];
395 }
396 components = count_sub_path_components(tmp, 0);
397 free(tmp);
398 } else {
399 /* skip the drive letter and while we're at it, the root slash too. */
400 path += drive_letter ? 3 : 1;
401 }
402 }
403#else
404 if (!IS_SLASH(path[0])) {
405 /*
406 * Relative path, must count cwd depth first.
407 */
408 char cwd[4096];
409 if (!getcwd(cwd, sizeof(cwd))) {
410 eval = err(1, "getcwd");
411 return -1;
412 }
413 components = count_sub_path_components(cwd, 0);
414 }
415#endif
416
417 /*
418 * We're now past any UNC or drive letter crap, possibly positioned
419 * at the root slash or at the start of a path component at the
420 * given depth. Count the remainder.
421 */
422 return count_sub_path_components(path, components);
423}
424
425
426/**
427 * Protect the upper layers of the file system against accidental
428 * or malicious deletetion attempt from within a makefile.
429 *
430 * @param path The path to check.
431 * @param required_depth The minimum number of components in the
432 * path counting from the root.
433 *
434 * @returns 0 on success.
435 * On failure an error is printed, eval is set and -1 is returned.
436 */
437static int
438enforce_protection(const char *path, unsigned required_depth)
439{
440 int components;
441
442 /*
443 * Count the path and compare it with the required depth.
444 */
445 components = count_path_components(path);
446 if (components < 0)
447 return -1;
448 if (components < required_depth) {
449 eval = errx(1, "%s: protected", path);
450 return -1;
451 }
452 return 0;
453}
454
455static void
456rm_tree(char **argv)
457{
458 FTS *fts;
459 FTSENT *p;
460 int needstat;
461 int flags;
462 int rval;
463
464 /*
465 * Check up front before anything is deleted. This will not catch
466 * everything, but we'll check the individual items later.
467 */
468 if (protectionflag) {
469 int i;
470 for (i = 0; argv[i]; i++) {
471 if (enforce_protection(argv[i], protectiondepth + 1))
472 return;
473 }
474 }
475
476 /*
477 * Remove a file hierarchy. If forcing removal (-f), or interactive
478 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
479 */
480 needstat = !uid || (!fflag && !iflag && stdin_ok);
481
482 /*
483 * If the -i option is specified, the user can skip on the pre-order
484 * visit. The fts_number field flags skipped directories.
485 */
486#define SKIPPED 1
487
488 flags = FTS_PHYSICAL;
489 if (!needstat)
490 flags |= FTS_NOSTAT;
491#ifdef FTS_WHITEOUT
492 if (Wflag)
493 flags |= FTS_WHITEOUT;
494#endif
495 if (!(fts = fts_open(argv, flags, NULL))) {
496 eval = err(1, "fts_open");
497 return;
498 }
499 while ((p = fts_read(fts)) != NULL) {
500 switch (p->fts_info) {
501 case FTS_DNR:
502 if (!fflag || p->fts_errno != ENOENT) {
503 fprintf(stderr, "%s: %s: %s\n",
504 argv0, p->fts_path, strerror(p->fts_errno));
505 eval = 1;
506 }
507 continue;
508 case FTS_ERR:
509 eval = errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
510 fts_close(fts);
511 return;
512 case FTS_NS:
513 /*
514 * Assume that since fts_read() couldn't stat the
515 * file, it can't be unlinked.
516 */
517 if (!needstat)
518 break;
519 if (!fflag || p->fts_errno != ENOENT) {
520 fprintf(stderr, "%s: %s: %s\n",
521 argv0, p->fts_path, strerror(p->fts_errno));
522 eval = 1;
523 }
524 continue;
525 case FTS_D:
526 /* Pre-order: give user chance to skip. */
527 if (!fflag && !check(p->fts_path, p->fts_accpath,
528 p->fts_statp)) {
529 (void)fts_set(fts, p, FTS_SKIP);
530 p->fts_number = SKIPPED;
531 }
532#ifdef UF_APPEND
533 else if (!uid &&
534 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
535 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
536 chflags(p->fts_accpath,
537 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
538 goto err;
539#endif
540 continue;
541 case FTS_DP:
542 /* Post-order: see if user skipped. */
543 if (p->fts_number == SKIPPED)
544 continue;
545 break;
546 default:
547 if (!fflag &&
548 !check(p->fts_path, p->fts_accpath, p->fts_statp))
549 continue;
550 }
551
552 /*
553 * Protect against deleting root files and directories.
554 */
555 if (protectionflag && enforce_protection(p->fts_accpath, protectiondepth + 1)) {
556 fts_close(fts);
557 return;
558 }
559
560 rval = 0;
561#ifdef UF_APPEND
562 if (!uid &&
563 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
564 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
565 rval = chflags(p->fts_accpath,
566 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
567#endif
568 if (rval == 0) {
569 /*
570 * If we can't read or search the directory, may still be
571 * able to remove it. Don't print out the un{read,search}able
572 * message unless the remove fails.
573 */
574 switch (p->fts_info) {
575 case FTS_DP:
576 case FTS_DNR:
577 rval = rmdir(p->fts_accpath);
578 if (rval == 0 || (fflag && errno == ENOENT)) {
579 if (rval == 0 && vflag)
580 (void)printf("%s\n",
581 p->fts_path);
582 continue;
583 }
584 break;
585
586#ifdef FTS_W
587 case FTS_W:
588 rval = undelete(p->fts_accpath);
589 if (rval == 0 && (fflag && errno == ENOENT)) {
590 if (vflag)
591 (void)printf("%s\n",
592 p->fts_path);
593 continue;
594 }
595 break;
596#endif
597
598 case FTS_NS:
599 /*
600 * Assume that since fts_read() couldn't stat
601 * the file, it can't be unlinked.
602 */
603 if (fflag)
604 continue;
605 /* FALLTHROUGH */
606 default:
607 if (Pflag)
608 if (!rm_overwrite(p->fts_accpath, NULL))
609 continue;
610 rval = unlink(p->fts_accpath);
611#ifdef _MSC_VER
612 if (rval != 0) {
613 chmod(p->fts_accpath, 0777);
614 rval = unlink(p->fts_accpath);
615 }
616#endif
617
618 if (rval == 0 || (fflag && errno == ENOENT)) {
619 if (rval == 0 && vflag)
620 (void)printf("%s\n",
621 p->fts_path);
622 continue;
623 }
624 }
625 }
626err:
627 fprintf(stderr, "%s: %s: %s\n", argv0, p->fts_path, strerror(errno));
628 eval = 1;
629 }
630 if (errno) {
631 fprintf(stderr, "%s: fts_read: %s\n", argv0, strerror(errno));
632 eval = 1;
633 }
634 fts_close(fts);
635}
636
637static void
638rm_file(char **argv)
639{
640 struct stat sb;
641 int rval;
642 char *f;
643
644 /*
645 * Check up front before anything is deleted.
646 */
647 if (fullprotectionflag) {
648 int i;
649 for (i = 0; argv[i]; i++) {
650 if (enforce_protection(argv[i], protectiondepth + 1))
651 return;
652 }
653 }
654
655 /*
656 * Remove a file. POSIX 1003.2 states that, by default, attempting
657 * to remove a directory is an error, so must always stat the file.
658 */
659 while ((f = *argv++) != NULL) {
660 /* Assume if can't stat the file, can't unlink it. */
661 if (lstat(f, &sb)) {
662#ifdef FTS_WHITEOUT
663 if (Wflag) {
664 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
665 } else {
666#else
667 {
668#endif
669 if (!fflag || errno != ENOENT) {
670 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
671 eval = 1;
672 }
673 continue;
674 }
675#ifdef FTS_WHITEOUT
676 } else if (Wflag) {
677 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(EEXIST));
678 eval = 1;
679 continue;
680#endif
681 }
682
683 if (S_ISDIR(sb.st_mode) && !dflag) {
684 fprintf(stderr, "%s: %s: is a directory\n", argv0, f);
685 eval = 1;
686 continue;
687 }
688 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
689 continue;
690 rval = 0;
691#ifdef UF_APPEND
692 if (!uid &&
693 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
694 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
695 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
696#endif
697 if (rval == 0) {
698 if (S_ISWHT(sb.st_mode))
699 rval = undelete(f);
700 else if (S_ISDIR(sb.st_mode))
701 rval = rmdir(f);
702 else {
703 if (Pflag)
704 if (!rm_overwrite(f, &sb))
705 continue;
706 rval = unlink(f);
707#ifdef _MSC_VER
708 if (rval != 0) {
709 chmod(f, 0777);
710 rval = unlink(f);
711 }
712#endif
713 }
714 }
715 if (rval && (!fflag || errno != ENOENT)) {
716 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
717 eval = 1;
718 }
719 if (vflag && rval == 0)
720 (void)printf("%s\n", f);
721 }
722}
723
724/*
725 * rm_overwrite --
726 * Overwrite the file 3 times with varying bit patterns.
727 *
728 * XXX
729 * This is a cheap way to *really* delete files. Note that only regular
730 * files are deleted, directories (and therefore names) will remain.
731 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
732 * System V file system). In a logging file system, you'll have to have
733 * kernel support.
734 */
735static int
736rm_overwrite(char *file, struct stat *sbp)
737{
738 struct stat sb;
739#ifdef HAVE_FSTATFS
740 struct statfs fsb;
741#endif
742 off_t len;
743 int bsize, fd, wlen;
744 char *buf = NULL;
745
746 fd = -1;
747 if (sbp == NULL) {
748 if (lstat(file, &sb))
749 goto err;
750 sbp = &sb;
751 }
752 if (!S_ISREG(sbp->st_mode))
753 return (1);
754 if ((fd = open(file, O_WRONLY, 0)) == -1)
755 goto err;
756#ifdef HAVE_FSTATFS
757 if (fstatfs(fd, &fsb) == -1)
758 goto err;
759 bsize = MAX(fsb.f_iosize, 1024);
760#elif defined(HAVE_ST_BLKSIZE)
761 bsize = MAX(sb.st_blksize, 1024);
762#else
763 bsize = 1024;
764#endif
765 if ((buf = malloc(bsize)) == NULL)
766 exit(err(1, "%s: malloc", file));
767
768#define PASS(byte) { \
769 memset(buf, byte, bsize); \
770 for (len = sbp->st_size; len > 0; len -= wlen) { \
771 wlen = len < bsize ? len : bsize; \
772 if (write(fd, buf, wlen) != wlen) \
773 goto err; \
774 } \
775}
776 PASS(0xff);
777 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
778 goto err;
779 PASS(0x00);
780 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
781 goto err;
782 PASS(0xff);
783 if (!fsync(fd) && !close(fd)) {
784 free(buf);
785 return (1);
786 }
787
788err: eval = 1;
789 if (buf)
790 free(buf);
791 if (fd != -1)
792 close(fd);
793 fprintf(stderr, "%s: %s: %s\n", argv0, file, strerror(errno));
794 return (0);
795}
796
797
798static int
799check(char *path, char *name, struct stat *sp)
800{
801 int ch, first;
802 char modep[15], *flagsp;
803
804 /* Check -i first. */
805 if (iflag)
806 (void)fprintf(stderr, "remove %s? ", path);
807 else {
808 /*
809 * If it's not a symbolic link and it's unwritable and we're
810 * talking to a terminal, ask. Symbolic links are excluded
811 * because their permissions are meaningless. Check stdin_ok
812 * first because we may not have stat'ed the file.
813 * Also skip this check if the -P option was specified because
814 * we will not be able to overwrite file contents and will
815 * barf later.
816 */
817 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
818 (!access(name, W_OK) &&
819#ifdef SF_APPEND
820 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
821 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))
822#else
823 1)
824#endif
825 )
826 return (1);
827 strmode(sp->st_mode, modep);
828#ifdef SF_APPEND
829 if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
830 exit(err(1, "fflagstostr"));
831 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
832 modep + 1, modep[9] == ' ' ? "" : " ",
833 user_from_uid(sp->st_uid, 0),
834 group_from_gid(sp->st_gid, 0),
835 *flagsp ? flagsp : "", *flagsp ? " " : "",
836 path);
837 free(flagsp);
838#else
839 (void)flagsp;
840 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
841 modep + 1, modep[9] == ' ' ? "" : " ",
842 sp->st_uid, sp->st_gid, path);
843#endif
844 }
845 (void)fflush(stderr);
846
847 first = ch = getchar();
848 while (ch != '\n' && ch != EOF)
849 ch = getchar();
850 return (first == 'y' || first == 'Y');
851}
852
853#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
854static void
855checkdot(char **argv)
856{
857 char *p, **save, **t;
858 int complained;
859
860 complained = 0;
861 for (t = argv; *t;) {
862#ifdef HAVE_DOS_PATHS
863 const char *tmp = p = *t;
864 while (*tmp) {
865 switch (*tmp) {
866 case '/':
867 case '\\':
868 case ':':
869 p = (char *)tmp + 1;
870 break;
871 }
872 tmp++;
873 }
874#else
875 if ((p = strrchr(*t, '/')) != NULL)
876 ++p;
877 else
878 p = *t;
879#endif
880 if (ISDOT(p)) {
881 if (!complained++)
882 fprintf(stderr, "%s: \".\" and \"..\" may not be removed\n", argv0);
883 eval = 1;
884 for (save = t; (t[0] = t[1]) != NULL; ++t)
885 continue;
886 t = save;
887 } else
888 ++t;
889 }
890}
891
892static int
893usage(FILE *pf)
894{
895 fprintf(pf,
896 "usage: %s [options] file ...\n"
897 " or: %s --help\n"
898 " or: %s --version\n"
899 "\n"
900 "Options:\n"
901 " -f\n"
902 " Attempt to remove files without prompting, regardless of the file\n"
903 " permission. Ignore non-existing files. Overrides previous -i's.\n"
904 " -i\n"
905 " Prompt for each file. Always.\n"
906 " -d\n"
907 " Attempt to remove directories as well as other kinds of files.\n"
908 " -P\n"
909 " Overwrite regular files before deleting; three passes: ff,0,ff\n"
910 " -R\n"
911 " Attempt to remove the file hierachy rooted in each file argument.\n"
912 " This option implies -d and file protection.\n"
913 " -v\n"
914 " Be verbose, show files as they are removed.\n"
915 " -W\n"
916 " Undelete without files.\n"
917 " --disable-protection\n"
918 " Will disable the protection file protection applied with -R.\n"
919 " --enable-protection\n"
920 " Will enable the protection file protection applied with -R.\n"
921 " --enable-full-protection\n"
922 " Will enable the protection file protection for all operations.\n"
923 " --disable-full-protection\n"
924 " Will disable the protection file protection for all operations.\n"
925 " --protection-depth\n"
926 " Number or path indicating the file protection depth. Default: %d\n"
927 "\n"
928 "Environment:\n"
929 " KMK_RM_DISABLE_PROTECTION\n"
930 " Same as --disable-protection. Overrides command line.\n"
931 " KMK_RM_ENABLE_PROTECTION\n"
932 " Same as --enable-protection. Overrides everyone else.\n"
933 " KMK_RM_ENABLE_FULL_PROTECTION\n"
934 " Same as --enable-full-protection. Overrides everyone else.\n"
935 " KMK_RM_DISABLE_FULL_PROTECTION\n"
936 " Same as --disable-full-protection. Overrides command line.\n"
937 " KMK_RM_PROTECTION_DEPTH\n"
938 " Same as --protection-depth. Overrides command line.\n"
939 "\n"
940 "The file protection of the top %d layers of the file hierarchy is there\n"
941 "to try prevent makefiles from doing bad things to your system. This\n"
942 "protection is not bulletproof, but should help prevent you from shooting\n"
943 "yourself in the foot.\n"
944 ,
945 g_progname, g_progname, g_progname,
946 DEFAULT_PROTECTION_DEPTH, DEFAULT_PROTECTION_DEPTH);
947 return EX_USAGE;
948}
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