Built win.arm64 against r3658
[kbuild-mirror.git] / src / kmk / kmkbuiltin / rm.c
bloba5fd49acf6919cf63ae6bf92d57e1a9c49047691
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
45 /*********************************************************************************************************************************
46 * Header Files *
47 *********************************************************************************************************************************/
48 #define FAKES_NO_GETOPT_H /* bird */
49 #include "config.h"
50 #include <sys/stat.h>
51 #if !defined(_MSC_VER) && !defined(__HAIKU__)
52 # include <sys/param.h>
53 # ifndef __gnu_hurd__
54 # include <sys/mount.h>
55 # endif
56 #endif
58 #include "err.h"
59 #include <errno.h>
60 #include <fcntl.h>
61 #include "fts.h"
62 #include <grp.h>
63 #include <pwd.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #ifndef __HAIKU__
68 # include <sysexits.h>
69 #endif
70 #include <unistd.h>
71 #include <ctype.h>
72 #include "getopt_r.h"
73 #ifdef __HAIKU__
74 # include "haikufakes.h"
75 #endif
76 #ifdef __NetBSD__
77 # include <util.h>
78 # define fflagstostr(flags) flags_to_string(flags, "")
79 #endif
80 #ifdef KBUILD_OS_WINDOWS
81 # ifdef _MSC_VER
82 # include "mscfakes.h"
83 # endif
84 # include "nt/ntunlink.h"
85 /* Use the special unlink implementation to do rmdir too. */
86 # undef rmdir
87 # define rmdir(a_pszPath) birdUnlinkForced(a_pszPath)
88 #endif
89 #if defined(__OS2__) || defined(_MSC_VER)
90 # include <direct.h>
91 # include <limits.h>
92 #endif
93 #include "kmkbuiltin.h"
94 #include "kbuild_protection.h"
95 #include "k/kDefs.h" /* for K_OS */
98 /*********************************************************************************************************************************
99 * Defined Constants And Macros *
100 *********************************************************************************************************************************/
101 #if defined(__EMX__) || defined(KBUILD_OS_WINDOWS)
102 # define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' )
103 # define HAVE_DOS_PATHS 1
104 # define DEFAULT_PROTECTION_DEPTH 1
105 #else
106 # define IS_SLASH(ch) ( (ch) == '/' )
107 # undef HAVE_DOS_PATHS
108 # define DEFAULT_PROTECTION_DEPTH 2
109 #endif
111 #ifdef __EMX__
112 #undef S_IFWHT
113 #undef S_ISWHT
114 #endif
115 #ifndef S_IFWHT
116 #define S_IFWHT 0
117 #define S_ISWHT(s) 0
118 #define undelete(s) (-1)
119 #endif
121 #if 1
122 #define CUR_LINE_H2(x) "[line " #x "]"
123 #define CUR_LINE_H1(x) CUR_LINE_H2(x)
124 #define CUR_LINE() CUR_LINE_H1(__LINE__)
125 #else
126 # define CUR_LINE()
127 #endif
130 /*********************************************************************************************************************************
131 * Structures and Typedefs *
132 *********************************************************************************************************************************/
133 typedef struct RMINSTANCE
135 PKMKBUILTINCTX pCtx;
136 int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
137 #ifdef KBUILD_OS_WINDOWS
138 int fUseNtDeleteFile;
139 #endif
140 uid_t uid;
141 KBUILDPROTECTION g_ProtData;
142 } RMINSTANCE;
143 typedef RMINSTANCE *PRMINSTANCE;
146 /*********************************************************************************************************************************
147 * Global Variables *
148 *********************************************************************************************************************************/
149 static struct option long_options[] =
151 { "help", no_argument, 0, 261 },
152 { "version", no_argument, 0, 262 },
153 { "disable-protection", no_argument, 0, 263 },
154 { "enable-protection", no_argument, 0, 264 },
155 { "enable-full-protection", no_argument, 0, 265 },
156 { "disable-full-protection", no_argument, 0, 266 },
157 { "protection-depth", required_argument, 0, 267 },
158 #ifdef KBUILD_OS_WINDOWS
159 { "nt-delete-file", no_argument, 0, 268 },
160 #endif
161 { 0, 0, 0, 0 },
165 /*********************************************************************************************************************************
166 * Internal Functions *
167 *********************************************************************************************************************************/
168 extern void bsd_strmode(mode_t mode, char *p); /* strmode.c */
170 static int check(PRMINSTANCE, char *, char *, struct stat *);
171 static void checkdot(PRMINSTANCE, char **);
172 static int rm_file(PRMINSTANCE, char **);
173 static int rm_overwrite(PRMINSTANCE, char *, struct stat *);
174 static int rm_tree(PRMINSTANCE, char **);
175 static int usage(PKMKBUILTINCTX, int);
180 * rm --
181 * This rm is different from historic rm's, but is expected to match
182 * POSIX 1003.2 behavior. The most visible difference is that -f
183 * has two specific effects now, ignore non-existent files and force
184 * file removal.
187 kmk_builtin_rm(int argc, char *argv[], char **envp, PKMKBUILTINCTX pCtx)
189 RMINSTANCE This;
190 struct getopt_state_r gos;
191 int ch, rflag;
193 /* Init global instance data */
194 This.pCtx = pCtx;
195 This.dflag = 0;
196 This.eval = 0;
197 This.fflag = 0;
198 This.iflag = 0;
199 This.Pflag = 0;
200 This.vflag = 0;
201 This.Wflag = 0;
202 This.stdin_ok = 0;
203 #ifdef KBUILD_OS_WINDOWS
204 This.fUseNtDeleteFile = 0;
205 #endif
206 This.uid = 0;
207 kBuildProtectionInit(&This.g_ProtData, pCtx);
209 rflag = 0;
210 getopt_initialize_r(&gos, argc, argv, "dfiPRvW", long_options, envp, pCtx);
211 while ((ch = getopt_long_r(&gos, NULL)) != -1)
212 switch (ch) {
213 case 'd':
214 This.dflag = 1;
215 break;
216 case 'f':
217 This.fflag = 1;
218 This.iflag = 0;
219 break;
220 case 'i':
221 This.fflag = 0;
222 This.iflag = 1;
223 break;
224 case 'P':
225 This.Pflag = 1;
226 break;
227 case 'R':
228 #if 0
229 case 'r': /* Compatibility. */
230 #endif
231 rflag = 1;
232 break;
233 case 'v':
234 This.vflag = 1;
235 break;
236 #ifdef FTS_WHITEOUT
237 case 'W':
238 This.Wflag = 1;
239 break;
240 #endif
241 case 261:
242 kBuildProtectionTerm(&This.g_ProtData);
243 usage(pCtx, 0);
244 return 0;
245 case 262:
246 kBuildProtectionTerm(&This.g_ProtData);
247 return kbuild_version(argv[0]);
248 case 263:
249 kBuildProtectionDisable(&This.g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
250 break;
251 case 264:
252 kBuildProtectionEnable(&This.g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
253 break;
254 case 265:
255 kBuildProtectionEnable(&This.g_ProtData, KBUILDPROTECTIONTYPE_FULL);
256 break;
257 case 266:
258 kBuildProtectionDisable(&This.g_ProtData, KBUILDPROTECTIONTYPE_FULL);
259 break;
260 case 267:
261 if (kBuildProtectionSetDepth(&This.g_ProtData, gos.optarg)) {
262 kBuildProtectionTerm(&This.g_ProtData);
263 return 1;
265 break;
266 #ifdef KBUILD_OS_WINDOWS
267 case 268:
268 This.fUseNtDeleteFile = 1;
269 break;
270 #endif
271 case '?':
272 default:
273 kBuildProtectionTerm(&This.g_ProtData);
274 return usage(pCtx, 1);
276 argc -= gos.optind;
277 argv += gos.optind;
279 if (argc < 1) {
280 kBuildProtectionTerm(&This.g_ProtData);
281 if (This.fflag)
282 return (0);
283 return usage(pCtx, 1);
286 if (!kBuildProtectionScanEnv(&This.g_ProtData, envp, "KMK_RM_")) {
287 checkdot(&This, argv);
288 This.uid = geteuid();
290 if (*argv) {
291 This.stdin_ok = isatty(STDIN_FILENO);
292 if (rflag)
293 This.eval |= rm_tree(&This, argv);
294 else
295 This.eval |= rm_file(&This, argv);
297 } else {
298 This.eval = 1;
301 kBuildProtectionTerm(&This.g_ProtData);
302 return This.eval;
305 #ifdef KMK_BUILTIN_STANDALONE
306 int main(int argc, char **argv, char **envp)
308 KMKBUILTINCTX Ctx = { "kmk_rm", NULL };
309 return kmk_builtin_rm(argc, argv, envp, &Ctx);
311 #endif
313 static int
314 rm_tree(PRMINSTANCE pThis, char **argv)
316 FTS *fts;
317 FTSENT *p;
318 int needstat;
319 int flags;
320 int rval;
323 * Check up front before anything is deleted. This will not catch
324 * everything, but we'll check the individual items later.
326 int i;
327 for (i = 0; argv[i]; i++) {
328 if (kBuildProtectionEnforce(&pThis->g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, argv[i])) {
329 return 1;
334 * Remove a file hierarchy. If forcing removal (-f), or interactive
335 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
337 needstat = !pThis->uid || (!pThis->fflag && !pThis->iflag && pThis->stdin_ok);
340 * If the -i option is specified, the user can skip on the pre-order
341 * visit. The fts_number field flags skipped directories.
343 #define SKIPPED 1
345 flags = FTS_PHYSICAL;
346 #ifndef KMK_BUILTIN_STANDALONE
347 flags |= FTS_NOCHDIR; /* Must not change the directory from inside kmk! */
348 #endif
349 if (!needstat)
350 flags |= FTS_NOSTAT;
351 #ifdef FTS_WHITEOUT
352 if (pThis->Wflag)
353 flags |= FTS_WHITEOUT;
354 #endif
355 if (!(fts = fts_open(argv, flags, NULL))) {
356 return err(pThis->pCtx, 1, "fts_open");
358 while ((p = fts_read(fts)) != NULL) {
359 const char *operation = "chflags";
360 switch (p->fts_info) {
361 case FTS_DNR:
362 if (!pThis->fflag || p->fts_errno != ENOENT)
363 pThis->eval = errx(pThis->pCtx, 1, "fts: %s: %s" CUR_LINE() "\n",
364 p->fts_path, strerror(p->fts_errno));
365 continue;
366 case FTS_ERR:
367 fts_close(fts);
368 return errx(pThis->pCtx, 1, "fts: %s: %s " CUR_LINE(), p->fts_path, strerror(p->fts_errno));
369 case FTS_NS:
371 * Assume that since fts_read() couldn't stat the
372 * file, it can't be unlinked.
374 if (!needstat)
375 break;
376 if (!pThis->fflag || p->fts_errno != ENOENT)
377 pThis->eval = errx(pThis->pCtx, 1, "fts: %s: %s " CUR_LINE() "\n",
378 p->fts_path, strerror(p->fts_errno));
379 continue;
380 case FTS_D:
381 /* Pre-order: give user chance to skip. */
382 if (!pThis->fflag && !check(pThis, p->fts_path, p->fts_accpath, p->fts_statp)) {
383 (void)fts_set(fts, p, FTS_SKIP);
384 p->fts_number = SKIPPED;
386 #ifdef UF_APPEND
387 else if (!pThis->uid &&
388 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
389 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
390 chflags(p->fts_accpath,
391 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
392 goto err;
393 #endif
394 continue;
395 case FTS_DP:
396 /* Post-order: see if user skipped. */
397 if (p->fts_number == SKIPPED)
398 continue;
399 break;
400 default:
401 if (!pThis->fflag && !check(pThis, p->fts_path, p->fts_accpath, p->fts_statp))
402 continue;
406 * Protect against deleting root files and directories.
408 if (kBuildProtectionEnforce(&pThis->g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, p->fts_accpath)) {
409 fts_close(fts);
410 return 1;
413 rval = 0;
414 #ifdef UF_APPEND
415 if (!pThis->uid &&
416 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
417 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
418 rval = chflags(p->fts_accpath,
419 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
420 #endif
421 if (rval == 0) {
423 * If we can't read or search the directory, may still be
424 * able to remove it. Don't print out the un{read,search}able
425 * message unless the remove fails.
427 switch (p->fts_info) {
428 case FTS_DP:
429 case FTS_DNR:
430 #ifdef KBUILD_OS_WINDOWS
431 if (p->fts_parent->fts_dirfd != NT_FTS_INVALID_HANDLE_VALUE) {
432 rval = birdUnlinkForcedEx(p->fts_parent->fts_dirfd, p->fts_name);
433 } else {
434 rval = birdUnlinkForced(p->fts_accpath);
436 #else
437 rval = rmdir(p->fts_accpath);
438 #endif
439 if (rval == 0 || (pThis->fflag && errno == ENOENT)) {
440 if (rval == 0 && pThis->vflag)
441 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s\n", p->fts_path);
442 #if defined(KMK) && defined(KBUILD_OS_WINDOWS)
443 if (rval == 0) {
444 extern int dir_cache_deleted_directory(const char *pszDir);
445 dir_cache_deleted_directory(p->fts_accpath);
447 #endif
448 continue;
450 operation = "rmdir";
451 break;
453 #ifdef FTS_W
454 case FTS_W:
455 rval = undelete(p->fts_accpath);
456 if (rval == 0 && (pThis->fflag && errno == ENOENT)) {
457 if (pThis->vflag)
458 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s\n", p->fts_path);
459 continue;
461 operation = "undelete";
462 break;
463 #endif
465 case FTS_NS:
467 * Assume that since fts_read() couldn't stat
468 * the file, it can't be unlinked.
470 if (pThis->fflag)
471 continue;
472 /* FALLTHROUGH */
473 default:
474 if (pThis->Pflag)
475 if (!rm_overwrite(pThis, p->fts_accpath, NULL))
476 continue;
477 #ifdef KBUILD_OS_WINDOWS
478 if (p->fts_parent->fts_dirfd != NT_FTS_INVALID_HANDLE_VALUE) {
479 if (p->fts_info != FTS_SL && p->fts_info != FTS_SLNONE) {
480 rval = birdUnlinkForcedFastEx(p->fts_parent->fts_dirfd, p->fts_name);
481 } else { /* NtDeleteFile doesn't work on directory links, so slow symlink deletion: */
482 rval = birdUnlinkForcedEx(p->fts_parent->fts_dirfd, p->fts_name);
484 } else {
485 if (p->fts_info != FTS_SL && p->fts_info != FTS_SLNONE) {
486 rval = birdUnlinkForcedFast(p->fts_accpath);
487 } else { /* NtDeleteFile doesn't work on directory links, so slow symlink deletion: */
488 rval = birdUnlinkForced(p->fts_accpath);
491 #else
492 rval = unlink(p->fts_accpath);
493 #endif
495 if (rval == 0 || (pThis->fflag && errno == ENOENT)) {
496 if (rval == 0 && pThis->vflag)
497 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s\n", p->fts_path);
498 continue;
500 operation = "unlink";
501 break;
504 #ifdef UF_APPEND
505 err:
506 #endif
507 pThis->eval = errx(pThis->pCtx, 1, "%s: %s failed: %s " CUR_LINE() "\n", p->fts_path, operation, strerror(errno));
509 if (errno) {
510 pThis->eval = errx(pThis->pCtx, 1, "fts_read: %s " CUR_LINE() "\n", strerror(errno));
512 fts_close(fts);
513 return pThis->eval;
516 static int
517 rm_file(PRMINSTANCE pThis, char **argv)
519 struct stat sb;
520 int rval;
521 char *f;
524 * Check up front before anything is deleted.
526 int i;
527 for (i = 0; argv[i]; i++) {
528 if (kBuildProtectionEnforce(&pThis->g_ProtData, KBUILDPROTECTIONTYPE_FULL, argv[i]))
529 return 1;
533 * Remove a file. POSIX 1003.2 states that, by default, attempting
534 * to remove a directory is an error, so must always stat the file.
536 while ((f = *argv++) != NULL) {
537 const char *operation = "?";
538 /* Assume if can't stat the file, can't unlink it. */
539 if (lstat(f, &sb)) {
540 #ifdef FTS_WHITEOUT
541 if (pThis->Wflag) {
542 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
543 } else {
544 #else
546 #endif
547 if (!pThis->fflag || errno != ENOENT)
548 pThis->eval = errx(pThis->pCtx, 1, "%s: lstat failed: %s " CUR_LINE() "\n",
549 f, strerror(errno));
550 continue;
552 #ifdef FTS_WHITEOUT
553 } else if (pThis->Wflag) {
554 errx(pThis->pCtx, 1, "%s: %s\n", f, strerror(EEXIST));
555 pThis->eval = 1;
556 continue;
557 #endif
560 if (S_ISDIR(sb.st_mode) && !pThis->dflag) {
561 pThis->eval = errx(pThis->pCtx, 1, "%s: is a directory\n", f);
562 continue;
564 if (!pThis->fflag && !S_ISWHT(sb.st_mode) && !check(pThis, f, f, &sb))
565 continue;
566 rval = 0;
567 #ifdef UF_APPEND
568 if (!pThis->uid &&
569 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
570 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
571 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
572 #endif
573 if (rval == 0) {
574 if (S_ISWHT(sb.st_mode)) {
575 rval = undelete(f);
576 operation = "undelete";
577 } else if (S_ISDIR(sb.st_mode)) {
578 rval = rmdir(f);
579 operation = "rmdir";
580 } else {
581 if (pThis->Pflag)
582 if (!rm_overwrite(pThis, f, &sb))
583 continue;
584 #ifndef KBUILD_OS_WINDOWS
585 rval = unlink(f);
586 operation = "unlink";
587 #else
588 if (pThis->fUseNtDeleteFile) {
589 rval = birdUnlinkForcedFast(f);
590 operation = "NtDeleteFile";
591 } else {
592 rval = birdUnlinkForced(f);
593 operation = "unlink";
595 #endif
598 if (rval && (!pThis->fflag || errno != ENOENT))
599 pThis->eval = errx(pThis->pCtx, 1, "%s: %s failed: %s" CUR_LINE() "\n", f, operation, strerror(errno));
600 if (pThis->vflag && rval == 0)
601 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s\n", f);
603 return pThis->eval;
607 * rm_overwrite --
608 * Overwrite the file 3 times with varying bit patterns.
610 * XXX
611 * This is a cheap way to *really* delete files. Note that only regular
612 * files are deleted, directories (and therefore names) will remain.
613 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
614 * System V file system). In a logging file system, you'll have to have
615 * kernel support.
617 static int
618 rm_overwrite(PRMINSTANCE pThis, char *file, struct stat *sbp)
620 struct stat sb;
621 #ifdef HAVE_FSTATFS
622 struct statfs fsb;
623 #endif
624 off_t len;
625 int bsize, fd, wlen;
626 char *buf = NULL;
627 const char *operation = "lstat";
628 int error;
630 fd = -1;
631 if (sbp == NULL) {
632 if (lstat(file, &sb))
633 goto err;
634 sbp = &sb;
636 if (!S_ISREG(sbp->st_mode))
637 return (1);
638 operation = "open";
639 if ((fd = open(file, O_WRONLY | KMK_OPEN_NO_INHERIT, 0)) == -1)
640 goto err;
641 #ifdef HAVE_FSTATFS
642 if (fstatfs(fd, &fsb) == -1)
643 goto err;
644 bsize = MAX(fsb.f_iosize, 1024);
645 #elif defined(HAVE_ST_BLKSIZE)
646 bsize = MAX(sb.st_blksize, 1024);
647 #else
648 bsize = 1024;
649 #endif
650 if ((buf = malloc(bsize)) == NULL) {
651 err(pThis->pCtx, 1, "%s: malloc", file);
652 close(fd);
653 return 1;
656 #define PASS(byte) { \
657 operation = "write"; \
658 memset(buf, byte, bsize); \
659 for (len = sbp->st_size; len > 0; len -= wlen) { \
660 wlen = len < bsize ? len : bsize; \
661 if (write(fd, buf, wlen) != wlen) \
662 goto err; \
665 PASS(0xff);
666 operation = "fsync/lseek";
667 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
668 goto err;
669 PASS(0x00);
670 operation = "fsync/lseek";
671 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
672 goto err;
673 PASS(0xff);
674 if (!fsync(fd) && !close(fd)) {
675 free(buf);
676 return (1);
678 operation = "fsync/close";
680 err: pThis->eval = 1;
681 error = errno;
682 if (buf)
683 free(buf);
684 if (fd != -1)
685 close(fd);
686 errx(pThis->pCtx, 1, "%s: %s: %s: %s" CUR_LINE() "\n", operation, pThis->pCtx->pszProgName, file, strerror(error));
687 return (0);
691 static int
692 check(PRMINSTANCE pThis, char *path, char *name, struct stat *sp)
694 int ch, first;
695 char modep[15], *flagsp;
697 /* Check -i first. */
698 if (pThis->iflag)
699 (void)fprintf(stderr, "%s: remove %s? ", pThis->pCtx->pszProgName, path);
700 else {
702 * If it's not a symbolic link and it's unwritable and we're
703 * talking to a terminal, ask. Symbolic links are excluded
704 * because their permissions are meaningless. Check stdin_ok
705 * first because we may not have stat'ed the file.
706 * Also skip this check if the -P option was specified because
707 * we will not be able to overwrite file contents and will
708 * barf later.
710 if (!pThis->stdin_ok || S_ISLNK(sp->st_mode) || pThis->Pflag ||
711 (!access(name, W_OK) &&
712 #ifdef SF_APPEND
713 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
714 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !pThis->uid))
715 #else
717 #endif
719 return (1);
720 bsd_strmode(sp->st_mode, modep);
721 #if defined(SF_APPEND) && K_OS != K_OS_GNU_KFBSD && K_OS != K_OS_GNU_HURD
722 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) {
723 err(pThis->pCtx, 1, "fflagstostr");
724 flagsp = "<bad-fflagstostr>";
726 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
727 modep + 1, modep[9] == ' ' ? "" : " ",
728 user_from_uid(sp->st_uid, 0),
729 group_from_gid(sp->st_gid, 0),
730 *flagsp ? flagsp : "", *flagsp ? " " : "",
731 path);
732 free(flagsp);
733 #else
734 (void)flagsp;
735 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
736 modep + 1, modep[9] == ' ' ? "" : " ",
737 sp->st_uid, sp->st_gid, path);
738 #endif
740 (void)fflush(stderr);
742 first = ch = getchar();
743 while (ch != '\n' && ch != EOF)
744 ch = getchar();
745 return (first == 'y' || first == 'Y');
748 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
749 static void
750 checkdot(PRMINSTANCE pThis, char **argv)
752 char *p, **save, **t;
753 int complained;
755 complained = 0;
756 for (t = argv; *t;) {
757 #ifdef HAVE_DOS_PATHS
758 const char *tmp = p = *t;
759 while (*tmp) {
760 switch (*tmp) {
761 case '/':
762 case '\\':
763 case ':':
764 p = (char *)tmp + 1;
765 break;
767 tmp++;
769 #else
770 if ((p = strrchr(*t, '/')) != NULL)
771 ++p;
772 else
773 p = *t;
774 #endif
775 if (ISDOT(p)) {
776 if (!complained++)
777 warnx(pThis->pCtx, "\".\" and \"..\" may not be removed\n");
778 pThis->eval = 1;
779 for (save = t; (t[0] = t[1]) != NULL; ++t)
780 continue;
781 t = save;
782 } else
783 ++t;
787 static int
788 usage(PKMKBUILTINCTX pCtx, int fIsErr)
790 kmk_builtin_ctx_printf(pCtx, fIsErr,
791 "usage: %s [options] file ...\n"
792 " or: %s --help\n"
793 " or: %s --version\n"
794 "\n"
795 "Options:\n"
796 " -f\n"
797 " Attempt to remove files without prompting, regardless of the file\n"
798 " permission. Ignore non-existing files. Overrides previous -i's.\n"
799 " -i\n"
800 " Prompt for each file. Always.\n"
801 " -d\n"
802 " Attempt to remove directories as well as other kinds of files.\n"
803 " -P\n"
804 " Overwrite regular files before deleting; three passes: ff,0,ff\n"
805 " -R\n"
806 " Attempt to remove the file hierachy rooted in each file argument.\n"
807 " This option implies -d and file protection.\n"
808 " -v\n"
809 " Be verbose, show files as they are removed.\n"
810 " -W\n"
811 " Undelete white-out files.\n"
812 " --disable-protection\n"
813 " Will disable the protection file protection applied with -R.\n"
814 " --enable-protection\n"
815 " Will enable the protection file protection applied with -R.\n"
816 " --enable-full-protection\n"
817 " Will enable the protection file protection for all operations.\n"
818 " --disable-full-protection\n"
819 " Will disable the protection file protection for all operations.\n"
820 " --protection-depth\n"
821 " Number or path indicating the file protection depth. Default: %d\n"
822 "\n"
823 "Environment:\n"
824 " KMK_RM_DISABLE_PROTECTION\n"
825 " Same as --disable-protection. Overrides command line.\n"
826 " KMK_RM_ENABLE_PROTECTION\n"
827 " Same as --enable-protection. Overrides everyone else.\n"
828 " KMK_RM_ENABLE_FULL_PROTECTION\n"
829 " Same as --enable-full-protection. Overrides everyone else.\n"
830 " KMK_RM_DISABLE_FULL_PROTECTION\n"
831 " Same as --disable-full-protection. Overrides command line.\n"
832 " KMK_RM_PROTECTION_DEPTH\n"
833 " Same as --protection-depth. Overrides command line.\n"
834 "\n"
835 "The file protection of the top %d layers of the file hierarchy is there\n"
836 "to try prevent makefiles from doing bad things to your system. This\n"
837 "protection is not bulletproof, but should help prevent you from shooting\n"
838 "yourself in the foot.\n"
840 pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName,
841 kBuildProtectionDefaultDepth(), kBuildProtectionDefaultDepth());
842 return EX_USAGE;