bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

mkdirat; openat and closed fds


From: Eric Blake
Subject: mkdirat; openat and closed fds
Date: Wed, 07 Oct 2009 07:16:14 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.23) Gecko/20090812 Thunderbird/2.0.0.23 Mnenhy/0.7.6.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I noticed that mkdirat didn't have any tests.  While I didn't find any
systems where mkdirat is implemented and mkdir("dir/") fails (which would
imply that we need rpl_mkdir), I did find that cygwin 1.5 and mingw have
bugs where mkdir("dir/./") mistakenly succeeds.  I also found that on
systems without /proc/self, our openat emulation has a bug:

int fd = dup (0);
close (fd);
openat (fd, "name", O_RDONLY);

attempts to open "./name" instead of failing with EBADF.  Why?  Because
the save_cwd call opens ".", which lands in the fd slot that we just
abandonded, such that our fchdir(fd) succeeds instead of detecting a bad
fd.  To properly fail with EBADF, we must either blacklist the fd passed
in by the user after calling save_cwd, or manually check that fd is good
prior to calling save_cwd (using dup2(fd)!=fd).  The former costs an open
and close on the failure path but has no impact to the normal case, while
the latter avoids the open(".") altogether on failure but costs an extra
syscall on the success path.  So I went with the former.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----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/

iEYEARECAAYFAkrMlJ4ACgkQ84KuGfSFAYCMcwCfTowtEry3i/LjXU68t8XBhtY5
MboAnRDJePWUkADcWdzYMUNRzM04+mlz
=Rb8s
-----END PGP SIGNATURE-----
>From da8f2a565cd9522f2a3a1400e538d1853374aa08 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 6 Oct 2009 15:43:12 -0600
Subject: [PATCH 1/3] mkdir, mkdirat: add tests

This test exposes failures on cygwin 1.5 and in our mkdirat emulation.

* modules/mkdir-tests: New test.
* tests/test-mkdir.h: New file.
* tests/test-mkdir.c: Likewise.
* tests/test-mkdirat.c: Likewise.
* modules/openat-tests (Files): Add new files.
(Makefile.am): Run new test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog            |   10 +++++
 modules/mkdir-tests  |   15 ++++++++
 modules/openat-tests |    7 +++-
 tests/test-mkdir.c   |   53 +++++++++++++++++++++++++++
 tests/test-mkdir.h   |   98 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-mkdirat.c |   91 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 272 insertions(+), 2 deletions(-)
 create mode 100644 modules/mkdir-tests
 create mode 100644 tests/test-mkdir.c
 create mode 100644 tests/test-mkdir.h
 create mode 100644 tests/test-mkdirat.c

diff --git a/ChangeLog b/ChangeLog
index cc4b51d..3ce4f94 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2009-10-07  Eric Blake  <address@hidden>
+
+       mkdir, mkdirat: add tests
+       * modules/mkdir-tests: New test.
+       * tests/test-mkdir.h: New file.
+       * tests/test-mkdir.c: Likewise.
+       * tests/test-mkdirat.c: Likewise.
+       * modules/openat-tests (Files): Add new files.
+       (Makefile.am): Run new test.
+
 2009-10-06  Eric Blake  <address@hidden>

        doc: tweak *at function documentation
diff --git a/modules/mkdir-tests b/modules/mkdir-tests
new file mode 100644
index 0000000..cff0a5b
--- /dev/null
+++ b/modules/mkdir-tests
@@ -0,0 +1,15 @@
+Files:
+tests/test-mkdir.h
+tests/test-mkdir.c
+
+Depends-on:
+progname
+stdbool
+symlink
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-mkdir
+check_PROGRAMS += test-mkdir
+test_mkdir_LDADD = $(LDADD) @LIBINTL@
diff --git a/modules/openat-tests b/modules/openat-tests
index 0d0ff8c..1440a9b 100644
--- a/modules/openat-tests
+++ b/modules/openat-tests
@@ -1,9 +1,11 @@
 Files:
 tests/test-lstat.h
+tests/test-mkdir.h
 tests/test-rmdir.h
 tests/test-stat.h
 tests/test-unlink.h
 tests/test-fstatat.c
+tests/test-mkdirat.c
 tests/test-openat.c
 tests/test-unlinkat.c

@@ -15,8 +17,9 @@ unlinkdir
 configure.ac:

 Makefile.am:
-TESTS += test-fstatat test-openat test-unlinkat
-check_PROGRAMS += test-fstatat test-openat test-unlinkat
+TESTS += test-fstatat test-mkdirat test-openat test-unlinkat
+check_PROGRAMS += test-fstatat test-mkdirat test-openat test-unlinkat
 test_fstatat_LDADD = $(LDADD) @LIBINTL@
+test_mkdirat_LDADD = $(LDADD) @LIBINTL@
 test_openat_LDADD = $(LDADD) @LIBINTL@
 test_unlinkat_LDADD = $(LDADD) @LIBINTL@
diff --git a/tests/test-mkdir.c b/tests/test-mkdir.c
new file mode 100644
index 0000000..cc51fce
--- /dev/null
+++ b/tests/test-mkdir.c
@@ -0,0 +1,53 @@
+/* Tests of mkdir.
+   Copyright (C) 2009 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/>.  */
+
+/* Written by Eric Blake <address@hidden>, 2009.  */
+
+#include <config.h>
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define ASSERT(expr) \
+  do                                                                         \
+    {                                                                        \
+      if (!(expr))                                                           \
+       {                                                                    \
+         fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
+         fflush (stderr);                                                   \
+         abort ();                                                          \
+       }                                                                    \
+    }                                                                        \
+  while (0)
+
+#define BASE "test-mkdir.t"
+
+#include "test-mkdir.h"
+
+int
+main ()
+{
+  /* Clean up any trash from prior testsuite runs.  */
+  ASSERT (system ("rm -rf " BASE "*") == 0);
+
+  return test_mkdir (mkdir, true);
+}
diff --git a/tests/test-mkdir.h b/tests/test-mkdir.h
new file mode 100644
index 0000000..1fd7fe4
--- /dev/null
+++ b/tests/test-mkdir.h
@@ -0,0 +1,98 @@
+/* Test of mkdir() function.
+   Copyright (C) 2009 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 2 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/>.  */
+
+/* This file is designed to test both mkdir(a,b) and
+   mkdirat(AT_FDCWD,a,b).  FUNC is the function to test.  Assumes that
+   BASE and ASSERT are already defined, and that appropriate headers
+   are already included.  If PRINT, warn before skipping tests with
+   status 77 when symlinks are unsupported.  */
+
+static int
+test_mkdir (int (*func) (char const *, mode_t), bool print)
+{
+  /* Test basic error handling.  */
+  ASSERT (close (creat (BASE "file", 0600)) == 0);
+  errno = 0;
+  ASSERT (func (BASE "file", 0700) == -1);
+  ASSERT (errno == EEXIST);
+  errno = 0;
+  ASSERT (func (BASE "file/", 0700) == -1);
+  ASSERT (errno == ENOTDIR || errno == EEXIST);
+  errno = 0;
+  ASSERT (func (BASE "file/dir", 0700) == -1);
+  ASSERT (errno == ENOTDIR || errno == ENOENT);
+  ASSERT (unlink (BASE "file") == 0);
+  errno = 0;
+  ASSERT (func ("", 0700) == -1);
+  ASSERT (errno == ENOENT);
+  errno = 0;
+  ASSERT (func (BASE "dir/sub", 0700) == -1);
+  ASSERT (errno == ENOENT);
+  errno = 0;
+  ASSERT (func (BASE "dir/.", 0700) == -1);
+  ASSERT (errno == ENOENT);
+  errno = 0;
+  ASSERT (func (BASE "dir/.//", 0700) == -1);
+  ASSERT (errno == ENOENT);
+
+  /* Test trailing slash handling.  */
+  ASSERT (func (BASE "dir", 0700) == 0);
+  errno = 0;
+  ASSERT (func (BASE "dir", 0700) == -1);
+  ASSERT (errno == EEXIST);
+  ASSERT (rmdir (BASE "dir") == 0);
+  ASSERT (func (BASE "dir/", 0700) == 0);
+  errno = 0;
+  ASSERT (func (BASE "dir/", 0700) == -1);
+  ASSERT (errno == EEXIST);
+  ASSERT (rmdir (BASE "dir") == 0);
+
+  /* Test symlink behavior.  POSIX requires the creation of
+     directories through a dangling symlink with trailing slash, but
+     GNU does not yet implement that, so we support either behavior
+     for now.  */
+  if (symlink (BASE "dir", BASE "link"))
+    {
+      if (print)
+        fputs ("skipping test: symlinks not supported on this filesystem\n",
+               stderr);
+      return 77;
+    }
+  errno = 0;
+  ASSERT (func (BASE "link", 0700) == -1);
+  ASSERT (errno == EEXIST);
+  {
+    int result;
+    errno = 0;
+    result = func (BASE "link/", 0700);
+    if (!result)
+      ASSERT (rmdir (BASE "dir") == 0);
+    else
+      {
+        ASSERT (result == -1);
+        ASSERT (errno == EEXIST);
+        errno = 0;
+        ASSERT (rmdir (BASE "dir") == -1);
+        ASSERT (errno == ENOENT);
+      }
+  }
+  errno = 0;
+  ASSERT (func (BASE "link/.", 0700) == -1);
+  ASSERT (errno == ENOENT);
+  ASSERT (unlink (BASE "link") == 0);
+
+  return 0;
+}
diff --git a/tests/test-mkdirat.c b/tests/test-mkdirat.c
new file mode 100644
index 0000000..69d90b8
--- /dev/null
+++ b/tests/test-mkdirat.c
@@ -0,0 +1,91 @@
+/* Tests of mkdirat.
+   Copyright (C) 2009 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/>.  */
+
+/* Written by Eric Blake <address@hidden>, 2009.  */
+
+#include <config.h>
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define ASSERT(expr) \
+  do                                                                         \
+    {                                                                        \
+      if (!(expr))                                                           \
+        {                                                                    \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
+          fflush (stderr);                                                   \
+          abort ();                                                          \
+        }                                                                    \
+    }                                                                        \
+  while (0)
+
+#define BASE "test-mkdirat.t"
+
+#include "test-mkdir.h"
+
+static int dfd = AT_FDCWD;
+
+/* Wrapper to test mkdirat like mkdir.  */
+static int
+do_mkdir (char const *name, mode_t mode)
+{
+  return mkdirat (dfd, name, mode);
+}
+
+int
+main ()
+{
+  int result;
+
+  /* Clean up any trash from prior testsuite runs.  */
+  ASSERT (system ("rm -rf " BASE "*") == 0);
+
+  /* Test basic mkdir functionality.  */
+  result = test_mkdir (do_mkdir, false);
+  dfd = open (".", O_RDONLY);
+  ASSERT (0 <= dfd);
+  ASSERT (test_mkdir (do_mkdir, false) == result);
+
+  /* Tests specific to mkdirat.  */
+  ASSERT (mkdirat (dfd, BASE "dir1", 0700) == 0);
+  ASSERT (chdir (BASE "dir1") == 0);
+  ASSERT (close (dfd) == 0);
+  dfd = open ("..", O_RDONLY);
+  ASSERT (0 <= dfd);
+  ASSERT (mkdirat (dfd, BASE "dir2", 0700) == 0);
+  ASSERT (close (dfd) == 0);
+  errno = 0;
+  ASSERT (mkdirat (dfd, BASE "dir3", 0700) == -1);
+  ASSERT (errno == EBADF);
+  dfd = open ("/dev/null", O_RDONLY);
+  ASSERT (0 <= dfd);
+  errno = 0;
+  ASSERT (mkdirat (dfd, "dir3", 0700) == -1);
+  ASSERT (errno == ENOTDIR);
+  ASSERT (close (dfd) == 0);
+  ASSERT (chdir ("..") == 0);
+  ASSERT (rmdir (BASE "dir1") == 0);
+  ASSERT (rmdir (BASE "dir2") == 0);
+
+  return result;
+}
-- 
1.6.5.rc1


>From cb7317d75af2de40fecdd949e3fb11a85b09e10a Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 6 Oct 2009 16:08:46 -0600
Subject: [PATCH 2/3] mkdir, mkdirat: fix cygwin 1.5.x bug

mkdir("dir/./",mode) mistakenly succeeded.

* lib/mkdir.c (rpl_mkdir) [FUNC_MKDIR_DOT_BUG]: Work around bug.
* m4/mkdir-slash.m4 (gl_FUNC_MKDIR_TRAILING_SLASH): Move...
* m4/mkdir.m4 (gl_FUNC_MKDIR): ...here, and add check for cygwin
bug.
(gl_PREREQ_MKDIR): Delete unused macro.
* modules/mkdir (Files): Track file rename.
(configure.ac): Update macro name.
* modules/openat (Depends-on): Add mkdir.
* doc/posix-functions/mkdir.texi (mkdir): Document the bug.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                      |   11 ++++++++
 doc/posix-functions/mkdir.texi |    4 +++
 lib/mkdir.c                    |   18 ++++++++++++-
 m4/mkdir-slash.m4              |   48 ------------------------------------
 m4/mkdir.m4                    |   52 ++++++++++++++++++++++++++++++++++++++++
 modules/mkdir                  |    4 +-
 modules/openat                 |    1 +
 7 files changed, 86 insertions(+), 52 deletions(-)
 delete mode 100644 m4/mkdir-slash.m4
 create mode 100644 m4/mkdir.m4

diff --git a/ChangeLog b/ChangeLog
index 3ce4f94..96296ef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2009-10-07  Eric Blake  <address@hidden>

+       mkdir, mkdirat: fix cygwin 1.5.x bug
+       * lib/mkdir.c (rpl_mkdir) [FUNC_MKDIR_DOT_BUG]: Work around bug.
+       * m4/mkdir-slash.m4 (gl_FUNC_MKDIR_TRAILING_SLASH): Move...
+       * m4/mkdir.m4 (gl_FUNC_MKDIR): ...here, and add check for cygwin
+       bug.
+       (gl_PREREQ_MKDIR): Delete unused macro.
+       * modules/mkdir (Files): Track file rename.
+       (configure.ac): Update macro name.
+       * modules/openat (Depends-on): Add mkdir.
+       * doc/posix-functions/mkdir.texi (mkdir): Document the bug.
+
        mkdir, mkdirat: add tests
        * modules/mkdir-tests: New test.
        * tests/test-mkdir.h: New file.
diff --git a/doc/posix-functions/mkdir.texi b/doc/posix-functions/mkdir.texi
index 20d13bb..69b67a5 100644
--- a/doc/posix-functions/mkdir.texi
+++ b/doc/posix-functions/mkdir.texi
@@ -11,6 +11,10 @@ mkdir
 @item
 When the argument ends in a slash, the function call fails on some platforms.
 @item
+This function mistakenly succeeds on @samp{mkdir("d/./",mode)} on
+some platforms:
+Cygwin 1.5.x, mingw.
address@hidden
 On Windows platforms (excluding Cygwin), this function is called @code{_mkdir}
 and takes only one argument.  The fix (without Gnulib) is to define a macro
 like this:
diff --git a/lib/mkdir.c b/lib/mkdir.c
index 5f4d235..3d9f043 100644
--- a/lib/mkdir.c
+++ b/lib/mkdir.c
@@ -1,7 +1,7 @@
 /* On some systems, mkdir ("foo/", 0700) fails because of the trailing
    slash.  On those systems, this wrapper removes the trailing slash.

-   Copyright (C) 2001, 2003, 2006, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2003, 2006, 2008, 2009 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
@@ -21,9 +21,9 @@
 #include <config.h>

 /* Specification.  */
-#include <sys/types.h>
 #include <sys/stat.h>

+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -63,6 +63,20 @@ rpl_mkdir (char const *dir, mode_t mode maybe_unused)
     {
       tmp_dir = (char *) dir;
     }
+#if FUNC_MKDIR_DOT_BUG
+  /* Additionally, cygwin 1.5 mistakenly creates a directory "d/./".  */
+  {
+    char *last = last_component (tmp_dir);
+    if (*last == '.' && (last[1] == '\0'
+                         || (last[1] == '.' && last[2] == '\0')))
+      {
+        struct stat st;
+        if (stat (tmp_dir, &st) == 0)
+          errno = EEXIST;
+        return -1;
+      }
+  }
+#endif /* FUNC_MKDIR_DOT_BUG */

   ret_val = mkdir (tmp_dir, mode);

diff --git a/m4/mkdir-slash.m4 b/m4/mkdir-slash.m4
deleted file mode 100644
index a0bc12e..0000000
--- a/m4/mkdir-slash.m4
+++ /dev/null
@@ -1,48 +0,0 @@
-# serial 8
-
-# Copyright (C) 2001, 2003-2004, 2006, 2008-2009 Free Software Foundation, Inc.
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# On some systems, mkdir ("foo/", 0700) fails because of the trailing slash.
-# On such systems, arrange to use a wrapper function that removes any
-# trailing slashes.
-AC_DEFUN([gl_FUNC_MKDIR_TRAILING_SLASH],
-[dnl
-  AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
-  AC_CHECK_HEADERS_ONCE([unistd.h])
-  AC_CACHE_CHECK([whether mkdir fails due to a trailing slash],
-    gl_cv_func_mkdir_trailing_slash_bug,
-    [
-      # Arrange for deletion of the temporary directory this test might create.
-      ac_clean_files="$ac_clean_files confdir-slash"
-      AC_RUN_IFELSE([AC_LANG_SOURCE([[
-#       include <sys/types.h>
-#       include <sys/stat.h>
-#       include <stdlib.h>
-#       ifdef HAVE_UNISTD_H
-#         include <unistd.h>
-#       endif
-       int main ()
-       {
-         rmdir ("confdir-slash");
-         exit (mkdir ("confdir-slash/", 0700));
-       }
-       ]])],
-      [gl_cv_func_mkdir_trailing_slash_bug=no],
-      [gl_cv_func_mkdir_trailing_slash_bug=yes],
-      [gl_cv_func_mkdir_trailing_slash_bug=yes]
-      )
-    ]
-  )
-
-  if test $gl_cv_func_mkdir_trailing_slash_bug = yes; then
-    REPLACE_MKDIR=1
-    AC_LIBOBJ([mkdir])
-    gl_PREREQ_MKDIR
-  fi
-])
-
-# Prerequisites of lib/mkdir.c.
-AC_DEFUN([gl_PREREQ_MKDIR], [:])
diff --git a/m4/mkdir.m4 b/m4/mkdir.m4
new file mode 100644
index 0000000..372718a
--- /dev/null
+++ b/m4/mkdir.m4
@@ -0,0 +1,52 @@
+# serial 9
+
+# Copyright (C) 2001, 2003-2004, 2006, 2008-2009 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# On some systems, mkdir ("foo/", 0700) fails because of the trailing slash.
+# On others, mkdir ("foo/./", 0700) mistakenly succeeds.
+# On such systems, arrange to use a wrapper function.
+AC_DEFUN([gl_FUNC_MKDIR],
+[dnl
+  AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
+  AC_CHECK_HEADERS_ONCE([unistd.h])
+  AC_CACHE_CHECK([whether mkdir handles trailing slash],
+    [gl_cv_func_mkdir_trailing_slash_works],
+    [rm -rf conftest.dir
+      AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#       include <sys/types.h>
+#       include <sys/stat.h>
+]], [return mkdir ("conftest.dir/", 0700);])],
+      [gl_cv_func_mkdir_trailing_slash_works=yes],
+      [gl_cv_func_mkdir_trailing_slash_works=no],
+      [gl_cv_func_mkdir_trailing_slash_works="guessing no"])
+    rm -rf conftest.dir
+    ]
+  )
+  if test "$gl_cv_func_mkdir_trailing_slash_works" != yes; then
+    REPLACE_MKDIR=1
+    AC_LIBOBJ([mkdir])
+  fi
+
+  AC_CACHE_CHECK([whether mkdir handles trailing dot],
+    [gl_cv_func_mkdir_trailing_dot_works],
+    [rm -rf conftest.dir
+      AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#       include <sys/types.h>
+#       include <sys/stat.h>
+]], [return !mkdir ("conftest.dir/./", 0700);])],
+      [gl_cv_func_mkdir_trailing_dot_works=yes],
+      [gl_cv_func_mkdir_trailing_dot_works=no],
+      [gl_cv_func_mkdir_trailing_dot_works="guessing no"])
+    rm -rf conftest.dir
+    ]
+  )
+  if test "$gl_cv_func_mkdir_trailing_dot_works" != yes; then
+    REPLACE_MKDIR=1
+    AC_LIBOBJ([mkdir])
+    AC_DEFINE([FUNC_MKDIR_DOT_BUG], [1], [Define to 1 if mkdir mistakenly
+      creates a directory given with a trailing dot component.])
+  fi
+])
diff --git a/modules/mkdir b/modules/mkdir
index 0a80f59..59d60fb 100644
--- a/modules/mkdir
+++ b/modules/mkdir
@@ -3,7 +3,7 @@ mkdir() function: create a directory.

 Files:
 lib/mkdir.c
-m4/mkdir-slash.m4
+m4/mkdir.m4

 Depends-on:
 sys_stat
@@ -11,7 +11,7 @@ xalloc
 dirname

 configure.ac:
-gl_FUNC_MKDIR_TRAILING_SLASH
+gl_FUNC_MKDIR

 Makefile.am:

diff --git a/modules/openat b/modules/openat
index 150853f..77a7870 100644
--- a/modules/openat
+++ b/modules/openat
@@ -27,6 +27,7 @@ inline
 intprops
 lchown
 lstat
+mkdir
 open
 openat-die
 rmdir
-- 
1.6.5.rc1


>From d4b2e989099c0ab05336cfa121b2374a461d213c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 7 Oct 2009 06:56:52 -0600
Subject: [PATCH 3/3] openat: avoid using wrong fd

Detected by a Solaris failure on:
int fd = dup (0);
close (fd);
mkdirat (fd, "dir", 0700);
which created "./dir" instead of failing with EBADF.

* lib/openat.c (openat_permissive): Reject user's fd if saving the
working directory chooses same fd.
* lib/at-func.c (AT_FUNC_NAME): Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog     |    7 +++++++
 lib/at-func.c |    9 +++++++++
 lib/openat.c  |    9 +++++++++
 3 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 96296ef..d14a3c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2009-10-07  Eric Blake  <address@hidden>

+       openat: avoid using wrong fd
+       * lib/openat.c (openat_permissive): Reject user's fd if saving the
+       working directory chooses same fd.
+       * lib/at-func.c (AT_FUNC_NAME): Likewise.
+
+2009-10-07  Eric Blake  <address@hidden>
+
        mkdir, mkdirat: fix cygwin 1.5.x bug
        * lib/mkdir.c (rpl_mkdir) [FUNC_MKDIR_DOT_BUG]: Work around bug.
        * m4/mkdir-slash.m4 (gl_FUNC_MKDIR_TRAILING_SLASH): Move...
diff --git a/lib/at-func.c b/lib/at-func.c
index 75c80d3..4e5ba2f 100644
--- a/lib/at-func.c
+++ b/lib/at-func.c
@@ -89,6 +89,15 @@ AT_FUNC_NAME (int fd, char const *file 
AT_FUNC_POST_FILE_PARAM_DECLS)

   if (save_cwd (&saved_cwd) != 0)
     openat_save_fail (errno);
+  if (0 <= fd && fd == saved_cwd.desc)
+    {
+      /* If saving the working directory collides with the user's
+         requested fd, then the user's fd must have been closed to
+         begin with.  */
+      free_cwd (&saved_cwd);
+      errno = EBADF;
+      return -1;
+    }

   if (fchdir (fd) != 0)
     {
diff --git a/lib/openat.c b/lib/openat.c
index 079039f..aae7782 100644
--- a/lib/openat.c
+++ b/lib/openat.c
@@ -202,6 +202,15 @@ openat_permissive (int fd, char const *file, int flags, 
mode_t mode,
        openat_save_fail (errno);
       *cwd_errno = errno;
     }
+  if (0 <= fd && fd == saved_cwd.desc)
+    {
+      /* If saving the working directory collides with the user's
+         requested fd, then the user's fd must have been closed to
+         begin with.  */
+      free_cwd (&saved_cwd);
+      errno = EBADF;
+      return -1;
+    }

   err = fchdir (fd);
   saved_errno = errno;
-- 
1.6.5.rc1


reply via email to

[Prev in Thread] Current Thread [Next in Thread]