~cfuhrman/+junk/netbsd-othersrc-trunk

« back to all changes in this revision

Viewing changes to bin/rm/rm.c

  • Committer: stacktic
  • Date: 2009-03-23 21:04:00 UTC
  • Revision ID: svn-v4:288d5a72-fed7-e111-8680-000c29dcf8fe:trunk:1946
ImportedĀ fs-utilsĀ binaries

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $NetBSD$ */
 
2
/* from */
 
3
/* NetBSD: rm.c,v 1.49 2009/02/14 08:05:04 lukem Exp  */
 
4
 
 
5
/*-
 
6
 * Copyright (c) 1990, 1993, 1994, 2003
 
7
 *      The Regents of the University of California.  All rights reserved.
 
8
 *
 
9
 * Redistribution and use in source and binary forms, with or without
 
10
 * modification, are permitted provided that the following conditions
 
11
 * are met:
 
12
 * 1. Redistributions of source code must retain the above copyright
 
13
 *    notice, this list of conditions and the following disclaimer.
 
14
 * 2. Redistributions in binary form must reproduce the above copyright
 
15
 *    notice, this list of conditions and the following disclaimer in the
 
16
 *    documentation and/or other materials provided with the distribution.
 
17
 * 3. Neither the name of the University nor the names of its contributors
 
18
 *    may be used to endorse or promote products derived from this software
 
19
 *    without specific prior written permission.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
31
 * SUCH DAMAGE.
 
32
 */
 
33
 
 
34
#include <sys/cdefs.h>
 
35
#ifndef lint
 
36
__COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
 
37
 The Regents of the University of California.  All rights reserved.");
 
38
#endif /* not lint */
 
39
 
 
40
#ifndef lint
 
41
#if 0
 
42
static char sccsid[] = "@(#)rm.c        8.8 (Berkeley) 4/27/95";
 
43
#else
 
44
__RCSID("$NetBSD: rm.c,v 1.49 2009/02/14 08:05:04 lukem Exp $");
 
45
#endif
 
46
#endif /* not lint */
 
47
 
 
48
#include <sys/param.h>
 
49
#include <sys/stat.h>
 
50
#include <sys/types.h>
 
51
 
 
52
#include <err.h>
 
53
#include <errno.h>
 
54
#include <fcntl.h>
 
55
#ifndef USE_UKFS
 
56
#include <fts.h>
 
57
#endif
 
58
#include <grp.h>
 
59
#include <locale.h>
 
60
#include <pwd.h>
 
61
#include <stdio.h>
 
62
#include <stdlib.h>
 
63
#include <string.h>
 
64
#include <unistd.h>
 
65
 
 
66
#ifdef USE_UKFS
 
67
#include <rump/ukfs.h>
 
68
 
 
69
#include <fts2fsufts.h>
 
70
#include <fsu_mount.h>
 
71
 
 
72
#define lstat(a, b) ukfs_lstat(ukfs, a, b)
 
73
#define rmdir(a) ukfs_rmdir(ukfs, a)
 
74
#define unlink(a) ukfs_remove(ukfs, a)
 
75
#define undelete(a) (0) /* not supported */
 
76
#define access(a, b) (0) /* we always have access */
 
77
 
 
78
DECLARE_UKFS(ukfs)
 
79
#endif
 
80
 
 
81
int dflag, eval, fflag, iflag, Pflag, stdin_ok, vflag, Wflag;
 
82
 
 
83
int     check(char *, char *, struct stat *);
 
84
void    checkdot(char **);
 
85
void    rm_file(char **);
 
86
int     rm_overwrite(char *, struct stat *);
 
87
void    rm_tree(char **);
 
88
void    usage(void);
 
89
int     main(int, char *[]);
 
90
 
 
91
/*
 
92
 * For the sake of the `-f' flag, check whether an error number indicates the
 
93
 * failure of an operation due to an non-existent file, either per se (ENOENT)
 
94
 * or because its filename argument was illegal (ENAMETOOLONG, ENOTDIR).
 
95
 */
 
96
#define NONEXISTENT(x) \
 
97
    ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR)
 
98
 
 
99
/*
 
100
 * rm --
 
101
 *      This rm is different from historic rm's, but is expected to match
 
102
 *      POSIX 1003.2 behavior.  The most visible difference is that -f
 
103
 *      has two specific effects now, ignore non-existent files and force
 
104
 *      file removal.
 
105
 */
 
106
int
 
107
main(int argc, char *argv[])
 
108
{
 
109
        int ch, rflag;
 
110
 
 
111
        setprogname(argv[0]);
 
112
        (void)setlocale(LC_ALL, "");
 
113
 
 
114
#ifdef USE_UKFS
 
115
        FSU_MOUNT(argc, argv, ukfs);
 
116
#endif
 
117
 
 
118
        Pflag = rflag = 0;
 
119
        while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1)
 
120
                switch (ch) {
 
121
                case 'd':
 
122
                        dflag = 1;
 
123
                        break;
 
124
                case 'f':
 
125
                        fflag = 1;
 
126
                        iflag = 0;
 
127
                        break;
 
128
                case 'i':
 
129
                        fflag = 0;
 
130
                        iflag = 1;
 
131
                        break;
 
132
                case 'P':
 
133
                        Pflag = 1;
 
134
                        break;
 
135
                case 'R':
 
136
                case 'r':                       /* Compatibility. */
 
137
                        rflag = 1;
 
138
                        break;
 
139
                case 'v':
 
140
                        vflag = 1;
 
141
                        break;
 
142
                case 'W':
 
143
                        Wflag = 1;
 
144
                        break;
 
145
                case '?':
 
146
                default:
 
147
                        usage();
 
148
                }
 
149
        argc -= optind;
 
150
        argv += optind;
 
151
 
 
152
        if (argc < 1) {
 
153
                if (fflag)
 
154
                        return 0;
 
155
                usage();
 
156
        }
 
157
 
 
158
        checkdot(argv);
 
159
 
 
160
        if (*argv) {
 
161
                stdin_ok = isatty(STDIN_FILENO);
 
162
 
 
163
                if (rflag)
 
164
                        rm_tree(argv);
 
165
                else
 
166
                        rm_file(argv);
 
167
        }
 
168
 
 
169
        exit(eval);
 
170
        /* NOTREACHED */
 
171
}
 
172
 
 
173
void
 
174
rm_tree(char **argv)
 
175
{
 
176
        FTS *fts;
 
177
        FTSENT *p;
 
178
        int flags, needstat, rval;
 
179
                        
 
180
        /*
 
181
         * Remove a file hierarchy.  If forcing removal (-f), or interactive
 
182
         * (-i) or can't ask anyway (stdin_ok), don't stat the file.
 
183
         */
 
184
        needstat = !fflag && !iflag && stdin_ok;
 
185
 
 
186
        /*
 
187
         * If the -i option is specified, the user can skip on the pre-order
 
188
         * visit.  The fts_number field flags skipped directories.
 
189
         */
 
190
#define SKIPPED 1
 
191
 
 
192
        flags = FTS_PHYSICAL;
 
193
        if (!needstat)
 
194
                flags |= FTS_NOSTAT;
 
195
        if (Wflag)
 
196
                flags |= FTS_WHITEOUT;
 
197
        if ((fts = fts_open(argv, flags, NULL)) == NULL)
 
198
                err(1, "fts_open failed");
 
199
        while ((p = fts_read(fts)) != NULL) {
 
200
        
 
201
                switch (p->fts_info) {
 
202
                case FTS_DNR:
 
203
                        if (!fflag || p->fts_errno != ENOENT) {
 
204
                                warnx("%s: %s", p->fts_path,
 
205
                                                strerror(p->fts_errno));
 
206
                                eval = 1;
 
207
                        }
 
208
                        continue;
 
209
                case FTS_ERR:
 
210
                        errx(EXIT_FAILURE, "%s: %s", p->fts_path,
 
211
                                        strerror(p->fts_errno));
 
212
                        /* NOTREACHED */
 
213
                case FTS_NS:
 
214
                        /*
 
215
                         * FTS_NS: assume that if can't stat the file, it
 
216
                         * can't be unlinked.
 
217
                         */
 
218
                        if (fflag && NONEXISTENT(p->fts_errno))
 
219
                                continue;
 
220
                        if (needstat) {
 
221
                                warnx("%s: %s", p->fts_path,
 
222
                                                strerror(p->fts_errno));
 
223
                                eval = 1;
 
224
                                continue;
 
225
                        }
 
226
                        break;
 
227
                case FTS_D:
 
228
                        /* Pre-order: give user chance to skip. */
 
229
                        if (!fflag && !check(p->fts_path, p->fts_accpath,
 
230
                            p->fts_statp)) {
 
231
                                (void)fts_set(fts, p, FTS_SKIP);
 
232
                                p->fts_number = SKIPPED;
 
233
                        }
 
234
                        continue;
 
235
                case FTS_DP:
 
236
                        /* Post-order: see if user skipped. */
 
237
                        if (p->fts_number == SKIPPED)
 
238
                                continue;
 
239
                        break;
 
240
                default:
 
241
                        if (!fflag &&
 
242
                            !check(p->fts_path, p->fts_accpath, p->fts_statp))
 
243
                                continue;
 
244
                }
 
245
 
 
246
                rval = 0;
 
247
                /*
 
248
                 * If we can't read or search the directory, may still be
 
249
                 * able to remove it.  Don't print out the un{read,search}able
 
250
                 * message unless the remove fails.
 
251
                 */
 
252
                switch (p->fts_info) {
 
253
                case FTS_DP:
 
254
                case FTS_DNR:
 
255
                        rval = rmdir(p->fts_accpath);
 
256
                        if (rval != 0 && fflag && errno == ENOENT)
 
257
                                continue;
 
258
                        break;
 
259
 
 
260
                case FTS_W:
 
261
                        rval = undelete(p->fts_accpath);
 
262
                        if (rval != 0 && fflag && errno == ENOENT)
 
263
                                continue;
 
264
                        break;
 
265
 
 
266
                default:
 
267
                        if (Pflag) {
 
268
                                if (rm_overwrite(p->fts_accpath, NULL))
 
269
                                        continue;
 
270
                        }
 
271
                        rval = unlink(p->fts_accpath);
 
272
                        if (rval != 0 && fflag && NONEXISTENT(errno))
 
273
                                continue;
 
274
                        break;
 
275
                }
 
276
                if (rval != 0) {
 
277
                        warn("%s", p->fts_path);
 
278
                        eval = 1;
 
279
                } else if (vflag)
 
280
                        (void)printf("%s\n", p->fts_path);
 
281
        }
 
282
        if (errno)
 
283
                err(1, "fts_read");
 
284
        fts_close(fts);
 
285
}
 
286
 
 
287
void
 
288
rm_file(char **argv)
 
289
{
 
290
        struct stat sb;
 
291
        int rval;
 
292
        char *f;
 
293
 
 
294
        /*
 
295
         * Remove a file.  POSIX 1003.2 states that, by default, attempting
 
296
         * to remove a directory is an error, so must always stat the file.
 
297
         */
 
298
        while ((f = *argv++) != NULL) {
 
299
                /* Assume if can't stat the file, can't unlink it. */
 
300
                if (lstat(f, &sb)) {
 
301
                        if (Wflag) {
 
302
                                sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
 
303
                        } else {
 
304
                                if (!fflag || !NONEXISTENT(errno)) {
 
305
                                        warn("%s", f);
 
306
                                        eval = 1;
 
307
                                }
 
308
                                continue;
 
309
                        }
 
310
                } else if (Wflag) {
 
311
                        warnx("%s: %s", f, strerror(EEXIST));
 
312
                        eval = 1;
 
313
                        continue;
 
314
                }
 
315
 
 
316
                if (S_ISDIR(sb.st_mode) && !dflag) {
 
317
                        warnx("%s: is a directory", f);
 
318
                        eval = 1;
 
319
                        continue;
 
320
                }
 
321
                if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
 
322
                        continue;
 
323
                if (S_ISWHT(sb.st_mode))
 
324
                        rval = undelete(f);
 
325
                else if (S_ISDIR(sb.st_mode))
 
326
                        rval = rmdir(f);
 
327
                else {
 
328
                        if (Pflag) {
 
329
                                if (rm_overwrite(f, &sb))
 
330
                                        continue;
 
331
                        }
 
332
                        rval = unlink(f);
 
333
                }
 
334
                if (rval && (!fflag || !NONEXISTENT(errno))) {
 
335
                        warn("%s", f);
 
336
                        eval = 1;
 
337
                }
 
338
                if (vflag && rval == 0)
 
339
                        (void)printf("%s\n", f);
 
340
        }
 
341
}
 
342
 
 
343
/*
 
344
 * rm_overwrite --
 
345
 *      Overwrite the file 3 times with varying bit patterns.
 
346
 *
 
347
 * This is an expensive way to keep people from recovering files from your
 
348
 * non-snapshotted FFS filesystems using fsdb(8).  Really.  No more.  Only
 
349
 * regular files are deleted, directories (and therefore names) will remain.
 
350
 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
 
351
 * System V file system).  In a logging file system, you'll have to have
 
352
 * kernel support.
 
353
 *
 
354
 * A note on standards:  U.S. DoD 5220.22-M "National Industrial Security
 
355
 * Program Operating Manual" ("NISPOM") is often cited as a reference
 
356
 * for clearing and sanitizing magnetic media.  In fact, a matrix of
 
357
 * "clearing" and "sanitization" methods for various media was given in
 
358
 * Chapter 8 of the original 1995 version of NISPOM.  However, that
 
359
 * matrix was *removed from the document* when Chapter 8 was rewritten
 
360
 * in Change 2 to the document in 2001.  Recently, the Defense Security
 
361
 * Service has made a revised clearing and sanitization matrix available
 
362
 * in Microsoft Word format on the DSS web site.  The standardization
 
363
 * status of this matrix is unclear.  Furthermore, one must be very
 
364
 * careful when referring to this matrix: it is intended for the "clearing"
 
365
 * prior to reuse or "sanitization" prior to disposal of *entire media*,
 
366
 * not individual files and the only non-physically-destructive method of
 
367
 * "sanitization" that is permitted for magnetic disks of any kind is
 
368
 * specifically noted to be prohibited for media that have contained
 
369
 * Top Secret data.
 
370
 *
 
371
 * It is impossible to actually conform to the exact procedure given in
 
372
 * the matrix if one is overwriting a file, not an entire disk, because
 
373
 * the procedure requires examination and comparison of the disk's defect
 
374
 * lists.  Any program that claims to securely erase *files* while 
 
375
 * conforming to the standard, then, is not correct.  We do as much of
 
376
 * what the standard requires as can actually be done when erasing a
 
377
 * file, rather than an entire disk; but that does not make us conformant.
 
378
 *
 
379
 * Furthermore, the presence of track caches, disk and controller write
 
380
 * caches, and so forth make it extremely difficult to ensure that data
 
381
 * have actually been written to the disk, particularly when one tries
 
382
 * to repeatedly overwrite the same sectors in quick succession.  We call
 
383
 * fsync(), but controllers with nonvolatile cache, as well as IDE disks
 
384
 * that just plain lie about the stable storage of data, will defeat this.
 
385
 *
 
386
 * Finally, widely respected research suggests that the given procedure
 
387
 * is nowhere near sufficient to prevent the recovery of data using special
 
388
 * forensic equipment and techniques that are well-known.  This is 
 
389
 * presumably one reason that the matrix requires physical media destruction,
 
390
 * rather than any technique of the sort attempted here, for secret data.
 
391
 *
 
392
 * Caveat Emptor.
 
393
 *
 
394
 * rm_overwrite will return 0 on success.
 
395
 */
 
396
 
 
397
int
 
398
rm_overwrite(char *file, struct stat *sbp)
 
399
{
 
400
#ifdef USE_UKFS
 
401
        return 0;
 
402
#endif
 
403
        struct stat sb;
 
404
        int fd, randint;
 
405
        char randchar;
 
406
 
 
407
        fd = -1;
 
408
        if (sbp == NULL) {
 
409
                if (lstat(file, &sb))
 
410
                        goto err;
 
411
                sbp = &sb;
 
412
        }
 
413
        if (!S_ISREG(sbp->st_mode))
 
414
                return 0;
 
415
 
 
416
        /* flags to try to defeat hidden caching by forcing seeks */
 
417
        if ((fd = open(file, O_RDWR|O_SYNC|O_RSYNC, 0)) == -1)
 
418
                goto err;
 
419
 
 
420
#define RAND_BYTES      1
 
421
#define THIS_BYTE       0
 
422
 
 
423
#define WRITE_PASS(mode, byte) do {                                     \
 
424
        off_t len;                                                      \
 
425
        size_t wlen, i;                                                 \
 
426
        char buf[8 * 1024];                                             \
 
427
                                                                        \
 
428
        if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))                 \
 
429
                goto err;                                               \
 
430
                                                                        \
 
431
        if (mode == THIS_BYTE)                                          \
 
432
                memset(buf, byte, sizeof(buf));                         \
 
433
        for (len = sbp->st_size; len > 0; len -= wlen) {                \
 
434
                if (mode == RAND_BYTES) {                               \
 
435
                        for (i = 0; i < sizeof(buf);                    \
 
436
                            i+= sizeof(u_int32_t))                      \
 
437
                                *(int *)(buf + i) = arc4random();       \
 
438
                }                                                       \
 
439
                wlen = len < (off_t)sizeof(buf) ? (size_t)len : sizeof(buf); \
 
440
                if ((size_t)write(fd, buf, wlen) != wlen)               \
 
441
                        goto err;                                       \
 
442
        }                                                               \
 
443
        sync();         /* another poke at hidden caches */             \
 
444
} while (/* CONSTCOND */ 0)
 
445
 
 
446
#define READ_PASS(byte) do {                                            \
 
447
        off_t len;                                                      \
 
448
        size_t rlen;                                                    \
 
449
        char pattern[8 * 1024];                                         \
 
450
        char buf[8 * 1024];                                             \
 
451
                                                                        \
 
452
        if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))                 \
 
453
                goto err;                                               \
 
454
                                                                        \
 
455
        memset(pattern, byte, sizeof(pattern));                         \
 
456
        for(len = sbp->st_size; len > 0; len -= rlen) {                 \
 
457
                rlen = len < (off_t)sizeof(buf) ? (size_t)len : sizeof(buf); \
 
458
                if((size_t)read(fd, buf, rlen) != rlen)                 \
 
459
                        goto err;                                       \
 
460
                if(memcmp(buf, pattern, rlen))                          \
 
461
                        goto err;                                       \
 
462
        }                                                               \
 
463
        sync();         /* another poke at hidden caches */             \
 
464
} while (/* CONSTCOND */ 0)
 
465
 
 
466
        /*
 
467
         * DSS sanitization matrix "clear" for magnetic disks: 
 
468
         * option 'c' "Overwrite all addressable locations with a single 
 
469
         * character."
 
470
         */
 
471
        randint = arc4random();
 
472
        randchar = *(char *)&randint;
 
473
        WRITE_PASS(THIS_BYTE, randchar);
 
474
 
 
475
        /*
 
476
         * DSS sanitization matrix "sanitize" for magnetic disks: 
 
477
         * option 'd', sub 2 "Overwrite all addressable locations with a
 
478
         * character, then its complement.  Verify "complement" character
 
479
         * was written successfully to all addressable locations, then
 
480
         * overwrite all addressable locations with random characters; or
 
481
         * verify third overwrite of random characters."  The rest of the
 
482
         * text in d-sub-2 specifies requirements for overwriting spared
 
483
         * sectors; we cannot conform to it when erasing only a file, thus
 
484
         * we do not conform to the standard.
 
485
         */
 
486
 
 
487
        /* 1. "a character" */
 
488
        WRITE_PASS(THIS_BYTE, 0xff);
 
489
 
 
490
        /* 2. "its complement" */
 
491
        WRITE_PASS(THIS_BYTE, 0x00);
 
492
 
 
493
        /* 3. "Verify 'complement' character" */
 
494
        READ_PASS(0x00);
 
495
 
 
496
        /* 4. "overwrite all addressable locations with random characters" */
 
497
 
 
498
        WRITE_PASS(RAND_BYTES, 0x00);
 
499
 
 
500
        /*
 
501
         * As the file might be huge, and we note that this revision of
 
502
         * the matrix says "random characters", not "a random character"
 
503
         * as the original did, we do not verify the random-character
 
504
         * write; the "or" in the standard allows this.
 
505
         */
 
506
 
 
507
        if (close(fd) == -1) {
 
508
                fd = -1;
 
509
                goto err;
 
510
        }
 
511
 
 
512
        return 0;
 
513
 
 
514
err:    eval = 1;
 
515
        warn("%s", file);
 
516
        if (fd != -1)
 
517
                close(fd);
 
518
        return 1;
 
519
}
 
520
 
 
521
int
 
522
check(char *path, char *name, struct stat *sp)
 
523
{
 
524
        int ch, first;
 
525
        char modep[15];
 
526
 
 
527
        /* Check -i first. */
 
528
        if (iflag)
 
529
                (void)fprintf(stderr, "remove '%s'? ", path);
 
530
        else {
 
531
                /*
 
532
                 * If it's not a symbolic link and it's unwritable and we're
 
533
                 * talking to a terminal, ask.  Symbolic links are excluded
 
534
                 * because their permissions are meaningless.  Check stdin_ok
 
535
                 * first because we may not have stat'ed the file.
 
536
                 */
 
537
                if (!stdin_ok || S_ISLNK(sp->st_mode) ||
 
538
                    !(access(name, W_OK) && (errno != ETXTBSY)))
 
539
                        return (1);
 
540
                strmode(sp->st_mode, modep);
 
541
                if (Pflag) {
 
542
                        warnx(
 
543
                            "%s: -P was specified but file could not"
 
544
                            " be overwritten", path);
 
545
                        return 0;
 
546
                }
 
547
                (void)fprintf(stderr, "override %s%s%s:%s for '%s'? ",
 
548
                    modep + 1, modep[9] == ' ' ? "" : " ",
 
549
                    user_from_uid(sp->st_uid, 0),
 
550
                    group_from_gid(sp->st_gid, 0), path);
 
551
        }
 
552
        (void)fflush(stderr);
 
553
 
 
554
        first = ch = getchar();
 
555
        while (ch != '\n' && ch != EOF)
 
556
                ch = getchar();
 
557
        return (first == 'y' || first == 'Y');
 
558
}
 
559
 
 
560
/*
 
561
 * POSIX.2 requires that if "." or ".." are specified as the basename
 
562
 * portion of an operand, a diagnostic message be written to standard
 
563
 * error and nothing more be done with such operands.
 
564
 *
 
565
 * Since POSIX.2 defines basename as the final portion of a path after
 
566
 * trailing slashes have been removed, we'll remove them here.
 
567
 */
 
568
#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
 
569
void
 
570
checkdot(char **argv)
 
571
{
 
572
        char *p, **save, **t;
 
573
        int complained;
 
574
 
 
575
        complained = 0;
 
576
        for (t = argv; *t;) {
 
577
                /* strip trailing slashes */
 
578
                p = strrchr(*t, '\0');
 
579
                while (--p > *t && *p == '/')
 
580
                        *p = '\0';
 
581
 
 
582
                /* extract basename */
 
583
                if ((p = strrchr(*t, '/')) != NULL)
 
584
                        ++p;
 
585
                else
 
586
                        p = *t;
 
587
 
 
588
                if (ISDOT(p)) {
 
589
                        if (!complained++)
 
590
                                warnx("\".\" and \"..\" may not be removed");
 
591
                        eval = 1;
 
592
                        for (save = t; (t[0] = t[1]) != NULL; ++t)
 
593
                                continue;
 
594
                        t = save;
 
595
                } else
 
596
                        ++t;
 
597
        }
 
598
}
 
599
 
 
600
void
 
601
usage(void)
 
602
{
 
603
 
 
604
 
 
605
#ifdef USE_UKFS
 
606
        (void)fprintf(stderr, "usage: %s %s [-f|-i] [-dPRrvW] file ...\n",
 
607
            getprogname(), fsu_mount_usage());
 
608
#else
 
609
        (void)fprintf(stderr, "usage: %s [-f|-i] [-dPRrvW] file ...\n",
 
610
            getprogname());
 
611
#endif
 
612
 
 
613
        exit(1);
 
614
        /* NOTREACHED */
 
615
}