From: Paul Eggert Date: Tue, 20 Feb 2018 17:05:48 +0000 (-0800) Subject: same: new function same_nameat X-Git-Tag: v1.0~5732 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=d50980ddfe6d3b25287725048abba2c65f753686;p=gnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index 798dbb3788..3d9335f80d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2018-02-20 Paul Eggert + + 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 (tiny change) warnings: Add support for Objective C. diff --git a/lib/same.c b/lib/same.c index 39d7be5ad9..47b50be96b 100644 --- a/lib/same.c +++ b/lib/same.c @@ -19,6 +19,7 @@ #include +#include #include #include #include @@ -44,11 +45,30 @@ # 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); @@ -61,10 +81,7 @@ same_name (const char *source, const char *dest) 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 @@ same_name (const char *source, const char *dest) { 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)) - { - /* Shouldn't happen. */ - error (1, errno, "%s", dest_dirname); - } - - same = SAME_INODE (source_dir_stats, dest_dir_stats); + char *dest_dirname = dir_name (dest); + int destdir_errno = 0; -#if ! _POSIX_NO_TRUNC && HAVE_PATHCONF && defined _PC_NAME_MAX - if (same && ! identical_basenames) +#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)) { - long name_max = (errno = 0, pathconf (dest_dirname, _PC_NAME_MAX)); - if (name_max < 0) + same = identical_basenames; + if (! same) { - if (errno) - { - /* Shouldn't happen. */ - error (1, errno, "%s", dest_dirname); - } - same = false; + 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)); } - 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); #endif - free (source_dirname); free (dest_dirname); } diff --git a/lib/same.h b/lib/same.h index cf40b1c092..859c18eb08 100644 --- a/lib/same.h +++ b/lib/same.h @@ -21,5 +21,6 @@ # include bool same_name (const char *source, const char *dest); +bool same_nameat (int, char const *, int, char const *); #endif /* SAME_H_ */ diff --git a/m4/same.m4 b/m4/same.m4 index e29a7231b7..1dcc003c41 100644 --- a/m4/same.m4 +++ b/m4/same.m4 @@ -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 @@ dnl Prerequisites of lib/same.c. AC_DEFUN([gl_SAME], [ AC_REQUIRE([AC_SYS_LONG_FILE_NAMES]) - AC_CHECK_FUNCS_ONCE([pathconf]) + AC_CHECK_FUNCS_ONCE([fpathconf]) ]) diff --git a/modules/same b/modules/same index 6401dfba6d..889ccbd55b 100644 --- a/modules/same +++ b/modules/same @@ -10,6 +10,8 @@ m4/same.m4 Depends-on: error dirname +fstatat +openat same-inode stat stdbool