Add rm from 4.4BSD-Lite Release 2
authorJason Self <j@jxself.org>
Mon, 10 Jun 2019 00:31:20 +0000 (17:31 -0700)
committerJason Self <j@jxself.org>
Mon, 10 Jun 2019 00:31:20 +0000 (17:31 -0700)
Signed-off-by: Jason Self <j@jxself.org>
usr/src/bin/rm/Makefile [new file with mode: 0644]
usr/src/bin/rm/rm.1 [new file with mode: 0644]
usr/src/bin/rm/rm.c [new file with mode: 0644]

diff --git a/usr/src/bin/rm/Makefile b/usr/src/bin/rm/Makefile
new file mode 100644 (file)
index 0000000..e3c2624
--- /dev/null
@@ -0,0 +1,5 @@
+#      @(#)Makefile    8.1 (Berkeley) 5/31/93
+
+PROG=  rm
+
+.include <bsd.prog.mk>
diff --git a/usr/src/bin/rm/rm.1 b/usr/src/bin/rm/rm.1
new file mode 100644 (file)
index 0000000..7f5caef
--- /dev/null
@@ -0,0 +1,151 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)rm.1        8.5 (Berkeley) 12/5/94
+.\"
+.Dd December 5, 1994
+.Dt RM 1
+.Os
+.Sh NAME
+.Nm rm
+.Nd remove directory entries
+.Sh SYNOPSIS
+.Nm rm
+.Op Fl f | Fl i
+.Op Fl dPRrW
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm rm
+utility attempts to remove the non-directory type files specified on the
+command line.
+If the permissions of the file do not permit writing, and the standard
+input device is a terminal, the user is prompted (on the standard error
+output) for confirmation.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl d
+Attempt to remove directories as well as other types of files.
+.It Fl f
+Attempt to remove the files without prompting for confirmation,
+regardless of the file's permissions.
+If the file does not exist, do not display a diagnostic message or modify
+the exit status to reflect an error.
+The
+.Fl f
+option overrides any previous
+.Fl i 
+options.
+.It Fl i
+Request confirmation before attempting to remove each file, regardless of
+the file's permissions, or whether or not the standard input device is a
+terminal.
+The
+.Fl i
+option overrides any previous
+.Fl f 
+options.
+.It Fl P
+Overwrite regular files before deleting them.
+Files are overwritten three times, first with the byte pattern 0xff,
+then 0x00, and then 0xff again, before they are deleted.
+.It Fl R
+Attempt to remove the file hierarchy rooted in each file argument.
+The 
+.Fl R
+option implies the
+.Fl d
+option.
+If the
+.Fl i
+option is specified, the user is prompted for confirmation before 
+each directory's contents are processed (as well as before the attempt
+is made to remove the directory).
+If the user does not respond affirmatively, the file hierarchy rooted in
+that directory is skipped.
+.Pp
+.It Fl r
+Equivalent to
+.Fl R .
+.It Fl W
+Attempts to undelete the named files.
+Currently, this option can only be used to recover
+files covered by whiteouts.
+.El
+.Pp
+The
+.Nm rm
+utility removes symbolic links, not the files referenced by the links.
+.Pp
+It is an error to attempt to remove the files ``.'' and ``..''.
+.Pp
+The
+.Nm rm
+utility exits 0 if all of the named files or file hierarchies were removed,
+or if the
+.Fl f
+option was specified and all of the existing files or file hierarchies were
+removed.
+If an error occurs,
+.Nm rm
+exits with a value >0.
+.Sh SEE ALSO
+.Xr rmdir 1 ,
+.Xr undelete 2 ,
+.Xr unlink 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh BUGS
+The
+.Fl P
+option assumes that the underlying file system is a fixed-block file
+system.
+UFS is a fixed-block file system, LFS is not.
+In addition, only regular files are overwritten, other types of files
+are not.
+.Sh COMPATIBILITY
+The
+.Nm rm
+utility differs from historical implementations in that the
+.Fl f
+option only masks attempts to remove non-existent files instead of
+masking a large variety of errors.
+.Pp
+Also, historical
+.Bx
+implementations prompted on the standard output,
+not the standard error output.
+.Sh STANDARDS
+The
+.Nm rm
+command is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr/src/bin/rm/rm.c b/usr/src/bin/rm/rm.c
new file mode 100644 (file)
index 0000000..9de3c3f
--- /dev/null
@@ -0,0 +1,399 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rm.c       8.8 (Berkeley) 4/27/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok;
+
+int    check __P((char *, char *, struct stat *));
+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));
+
+/*
+ * rm --
+ *     This rm is different from historic rm's, but is expected to match
+ *     POSIX 1003.2 behavior.  The most visible difference is that -f
+ *     has two specific effects now, ignore non-existent files and force
+ *     file removal.
+ */
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch, rflag;
+
+       Pflag = rflag = 0;
+       while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
+               switch(ch) {
+               case 'd':
+                       dflag = 1;
+                       break;
+               case 'f':
+                       fflag = 1;
+                       iflag = 0;
+                       break;
+               case 'i':
+                       fflag = 0;
+                       iflag = 1;
+                       break;
+               case 'P':
+                       Pflag = 1;
+                       break;
+               case 'R':
+               case 'r':                       /* Compatibility. */
+                       rflag = 1;
+                       break;
+               case 'W':
+                       Wflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1)
+               usage();
+
+       checkdot(argv);
+
+       if (*argv) {
+               stdin_ok = isatty(STDIN_FILENO);
+
+               if (rflag)
+                       rm_tree(argv);
+               else
+                       rm_file(argv);
+       }
+
+       exit (eval);
+}
+
+void
+rm_tree(argv)
+       char **argv;
+{
+       FTS *fts;
+       FTSENT *p;
+       int needstat;
+       int flags;
+
+       /*
+        * 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;
+
+       /*
+        * If the -i option is specified, the user can skip on the pre-order
+        * visit.  The fts_number field flags skipped directories.
+        */
+#define        SKIPPED 1
+
+       flags = FTS_PHYSICAL;
+       if (!needstat)
+               flags |= FTS_NOSTAT;
+       if (Wflag)
+               flags |= FTS_WHITEOUT;
+       if (!(fts = fts_open(argv, flags, (int (*)())NULL)))
+               err(1, NULL);
+       while ((p = fts_read(fts)) != NULL) {
+               switch (p->fts_info) {
+               case FTS_DNR:
+                       if (!fflag || p->fts_errno != ENOENT) {
+                               warnx("%s: %s",
+                                   p->fts_path, strerror(p->fts_errno));
+                               eval = 1;
+                       }
+                       continue;
+               case FTS_ERR:
+                       errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
+               case FTS_NS:
+                       /*
+                        * FTS_NS: assume that if can't stat the file, it
+                        * can't be unlinked.
+                        */
+                       if (!needstat)
+                               break;
+                       if (!fflag || p->fts_errno != ENOENT) {
+                               warnx("%s: %s",
+                                   p->fts_path, strerror(p->fts_errno));
+                               eval = 1;
+                       }
+                       continue;
+               case FTS_D:
+                       /* Pre-order: give user chance to skip. */
+                       if (!fflag && !check(p->fts_path, p->fts_accpath,
+                           p->fts_statp)) {
+                               (void)fts_set(fts, p, FTS_SKIP);
+                               p->fts_number = SKIPPED;
+                       }
+                       continue;
+               case FTS_DP:
+                       /* Post-order: see if user skipped. */
+                       if (p->fts_number == SKIPPED)
+                               continue;
+                       break;
+               default:
+                       if (!fflag &&
+                           !check(p->fts_path, p->fts_accpath, p->fts_statp))
+                               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;
+
+               case FTS_W:
+                       if (!undelete(p->fts_accpath) ||
+                           fflag && errno == ENOENT)
+                               continue;
+                       break;
+
+               default:
+                       if (Pflag)
+                               rm_overwrite(p->fts_accpath, NULL);
+                       if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
+                               continue;
+               }
+               warn("%s", p->fts_path);
+               eval = 1;
+       }
+       if (errno)
+               err(1, "fts_read");
+}
+
+void
+rm_file(argv)
+       char **argv;
+{
+       struct stat sb;
+       int rval;
+       char *f;
+
+       /*
+        * Remove a file.  POSIX 1003.2 states that, by default, attempting
+        * to remove a directory is an error, so must always stat the file.
+        */
+       while ((f = *argv++) != NULL) {
+               /* Assume if can't stat the file, can't unlink it. */
+               if (lstat(f, &sb)) {
+                       if (Wflag) {
+                               sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
+                       } else {
+                               if (!fflag || errno != ENOENT) {
+                                       warn("%s", f);
+                                       eval = 1;
+                               }
+                               continue;
+                       }
+               } else if (Wflag) {
+                       warnx("%s: %s", f, strerror(EEXIST));
+                       eval = 1;
+                       continue;
+               }
+
+               if (S_ISDIR(sb.st_mode) && !dflag) {
+                       warnx("%s: is a directory", f);
+                       eval = 1;
+                       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);
+               }
+               if (rval && (!fflag || errno != ENOENT)) {
+                       warn("%s", f);
+                       eval = 1;
+               }
+       }
+}
+
+/*
+ * rm_overwrite --
+ *     Overwrite the file 3 times with varying bit patterns.
+ *
+ * XXX
+ * This is a cheap way to *really* delete files.  Note that only regular
+ * files are deleted, directories (and therefore names) will remain.
+ * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
+ * System V file system).  In a logging file system, you'll have to have
+ * kernel support.
+ */
+void
+rm_overwrite(file, sbp)
+       char *file;
+       struct stat *sbp;
+{
+       struct stat sb;
+       off_t len;
+       int fd, wlen;
+       char buf[8 * 1024];
+
+       fd = -1;
+       if (sbp == NULL) {
+               if (lstat(file, &sb))
+                       goto err;
+               sbp = &sb;
+       }
+       if (!S_ISREG(sbp->st_mode))
+               return;
+       if ((fd = open(file, O_WRONLY, 0)) == -1)
+               goto err;
+
+#define        PASS(byte) {                                                    \
+       memset(buf, byte, sizeof(buf));                                 \
+       for (len = sbp->st_size; len > 0; len -= wlen) {                \
+               wlen = len < sizeof(buf) ? len : sizeof(buf);           \
+               if (write(fd, buf, wlen) != wlen)                       \
+                       goto err;                                       \
+       }                                                               \
+}
+       PASS(0xff);
+       if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
+               goto err;
+       PASS(0x00);
+       if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
+               goto err;
+       PASS(0xff);
+       if (!fsync(fd) && !close(fd))
+               return;
+
+err:   eval = 1;
+       warn("%s", file);
+}
+
+
+int
+check(path, name, sp)
+       char *path, *name;
+       struct stat *sp;
+{
+       int ch, first;
+       char modep[15];
+
+       /* Check -i first. */
+       if (iflag)
+               (void)fprintf(stderr, "remove %s? ", path);
+       else {
+               /*
+                * If it's not a symbolic link and it's unwritable and we're
+                * talking to a terminal, ask.  Symbolic links are excluded
+                * 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))
+                       return (1);
+               strmode(sp->st_mode, modep);
+               (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
+                   modep + 1, modep[9] == ' ' ? "" : " ",
+                   user_from_uid(sp->st_uid, 0),
+                   group_from_gid(sp->st_gid, 0), path);
+       }
+       (void)fflush(stderr);
+
+       first = ch = getchar();
+       while (ch != '\n' && ch != EOF)
+               ch = getchar();
+       return (first == 'y');
+}
+
+#define ISDOT(a)       ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
+void
+checkdot(argv)
+       char **argv;
+{
+       char *p, **save, **t;
+       int complained;
+
+       complained = 0;
+       for (t = argv; *t;) {
+               if ((p = strrchr(*t, '/')) != NULL)
+                       ++p;
+               else
+                       p = *t;
+               if (ISDOT(p)) {
+                       if (!complained++)
+                               warnx("\".\" and \"..\" may not be removed");
+                       eval = 1;
+                       for (save = t; (t[0] = t[1]) != NULL; ++t)
+                               continue;
+                       t = save;
+               } else
+                       ++t;
+       }
+}
+
+void
+usage()
+{
+
+       (void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n");
+       exit(1);
+}