source: rtems/cpukit/libmisc/shell/main_rm.c @ 575babc

4.104.114.95
Last change on this file since 575babc was 575babc, checked in by Ralf Corsepius <ralf.corsepius@…>, on 08/21/08 at 12:29:02

Include "config.h".

  • Property mode set to 100644
File size: 16.7 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#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#if 0
35#ifndef lint
36static const char copyright[] =
37"@(#) Copyright (c) 1990, 1993, 1994\n\
38        The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42static char sccsid[] = "@(#)rm.c        8.5 (Berkeley) 4/18/94";
43#endif /* not lint */
44#endif
45#if 0
46#include <sys/cdefs.h>
47__FBSDID("$FreeBSD: src/bin/rm/rm.c,v 1.58 2006/10/31 02:22:36 delphij Exp $");
48#endif
49
50#include <rtems.h>
51#include <rtems/shell.h>
52#include <rtems/shellconfig.h>
53#include <getopt.h>
54
55#include <sys/stat.h>
56#include <sys/param.h>
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#include <unistd.h>
68
69/* RTEMS specific changes */
70typedef struct {
71  int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
72  int rflag, Iflag;
73  uid_t uid;
74  int exit_code;
75  jmp_buf exit_jmp;
76} rtems_shell_rm_globals;
77
78#define dflag     globals->dflag
79#define eval      globals->eval
80#define fflag     globals->fflag
81#define iflag     globals->iflag
82#define Pflag     globals->Pflag
83#define vflag     globals->vflag
84#define Wflag     globals->Wflag
85#define stdin_ok  globals->stdin_ok
86#define rflag     globals->rflag
87#define Iflag     globals->Iflag
88#define xuid       globals->uid
89#define exit_jump &(globals->exit_jmp)
90
91#include <setjmp.h>
92
93#define exit(ec) rtems_shell_rm_exit(globals, ec)
94void
95rtems_shell_rm_exit (rtems_shell_rm_globals* globals, int code)
96{
97  globals->exit_code = code;
98  longjmp (globals->exit_jmp, 1);
99}
100
101static int main_rm(rtems_shell_rm_globals* globals, int argc, char *argv[]);
102
103int
104rtems_shell_main_rm(int argc, char *argv[])
105{
106  rtems_shell_rm_globals rm_globals;
107  memset (&rm_globals, 0, sizeof (rm_globals));
108  rm_globals.exit_code = 1;
109  if (setjmp (rm_globals.exit_jmp) == 0)
110    return main_rm (&rm_globals, argc, argv);
111  return rm_globals.exit_code;
112}
113
114#define check(a1, a2, a3)    check_rm(globals, a1, a2, a3)
115#define check2(a1)           check2_rm(globals, a1)
116#define checkdot(a1)         checkdot_rm(globals, a1)
117#define checkslash(a1)       checkslash_rm(globals, a1)
118#define rm_file(a1)          rm_file_rm(globals, a1)
119#define rm_overwrite(a1, a2) rm_overwrite_rm(globals, a1, a2)
120#define rm_tree(a1)          rm_tree_rm(globals, a1)
121#define usage()              usage_rm(globals)
122
123
124/* RTEMS changes */
125
126static int      check_rm(rtems_shell_rm_globals* globals, char *, char *, struct stat *);
127static int      check2_rm(rtems_shell_rm_globals* globals, char **);
128static void     checkdot_rm(rtems_shell_rm_globals* globals, char **);
129static void     checkslash_rm(rtems_shell_rm_globals* globals, char **);
130static void     rm_file_rm(rtems_shell_rm_globals* globals, char **);
131static int      rm_overwrite_rm(rtems_shell_rm_globals* globals, char *, struct stat *);
132static void     rm_tree_rm(rtems_shell_rm_globals* globals, char **);
133static void     usage_rm(rtems_shell_rm_globals* globals);
134
135/*
136 * rm --
137 *      This rm is different from historic rm's, but is expected to match
138 *      POSIX 1003.2 behavior.  The most visible difference is that -f
139 *      has two specific effects now, ignore non-existent files and force
140 *      file removal.
141 */
142int
143main_rm(rtems_shell_rm_globals* globals, int argc, char *argv[])
144{
145        int ch;
146        char *p;
147 
148  struct getopt_data getopt_reent;
149  memset(&getopt_reent, 0, sizeof(getopt_data));
150 
151        /*
152         * Test for the special case where the utility is called as
153         * "unlink", for which the functionality provided is greatly
154         * simplified.
155         */
156        if ((p = rindex(argv[0], '/')) == NULL)
157                p = argv[0];
158        else
159                ++p;
160        if (strcmp(p, "unlink") == 0) {
161                while (getopt_r(argc, argv, "", &getopt_reent) != -1)
162                        usage();
163                argc -= getopt_reent.optind;
164                argv += getopt_reent.optind;
165                if (argc != 1)
166                        usage();
167                rm_file(&argv[0]);
168                exit(eval);
169        }
170
171        Pflag = rflag = 0;
172        while ((ch = getopt_r(argc, argv, "dfiIPRrvW", &getopt_reent)) != -1)
173                switch(ch) {
174                case 'd':
175                        dflag = 1;
176                        break;
177                case 'f':
178                        fflag = 1;
179                        iflag = 0;
180                        break;
181                case 'i':
182                        fflag = 0;
183                        iflag = 1;
184                        break;
185                case 'I':
186                        Iflag = 1;
187                        break;
188                case 'P':
189                        Pflag = 1;
190                        break;
191                case 'R':
192                case 'r':                       /* Compatibility. */
193                        rflag = 1;
194                        break;
195                case 'v':
196                        vflag = 1;
197                        break;
198                case 'W':
199                        Wflag = 1;
200                        break;
201                default:
202                        usage();
203                }
204        argc -= getopt_reent.optind;
205        argv += getopt_reent.optind;
206
207        if (argc < 1) {
208                if (fflag)
209                        return (0);
210                usage();
211        }
212
213        checkdot(argv);
214        if (getenv("POSIXLY_CORRECT") == NULL)
215                checkslash(argv);
216        xuid = geteuid();
217
218        if (*argv) {
219                stdin_ok = isatty(STDIN_FILENO);
220
221                if (Iflag) {
222                        if (check2(argv) == 0)
223                                exit (1);
224                }
225                if (rflag)
226                        rm_tree(argv);
227                else
228                        rm_file(argv);
229        }
230
231        exit (eval);
232  return 0;
233}
234
235void
236rm_tree_rm(rtems_shell_rm_globals* globals, char **argv)
237{
238        FTS *fts;
239        FTSENT *p;
240        int needstat;
241        int flags;
242        int rval;
243
244        /*
245         * Remove a file hierarchy.  If forcing removal (-f), or interactive
246         * (-i) or can't ask anyway (stdin_ok), don't stat the file.
247         */
248        needstat = !xuid || (!fflag && !iflag && stdin_ok);
249
250        /*
251         * If the -i option is specified, the user can skip on the pre-order
252         * visit.  The fts_number field flags skipped directories.
253         */
254#define SKIPPED 1
255
256        flags = FTS_PHYSICAL;
257        if (!needstat)
258                flags |= FTS_NOSTAT;
259        if (Wflag)
260                flags |= FTS_WHITEOUT;
261        if (!(fts = fts_open(argv, flags, NULL))) {
262                if (fflag && errno == ENOENT)
263                        return;
264                err(exit_jump, 1, "fts_open");
265        }
266        while ((p = fts_read(fts)) != NULL) {
267                switch (p->fts_info) {
268                case FTS_DNR:
269                        if (!fflag || p->fts_errno != ENOENT) {
270                                warnx("%s: %s",
271                                    p->fts_path, strerror(p->fts_errno));
272                                eval = 1;
273                        }
274                        continue;
275                case FTS_ERR:
276                        errx(exit_jump, 1, "%s: %s", p->fts_path, strerror(p->fts_errno));
277                case FTS_NS:
278                        /*
279                         * Assume that since fts_read() couldn't stat the
280                         * file, it can't be unlinked.
281                         */
282                        if (!needstat)
283                                break;
284                        if (!fflag || p->fts_errno != ENOENT) {
285                                warnx("%s: %s",
286                                    p->fts_path, strerror(p->fts_errno));
287                                eval = 1;
288                        }
289                        continue;
290                case FTS_D:
291                        /* Pre-order: give user chance to skip. */
292                        if (!fflag && !check(p->fts_path, p->fts_accpath,
293                            p->fts_statp)) {
294                                (void)fts_set(fts, p, FTS_SKIP);
295                                p->fts_number = SKIPPED;
296                        }
297#if RTEMS_REMOVED
298                        else if (!xuid &&
299                                 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
300                                 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
301                                 chflags(p->fts_accpath,
302                                         p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
303                                goto err;
304#endif
305                        continue;
306                case FTS_DP:
307                        /* Post-order: see if user skipped. */
308                        if (p->fts_number == SKIPPED)
309                                continue;
310                        break;
311                default:
312                        if (!fflag &&
313                            !check(p->fts_path, p->fts_accpath, p->fts_statp))
314                                continue;
315                }
316
317                rval = 0;
318#if RTEMS_REMOVED
319                if (!xuid &&
320                    (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
321                    !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
322                        rval = chflags(p->fts_accpath,
323                                       p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
324#endif
325                if (rval == 0) {
326                        /*
327                         * If we can't read or search the directory, may still be
328                         * able to remove it.  Don't print out the un{read,search}able
329                         * message unless the remove fails.
330                         */
331                        switch (p->fts_info) {
332                        case FTS_DP:
333                        case FTS_DNR:
334                                rval = rmdir(p->fts_accpath);
335                                if (rval == 0 || (fflag && errno == ENOENT)) {
336                                        if (rval == 0 && vflag)
337                                                (void)printf("%s\n",
338                                                    p->fts_path);
339                                        continue;
340                                }
341                                break;
342
343#if RTEMS_REMOVED
344                        case FTS_W:
345                                rval = undelete(p->fts_accpath);
346                                if (rval == 0 && (fflag && errno == ENOENT)) {
347                                        if (vflag)
348                                                (void)printf("%s\n",
349                                                    p->fts_path);
350                                        continue;
351                                }
352                                break;
353#endif
354
355                        case FTS_NS:
356                                /*
357                                 * Assume that since fts_read() couldn't stat
358                                 * the file, it can't be unlinked.
359                                 */
360                                if (fflag)
361                                        continue;
362                                /* FALLTHROUGH */
363                        default:
364                                if (Pflag)
365                                        if (!rm_overwrite(p->fts_accpath, NULL))
366                                                continue;
367                                rval = unlink(p->fts_accpath);
368                                if (rval == 0 || (fflag && errno == ENOENT)) {
369                                        if (rval == 0 && vflag)
370                                                (void)printf("%s\n",
371                                                    p->fts_path);
372                                        continue;
373                                }
374                        }
375                }
376#if RTEMS_REMOVED
377err:
378#endif
379                warn("%s", p->fts_path);
380                eval = 1;
381        }
382        if (errno)
383                err(exit_jump, 1, "fts_read");
384        fts_close(fts);
385}
386
387#define S_ISWHT(m) (0)
388
389void
390rm_file_rm(rtems_shell_rm_globals* globals, char **argv)
391{
392        struct stat sb;
393        int rval;
394        char *f;
395
396        /*
397         * Remove a file.  POSIX 1003.2 states that, by default, attempting
398         * to remove a directory is an error, so must always stat the file.
399         */
400        while ((f = *argv++) != NULL) {
401                /* Assume if can't stat the file, can't unlink it. */
402                if (lstat(f, &sb)) {
403#if RTEMS_REMOVED
404                        if (Wflag) {
405                                sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
406                        } else {
407#endif
408                                if (!fflag || errno != ENOENT) {
409                                        warn("%s", f);
410                                        eval = 1;
411                                }
412                                continue;
413#if RTEMS_REMOVED
414                        }
415#endif
416                } else if (Wflag) {
417                        warnx("%s: %s", f, strerror(EEXIST));
418                        eval = 1;
419                        continue;
420                }
421
422                if (S_ISDIR(sb.st_mode) && !dflag) {
423                        warnx("%s: is a directory", f);
424                        eval = 1;
425                        continue;
426                }
427                if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
428                        continue;
429                rval = 0;
430#if RTEMS_REMOVED
431                if (!xuid && !S_ISWHT(sb.st_mode) &&
432                    (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
433                    !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
434                        rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
435#endif
436                if (rval == 0) {
437                        if (S_ISWHT(sb.st_mode))
438#if RTEMS_REMOVED
439                                rval = undelete(f);
440#endif
441      ;
442                        else if (S_ISDIR(sb.st_mode))
443                                rval = rmdir(f);
444                        else {
445                                if (Pflag)
446                                        if (!rm_overwrite(f, &sb))
447                                                continue;
448                                rval = unlink(f);
449                        }
450                }
451                if (rval && (!fflag || errno != ENOENT)) {
452                        warn("%s", f);
453                        eval = 1;
454                }
455                if (vflag && rval == 0)
456                        (void)printf("%s\n", f);
457        }
458}
459
460/*
461 * rm_overwrite --
462 *      Overwrite the file 3 times with varying bit patterns.
463 *
464 * XXX
465 * This is a cheap way to *really* delete files.  Note that only regular
466 * files are deleted, directories (and therefore names) will remain.
467 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
468 * System V file system).  In a logging file system, you'll have to have
469 * kernel support.
470 */
471int
472rm_overwrite_rm(rtems_shell_rm_globals* globals, char *file, struct stat *sbp)
473{
474        struct stat sb;
475#if RTEMS_REMOVED
476        struct statfs fsb;
477#endif
478        off_t len;
479        int bsize, fd, wlen;
480        char *buf = NULL;
481
482        fd = -1;
483        if (sbp == NULL) {
484                if (lstat(file, &sb))
485                        goto err;
486                sbp = &sb;
487        }
488        if (!S_ISREG(sbp->st_mode))
489                return (1);
490        if (sbp->st_nlink > 1 && !fflag) {
491                warnx("%s (inode %lu): not overwritten due to multiple links",
492                    file, sbp->st_ino);
493                return (0);
494        }
495        if ((fd = open(file, O_WRONLY, 0)) == -1)
496                goto err;
497#if RTEMS_REMOVED
498        if (fstatfs(fd, &fsb) == -1)
499                goto err;
500        bsize = MAX(fsb.f_iosize, 1024);
501#endif
502  bsize = 1024;
503        if ((buf = malloc(bsize)) == NULL)
504                err(exit_jump, 1, "%s: malloc", file);
505
506#define PASS(byte) {                                                    \
507        memset(buf, byte, bsize);                                       \
508        for (len = sbp->st_size; len > 0; len -= wlen) {                \
509                wlen = len < bsize ? len : bsize;                       \
510                if (write(fd, buf, wlen) != wlen)                       \
511                        goto err;                                       \
512        }                                                               \
513}
514        PASS(0xff);
515        if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
516                goto err;
517        PASS(0x00);
518        if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
519                goto err;
520        PASS(0xff);
521        if (!fsync(fd) && !close(fd)) {
522                free(buf);
523                return (1);
524        }
525
526err:    eval = 1;
527        if (buf)
528                free(buf);
529        if (fd != -1)
530                close(fd);
531        warn("%s", file);
532        return (0);
533}
534
535void strmode(mode_t mode, char *p);
536char *fflagstostr(u_long flags);
537const char *user_from_uid(uid_t uid, int nouser);
538
539int
540check_rm(rtems_shell_rm_globals* globals, char *path, char *name, struct stat *sp)
541{
542        int ch, first;
543        char modep[15], *flagsp;
544
545        /* Check -i first. */
546        if (iflag)
547                (void)fprintf(stderr, "remove %s? ", path);
548        else {
549                /*
550                 * If it's not a symbolic link and it's unwritable and we're
551                 * talking to a terminal, ask.  Symbolic links are excluded
552                 * because their permissions are meaningless.  Check stdin_ok
553                 * first because we may not have stat'ed the file.
554                 */
555#if RTEMS_REMOVED
556                if (!stdin_ok || S_ISLNK(sp->st_mode) ||
557                    (!access(name, W_OK) &&
558                    !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
559                    (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !xuid)))
560#endif
561                if (!stdin_ok || S_ISLNK(sp->st_mode) ||
562                    (!access(name, W_OK)))
563                        return (1);
564                strmode(sp->st_mode, modep);
565#if RTEMS_REMOVED
566                if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
567                        err(exit_jump, 1, "fflagstostr");
568#else
569    flagsp = "no supported";
570#endif
571                if (Pflag)
572                        errx(exit_jump, 1,
573                            "%s: -P was specified, but file is not writable",
574                            path);
575                (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
576                    modep + 1, modep[9] == ' ' ? "" : " ",
577                    user_from_uid(sp->st_uid, 0),
578                    group_from_gid(sp->st_gid, 0),
579                    *flagsp ? flagsp : "", *flagsp ? " " : "",
580                    path);
581                free(flagsp);
582        }
583        (void)fflush(stderr);
584
585        first = ch = getchar();
586        while (ch != '\n' && ch != EOF)
587                ch = getchar();
588        return (first == 'y' || first == 'Y');
589}
590
591#define ISSLASH(a)      ((a)[0] == '/' && (a)[1] == '\0')
592void
593checkslash_rm(rtems_shell_rm_globals* globals, char **argv)
594{
595        char **t, **u;
596        int complained;
597
598        complained = 0;
599        for (t = argv; *t;) {
600                if (ISSLASH(*t)) {
601                        if (!complained++)
602                                warnx("\"/\" may not be removed");
603                        eval = 1;
604                        for (u = t; u[0] != NULL; ++u)
605                                u[0] = u[1];
606                } else {
607                        ++t;
608                }
609        }
610}
611
612int
613check2_rm(rtems_shell_rm_globals* globals, char **argv)
614{
615        struct stat st;
616        int first;
617        int ch;
618        int fcount = 0;
619        int dcount = 0;
620        int i;
621        const char *dname = NULL;
622
623        for (i = 0; argv[i]; ++i) {
624                if (lstat(argv[i], &st) == 0) {
625                        if (S_ISDIR(st.st_mode)) {
626                                ++dcount;
627                                dname = argv[i];    /* only used if 1 dir */
628                        } else {
629                                ++fcount;
630                        }
631                }
632        }
633        first = 0;
634        while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') {
635                if (dcount && rflag) {
636                        fprintf(stderr, "recursively remove");
637                        if (dcount == 1)
638                                fprintf(stderr, " %s", dname);
639                        else
640                                fprintf(stderr, " %d dirs", dcount);
641                        if (fcount == 1)
642                                fprintf(stderr, " and 1 file");
643                        else if (fcount > 1)
644                                fprintf(stderr, " and %d files", fcount);
645                } else if (dcount + fcount > 3) {
646                        fprintf(stderr, "remove %d files", dcount + fcount);
647                } else {
648                        return(1);
649                }
650                fprintf(stderr, "? ");
651                fflush(stderr);
652
653                first = ch = getchar();
654                while (ch != '\n' && ch != EOF)
655                        ch = getchar();
656                if (ch == EOF)
657                        break;
658        }
659        return (first == 'y' || first == 'Y');
660}
661
662#define ISDOT(a)        ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
663void
664checkdot_rm(rtems_shell_rm_globals* globals, char **argv)
665{
666        char *p, **save, **t;
667        int complained;
668
669        complained = 0;
670        for (t = argv; *t;) {
671                if ((p = strrchr(*t, '/')) != NULL)
672                        ++p;
673                else
674                        p = *t;
675                if (ISDOT(p)) {
676                        if (!complained++)
677                                warnx("\".\" and \"..\" may not be removed");
678                        eval = 1;
679                        for (save = t; (t[0] = t[1]) != NULL; ++t)
680                                continue;
681                        t = save;
682                } else
683                        ++t;
684        }
685}
686
687void
688usage_rm(rtems_shell_rm_globals* globals)
689{
690
691        (void)fprintf(stderr, "%s\n%s\n",
692            "usage: rm [-f | -i] [-dIPRrvW] file ...",
693            "       unlink file");
694        exit(1);
695}
696
697rtems_shell_cmd_t rtems_shell_RM_Command = {
698  "rm",                                      /* name */
699  "[-f | -i] [-dIPRrvW] file ...",           /* usage */
700  "files",                                   /* topic */
701  rtems_shell_main_rm,                       /* command */
702  NULL,                                      /* alias */
703  NULL                                       /* next */
704};
Note: See TracBrowser for help on using the repository browser.