changeset 39248:c60b370bde79

same: new function same_nameat * lib/same.c: Include fcntl.h. * lib/same.c (same_nameat): New function, generalizing same_name. (same_name): Now a thin layer around same_nameat. * m4/same.m4 (gl_SAME): Check for fpathconf, not pathconf. * modules/same (Depends-on): Depend on fstatat, openat.
author Paul Eggert <eggert@cs.ucla.edu>
date Tue, 20 Feb 2018 09:05:48 -0800
parents 2a1fb875b76e
children 5d69f74071ad
files ChangeLog lib/same.c lib/same.h m4/same.m4 modules/same
diffstat 5 files changed, 70 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Feb 18 15:41:09 2018 +0100
+++ b/ChangeLog	Tue Feb 20 09:05:48 2018 -0800
@@ -1,3 +1,12 @@
+2018-02-20  Paul Eggert  <eggert@cs.ucla.edu>
+
+	same: new function same_nameat
+	* lib/same.c: Include fcntl.h.
+	* lib/same.c (same_nameat): New function, generalizing same_name.
+	(same_name): Now a thin layer around same_nameat.
+	* m4/same.m4 (gl_SAME): Check for fpathconf, not pathconf.
+	* modules/same (Depends-on): Depend on fstatat, openat.
+
 2018-02-18  Eric Gallager  <egall@gwmail.gwu.edu>  (tiny change)
 
 	warnings: Add support for Objective C.
--- a/lib/same.c	Sun Feb 18 15:41:09 2018 +0100
+++ b/lib/same.c	Tue Feb 20 09:05:48 2018 -0800
@@ -19,6 +19,7 @@
 
 #include <config.h>
 
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -44,12 +45,31 @@
 # define MIN(a, b) ((a) < (b) ? (a) : (b))
 #endif
 
+/* Whether file name components are silently truncated (behavior that
+   POSIX stopped allowing in 2008).  This enables checks whether
+   truncated base names are the same, while checking the directories.  */
+#if !_POSIX_NO_TRUNC && HAVE_FPATHCONF && defined _PC_NAME_MAX
+# define CHECK_TRUNCATION true
+#else
+# define CHECK_TRUNCATION false
+#endif
+
 /* Return nonzero if SOURCE and DEST point to the same name in the same
    directory.  */
 
 bool
 same_name (const char *source, const char *dest)
 {
+  return same_nameat (AT_FDCWD, source, AT_FDCWD, dest);
+}
+
+/* Likewise, but interpret the file names relative to SOURCE_FD and DEST_FD,
+   in the style of openat.  */
+
+bool
+same_nameat (int source_dfd, char const *source,
+             int dest_dfd, char const *dest)
+{
   /* Compare the basenames.  */
   char const *source_basename = last_component (source);
   char const *dest_basename = last_component (dest);
@@ -61,10 +81,7 @@
   bool compare_dirs = identical_basenames;
   bool same = false;
 
-#if ! _POSIX_NO_TRUNC && HAVE_PATHCONF && defined _PC_NAME_MAX
-  /* This implementation silently truncates components of file names.  If
-     the base names might be truncated, check whether the truncated
-     base names are the same, while checking the directories.  */
+#if CHECK_TRUNCATION
   size_t slen_max = HAVE_LONG_FILE_NAMES ? 255 : _POSIX_NAME_MAX;
   size_t min_baselen = MIN (source_baselen, dest_baselen);
   if (slen_max <= min_baselen
@@ -76,46 +93,55 @@
     {
       struct stat source_dir_stats;
       struct stat dest_dir_stats;
-      char *source_dirname, *dest_dirname;
 
       /* Compare the parent directories (via the device and inode numbers).  */
-      source_dirname = dir_name (source);
-      dest_dirname = dir_name (dest);
-
-      if (stat (source_dirname, &source_dir_stats))
+      char *source_dirname = dir_name (source);
+      int flags = AT_SYMLINK_NOFOLLOW;
+      if (fstatat (source_dfd, source_dirname, &source_dir_stats, flags) != 0)
         {
           /* Shouldn't happen.  */
           error (1, errno, "%s", source_dirname);
         }
+      free (source_dirname);
 
-      if (stat (dest_dirname, &dest_dir_stats))
+      char *dest_dirname = dir_name (dest);
+      int destdir_errno = 0;
+
+#if CHECK_TRUNCATION
+      int open_flags = O_SEARCH | O_CLOEXEC | O_DIRECTORY;
+      int destdir_fd = openat (dest_dfd, dest_dirname, open_flags);
+      if (destdir_fd < 0 || fstat (destdir_fd, &dest_dir_stats) != 0)
+        destdir_errno = errno;
+      else if (SAME_INODE (source_dir_stats, dest_dir_stats))
+        {
+          same = identical_basenames;
+          if (! same)
+            {
+              errno = 0;
+              long name_max = fpathconf (destdir_fd, _PC_NAME_MAX);
+              if (name_max < 0)
+                destdir_errno = errno;
+              else
+                same = (name_max <= min_baselen
+                        && (memcmp (source_basename, dest_basename, name_max)
+                            == 0));
+            }
+        }
+      close (destdir_fd);
+      if (destdir_errno != 0)
+        {
+          /* Shouldn't happen.  */
+          error (1, destdir_errno, "%s", dest_dirname);
+        }
+#else
+      if (fstatat (dest_dfd, dest_dirname, &dest_dir_stats, flags) != 0)
         {
           /* Shouldn't happen.  */
           error (1, errno, "%s", dest_dirname);
         }
-
       same = SAME_INODE (source_dir_stats, dest_dir_stats);
-
-#if ! _POSIX_NO_TRUNC && HAVE_PATHCONF && defined _PC_NAME_MAX
-      if (same && ! identical_basenames)
-        {
-          long name_max = (errno = 0, pathconf (dest_dirname, _PC_NAME_MAX));
-          if (name_max < 0)
-            {
-              if (errno)
-                {
-                  /* Shouldn't happen.  */
-                  error (1, errno, "%s", dest_dirname);
-                }
-              same = false;
-            }
-          else
-            same = (name_max <= min_baselen
-                    && memcmp (source_basename, dest_basename, name_max) == 0);
-        }
 #endif
 
-      free (source_dirname);
       free (dest_dirname);
     }
 
--- a/lib/same.h	Sun Feb 18 15:41:09 2018 +0100
+++ b/lib/same.h	Tue Feb 20 09:05:48 2018 -0800
@@ -21,5 +21,6 @@
 # include <stdbool.h>
 
 bool same_name (const char *source, const char *dest);
+bool same_nameat (int, char const *, int, char const *);
 
 #endif /* SAME_H_ */
--- a/m4/same.m4	Sun Feb 18 15:41:09 2018 +0100
+++ b/m4/same.m4	Tue Feb 20 09:05:48 2018 -0800
@@ -1,4 +1,4 @@
-#serial 9
+#serial 10
 dnl Copyright (C) 2002-2003, 2005-2006, 2009-2018 Free Software Foundation,
 dnl Inc.
 dnl This file is free software; the Free Software Foundation
@@ -9,5 +9,5 @@
 AC_DEFUN([gl_SAME],
 [
   AC_REQUIRE([AC_SYS_LONG_FILE_NAMES])
-  AC_CHECK_FUNCS_ONCE([pathconf])
+  AC_CHECK_FUNCS_ONCE([fpathconf])
 ])
--- a/modules/same	Sun Feb 18 15:41:09 2018 +0100
+++ b/modules/same	Tue Feb 20 09:05:48 2018 -0800
@@ -10,6 +10,8 @@
 Depends-on:
 error
 dirname
+fstatat
+openat
 same-inode
 stat
 stdbool