Austin Group Defect Tracker

Aardvark Mark IV


Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
ID Category Severity Type Date Submitted Last Update
0000802 [1003.1(2013)/Issue7+TC1] Shell and Utilities Objection Enhancement Request 2013-11-21 20:00 2024-06-11 09:02
Reporter eblake View Status public  
Assigned To
Priority normal Resolution Accepted  
Status Closed  
Name Eric Blake
Organization Red Hat
User Reference ebb.rm
Section rm
Page Number 3161
Line Number 105667
Interp Status ---
Final Accepted Text
Summary 0000802: Add 'rm -d'
Description Both BSD and GNU rm provide an extension option '-d' that makes rm behave as if it uses remove() instead of unlink() to remove its argument, making it possible to use rm to safely remove empty directories without risking recursion if the directory is not empty. While it is true that the standard provides rmdir for removing an empty directory without recursion, the burden is currently on the user to determine whether a name is a directory or not, which leads to a time-of-check-to-time-of-use race between discovering a file's type and then using either rm or rmdir on that file. Of course, if someone else is racing a recursive deletion process by filling directories to be non-empty, by replacing directories with files, or replacing files with directories, the entire attempt to empty out the directory is likely to fail anyways, so the race isn't that much to worry about. Still, it seems like it is worth standardizing 'rm -d'.


Side note:
On the GNU Coreutils list (http://debbugs.gnu.org/15926), [^] Linda Walsh raised an interesting observation that there is no clean way using just POSIX interfaces to attempt empty out the contents of a directory while leaving the directory itself intact, while still having a safety valve of not emptying beyond a mount point. An initial short attempt looks something like:

rm -r * .[!.] .?*

by using globs to cover all possible names, but that would produce spurious error messages about files not existing if not all the globs get expanded; using 'rm -rf' to silence the spurious error messages may also silence real errors. Using globs also potentially falls foul of maximum command line length. Furthermore, rm has no way to limit recursion to a single device (GNU rm has an extension --one-file-system that behaves similar to find's -xdev, but that's outside of POSIX). Something like:

find . -xdev -depth ! -name . \( \( -type d -exec rmdir {} \; \) -o -exec rm {} \; \)

works in the absence of races, but is inefficient at one process per deletion; it's also a lot of typing. Something like:

find . ! -name . -prune -exec rm -r {} +

benefits from fewer processes and no races between find learning a file's type and choosing which command to execute, but has no safety valves of stopping recursion across device boundaries (again, rm has no -xdev counterpart). Meanwhile, with this proposal, using:

find . -xdev -depth ! -name . -exec rm -d {} +

is a fairly short way to achieve the goal.

She also proposed the syntax 'rm -rF .' as the way to recurse through the contents of '.' rather than short-circuiting due to the current rule [line 105670] that an rm argument ending with a basename of '.' be entirely skipped; but such a proposal belongs in a different bug report, and would need existing practice, whereas 'rm -d' already has existing practice.
Desired Action At XCU page 3161 line 105667 [rm SYNOPSIS], change:

rm [−fiRr] file...
to:

rm [−dfiRr] file...


At line 105679 [DESCRIPTION 2.a.], change:

If neither the −R option nor the −r option is specified, rm shall write a diagnostic message to standard error, do nothing more with file, and go on to any remaining files.
to:

If neither the −R option nor the −r option is specified, but −d is specified, rm shall proceed with step 3 for the current file. If none of −r, −R, or −d is specified, rm shall write a diagnostic message to standard error, do nothing more with file, and go on to any remaining files.


After line 105693 [DESCRIPTION 2.d.], add a step:

e. rm shall proceed with step 4 for the current file.


At line 105694 [DESCRIPTION 3.], change:

If file is not of type directory, the −f option is not specified,...
to:

If the −f option is not specified,...


At line 105699 [DESCRIPTION 4.], change:

If the current file is a directory, rm shall perform actions equivalent to the rmdir( ) function defined in the System Interfaces volume of POSIX.1-2008 called with a pathname of the current file used as the path argument. If the current file is not a directory, rm shall perform actions equivalent to the unlink( ) function defined in the System Interfaces volume of POSIX.1-2008 called with a pathname of the current file used as the path argument.
to:

rm shall perform actions equivalent to the remove( ) function defined in the System Interfaces volume of POSIX.1-2008 called with a pathname of the current file used as the path argument.


At line 105713 [OPTIONS], insert a new paragraph:

−d Remove empty directories. See the DESCRIPTION.


At line 105781 [EXAMPLES], change:

removes the directory entries: a.out and core.
to:

removes the directory entries: a.out and core, or issues an error if either directory entry is itself a directory or does not exist.


After line 105784 [EXAMPLES], add a new paragraph:

3. The following command:
rm -d name

behaves like
rmdir name
if name is a directory (including failing if name is not empty), as if by the rmdir( ) function; and behaves like
rm name
if name is not a directory, as if by the unlink( ) function.


After line 105825 [RATIONALE], add a paragraph:

The addition of the −d option allows the use of rm to delete either a file or an empty directory without the risk of recursion into a non-empty directory, and without the inherent race between determining a file's type and deciding what action to attempt on that file. If either the −r or −R option is specified, the use of recursion takes precedence.

Tags issue8
Attached Files

- Relationships
related to 0000819Closed permit prompting only once when encountering a directory with 'rm -ir' 

-  Notes
(0002120)
rhansen (manager)
2014-01-30 18:26

I may have spotted a bug in this change with regard to interactive prompting (the behavior might be intentional, not a bug):

Consider this sequence of events:

  $ mkdir /tmp/foo
  $ chmod 500 /tmp/foo
  $ rm -ir /tmp/foo

With the above, the following steps are taken in rm:

  1. the file exists, so continue to step 2
  2. the file is of type directory, so continue to step 2.a.
     2.a. the -r option is specified, so continue to step 2.b.
     2.b. the -f option is not specified, the permissions of /tmp/foo
          do not permit writing, and the -i option is specified, so
          prompt the user. Assume the response is affirmative, so
          continue to step 2.c.
     2.c. there are no entries in /tmp/foo other than . and .. so
          continue to step 2.d.
     2.d. the -i option is specified, so prompt the user. Assume the
          response is affirmative, so continue to step 2.e.
     2.e. continue to step 4.
  4. remove("/tmp/foo")

Note that the user is prompted twice: once due to lack of write permission and once due to -i. (This double-prompting also happens in Issue7+TC2 without the bug #802 change applied.)

Contrast the above behavior with the behavior in this sequence of events:

  $ mkdir /tmp/foo
  $ chmod 500 /tmp/foo
  $ rm -id /tmp/foo

  1. the file exists, so continue to step 2
  2. the file is of type directory, so continue to step 2.a.
     2.a. neither -R nor -r are specified but -d is specified so
          continue to step 3
  3. the -f option is not specified, the permissions of the file do not
     permit writing, and the -i option is specified, so prompt the
     user. Assume the response is affirmative, so continue to step 4.
  4. remove("/tmp/foo")

Note that the user is prompted only once.

What is the intended/desired behavior? Is it correct as-is? Should the 'rm -id' case prompt the user twice to match the behavior of 'rm -ir'? Or is it a bug in the spec that the 'rm -ir' case prompts twice? (Both rm from GNU coreutils and rm from NetBSD only prompt once in the 'rm -ir' case.)
(0002121)
shware_systems (reporter)
2014-01-31 00:42

I read it the same way as you describe, but the wording could be clearer that the first case is so a directory marked read only isn't even examined for empty or has files, which I think is the intent, and the second prompt is for removal of an emptied directory, or one that was already empty. Those implementations may be checking if no subdir entries are present before the first prompt, though it doesn't seem they should as that's more part of Step 2.c., and just issuing the one prompt to combine 2.b. and 2.d. If the prompt used has both 'file is read only' and 'there are no entries' in some form I'd think that could be considered a benign extension. I read it as the intent is two prompts should be given because there may be multiple subdirs removed and the initial entry into dir "a" of a path "/tmp/foo/a/b/c/d/e" may have many intermediate "Remove .../e/file1 ?", .../e/file2, etc. prompts before getting back to the point where "a" now empty and is a candidate for removal. I think 2.d. should have the qualifier it's not required if any file in a subdir was explicitly skipped, because then it's already known the rmdir() will fail so asking superfluous.

Strictly, it seems 2.b. should have "This directory <file> read only, Skip Y/N?" as a prompt, and 2.d. something like "Remove empty directory <file> Y/N?" in some fashion. Step 2.b. with -i specified could be also construed as "<file> is a directory, Skip Y/N?" whether <file> read-only or read-write, and the read-only prompt version happens just when -i not specified but stdin is a terminal. It's missing a comma somewhere to disambiguate those two possibilities, it looks.

I'd consider it a bug in how they did it if a preemptive check for files has a side effect of modifying the last access time stamp and the operator says to skip it. The stat() call is required to flush pending time stamp changes, but I don't see it as expected to queue a new access time change if it succeeds. An opendir() call is left more as implementation-defined if that sort of change will occur, so a check using it might have that side effect and shouldn't.
(0002122)
geoffclare (manager)
2014-02-06 11:08

Solaris and HP-UX both behave as described in the standard for the
rm -ir case in Note: 0002120.

Solaris makes a clear distinction between the two prompts:

$ rm -ir /tmp/foo
rm: examine files in directory /tmp/foo (yes/no)? y
rm: remove /tmp/foo (yes/no)? y
$

The first prompt is asking whether to recurse into the directory; the
second is asking whether to remove the directory itself.

HP-UX also has different prompts, although they are both more cryptic:

$ rm -ir /tmp/foo
directory /tmp/foo: ? (y/n) y
/tmp/foo: ? (y/n) y
$

So it looks like the double prompting requirement for rm -ir is intentional.

For rm -id it makes sense that there is only one prompt as there is no
recursion to ask about.
(0002123)
eblake (manager)
2014-02-06 16:14
edited on: 2014-02-06 16:20

FreeBSD behavior:

$ cd /tmp
$ mkdir foo
$ touch foo/bar
$ chmod 500 foo
$ rm -ir foo
remove foo? y
remove foo/bar? y
rm: foo/bar: Permission denied
rm: foo: Directory not empty
$ chmod 700 foo
$ rm foo/bar
chmod 500 foo
$ rm -ir foo
remove foo? y
$ mkdir foo
$ touch foo/bar
$ rm -ir foo
remove foo? n
$ rm -ir foo
remove foo? y
remove foo/bar? y
$

So FreeBSD optimizes and only prompts once for an empty directory, and in the non-empty case only asks once for a directory - which is a bug, because all other implementations allow you to descend into a directory, remove all contents, but then answer no to removing the directory itself.

(0002131)
shware_systems (reporter)
2014-02-06 22:07

Issue raised in note #2120 moved to bug #819 as separate TC2 concern.

- Issue History
Date Modified Username Field Change
2013-11-21 20:00 eblake New Issue
2013-11-21 20:00 eblake Name => Eric Blake
2013-11-21 20:00 eblake Organization => Red Hat
2013-11-21 20:00 eblake User Reference => ebb.rm
2013-11-21 20:00 eblake Section => rm
2013-11-21 20:00 eblake Page Number => 3161
2013-11-21 20:00 eblake Line Number => 105667
2013-11-21 20:00 eblake Interp Status => ---
2013-11-21 20:08 eblake Desired Action Updated
2013-11-21 20:08 eblake Tag Attached: issue8
2014-01-30 17:26 Don Cragun Status New => Resolved
2014-01-30 17:26 Don Cragun Resolution Open => Accepted
2014-01-30 18:26 rhansen Note Added: 0002120
2014-01-31 00:42 shware_systems Note Added: 0002121
2014-02-06 11:08 geoffclare Note Added: 0002122
2014-02-06 16:14 eblake Note Added: 0002123
2014-02-06 16:16 eblake Note Edited: 0002123
2014-02-06 16:20 eblake Note Edited: 0002123
2014-02-06 17:32 eblake Relationship added related to 0000819
2014-02-06 22:07 shware_systems Note Added: 0002131
2020-03-25 16:02 geoffclare Status Resolved => Applied
2024-06-11 09:02 agadmin Status Applied => Closed


Mantis 1.1.6[^]
Copyright © 2000 - 2008 Mantis Group
Powered by Mantis Bugtracker