Subject: bug#12260: [patch] rm -d in coreutils 8.19

From: Jim Meyering <[email-masked]>
Date: Thu, 23 Aug 2012 20:53:22 +0200

Jim Meyering wrote:
> Jim Meyering wrote:
>> Pádraig Brady wrote:
>>> On 08/23/2012 12:17 PM, Robert Day wrote:
>>>> On 22 August 2012 23:23, Robert Day <[email-masked]> wrote:
>>>>
>>>>> I've attached a patch which fixes this bug and adds a test of that code
>>>>> path. The fixes can also be retrieved from
>>>>> https://github.com/rkd91/coreutils_rm_di_patch.
>>>>>
>>>>
>>>> I've made a couple of related fixes (comments/code niceness and adding a
>>>> NEWS item), so I've attached a new patch and updated my github.
>>>
>>> Thanks for handling that Robert.
>>> It's on the borderline for copyright assignment
>>> (which I don't think you have?).
>>> It's probably OK to waive in this case anyway.
>>
>> I agree that it's borderline, but also that it's ok.
>>
>>> I've not got time to fully squash/review your
>>> patches just now, but we'll add them in soon.
>>
>> I'm going through it now, constructing a proper commit log, attributing
>> the reporter, fixing NEWS to avoid the minor syntax-check failure,
>> adjusting comment formatting, e.g.,

Thanks again for the patch, Robert.
I'll wait for your ACK before pushing this.

>From dd22da8e9539cc88193987b6997769ae4ede2b15 Mon Sep 17 00:00:00 2001
From: Rob Day <[email-masked]>
Date: Wed, 22 Aug 2012 23:04:19 +0100
Subject: [PATCH] rm: fix the new --dir (-d) option to work with -i

* src/remove.c (prompt): Hoist the computation of is_empty, since we'll
need it slightly earlier.
Before, this function would arrange to fail with EISDIR when processing
a directory without --recursive (-r).  Adjust the condition to exempt
an empty directory when --dir has been specified.
Improve comments.
* tests/rm/d-3: New file, to ensure that rm -d -i dir works.
* tests/Makefile.am (TESTS): Add it.
* NEWS (Bug fixes): Mention it.
* THANKS.in: Update.
Reported by Michael Price in http://bugs.gnu.org/12260
---
 NEWS              |  4 ++++
 THANKS.in         |  1 +
 src/remove.c      | 24 +++++++++++++-----------
 tests/Makefile.am |  1 +
 tests/rm/d-3      | 37 +++++++++++++++++++++++++++++++++++++
 5 files changed, 56 insertions(+), 11 deletions(-)
 create mode 100755 tests/rm/d-3

diff --git a/NEWS b/NEWS
index d8a47ab..e6d79bf 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   it detects this precise type of cycle, diagnoses it as such and
   eventually exits nonzero.

+  rm -i -d now prompts the user then removes an empty directory, rather
+  than ignoring the -d option and failing with an 'Is a directory' error.
+  [bug introduced in coreutils-8.19, with the addition of --dir (-d)]
+

 * Noteworthy changes in release 8.19 (2012-08-20) [stable]

diff --git a/THANKS.in b/THANKS.in
index ca22e15..f288174 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -425,6 +425,7 @@ Michael McFarland                   [email-masked]
 Michael McLagan                     [email-masked]
 Michael Mol                         [email-masked]
 Michael Piefel                      [email-masked]
+Michael Price                       [email-masked]
 Michael Steffens                    [email-masked]
 Michael Stummvoll                   [email-masked]
 Michael Stutz                       [email-masked]
diff --git a/src/remove.c b/src/remove.c
index c4972ac..69faae6 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -190,6 +190,13 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir,
   int dirent_type = is_dir ? DT_DIR : DT_UNKNOWN;
   int write_protected = 0;

+  bool is_empty = false;
+  if (is_empty_p)
+    {
+      is_empty = is_empty_dir (fd_cwd, filename);
+      *is_empty_p = is_empty ? T_YES : T_NO;
+    }
+
   /* When nonzero, this indicates that we failed to remove a child entry,
      either because the user declined an interactive prompt, or due to
      some other failure, like permissions.  */
@@ -238,7 +245,10 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir,
             break;

           case DT_DIR:
-            if (!x->recursive)
+             /* Unless we're either deleting directories or deleting
+                recursively, we want to raise an EISDIR error rather than
+                prompting the user  */
+            if ( ! (x->recursive || (x->remove_empty_directories && is_empty)))
               {
                 write_protected = -1;
                 wp_errno = EISDIR;
@@ -254,15 +264,6 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir,
           return RM_ERROR;
         }

-      bool is_empty;
-      if (is_empty_p)
-        {
-          is_empty = is_empty_dir (fd_cwd, filename);
-          *is_empty_p = is_empty ? T_YES : T_NO;
-        }
-      else
-        is_empty = false;
-
       /* Issue the prompt.  */
       if (dirent_type == DT_DIR
           && mode == PA_DESCEND_INTO_DIR
@@ -420,7 +421,8 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
         {
           /* This is the first (pre-order) encounter with a directory
              that we cannot delete.
-             Not recursive, so arrange to skip contents.  */
+             Not recursive, and it's not an empty directory (if we're removing
+             them) so arrange to skip contents.  */
           int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR;
           error (0, err, _("cannot remove %s"), quote (ent->fts_path));
           mark_ancestor_dirs (ent);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index acd816d..87d6cad 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -101,6 +101,7 @@ TESTS =						\
   misc/ls-time					\
   rm/d-1					\
   rm/d-2					\
+  rm/d-3					\
   rm/deep-1					\
   rm/deep-2					\
   rm/dir-no-w					\
diff --git a/tests/rm/d-3 b/tests/rm/d-3
new file mode 100755
index 0000000..2f2cf74
--- /dev/null
+++ b/tests/rm/d-3
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Ensure that 'rm -d -i dir' (i.e., without --recursive) gives a prompt and
+# then deletes the directory if it is empty
+
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+print_ver_ rm
+
+mkdir d || framework_failure_
+
+echo "y" | rm -i -d --verbose d > out 2> out.err || fail=1
+printf "%s" \
+    "rm: remove directory 'd'? " \
+    > exp.err || framework_failure_
+
+printf "%s\n" \
+    "removed directory: 'd'" \
+    > exp || framework_failure_
+
+compare exp out || fail=1
+compare exp.err out.err || fail=1
+
+Exit $fail
--
1.7.12.70.g851f7e6