In episode 103 of BSD Now, Bryan Cantrill talks about the fact that on illumos "rm -rf /" is an error. It turns out that this behavior is mandated by POSIX 1003.1-2013:
"If either of the files dot or dot-dot are specified as the basename portion of an operand (that is, the final pathname component) or if an operand resolves to the root directory, rm shall write a diagnostic message to standard error and do nothing more with such operands." http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html On FreeBSD, a check for "/" only happens if POSIXLY_CORRECT is set, while on NetBSD, there currently is no such check at all. Index: bin/rm/rm.1 =================================================================== RCS file: /var/cvs/src/bin/rm/rm.1,v retrieving revision 1.37 diff -u -p -r1.37 rm.1 --- bin/rm/rm.1 25 May 2014 19:07:36 -0000 1.37 +++ bin/rm/rm.1 22 Aug 2015 21:49:02 -0000 @@ -102,6 +102,7 @@ The utility removes symbolic links, not the files referenced by the links. .Pp It is an error to attempt to remove the files +.Dq / , .Dq \&. or .Dq .. . Index: bin/rm/rm.c =================================================================== RCS file: /var/cvs/src/bin/rm/rm.c,v retrieving revision 1.30 diff -u -p -r1.30 rm.c --- bin/rm/rm.c 16 Jan 2015 06:39:32 -0000 1.30 +++ bin/rm/rm.c 22 Aug 2015 21:58:28 -0000 @@ -54,7 +54,7 @@ extern char *__progname; int dflag, eval, fflag, iflag, Pflag, stdin_ok; int check(char *, char *, struct stat *); -void checkdot(char **); +void checkdotorslash(char **); void rm_file(char **); int rm_overwrite(char *, struct stat *); int pass(int, off_t, char *, size_t); @@ -105,7 +105,7 @@ main(int argc, char *argv[]) if (argc < 1 && fflag == 0) usage(); - checkdot(argv); + checkdotorslash(argv); if (*argv) { stdin_ok = isatty(STDIN_FILENO); @@ -383,7 +383,7 @@ check(char *path, char *name, struct sta */ #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) void -checkdot(char **argv) +checkdotorslash(char **argv) { char *p, **save, **t; int complained; @@ -401,9 +401,10 @@ checkdot(char **argv) else p = *t; - if (ISDOT(p)) { + if (ISDOT(p) || *p == '\0') { if (!complained++) - warnx("\".\" and \"..\" may not be removed"); + warnx("\"/\", \".\" and \"..\" may not be " + "removed"); eval = 1; for (save = t; (t[0] = t[1]) != NULL; ++t) continue;
