View Issue Details

IDProjectCategoryView StatusLast Update
00001611003.1(2008)/Issue 7Shell and Utilitiespublic2013-04-16 13:06
Reportereblake Assigned Toajosey  
PrioritynormalSeverityObjectionTypeError
Status ClosedResolutionAccepted As Marked 
NameEric Blake
OrganizationN/A
User Referenceebb.mkdir
Sectionmkdir
Page Number2937
Line Number96644
Interp StatusApproved
Final Accepted TextSee 0000161:0000247
Summary0000161: mkdir -p through dangling symlink
DescriptionThe description of mkdir -p in terms of a shell script is incorrect in the presence of whitespace in the directory name. In particular, consider:

mkdir -p "$(printf 'a b\n'/c')"

which in practice creates the parent directory with a trailing newline, but which according to the spec effectively recursively calls mkdir(1) with two arguments and no newline, instead of a single argument.

Furthermore, it seems inconsistent that:

ln -s dir dangling
mkdir -p dangling/sub

is required to fail (since it recurses to 'mkdir -p dangling', but that must fail with EEXIST), while

mkdir -p dangling/

is required to succeed (the recursion sees $(dirname dangling/) which is ".", and that exists; next the code has the same effect as mkdir("dangling/") which is required to follow through the symlink per mkdir(2)). (It doesn't help that GNU/Linux mkdir(2) fails with EEXIST when dealing with a symlink with trailing slash, in violation of the standard as written).

Correctly describing the dirname operation in shell without losing a trailing newline gets rather lengthy. Providing a trailing slash to the recursive mkdir call solves the problem of stripping trailing newline. Furthermore, requiring a trailing slash on all intermediate components will give more predictable behavior, where a directory can be created through a dangling symlink.
Desired ActionReplace lines 96644-5:

mkdir −p −m $(umask −S),u+wx $(dirname dir) &&
mkdir [−m mode] dir

with:

mkdir −p −m $(umask −S),u+wx \
  "$(dirname "dir" | head -n -1; printf /)" &&
mkdir [−m mode] "dir"
Tagstc1-2008

Relationships

parent of 0000682 Applied Online Pubs html rendition of 'mkdir -p' is missing important text 

Activities

eblake

2009-10-03 23:24

manager   bugnote:0000245

Typo in the Description:

mkdir -p "$(printf 'a b\n'/c')"

should be:

mkdir -p "$(printf 'a b\n/c')"

eblake

2009-10-03 23:38

manager   bugnote:0000246

The desired action also has a typo; 'head -c -1' was intended instead of 'head -n -1'. But unfortunately, even that is not portable (unlike the counterpart of 'tail -c -1'). For that matter, the only portable means I could find for discarding only the trailing newline involves even more processes, including running the dirname command twice:

mkdir −p −m $(umask −S),u+wx "$(dirname "dir" |
  dd bs=1 count=$(($(dirname "dir" | wc -c) - 1)) 2>/dev/null; echo /)" &&
mkdir [−m mode] "dir"

geoffclare

2009-10-05 09:59

manager   bugnote:0000247

Last edited: 2009-10-09 08:12

Interpretation response
------------------------
The standard states the requirements for mkdir -p,
and conforming implementations must conform to this. However, concerns
have been raised about this which are being referred to the sponsor.

Rationale:
-------------
None.

Notes to the Editor (not part of this interpretation):
-------------------------------------------------------

Replace the description of -p with:
-p  Create any missing intermediate pathname components.

    For each dir operand that does not name an existing directory,
    before performing the actions described in the DESCRIPTION above
    the mkdir utility shall create any pathname components of the path
    prefix of dir that do not name an existing directory by performing
    actions equivalent to first calling the mkdir() function with the
    following arguments:

      1. A pathname naming the missing pathname component, ending
         with a trailing <slash> character, as the path argument.

      2. The value zero as the mode argument.

    and then calling the chmod() function with the following
    arguments:

      1. The same path argument as in the mkdir() call.

      2. The value (S_IWUSR|S_IXUSR|~filemask)&0777 as the mode argument,
         where filemask is the file mode creation mask of the process
         (see [xref to umask()]).

    Each dir operand that names an existing directory shall be ignored
    without error.


Issue History

Date Modified Username Field Change
2009-10-03 23:17 eblake New Issue
2009-10-03 23:17 eblake Status New => Under Review
2009-10-03 23:17 eblake Assigned To => ajosey
2009-10-03 23:17 eblake Name => Eric Blake
2009-10-03 23:17 eblake Organization => N/A
2009-10-03 23:17 eblake User Reference => ebb.mkdir
2009-10-03 23:17 eblake Section => mkdir
2009-10-03 23:17 eblake Page Number => 2937
2009-10-03 23:17 eblake Line Number => 96644
2009-10-03 23:24 eblake Note Added: 0000245
2009-10-03 23:38 eblake Note Added: 0000246
2009-10-05 09:59 geoffclare Note Added: 0000247
2009-10-05 10:00 geoffclare Note Edited: 0000247
2009-10-05 10:01 geoffclare Note Edited: 0000247
2009-10-08 16:22 nick Interp Status => Pending
2009-10-08 16:22 nick Final Accepted Text => See 0000161:0000247
2009-10-08 16:22 nick Status Under Review => Interpretation Required
2009-10-08 16:22 nick Resolution Open => Accepted As Marked
2009-10-08 16:26 nick Note Edited: 0000247
2009-10-09 08:12 geoffclare Note Edited: 0000247
2009-11-07 07:20 ajosey Interp Status Pending => Proposed
2009-12-08 10:00 ajosey Interp Status Proposed => Approved
2010-09-21 11:25 geoffclare Tag Attached: tc1-2008
2013-04-16 13:06 ajosey Status Interpretation Required => Closed
2013-04-23 17:52 eblake Relationship added parent of 0000682