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

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

strncmp can be a freeking macro of course, so STR_SIZE_PAIR won't work. (glibc rules)

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