-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 According to Eric Blake on 10/4/2009 8:29 AM: >> If you feel like addressing that right away, that would >> be great. Otherwise, I think it's safe to say that no one >> will complain if it is deferred until 8.1. > > At this point, it's enough of a corner case that I'm okay deferring any > mkdir(1) (or even mkdir(2) cleanups in gnulib) until after coreutils 8.0.
For the record, I tried the quick hack below to change things in just coreutils for 'mkdir -p' (without even touching plain 'mkdir' for Linux), all without modifying gnulib. But it still fails on Solaris, because gnulib's make_dir_parents fails on savewd_chdir if the trailing slash is not present on a symlink to a directory. So since fixing this requires gnulib tweaks, and is more invasive than I originally suspected, I'd feel more comfortable waiting for the Austin group ruling on mkdir -p before changing any behavior. - -- Don't work too hard, make some time for fun as well! Eric Blake e...@byu.net -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (Cygwin) Comment: Public key at home.comcast.net/~ericblake/eblake.gpg Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkrIupwACgkQ84KuGfSFAYAdMgCgj7tYc5uetn1VADy+ENvlRnro 1YUAnjrNYenWD/EZ6dqlPGCiNY/qJi9V =yJGI -----END PGP SIGNATURE-----
diff --git a/NEWS b/NEWS index aff0744..fe287f6 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,9 @@ GNU coreutils NEWS -*- outline -*- id no longer prints SELinux " context=..." when the POSIXLY_CORRECT environment variable is set. + mkdir now reliably creates a directory through a dangling symlink + given with a trailing slash. + readlink -f now ignores a trailing slash when deciding if the last component (possibly via a dangling symlink) can be created, since mkdir will succeed in that case. diff --git a/src/mkdir.c b/src/mkdir.c index 756cc40..ffdc0b1 100644 --- a/src/mkdir.c +++ b/src/mkdir.c @@ -29,6 +29,7 @@ #include "prog-fprintf.h" #include "quote.h" #include "savewd.h" +#include "canonicalize.h" /* The official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "mkdir" @@ -105,6 +106,36 @@ announce_mkdir (char const *dir, void *options) prog_fprintf (stdout, o->created_directory_format, quote (dir)); } +/* Create an ancestor directory DIR with the given MODE. If DIR is a + symlink, guarantee the POSIX semantics as if we had called + mkdir(DIR/). */ +static int +mkdir_follow (char const *dir, mode_t mode) +{ + int r = mkdir (dir, mode); + int saved_errno = errno; + if (r && saved_errno == EEXIST) + { + /* If "dir" is a dangling symlink, create a directory at the + location pointed to by the link so that "dir/" will + resolve. */ + struct stat st; + if (stat (dir, &st) == -1 && errno == ENOENT) + { + char *target = canonicalize_filename_mode (dir, CAN_ALL_BUT_LAST); + if (target) + { + int saved_errno; + r = mkdir (target, mode); + saved_errno = errno; + free (target); + } + } + } + errno = saved_errno; + return r; +} + /* Make ancestor directory DIR, whose last component is COMPONENT, with options OPTIONS. Assume the working directory is COMPONENT's parent. Return 0 if successful and the resulting directory is @@ -114,7 +145,7 @@ static int make_ancestor (char const *dir, char const *component, void *options) { struct mkdir_options const *o = options; - int r = mkdir (component, o->ancestor_mode); + int r = mkdir_follow (component, o->ancestor_mode); if (r == 0) { r = ! (o->ancestor_mode & S_IRUSR); diff --git a/tests/mkdir/p-thru-slink b/tests/mkdir/p-thru-slink index 30df4fe..a1e792a 100755 --- a/tests/mkdir/p-thru-slink +++ b/tests/mkdir/p-thru-slink @@ -24,9 +24,14 @@ fi . $srcdir/test-lib.sh ln -s . slink || framework_failure +ln -s dir slink2 || framework_failure fail=0 mkdir -p slink/x || fail=1 test -d x || fail=1 +# This test failed on GNU/Linux through coreutils 7.6. +mkdir -p slink2/x || fail=1 +test -d dir/x || fail=1 + Exit $fail diff --git a/tests/mkdir/t-slash b/tests/mkdir/t-slash index 3214bf4..128fecd 100755 --- a/tests/mkdir/t-slash +++ b/tests/mkdir/t-slash @@ -33,4 +33,9 @@ test -d dir || fail=1 mkdir d2/ || fail=1 test -d d2 || fail=1 +# This test failed on GNU/Linux through coreutils 7.6. +ln -s d3 link || framework_failure +mkdir link/ || fail=1 +test -d d3 || fail=1 + Exit $fail