source: trunk/src/kmk/kmkbuiltin/rm.c@ 1555

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

bugfixes for windows/os2.

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