chown: detect Solaris and FreeBSD bug
[gnulib.git] / lib / chown.c
index b851cbc..edbccc6 100644 (file)
 /* Specification.  */
 #include <unistd.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <stdbool.h>
-#include <sys/types.h>
+#include <string.h>
 #include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
+
+#if !HAVE_CHOWN
+
+/* Simple stub that always fails with ENOSYS, for mingw.  */
+int
+chown (const char *file _UNUSED_PARAMETER_, uid_t uid _UNUSED_PARAMETER_,
+       gid_t gid _UNUSED_PARAMETER_)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_CHOWN */
 
 /* Below we refer to the system's chown().  */
-#undef chown
+# undef chown
 
 /* The results of open() in this file are not used with fchdir,
    therefore save some unnecessary work in fchdir.c.  */
-#undef open
-#undef close
+# undef open
+# undef close
 
 /* Provide a more-closely POSIX-conforming version of chown on
    systems with one or both of the following problems:
@@ -46,7 +59,6 @@
 int
 rpl_chown (const char *file, uid_t uid, gid_t gid)
 {
-#if HAVE_CHOWN
 # if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE
   if (gid == (gid_t) -1 || uid == (uid_t) -1)
     {
@@ -54,13 +66,13 @@ rpl_chown (const char *file, uid_t uid, gid_t gid)
 
       /* Stat file to get id(s) that should remain unchanged.  */
       if (stat (file, &file_stats))
-       return -1;
+        return -1;
 
       if (gid == (gid_t) -1)
-       gid = file_stats.st_gid;
+        gid = file_stats.st_gid;
 
       if (uid == (uid_t) -1)
-       uid = file_stats.st_uid;
+        uid = file_stats.st_uid;
     }
 # endif
 
@@ -74,36 +86,42 @@ rpl_chown (const char *file, uid_t uid, gid_t gid)
     int open_flags = O_NONBLOCK | O_NOCTTY;
     int fd = open (file, O_RDONLY | open_flags);
     if (0 <= fd
-       || (errno == EACCES
-           && 0 <= (fd = open (file, O_WRONLY | open_flags))))
+        || (errno == EACCES
+            && 0 <= (fd = open (file, O_WRONLY | open_flags))))
       {
-       int result = fchown (fd, uid, gid);
-       int saved_errno = errno;
-
-       /* POSIX says fchown can fail with errno == EINVAL on sockets,
-          so fall back on chown in that case.  */
-       struct stat sb;
-       bool fchown_socket_failure =
-         (result != 0 && saved_errno == EINVAL
-          && fstat (fd, &sb) == 0 && S_ISFIFO (sb.st_mode));
-
-       close (fd);
-
-       if (! fchown_socket_failure)
-         {
-           errno = saved_errno;
-           return result;
-         }
+        int result = fchown (fd, uid, gid);
+        int saved_errno = errno;
+
+        /* POSIX says fchown can fail with errno == EINVAL on sockets,
+           so fall back on chown in that case.  */
+        struct stat sb;
+        bool fchown_socket_failure =
+          (result != 0 && saved_errno == EINVAL
+           && fstat (fd, &sb) == 0 && S_ISFIFO (sb.st_mode));
+
+        close (fd);
+
+        if (! fchown_socket_failure)
+          {
+            errno = saved_errno;
+            return result;
+          }
       }
     else if (errno != EACCES)
       return -1;
   }
 # endif
 
-  return chown (file, uid, gid);
+# if CHOWN_TRAILING_SLASH_BUG
+  {
+    size_t len = strlen (file);
+    struct stat st;
+    if (len && file[len - 1] == '/' && stat (file, &st))
+      return -1;
+  }
+# endif
 
-#else /* !HAVE_CHOWN */
-  errno = ENOSYS;
-  return -1;
-#endif
+  return chown (file, uid, gid);
 }
+
+#endif /* HAVE_CHOWN */