]> git.saurik.com Git - apple/file_cmds.git/blobdiff - rm/rm.c
file_cmds-264.50.1.tar.gz
[apple/file_cmds.git] / rm / rm.c
diff --git a/rm/rm.c b/rm/rm.c
index 38e01b8e196a733030ee7b80b826df587622abb9..ec13b2a4f50f05ac787ddcb42cf655b00b00743a 100644 (file)
--- a/rm/rm.c
+++ b/rm/rm.c
@@ -1,5 +1,3 @@
-/*     $NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $  */
-
 /*-
  * Copyright (c) 1990, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
 /*-
  * Copyright (c) 1990, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
 
 #include <sys/cdefs.h>
 #ifndef lint
 
 #include <sys/cdefs.h>
 #ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\
-       The Regents of the University of California.  All rights reserved.\n");
+__used static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
 #if 0
 #endif /* not lint */
 
 #ifndef lint
 #if 0
-static char sccsid[] = "@(#)rm.c       8.8 (Berkeley) 4/27/95";
+static char sccsid[] = "@(#)rm.c       8.5 (Berkeley) 4/18/94";
 #else
 #else
-__RCSID("$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $");
+__used static const char rcsid[] =
+  "$FreeBSD: src/bin/rm/rm.c,v 1.33 2001/06/13 15:01:25 ru Exp $";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
-#include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mount.h>
 
 
-#include <locale.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -58,31 +58,29 @@ __RCSID("$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $");
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sysexits.h>
 #include <unistd.h>
 #include <unistd.h>
+
+#ifdef __APPLE__
+#include <removefile.h>
 #include <pwd.h>
 #include <grp.h>
 #include <pwd.h>
 #include <grp.h>
+#include "get_compat.h"
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
 
 
-int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok;
+int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
+uid_t uid;
 
 int    check __P((char *, char *, struct stat *));
 
 int    check __P((char *, char *, struct stat *));
+int checkdir __P((char *));
+int            yes_or_no __P((void));
 void   checkdot __P((char **));
 void   rm_file __P((char **));
 void   rm_overwrite __P((char *, struct stat *));
 void   rm_tree __P((char **));
 void   usage __P((void));
 void   checkdot __P((char **));
 void   rm_file __P((char **));
 void   rm_overwrite __P((char *, struct stat *));
 void   rm_tree __P((char **));
 void   usage __P((void));
-int    main __P((int, char *[]));
-
-#ifdef __APPLE__ /* We're missing this prototype */
-int undelete __P((char *));
-#endif
-
-/*
- * For the sake of the `-f' flag, check whether an error number indicates the
- * failure of an operation due to an non-existent file, either per se (ENOENT)
- * or because its filename argument was illegal (ENAMETOOLONG, ENOTDIR).
- */
-#define NONEXISTENT(x) \
-    ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR)
 
 /*
  * rm --
 
 /*
  * rm --
@@ -97,11 +95,31 @@ main(argc, argv)
        char *argv[];
 {
        int ch, rflag;
        char *argv[];
 {
        int ch, rflag;
+       char *p;
 
 
-       (void)setlocale(LC_ALL, "");
+       if (argc < 1)
+               usage();
+
+       /*
+        * Test for the special case where the utility is called as
+        * "unlink", for which the functionality provided is greatly
+        * simplified.
+        */
+       if ((p = rindex(argv[0], '/')) == NULL)
+               p = argv[0];
+       else
+               ++p;
+       uid = geteuid();
+       if (strcmp(p, "unlink") == 0) {
+               if (argc == 2) {
+                       rm_file(&argv[1]);
+                       exit(eval);
+               } else 
+                       usage();
+       }
 
        Pflag = rflag = 0;
 
        Pflag = rflag = 0;
-       while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
+       while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1)
                switch(ch) {
                case 'd':
                        dflag = 1;
                switch(ch) {
                case 'd':
                        dflag = 1;
@@ -121,18 +139,23 @@ main(argc, argv)
                case 'r':                       /* Compatibility. */
                        rflag = 1;
                        break;
                case 'r':                       /* Compatibility. */
                        rflag = 1;
                        break;
+               case 'v':
+                       vflag = 1;
+                       break;
                case 'W':
                        Wflag = 1;
                        break;
                case 'W':
                        Wflag = 1;
                        break;
-               case '?':
                default:
                        usage();
                }
        argc -= optind;
        argv += optind;
 
                default:
                        usage();
                }
        argc -= optind;
        argv += optind;
 
-       if (argc < 1)
+       if (argc < 1) {
+               if (fflag)
+                       return 0;
                usage();
                usage();
+       }
 
        checkdot(argv);
 
 
        checkdot(argv);
 
@@ -145,8 +168,7 @@ main(argc, argv)
                        rm_file(argv);
        }
 
                        rm_file(argv);
        }
 
-       exit(eval);
-       /* NOTREACHED */
+       exit (eval);
 }
 
 void
 }
 
 void
@@ -157,12 +179,13 @@ rm_tree(argv)
        FTSENT *p;
        int needstat;
        int flags;
        FTSENT *p;
        int needstat;
        int flags;
-
+       int rval;
+       int wantConformance = COMPAT_MODE("bin/rm", "unix2003");
        /*
         * Remove a file hierarchy.  If forcing removal (-f), or interactive
         * (-i) or can't ask anyway (stdin_ok), don't stat the file.
         */
        /*
         * Remove a file hierarchy.  If forcing removal (-f), or interactive
         * (-i) or can't ask anyway (stdin_ok), don't stat the file.
         */
-       needstat = !fflag && !iflag && stdin_ok;
+       needstat = !uid || (!fflag && !iflag && stdin_ok);
 
        /*
         * If the -i option is specified, the user can skip on the pre-order
 
        /*
         * If the -i option is specified, the user can skip on the pre-order
@@ -175,9 +198,11 @@ rm_tree(argv)
                flags |= FTS_NOSTAT;
        if (Wflag)
                flags |= FTS_WHITEOUT;
                flags |= FTS_NOSTAT;
        if (Wflag)
                flags |= FTS_WHITEOUT;
-       if (!(fts = fts_open(argv, flags,
-               (int (*) __P((const FTSENT **, const FTSENT **)))NULL)))
-               err(1, "%s", "");
+       if (!(fts = fts_open(argv, flags, NULL))) {
+               if (fflag && errno == ENOENT)
+                       return;
+               err(1, NULL);
+       }
        while ((p = fts_read(fts)) != NULL) {
                switch (p->fts_info) {
                case FTS_DNR:
        while ((p = fts_read(fts)) != NULL) {
                switch (p->fts_info) {
                case FTS_DNR:
@@ -189,7 +214,6 @@ rm_tree(argv)
                        continue;
                case FTS_ERR:
                        errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
                        continue;
                case FTS_ERR:
                        errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
-                       /* NOTREACHED */
                case FTS_NS:
                        /*
                         * FTS_NS: assume that if can't stat the file, it
                case FTS_NS:
                        /*
                         * FTS_NS: assume that if can't stat the file, it
@@ -197,7 +221,7 @@ rm_tree(argv)
                         */
                        if (!needstat)
                                break;
                         */
                        if (!needstat)
                                break;
-                       if (!fflag || !NONEXISTENT(p->fts_errno)) {
+                       if (!fflag || p->fts_errno != ENOENT) {
                                warnx("%s: %s",
                                    p->fts_path, strerror(p->fts_errno));
                                eval = 1;
                                warnx("%s: %s",
                                    p->fts_path, strerror(p->fts_errno));
                                eval = 1;
@@ -205,16 +229,35 @@ rm_tree(argv)
                        continue;
                case FTS_D:
                        /* Pre-order: give user chance to skip. */
                        continue;
                case FTS_D:
                        /* Pre-order: give user chance to skip. */
-                       if (!fflag && !check(p->fts_path, p->fts_accpath,
-                           p->fts_statp)) {
+                       /* In conformance mode the user is prompted to skip processing the contents.
+                        * Then the option to delete the dir is presented post-order */
+                       if (!fflag && 
+                                       ( (wantConformance && !checkdir(p->fts_path)) ||
+                                         (!wantConformance && !check(p->fts_path, p->fts_accpath, p->fts_statp))
+                                       )
+                          ){
                                (void)fts_set(fts, p, FTS_SKIP);
                                p->fts_number = SKIPPED;
                        }
                                (void)fts_set(fts, p, FTS_SKIP);
                                p->fts_number = SKIPPED;
                        }
+                       else if (!uid &&
+                                (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+                                !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+                                chflags(p->fts_accpath,
+                                        p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
+                               goto err;
                        continue;
                case FTS_DP:
                        /* Post-order: see if user skipped. */
                        continue;
                case FTS_DP:
                        /* Post-order: see if user skipped. */
-                       if (p->fts_number == SKIPPED)
+                       if(p->fts_number == SKIPPED)/*in legacy mode, the user was prompted pre-order */
                                continue;
                                continue;
+                       else if(wantConformance)
+                       {
+                               /* delete directory if force is on, or if user answers Y to prompt */
+                               if(fflag || check(p->fts_path, p->fts_accpath, p->fts_statp)) 
+                                       break;
+                               else
+                                       continue;
+                       }
                        break;
                default:
                        if (!fflag &&
                        break;
                default:
                        if (!fflag &&
@@ -222,37 +265,67 @@ rm_tree(argv)
                                continue;
                }
 
                                continue;
                }
 
-               /*
-                * If we can't read or search the directory, may still be
-                * able to remove it.  Don't print out the un{read,search}able
-                * message unless the remove fails.
-                */
-               switch (p->fts_info) {
-               case FTS_DP:
-               case FTS_DNR:
-                       if (!rmdir(p->fts_accpath) ||
-                           (fflag && errno == ENOENT))
-                               continue;
-                       break;
+               rval = 0;
+               if (!uid &&
+                   (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+                   !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
+                       rval = chflags(p->fts_accpath,
+                                      p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
+               if (rval == 0) {
+                       /*
+                        * If we can't read or search the directory, may still be
+                        * able to remove it.  Don't print out the un{read,search}able
+                        * message unless the remove fails.
+                        */
+                       switch (p->fts_info) {
+                       case FTS_DP:
+                       case FTS_DNR:
+                               rval = rmdir(p->fts_accpath);
+                               if (rval == 0 || (fflag && errno == ENOENT)) {
+                                       if (rval == 0 && vflag)
+                                               (void)printf("%s\n",
+                                                   p->fts_path);
+                                       continue;
+                               }
+                               break;
 
 
-               case FTS_W:
-                       if (!undelete(p->fts_accpath) ||
-                           (fflag && errno == ENOENT))
-                               continue;
-                       break;
+                       case FTS_W:
+                               rval = undelete(p->fts_accpath);
+                               if (rval == 0 && (fflag && errno == ENOENT)) {
+                                       if (vflag)
+                                               (void)printf("%s\n",
+                                                   p->fts_path);
+                                       continue;
+                               }
+                               break;
 
 
-               default:
-                       if (Pflag)
-                               rm_overwrite(p->fts_accpath, NULL);
-                       if (!unlink(p->fts_accpath) ||
-                           (fflag && NONEXISTENT(errno)))
-                               continue;
+                       default:
+#ifdef __APPLE__
+                               if (Pflag) {
+                                       if (removefile(p->fts_accpath, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */
+                                               eval = rval = 1;
+                               } else
+                                       rval = unlink(p->fts_accpath);
+#else  /* !__APPLE_ */
+                               if (Pflag)
+                                       rm_overwrite(p->fts_accpath, NULL);
+                               rval = unlink(p->fts_accpath);
+#endif /* __APPLE__ */
+                               if (rval == 0 || (fflag && errno == ENOENT)) {
+                                       if (rval == 0 && vflag)
+                                               (void)printf("%s\n",
+                                                   p->fts_path);
+                                       continue;
+                               }
+                       }
                }
                }
+err:
                warn("%s", p->fts_path);
                eval = 1;
        }
        if (errno)
                err(1, "fts_read");
                warn("%s", p->fts_path);
                eval = 1;
        }
        if (errno)
                err(1, "fts_read");
+       fts_close(fts);
 }
 
 void
 }
 
 void
@@ -273,7 +346,7 @@ rm_file(argv)
                        if (Wflag) {
                                sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
                        } else {
                        if (Wflag) {
                                sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
                        } else {
-                               if (!fflag || !NONEXISTENT(errno)) {
+                               if (!fflag || errno != ENOENT) {
                                        warn("%s", f);
                                        eval = 1;
                                }
                                        warn("%s", f);
                                        eval = 1;
                                }
@@ -292,19 +365,36 @@ rm_file(argv)
                }
                if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
                        continue;
                }
                if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
                        continue;
-               if (S_ISWHT(sb.st_mode))
-                       rval = undelete(f);
-               else if (S_ISDIR(sb.st_mode))
-                       rval = rmdir(f);
-               else {
-                       if (Pflag)
-                               rm_overwrite(f, &sb);
-                       rval = unlink(f);
+               rval = 0;
+               if (!uid &&
+                   (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+                   !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
+                       rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
+               if (rval == 0) {
+                       if (S_ISWHT(sb.st_mode))
+                               rval = undelete(f);
+                       else if (S_ISDIR(sb.st_mode))
+                               rval = rmdir(f);
+                       else {
+#ifdef __APPLE__
+                               if (Pflag) {
+                                       if (removefile(f, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */
+                                               eval = rval = 1;
+                               } else
+                                       rval = unlink(f);
+#else  /* !__APPLE__ */
+                               if (Pflag)
+                                       rm_overwrite(f, &sb);
+                               rval = unlink(f);
+#endif /* __APPLE__ */
+                       }
                }
                }
-               if (rval && (!fflag || !NONEXISTENT(errno))) {
+               if (rval && (!fflag || errno != ENOENT)) {
                        warn("%s", f);
                        eval = 1;
                }
                        warn("%s", f);
                        eval = 1;
                }
+               if (vflag && rval == 0)
+                       (void)printf("%s\n", f);
        }
 }
 
        }
 }
 
@@ -325,11 +415,11 @@ rm_overwrite(file, sbp)
        struct stat *sbp;
 {
        struct stat sb;
        struct stat *sbp;
 {
        struct stat sb;
+       struct statfs fsb;
        off_t len;
        off_t len;
-       int fd, wlen;
-       char buf[8 * 1024];
+       int bsize, fd, wlen;
+       char *buf = NULL;
 
 
-       fd = -1;
        if (sbp == NULL) {
                if (lstat(file, &sb))
                        goto err;
        if (sbp == NULL) {
                if (lstat(file, &sb))
                        goto err;
@@ -339,11 +429,16 @@ rm_overwrite(file, sbp)
                return;
        if ((fd = open(file, O_WRONLY, 0)) == -1)
                goto err;
                return;
        if ((fd = open(file, O_WRONLY, 0)) == -1)
                goto err;
+       if (fstatfs(fd, &fsb) == -1)
+               goto err;
+       bsize = MAX(fsb.f_iosize, 1024);
+       if ((buf = malloc(bsize)) == NULL)
+               err(1, "malloc");
 
 #define        PASS(byte) {                                                    \
 
 #define        PASS(byte) {                                                    \
-       memset(buf, byte, sizeof(buf));                                 \
+       memset(buf, byte, bsize);                                       \
        for (len = sbp->st_size; len > 0; len -= wlen) {                \
        for (len = sbp->st_size; len > 0; len -= wlen) {                \
-               wlen = len < sizeof(buf) ? len : sizeof(buf);           \
+               wlen = len < bsize ? (int)len : bsize;                  \
                if (write(fd, buf, wlen) != wlen)                       \
                        goto err;                                       \
        }                                                               \
                if (write(fd, buf, wlen) != wlen)                       \
                        goto err;                                       \
        }                                                               \
@@ -355,21 +450,45 @@ rm_overwrite(file, sbp)
        if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
                goto err;
        PASS(0xff);
        if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
                goto err;
        PASS(0xff);
-       if (!fsync(fd) && !close(fd))
+       if (!fsync(fd) && !close(fd)) {
+               free(buf);
                return;
                return;
+       }
 
 err:   eval = 1;
 
 err:   eval = 1;
+       if (buf)
+               free(buf);
        warn("%s", file);
 }
 
        warn("%s", file);
 }
 
+int 
+yes_or_no()
+{
+       int ch, first;
+       (void)fflush(stderr);
+
+       first = ch = getchar();
+       while (ch != '\n' && ch != EOF)
+               ch = getchar();
+       return (first == 'y' || first == 'Y');
+}
+
+int
+checkdir(path)
+       char *path;
+{
+       if(!iflag)
+               return 1;       //if not interactive, process directory's contents
+       (void)fprintf(stderr, "examine files in directory %s? ", path);
+       return yes_or_no();
+}
 
 int
 check(path, name, sp)
        char *path, *name;
        struct stat *sp;
 {
 
 int
 check(path, name, sp)
        char *path, *name;
        struct stat *sp;
 {
-       int ch, first;
-       char modep[15];
+       char modep[15], *flagsp;
 
        /* Check -i first. */
        if (iflag)
 
        /* Check -i first. */
        if (iflag)
@@ -381,31 +500,27 @@ check(path, name, sp)
                 * because their permissions are meaningless.  Check stdin_ok
                 * first because we may not have stat'ed the file.
                 */
                 * because their permissions are meaningless.  Check stdin_ok
                 * first because we may not have stat'ed the file.
                 */
-               if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK))
+               if (!stdin_ok || S_ISLNK(sp->st_mode) ||
+                   (!access(name, W_OK) &&
+                   !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+                   (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid)))
                        return (1);
                strmode(sp->st_mode, modep);
                        return (1);
                strmode(sp->st_mode, modep);
-               (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
+               if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
+                       err(1, NULL);
+               (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
                    modep + 1, modep[9] == ' ' ? "" : " ",
                    user_from_uid(sp->st_uid, 0),
                    modep + 1, modep[9] == ' ' ? "" : " ",
                    user_from_uid(sp->st_uid, 0),
-                   group_from_gid(sp->st_gid, 0), path);
+                   group_from_gid(sp->st_gid, 0),
+                   *flagsp ? flagsp : "", *flagsp ? " " : "", 
+                   path);
+               free(flagsp);
        }
        }
-       (void)fflush(stderr);
-
-       first = ch = getchar();
-       while (ch != '\n' && ch != EOF)
-               ch = getchar();
-       return (first == 'y' || first == 'Y');
+       return yes_or_no();
 }
 
 }
 
-/*
- * POSIX.2 requires that if "." or ".." are specified as the basename
- * portion of an operand, a diagnostic message be written to standard
- * error and nothing more be done with such operands.
- *
- * Since POSIX.2 defines basename as the final portion of a path after
- * trailing slashes have been removed, we'll remove them here.
- */
-#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
+
+#define ISDOT(a)       ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
 void
 checkdot(argv)
        char **argv;
 void
 checkdot(argv)
        char **argv;
@@ -415,17 +530,24 @@ checkdot(argv)
 
        complained = 0;
        for (t = argv; *t;) {
 
        complained = 0;
        for (t = argv; *t;) {
-               /* strip trailing slashes */
-               p = strrchr (*t, '\0');
-               while (--p > *t && *p == '/')
-                       *p = '\0';
-
-               /* extract basename */
-               if ((p = strrchr(*t, '/')) != NULL)
-                       ++p;
-               else
+               size_t len = strlen(*t);
+               char truncated[len];
+
+               if ((p = strrchr(*t, '/')) != NULL) {
+                       if (p[1] == '\0') { // trailing / -- treat as if not present
+                               strlcpy(truncated, *t, len);
+                               p = strrchr(truncated, '/');
+                               if (p) {
+                                       ++p;
+                               } else {
+                                       p = truncated;
+                               }
+                       } else {
+                               ++p;
+                       }
+               } else {
                        p = *t;
                        p = *t;
-
+               }
                if (ISDOT(p)) {
                        if (!complained++)
                                warnx("\".\" and \"..\" may not be removed");
                if (ISDOT(p)) {
                        if (!complained++)
                                warnx("\".\" and \"..\" may not be removed");
@@ -442,7 +564,8 @@ void
 usage()
 {
 
 usage()
 {
 
-       (void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n");
-       exit(1);
-       /* NOTREACHED */
+       (void)fprintf(stderr, "%s\n%s\n",
+           "usage: rm [-f | -i] [-dPRrvW] file ...",
+           "       unlink file");
+       exit(EX_USAGE);
 }
 }