bug-coreutils
[Top][All Lists]
Advanced

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

Re: new snapshot available: coreutils-7.6.63-addb6


From: Eric Blake
Subject: Re: new snapshot available: coreutils-7.6.63-addb6
Date: Mon, 05 Oct 2009 21:53:17 -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

According to Pádraig Brady on 10/5/2009 3:53 PM:
>>>> This is a new test, but FC5 is soooo old,
>>>> that I'm not sure it's worth worrying about.
>>> March 2006?
>> The failure is probably a function of the kernel.
>> Which is it?
> 
> In summary this is what fails:
> 
> $ touch a
> $ ln -s a symlink
> $ ln -L symlink hardlink
> ln: creating hard link `hardlink' => `symlink': Invalid argument
> 
> `man linkat` says that AT_SYMLINK_FOLLOW is only supported since 2.6.18
> and my FC5 system is 2.6.17

This should fix it.  I don't have access to FC5, but I tested the new code
path by priming the cache (gl_cv_func_linkat_follow=runtime ./configure)
along with a temporary setting of have_follow_really=-1 in linkat.c.  I
also verified that the replacement is not picked up on cygwin 1.7, where
AT_SYMLINK_FOLLOW was implemented at the same time as linkat.

The patch copies from areadlink.c, as well as link_follow earlier in
linkat.c, to create two new fd-relative helpers.  For now, I didn't see
any reason to expose them, but areadlinkat may someday be worth making
into a full-blown module.

- --
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/

iEYEARECAAYFAkrKvywACgkQ84KuGfSFAYDpiACgkOS074Zvsnwu1tOc8z9YWClD
uM0AoLh4EpxWw8Y7FMYWNgi713r3hCMO
=A+pL
-----END PGP SIGNATURE-----
From cdba659e0746e0d7c6ecbcabfbb25132f5418a38 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Mon, 5 Oct 2009 21:30:33 -0600
Subject: [PATCH] linkat: support Linux 2.6.17
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* m4/linkat.m4 (gl_FUNC_LINKAT): Default to always replacing
linkat on Linux, but allow cache variable override.
* lib/linkat.c (rpl_linkat): Define override.
* modules/linkat (Depends-on): Add symlinkat.
* m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Add new default.
* modules/unistd (Makefile.am): Substitute it.
* lib/unistd.in.h (linkat): Declare replacement.
Reported by Pádraig Brady.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog       |   10 +++
 lib/linkat.c    |  201 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 lib/unistd.in.h |    6 ++-
 m4/linkat.m4    |   24 ++++++-
 m4/unistd_h.m4  |    3 +-
 modules/linkat  |    1 +
 modules/unistd  |    1 +
 7 files changed, 235 insertions(+), 11 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0782170..0362497 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2009-10-05  Eric Blake  <address@hidden>

+       linkat: support Linux 2.6.17
+       * m4/linkat.m4 (gl_FUNC_LINKAT): Default to always replacing
+       linkat on Linux, but allow cache variable override.
+       * lib/linkat.c (rpl_linkat): Define override.
+       * modules/linkat (Depends-on): Add symlinkat.
+       * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Add new default.
+       * modules/unistd (Makefile.am): Substitute it.
+       * lib/unistd.in.h (linkat): Declare replacement.
+       Reported by Pádraig Brady.
+
        quotearg: port test to systems with C.UTF-8 locale
        * tests/test-quotearg.c (struct result_strings): Add another
        member, differentiating between C.ASCII and C.UTF-8 handling.
diff --git a/lib/linkat.c b/lib/linkat.c
index bda0627..f785d09 100644
--- a/lib/linkat.c
+++ b/lib/linkat.c
@@ -23,6 +23,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>

 #include "areadlink.h"
@@ -41,11 +43,13 @@
 # endif
 #endif

+#if !HAVE_LINKAT
+
 /* Create a link.  If FILE1 is a symlink, either create a hardlink to
    that symlink, or fake it by creating an identical symlink.  */
-#if LINK_FOLLOWS_SYMLINKS == 0
-# define link_immediate link
-#else
+# if LINK_FOLLOWS_SYMLINKS == 0
+#  define link_immediate link
+# else
 static int
 link_immediate (char const *file1, char const *file2)
 {
@@ -88,13 +92,13 @@ link_immediate (char const *file1, char const *file2)
     return -1;
   return link (file1, file2);
 }
-#endif
+# endif /* LINK_FOLLOWS_SYMLINKS == 0 */

 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
    canonicalized file.  */
-#if 0 < LINK_FOLLOWS_SYMLINKS
-# define link_follow link
-#else
+# if 0 < LINK_FOLLOWS_SYMLINKS
+#  define link_follow link
+# else
 static int
 link_follow (char const *file1, char const *file2)
 {
@@ -159,7 +163,7 @@ link_follow (char const *file1, char const *file2)
     }
   return result;
 }
-#endif
+# endif /* 0 < LINK_FOLLOWS_SYMLINKS */

 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
    in the directory open on descriptor FD2.  If FILE1 is a symlink, FLAG
@@ -179,3 +183,184 @@ linkat (int fd1, char const *file1, int fd2, char const 
*file2, int flag)
   return at_func2 (fd1, file1, fd2, file2,
                    flag ? link_follow : link_immediate);
 }
+
+#else /* HAVE_LINKAT */
+
+# undef linkat
+
+/* Read a symlink, like areadlink, but relative to FD.  */
+
+static char *
+areadlinkat (int fd, char const *filename)
+{
+  /* The initial buffer size for the link value.  A power of 2
+     detects arithmetic overflow earlier, but is not required.  */
+# define INITIAL_BUF_SIZE 1024
+
+  /* Allocate the initial buffer on the stack.  This way, in the common
+     case of a symlink of small size, we get away with a single small malloc()
+     instead of a big malloc() followed by a shrinking realloc().  */
+  char initial_buf[INITIAL_BUF_SIZE];
+
+  char *buffer = initial_buf;
+  size_t buf_size = sizeof (initial_buf);
+
+  while (1)
+    {
+      /* Attempt to read the link into the current buffer.  */
+      ssize_t link_length = readlinkat (fd, filename, buffer, buf_size);
+
+      /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
+         with errno == ERANGE if the buffer is too small.  */
+      if (link_length < 0 && errno != ERANGE)
+        {
+          if (buffer != initial_buf)
+            {
+              int saved_errno = errno;
+              free (buffer);
+              errno = saved_errno;
+            }
+          return NULL;
+        }
+
+      if ((size_t) link_length < buf_size)
+        {
+          buffer[link_length++] = '\0';
+
+          /* Return it in a chunk of memory as small as possible.  */
+          if (buffer == initial_buf)
+            {
+              buffer = (char *) malloc (link_length);
+              if (buffer == NULL)
+                /* errno is ENOMEM.  */
+                return NULL;
+              memcpy (buffer, initial_buf, link_length);
+            }
+          else
+            {
+              /* Shrink buffer before returning it.  */
+              if ((size_t) link_length < buf_size)
+                {
+                  char *smaller_buffer = (char *) realloc (buffer, 
link_length);
+
+                  if (smaller_buffer != NULL)
+                    buffer = smaller_buffer;
+                }
+            }
+          return buffer;
+        }
+
+      if (buffer != initial_buf)
+        free (buffer);
+      buf_size *= 2;
+      if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))
+        {
+          errno = ENOMEM;
+          return NULL;
+        }
+      buffer = (char *) malloc (buf_size);
+      if (buffer == NULL)
+        /* errno is ENOMEM.  */
+        return NULL;
+    }
+}
+
+/* Create a link.  If FILE1 is a symlink, create a hardlink to the
+   canonicalized file.  */
+
+static int
+linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
+{
+  char *name = (char *) file1;
+  char *target;
+  int result;
+  int i = MAXSYMLINKS;
+
+  /* There is no realpathat.  */
+  while (i-- && (target = areadlinkat (fd1, name)))
+    {
+      if (IS_ABSOLUTE_FILE_NAME (target))
+        {
+          if (name != file1)
+            free (name);
+          name = target;
+        }
+      else
+        {
+          char *dir = mdir_name (name);
+          if (name != file1)
+            free (name);
+          if (!dir)
+            {
+              free (target);
+              errno = ENOMEM;
+              return -1;
+            }
+          name = mfile_name_concat (dir, target, NULL);
+          free (dir);
+          free (target);
+          if (!name)
+            {
+              errno = ENOMEM;
+              return -1;
+            }
+        }
+    }
+  if (i < 0)
+    {
+      target = NULL;
+      errno = ELOOP;
+    }
+  if (!target && errno != EINVAL)
+    {
+      if (name != file1)
+        {
+          int saved_errno = errno;
+          free (name);
+          errno = saved_errno;
+        }
+      return -1;
+    }
+  result = linkat (fd1, name, fd2, file2, 0);
+  if (name != file1)
+    {
+      int saved_errno = errno;
+      free (name);
+      errno = saved_errno;
+    }
+  return result;
+}
+
+
+/* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on
+   older Linux kernels.  */
+
+int
+rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
+{
+  if (!flag)
+    return linkat (fd1, file1, fd2, file2, flag);
+  if (flag & ~AT_SYMLINK_FOLLOW)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Cache the information on whether the system call really works.  */
+  {
+    static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
+    if (0 <= have_follow_really)
+    {
+      int result = linkat (fd1, file1, fd2, file2, flag);
+      if (!(result == -1 && errno == EINVAL))
+        {
+          have_follow_really = 1;
+          return result;
+        }
+      have_follow_really = -1;
+    }
+  }
+  return linkat_follow (fd1, file1, fd2, file2);
+}
+
+#endif /* HAVE_LINKAT */
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index 8a96e79..38e2e13 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -582,10 +582,14 @@ extern int link (const char *path1, const char *path2);
 #endif

 #if @GNULIB_LINKAT@
+# if @REPLACE_LINKAT@
+#  undef linkat
+#  define linkat rpl_linkat
+# endif
 /* Create a new hard link for an existing file, relative to two
    directories.  FLAG controls whether symlinks are followed.
    Return 0 if successful, otherwise -1 and errno set.  */
-# if address@hidden@
+# if address@hidden@ || @REPLACE_LINKAT@
 extern int linkat (int fd1, const char *path1, int fd2, const char *path2,
                   int flag);
 # endif
diff --git a/m4/linkat.m4 b/m4/linkat.m4
index be68c5f..d6a1671 100644
--- a/m4/linkat.m4
+++ b/m4/linkat.m4
@@ -1,4 +1,4 @@
-# serial 1
+# serial 2
 # See if we need to provide linkat replacement.

 dnl Copyright (C) 2009 Free Software Foundation, Inc.
@@ -21,5 +21,27 @@ AC_DEFUN([gl_FUNC_LINKAT],
     HAVE_LINKAT=0
     AC_LIBOBJ([linkat])
     AC_LIBOBJ([at-func2])
+  else
+    AC_CACHE_CHECK([whether linkat(,AT_SYMLINK_FOLLOW) works],
+      [gl_cv_func_linkat_follow],
+      [rm -rf conftest.f1 conftest.f2
+       touch conftest.f1
+       AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef __linux__
+/* Linux added linkat in 2.6.16, but did not add AT_SYMLINK_FOLLOW
+   until 2.6.18.  Always replace linkat to support older kernels.  */
+choke me
+#endif
+]], [return linkat (AT_FDCWD, "conftest.f1", AT_FDCWD, "conftest.f2",
+                    AT_SYMLINK_FOLLOW);])],
+         [gl_cv_func_linkat_follow=yes],
+         [gl_cv_func_linkat_follow="need runtime check"])
+       rm -rf conftest.f1 conftest.f2])
+    if test "$gl_cv_func_linkat_follow" != yes; then
+      REPLACE_LINKAT=1
+      AC_LIBOBJ([linkat])
+    fi
   fi
 ])
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index 16daed8..5aa39ae 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -1,4 +1,4 @@
-# unistd_h.m4 serial 30
+# unistd_h.m4 serial 31
 dnl Copyright (C) 2006-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -102,6 +102,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
   REPLACE_GETPAGESIZE=0;  AC_SUBST([REPLACE_GETPAGESIZE])
   REPLACE_LCHOWN=0;       AC_SUBST([REPLACE_LCHOWN])
   REPLACE_LINK=0;         AC_SUBST([REPLACE_LINK])
+  REPLACE_LINKAT=0;       AC_SUBST([REPLACE_LINKAT])
   REPLACE_LSEEK=0;        AC_SUBST([REPLACE_LSEEK])
   REPLACE_READLINK=0;     AC_SUBST([REPLACE_READLINK])
   REPLACE_RMDIR=0;        AC_SUBST([REPLACE_RMDIR])
diff --git a/modules/linkat b/modules/linkat
index 8d9dec3..6b56144 100644
--- a/modules/linkat
+++ b/modules/linkat
@@ -21,6 +21,7 @@ readlink
 same-inode
 stpcpy
 symlink
+symlinkat
 unistd

 configure.ac:
diff --git a/modules/unistd b/modules/unistd
index d21a204..d299e4a 100644
--- a/modules/unistd
+++ b/modules/unistd
@@ -94,6 +94,7 @@ unistd.h: unistd.in.h
              -e 's|@''REPLACE_GETPAGESIZE''@|$(REPLACE_GETPAGESIZE)|g' \
              -e 's|@''REPLACE_LCHOWN''@|$(REPLACE_LCHOWN)|g' \
              -e 's|@''REPLACE_LINK''@|$(REPLACE_LINK)|g' \
+             -e 's|@''REPLACE_LINKAT''@|$(REPLACE_LINKAT)|g' \
              -e 's|@''REPLACE_LSEEK''@|$(REPLACE_LSEEK)|g' \
              -e 's|@''REPLACE_READLINK''@|$(REPLACE_READLINK)|g' \
              -e 's|@''REPLACE_RMDIR''@|$(REPLACE_RMDIR)|g' \
-- 
1.6.5.rc1


reply via email to

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