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

Last change on this file since 3682 was 3682, checked in by bird, 4 weeks ago

lib/nt,kmk: Fixed around rm/unlink semantics and general support for long file names in lib/nt.

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