source: rtems/cpukit/libmisc/shell/main_rm.c @ 6cdaa85

5
Last change on this file since 6cdaa85 was 6cdaa85, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 4, 2018 at 6:16:45 PM

shell: Use #include "..." for local header files

Update #3375.

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