kmk: Made chmod build on windows. Some cleanup of the bsdisms.
[kbuild-mirror.git] / src / kmk / kmkbuiltin / rm.c
blob6d74d4fd75d1e049bc55edd7459ed84188c89f31
1 /*-
2 * Copyright (c) 1990, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
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.
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.
30 #if 0
31 #ifndef lint
32 static 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 */
37 #ifndef lint
38 static 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
44 #include <sys/stat.h>
45 #ifndef _MSC_VER
46 # include <sys/param.h>
47 # include <sys/mount.h>
48 #endif
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 #if defined(__OS2__) || defined(_MSC_VER)
67 # include <direct.h>
68 # include <limits.h>
69 #endif
70 #include "kmkbuiltin.h"
71 #include "kbuild_protection.h"
73 #if defined(__EMX__) || defined(_MSC_VER)
74 # define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' )
75 # define HAVE_DOS_PATHS 1
76 # define DEFAULT_PROTECTION_DEPTH 1
77 #else
78 # define IS_SLASH(ch) ( (ch) == '/' )
79 # undef HAVE_DOS_PATHS
80 # define DEFAULT_PROTECTION_DEPTH 2
81 #endif
83 #ifdef __EMX__
84 #undef S_IFWHT
85 #undef S_ISWHT
86 #endif
87 #ifndef S_IFWHT
88 #define S_IFWHT 0
89 #define S_ISWHT(s) 0
90 #define undelete(s) (-1)
91 #endif
93 extern void bsd_strmode(mode_t mode, char *p);
95 static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
96 static uid_t uid;
98 static char *argv0;
99 static KBUILDPROTECTION g_ProtData;
101 static struct option long_options[] =
103 { "help", no_argument, 0, 261 },
104 { "version", no_argument, 0, 262 },
105 { "disable-protection", no_argument, 0, 263 },
106 { "enable-protection", no_argument, 0, 264 },
107 { "enable-full-protection", no_argument, 0, 265 },
108 { "disable-full-protection", no_argument, 0, 266 },
109 { "protection-depth", required_argument, 0, 267 },
110 { 0, 0, 0, 0 },
114 static int check(char *, char *, struct stat *);
115 static void checkdot(char **);
116 static int rm_file(char **);
117 static int rm_overwrite(char *, struct stat *);
118 static int rm_tree(char **);
119 static int count_path_components(const char *);
120 static int usage(FILE *);
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.
132 kmk_builtin_rm(int argc, char *argv[], char **envp)
134 int ch, rflag;
136 /* reinitialize globals */
137 argv0 = argv[0];
138 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0;
139 uid = 0;
140 kBuildProtectionInit(&g_ProtData);
142 /* kmk: reset getopt and set program name. */
143 g_progname = argv[0];
144 opterr = 1;
145 optarg = NULL;
146 optopt = 0;
147 optind = 0; /* init */
149 Pflag = rflag = 0;
150 while ((ch = getopt_long(argc, argv, "dfiPRvW", long_options, NULL)) != -1)
151 switch(ch) {
152 case 'd':
153 dflag = 1;
154 break;
155 case 'f':
156 fflag = 1;
157 iflag = 0;
158 break;
159 case 'i':
160 fflag = 0;
161 iflag = 1;
162 break;
163 case 'P':
164 Pflag = 1;
165 break;
166 case 'R':
167 #if 0
168 case 'r': /* Compatibility. */
169 #endif
170 rflag = 1;
171 break;
172 case 'v':
173 vflag = 1;
174 break;
175 #ifdef FTS_WHITEOUT
176 case 'W':
177 Wflag = 1;
178 break;
179 #endif
180 case 261:
181 kBuildProtectionTerm(&g_ProtData);
182 usage(stdout);
183 return 0;
184 case 262:
185 kBuildProtectionTerm(&g_ProtData);
186 return kbuild_version(argv[0]);
187 case 263:
188 kBuildProtectionDisable(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
189 break;
190 case 264:
191 kBuildProtectionEnable(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
192 break;
193 case 265:
194 kBuildProtectionEnable(&g_ProtData, KBUILDPROTECTIONTYPE_FULL);
195 break;
196 case 266:
197 kBuildProtectionDisable(&g_ProtData, KBUILDPROTECTIONTYPE_FULL);
198 break;
199 case 267:
200 if (kBuildProtectionSetDepth(&g_ProtData, optarg)) {
201 kBuildProtectionTerm(&g_ProtData);
202 return 1;
204 break;
205 case '?':
206 default:
207 kBuildProtectionTerm(&g_ProtData);
208 return usage(stderr);
210 argc -= optind;
211 argv += optind;
213 if (argc < 1) {
214 kBuildProtectionTerm(&g_ProtData);
215 if (fflag)
216 return (0);
217 return usage(stderr);
220 if (!kBuildProtectionScanEnv(&g_ProtData, envp, "KMK_RM_")) {
221 checkdot(argv);
222 uid = geteuid();
224 if (*argv) {
225 stdin_ok = isatty(STDIN_FILENO);
226 if (rflag)
227 eval |= rm_tree(argv);
228 else
229 eval |= rm_file(argv);
231 } else {
232 eval = 1;
235 kBuildProtectionTerm(&g_ProtData);
236 return eval;
239 static int
240 rm_tree(char **argv)
242 FTS *fts;
243 FTSENT *p;
244 int needstat;
245 int flags;
246 int rval;
249 * Check up front before anything is deleted. This will not catch
250 * everything, but we'll check the individual items later.
252 int i;
253 for (i = 0; argv[i]; i++) {
254 if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, argv[i])) {
255 return 1;
260 * Remove a file hierarchy. If forcing removal (-f), or interactive
261 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
263 needstat = !uid || (!fflag && !iflag && stdin_ok);
266 * If the -i option is specified, the user can skip on the pre-order
267 * visit. The fts_number field flags skipped directories.
269 #define SKIPPED 1
271 flags = FTS_PHYSICAL;
272 if (!needstat)
273 flags |= FTS_NOSTAT;
274 #ifdef FTS_WHITEOUT
275 if (Wflag)
276 flags |= FTS_WHITEOUT;
277 #endif
278 if (!(fts = fts_open(argv, flags, NULL))) {
279 return err(1, "fts_open");
281 while ((p = fts_read(fts)) != NULL) {
282 switch (p->fts_info) {
283 case FTS_DNR:
284 if (!fflag || p->fts_errno != ENOENT) {
285 fprintf(stderr, "%s: %s: %s\n",
286 argv0, p->fts_path, strerror(p->fts_errno));
287 eval = 1;
289 continue;
290 case FTS_ERR:
291 fts_close(fts);
292 return errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
293 case FTS_NS:
295 * Assume that since fts_read() couldn't stat the
296 * file, it can't be unlinked.
298 if (!needstat)
299 break;
300 if (!fflag || p->fts_errno != ENOENT) {
301 fprintf(stderr, "%s: %s: %s\n",
302 argv0, p->fts_path, strerror(p->fts_errno));
303 eval = 1;
305 continue;
306 case FTS_D:
307 /* Pre-order: give user chance to skip. */
308 if (!fflag && !check(p->fts_path, p->fts_accpath,
309 p->fts_statp)) {
310 (void)fts_set(fts, p, FTS_SKIP);
311 p->fts_number = SKIPPED;
313 #ifdef UF_APPEND
314 else if (!uid &&
315 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
316 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
317 chflags(p->fts_accpath,
318 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
319 goto err;
320 #endif
321 continue;
322 case FTS_DP:
323 /* Post-order: see if user skipped. */
324 if (p->fts_number == SKIPPED)
325 continue;
326 break;
327 default:
328 if (!fflag &&
329 !check(p->fts_path, p->fts_accpath, p->fts_statp))
330 continue;
334 * Protect against deleting root files and directories.
336 if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, p->fts_accpath)) {
337 fts_close(fts);
338 return 1;
341 rval = 0;
342 #ifdef UF_APPEND
343 if (!uid &&
344 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
345 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
346 rval = chflags(p->fts_accpath,
347 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
348 #endif
349 if (rval == 0) {
351 * If we can't read or search the directory, may still be
352 * able to remove it. Don't print out the un{read,search}able
353 * message unless the remove fails.
355 switch (p->fts_info) {
356 case FTS_DP:
357 case FTS_DNR:
358 rval = rmdir(p->fts_accpath);
359 if (rval == 0 || (fflag && errno == ENOENT)) {
360 if (rval == 0 && vflag)
361 (void)printf("%s\n",
362 p->fts_path);
363 continue;
365 break;
367 #ifdef FTS_W
368 case FTS_W:
369 rval = undelete(p->fts_accpath);
370 if (rval == 0 && (fflag && errno == ENOENT)) {
371 if (vflag)
372 (void)printf("%s\n",
373 p->fts_path);
374 continue;
376 break;
377 #endif
379 case FTS_NS:
381 * Assume that since fts_read() couldn't stat
382 * the file, it can't be unlinked.
384 if (fflag)
385 continue;
386 /* FALLTHROUGH */
387 default:
388 if (Pflag)
389 if (!rm_overwrite(p->fts_accpath, NULL))
390 continue;
391 rval = unlink(p->fts_accpath);
392 #ifdef _MSC_VER
393 if (rval != 0) {
394 chmod(p->fts_accpath, 0777);
395 rval = unlink(p->fts_accpath);
397 #endif
399 if (rval == 0 || (fflag && errno == ENOENT)) {
400 if (rval == 0 && vflag)
401 (void)printf("%s\n",
402 p->fts_path);
403 continue;
407 #ifdef UF_APPEND
408 err:
409 #endif
410 fprintf(stderr, "%s: %s: %s\n", argv0, p->fts_path, strerror(errno));
411 eval = 1;
413 if (errno) {
414 fprintf(stderr, "%s: fts_read: %s\n", argv0, strerror(errno));
415 eval = 1;
417 fts_close(fts);
418 return eval;
421 static int
422 rm_file(char **argv)
424 struct stat sb;
425 int rval;
426 char *f;
429 * Check up front before anything is deleted.
431 int i;
432 for (i = 0; argv[i]; i++) {
433 if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_FULL, argv[i]))
434 return 1;
438 * Remove a file. POSIX 1003.2 states that, by default, attempting
439 * to remove a directory is an error, so must always stat the file.
441 while ((f = *argv++) != NULL) {
442 /* Assume if can't stat the file, can't unlink it. */
443 if (lstat(f, &sb)) {
444 #ifdef FTS_WHITEOUT
445 if (Wflag) {
446 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
447 } else {
448 #else
450 #endif
451 if (!fflag || errno != ENOENT) {
452 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
453 eval = 1;
455 continue;
457 #ifdef FTS_WHITEOUT
458 } else if (Wflag) {
459 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(EEXIST));
460 eval = 1;
461 continue;
462 #endif
465 if (S_ISDIR(sb.st_mode) && !dflag) {
466 fprintf(stderr, "%s: %s: is a directory\n", argv0, f);
467 eval = 1;
468 continue;
470 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
471 continue;
472 rval = 0;
473 #ifdef UF_APPEND
474 if (!uid &&
475 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
476 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
477 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
478 #endif
479 if (rval == 0) {
480 if (S_ISWHT(sb.st_mode))
481 rval = undelete(f);
482 else if (S_ISDIR(sb.st_mode))
483 rval = rmdir(f);
484 else {
485 if (Pflag)
486 if (!rm_overwrite(f, &sb))
487 continue;
488 rval = unlink(f);
489 #ifdef _MSC_VER
490 if (rval != 0) {
491 chmod(f, 0777);
492 rval = unlink(f);
494 #endif
497 if (rval && (!fflag || errno != ENOENT)) {
498 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
499 eval = 1;
501 if (vflag && rval == 0)
502 (void)printf("%s\n", f);
504 return eval;
508 * rm_overwrite --
509 * Overwrite the file 3 times with varying bit patterns.
511 * XXX
512 * This is a cheap way to *really* delete files. Note that only regular
513 * files are deleted, directories (and therefore names) will remain.
514 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
515 * System V file system). In a logging file system, you'll have to have
516 * kernel support.
518 static int
519 rm_overwrite(char *file, struct stat *sbp)
521 struct stat sb;
522 #ifdef HAVE_FSTATFS
523 struct statfs fsb;
524 #endif
525 off_t len;
526 int bsize, fd, wlen;
527 char *buf = NULL;
529 fd = -1;
530 if (sbp == NULL) {
531 if (lstat(file, &sb))
532 goto err;
533 sbp = &sb;
535 if (!S_ISREG(sbp->st_mode))
536 return (1);
537 if ((fd = open(file, O_WRONLY, 0)) == -1)
538 goto err;
539 #ifdef HAVE_FSTATFS
540 if (fstatfs(fd, &fsb) == -1)
541 goto err;
542 bsize = MAX(fsb.f_iosize, 1024);
543 #elif defined(HAVE_ST_BLKSIZE)
544 bsize = MAX(sb.st_blksize, 1024);
545 #else
546 bsize = 1024;
547 #endif
548 if ((buf = malloc(bsize)) == NULL)
549 exit(err(1, "%s: malloc", file));
551 #define PASS(byte) { \
552 memset(buf, byte, bsize); \
553 for (len = sbp->st_size; len > 0; len -= wlen) { \
554 wlen = len < bsize ? len : bsize; \
555 if (write(fd, buf, wlen) != wlen) \
556 goto err; \
559 PASS(0xff);
560 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
561 goto err;
562 PASS(0x00);
563 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
564 goto err;
565 PASS(0xff);
566 if (!fsync(fd) && !close(fd)) {
567 free(buf);
568 return (1);
571 err: eval = 1;
572 if (buf)
573 free(buf);
574 if (fd != -1)
575 close(fd);
576 fprintf(stderr, "%s: %s: %s\n", argv0, file, strerror(errno));
577 return (0);
581 static int
582 check(char *path, char *name, struct stat *sp)
584 int ch, first;
585 char modep[15], *flagsp;
587 /* Check -i first. */
588 if (iflag)
589 (void)fprintf(stderr, "remove %s? ", path);
590 else {
592 * If it's not a symbolic link and it's unwritable and we're
593 * talking to a terminal, ask. Symbolic links are excluded
594 * because their permissions are meaningless. Check stdin_ok
595 * first because we may not have stat'ed the file.
596 * Also skip this check if the -P option was specified because
597 * we will not be able to overwrite file contents and will
598 * barf later.
600 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
601 (!access(name, W_OK) &&
602 #ifdef SF_APPEND
603 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
604 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))
605 #else
607 #endif
609 return (1);
610 bsd_strmode(sp->st_mode, modep);
611 #ifdef SF_APPEND
612 if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
613 exit(err(1, "fflagstostr"));
614 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
615 modep + 1, modep[9] == ' ' ? "" : " ",
616 user_from_uid(sp->st_uid, 0),
617 group_from_gid(sp->st_gid, 0),
618 *flagsp ? flagsp : "", *flagsp ? " " : "",
619 path);
620 free(flagsp);
621 #else
622 (void)flagsp;
623 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
624 modep + 1, modep[9] == ' ' ? "" : " ",
625 sp->st_uid, sp->st_gid, path);
626 #endif
628 (void)fflush(stderr);
630 first = ch = getchar();
631 while (ch != '\n' && ch != EOF)
632 ch = getchar();
633 return (first == 'y' || first == 'Y');
636 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
637 static void
638 checkdot(char **argv)
640 char *p, **save, **t;
641 int complained;
643 complained = 0;
644 for (t = argv; *t;) {
645 #ifdef HAVE_DOS_PATHS
646 const char *tmp = p = *t;
647 while (*tmp) {
648 switch (*tmp) {
649 case '/':
650 case '\\':
651 case ':':
652 p = (char *)tmp + 1;
653 break;
655 tmp++;
657 #else
658 if ((p = strrchr(*t, '/')) != NULL)
659 ++p;
660 else
661 p = *t;
662 #endif
663 if (ISDOT(p)) {
664 if (!complained++)
665 fprintf(stderr, "%s: \".\" and \"..\" may not be removed\n", argv0);
666 eval = 1;
667 for (save = t; (t[0] = t[1]) != NULL; ++t)
668 continue;
669 t = save;
670 } else
671 ++t;
675 static int
676 usage(FILE *pf)
678 fprintf(pf,
679 "usage: %s [options] file ...\n"
680 " or: %s --help\n"
681 " or: %s --version\n"
682 "\n"
683 "Options:\n"
684 " -f\n"
685 " Attempt to remove files without prompting, regardless of the file\n"
686 " permission. Ignore non-existing files. Overrides previous -i's.\n"
687 " -i\n"
688 " Prompt for each file. Always.\n"
689 " -d\n"
690 " Attempt to remove directories as well as other kinds of files.\n"
691 " -P\n"
692 " Overwrite regular files before deleting; three passes: ff,0,ff\n"
693 " -R\n"
694 " Attempt to remove the file hierachy rooted in each file argument.\n"
695 " This option implies -d and file protection.\n"
696 " -v\n"
697 " Be verbose, show files as they are removed.\n"
698 " -W\n"
699 " Undelete without files.\n"
700 " --disable-protection\n"
701 " Will disable the protection file protection applied with -R.\n"
702 " --enable-protection\n"
703 " Will enable the protection file protection applied with -R.\n"
704 " --enable-full-protection\n"
705 " Will enable the protection file protection for all operations.\n"
706 " --disable-full-protection\n"
707 " Will disable the protection file protection for all operations.\n"
708 " --protection-depth\n"
709 " Number or path indicating the file protection depth. Default: %d\n"
710 "\n"
711 "Environment:\n"
712 " KMK_RM_DISABLE_PROTECTION\n"
713 " Same as --disable-protection. Overrides command line.\n"
714 " KMK_RM_ENABLE_PROTECTION\n"
715 " Same as --enable-protection. Overrides everyone else.\n"
716 " KMK_RM_ENABLE_FULL_PROTECTION\n"
717 " Same as --enable-full-protection. Overrides everyone else.\n"
718 " KMK_RM_DISABLE_FULL_PROTECTION\n"
719 " Same as --disable-full-protection. Overrides command line.\n"
720 " KMK_RM_PROTECTION_DEPTH\n"
721 " Same as --protection-depth. Overrides command line.\n"
722 "\n"
723 "The file protection of the top %d layers of the file hierarchy is there\n"
724 "to try prevent makefiles from doing bad things to your system. This\n"
725 "protection is not bulletproof, but should help prevent you from shooting\n"
726 "yourself in the foot.\n"
728 g_progname, g_progname, g_progname,
729 kBuildProtectionDefaultDepth(), kBuildProtectionDefaultDepth());
730 return EX_USAGE;