linkat: Work around AIX 7.1 bug.
[gnulib.git] / lib / linkat.c
index 524ddde..73b1e3e 100644 (file)
@@ -1,5 +1,5 @@
 /* Create a hard link relative to open directories.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009-2010 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
@@ -262,14 +262,38 @@ linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
 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;
     }
 
+#if LINKAT_TRAILING_SLASH_BUG
+  /* Reject trailing slashes on non-directories.  */
+  {
+    size_t len1 = strlen (file1);
+    size_t len2 = strlen (file2);
+    if ((len1 && file1[len1 - 1] == '/')
+        || (len2 && file2[len2 - 1] == '/'))
+      {
+        /* Let linkat() decide whether hard-linking directories is legal.
+           If fstatat() fails, then linkat() should fail for the same reason;
+           if fstatat() succeeds, require a directory.  */
+        struct stat st;
+        if (fstatat (fd1, file1, &st, flag ? 0 : AT_SYMLINK_NOFOLLOW))
+          return -1;
+        if (!S_ISDIR (st.st_mode))
+          {
+            errno = ENOTDIR;
+            return -1;
+          }
+      }
+  }
+#endif
+
+  if (!flag)
+    return linkat (fd1, file1, fd2, file2, flag);
+
   /* Cache the information on whether the system call really works.  */
   {
     static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */