source: rtems/cpukit/libmisc/shell/main_rm.c @ 9fcba88

4.104.115
Last change on this file since 9fcba88 was c648ca5, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/26/10 at 17:42:24

Add HAVE_STRINGS_H for better POSIX compliance.

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