Subject: bug#12339: Bug: rm -fr . doesn't dir depth first deletion yet it is documented to do so.

From: Linda Walsh <[email-masked]>
Date: Fri, 07 Sep 2012 08:02:58 -0700

Eric Blake wrote:
> 
> Then set up a shell alias or a wrapper script that comes first in your
> $PATH.  Then it is under your explicit control, while the default is
> still appropriate for everyone else.
> 
> Just because the defaults don't match your expectations doesn't mean you
> can't change the behavior _on your system_ to avoid extra typing on your
> part.
---
	Doesn't work for programs that need to call rm to remove all files
in a dir.

And I already have changed default behavior on MY systems.

I already added the patch below -- that only does my behavior if the
user isn't running in POSIXLY_CORRECT mode.

Now it's certainly easier to set an env var 1 place to control script
behavior than making changes in all the places...

I'm just trying to get the env var in so I don't have to distribute
a version of rm with my scripts that works.

-----

--- src/remove.c        2011-10-10 00:56:46.000000000 -0700
+++ src/remove.c.new    2012-09-06 14:28:07.816810683 -0700
@@ -173,6 +173,35 @@
    }
  }

+
+/* separate functions to check for next part of file name being dotdot or...*/
+
+static inline bool
+dotdot (char const *file_name)
+{
+  if (file_name[0] == '.' && file_name[1])
+    {
+      char sep = file_name[(file_name[1] == '.') + 1];
+      return (! sep || ISSLASH (sep));
+    }
+  else
+    return false;
+}
+
+/* dot */
+
+static inline bool
+dot (char const * file_name)
+{
+       if (file_name[0] == '.')
+               {
+                       char sep = file_name[1];
+                       return (! sep || ISSLASH(sep));
+               }
+       else
+               return false;
+}
+
  /* Prompt whether to remove FILENAME (ent->, if required via a combination of
     the options specified by X and/or file attributes.  If the file may
     be removed, return RM_OK.  If the user declines to remove the file,
@@ -203,6 +232,7 @@

    int dirent_type = is_dir ? DT_DIR : DT_UNKNOWN;
    int write_protected = 0;
+       int special_delete_content = 0;

    /* When nonzero, this indicates that we failed to remove a child entry,
       either because the user declined an interactive prompt, or due to
@@ -222,7 +252,11 @@
        wp_errno = errno;
      }

-  if (write_protected || x->interactive == RMI_ALWAYS)
+       if (!x->posix_correctly && dot(filename) && !x->force)
+               special_delete_content = 1;
+
+
+  if (write_protected || x->interactive == RMI_ALWAYS || special_delete_content)
      {
        if (0 <= write_protected && dirent_type == DT_UNKNOWN)
          {
@@ -281,11 +315,16 @@
        if (dirent_type == DT_DIR
            && mode == PA_DESCEND_INTO_DIR
            && !is_empty)
-        fprintf (stderr,
-                 (write_protected
-                  ? _("%s: descend into write-protected directory %s? ")
-                  : _("%s: descend into directory %s? ")),
-                 program_name, quoted_name);
+                               {
+                                       char * action = special_delete_content
+                                                                              ? 
_("delete contents of")
+                                                                              : 
_("descend into");
+                                       fprintf (stderr,
+ 
(write_protected
+                                                                              ? 
_("%s: %s write-protected directory %s? ")
+                                                                              : 
_("%s: %s directory %s? ")),
+                                                                        action, 
program_name, quoted_name);
+                               }
        else
          {
            if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) != 0)
@@ -476,7 +515,8 @@

            /* If the basename of a command line argument is "." or "..",
               diagnose it and do nothing more with that argument.  */
-          if (dot_or_dotdot (last_component (ent->fts_accpath)))
+          if ( (x->posix_correctly ? dot_or_dotdot : dotdot)
+                                                       (last_component 
(ent->fts_accpath)))
              {
                error (0, 0, _("cannot remove directory: %s"),
                       quote (ent->fts_path));
--- src/remove.h        2011-07-28 03:38:27.000000000 -0700
+++ src/remove.h        2012-09-06 13:33:01.282362765 -0700
@@ -34,6 +34,14 @@
    /* If true, ignore nonexistent files.  */
    bool ignore_missing_files;

+       /* true if force (-f) was specified indicating user knows what they
+        * are doing and don't want to questioned or see errors from command */
+       bool force;
+
+       /* true for users wanting strict posix compliance of more flexible, lax,
+        * or useful behaviors */
+       bool posix_correctly;
+
    /* If true, query the user about whether to remove each file.  */
    enum rm_interactive interactive;

--- src/rm.c    2011-10-02 02:20:54.000000000 -0700
+++ src/rm.c    2012-09-06 13:33:04.132500554 -0700
@@ -206,6 +206,7 @@
    bool preserve_root = true;
    struct rm_options x;
    bool prompt_once = false;
+       x.posix_correctly = (getenv ("POSIXLY_CORRECT") != NULL );
    int c;

    initialize_main (&argc, &argv);
@@ -222,10 +226,11 @@
      {
        switch (c)
          {
-        case 'f':
+        case 'f':              /* suppress warnings/errors */
            x.interactive = RMI_NEVER;
            x.ignore_missing_files = true;
            prompt_once = false;
+                                       x.force = true;
            break;

          case 'i':