changeset 39948:c01fb99adfe2

backupfile: new dir_fd args New module opendirat with code taken from fts. Use this module to let backupfile use a directory file descriptor. * NEWS: Document the incompatible change. * lib/backup-find.c (find_backup_file_name): * lib/backup-rename.c (backup_file_rename): New arg DIR_FD. * lib/backupfile.c: Include stdint.h, for SIZE_MAX. (SIZE_MAX): Remove. Include opendirat.h rather than dirent--.h. (check_extension): New args DIR_FD and BASE_MAX. All callers changed. (numbered_backup): New args DIR_FD and PNEW_FD. All callers changed. (backupfile_internal): New arg DIR_FD. All callers changed. * lib/fts.c: Include opendirat.h. (opendirat): Move to opendirat.c. * lib/opendirat.c, lib/opendirat.h, modules/opendirat: New files. * modules/backupfile (Depends-on): Remove dirfd, opendir. Add opendirat. * modules/fts (Depends-on): Remove fdopendir, openat-safer. Add opendirat.
author Paul Eggert <eggert@cs.ucla.edu>
date Tue, 23 Oct 2018 19:10:21 -0700
parents 9912dc28baf1
children e42f23351bef
files ChangeLog NEWS lib/backup-find.c lib/backup-internal.h lib/backup-rename.c lib/backupfile.c lib/backupfile.h lib/fts.c lib/opendirat.c lib/opendirat.h modules/backupfile modules/fts modules/opendirat
diffstat 13 files changed, 190 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Oct 23 22:18:24 2018 +0200
+++ b/ChangeLog	Tue Oct 23 19:10:21 2018 -0700
@@ -1,3 +1,26 @@
+2018-10-23  Paul Eggert  <eggert@cs.ucla.edu>
+
+	backupfile: new dir_fd args
+	New module opendirat with code taken from fts.
+	Use this module to let backupfile use a directory file descriptor.
+	* NEWS: Document the incompatible change.
+	* lib/backup-find.c (find_backup_file_name):
+	* lib/backup-rename.c (backup_file_rename):
+	New arg DIR_FD.
+	* lib/backupfile.c: Include stdint.h, for SIZE_MAX.
+	(SIZE_MAX): Remove.
+	Include opendirat.h rather than dirent--.h.
+	(check_extension): New args DIR_FD and BASE_MAX.  All callers changed.
+	(numbered_backup): New args DIR_FD and PNEW_FD.  All callers changed.
+	(backupfile_internal): New arg DIR_FD.  All callers changed.
+	* lib/fts.c: Include opendirat.h.
+	(opendirat): Move to opendirat.c.
+	* lib/opendirat.c, lib/opendirat.h, modules/opendirat: New files.
+	* modules/backupfile (Depends-on): Remove dirfd, opendir.
+	Add opendirat.
+	* modules/fts (Depends-on): Remove fdopendir, openat-safer.
+	Add opendirat.
+
 2018-10-23  Bruno Haible  <bruno@clisp.org>
 
 	localename: Simplify support for per-thread locales on Solaris 11.4.
--- a/NEWS	Tue Oct 23 22:18:24 2018 +0200
+++ b/NEWS	Tue Oct 23 19:10:21 2018 -0700
@@ -44,6 +44,10 @@
 
 Date        Modules         Changes
 
+2018-10-23  backupfile      backup_file_rename and find_backup_file_name
+                            now take an additional directory file descriptor
+                            argument.  Pass AT_FDCWD to get the old behavior.
+
 2018-08-18  getpass         The include file is changed from "getpass.h" to
             getpass-gnu     <unistd.h>.
 
--- a/lib/backup-find.c	Tue Oct 23 22:18:24 2018 +0200
+++ b/lib/backup-find.c	Tue Oct 23 19:10:21 2018 -0700
@@ -24,14 +24,16 @@
 
 #include <stdlib.h>
 
-/* Return the name of a backup file for the existing file FILE,
-   allocated with malloc.  Report an error and exit if out of memory.
-   Do not call this function if backup_type == no_backups.  */
+/* Relative to DIR_FD, return the name of a backup file for the
+   existing file FILE, allocated with malloc.  Report an error and
+   exit if out of memory.  Do not call this function if
+   backup_type == no_backups.  */
 
 char *
-find_backup_file_name (char const *file, enum backup_type backup_type)
+find_backup_file_name (int dir_fd, char const *file,
+                       enum backup_type backup_type)
 {
-  char *result = backupfile_internal (file, backup_type, false);
+  char *result = backupfile_internal (dir_fd, file, backup_type, false);
   if (!result)
     xalloc_die ();
   return result;
--- a/lib/backup-internal.h	Tue Oct 23 22:18:24 2018 +0200
+++ b/lib/backup-internal.h	Tue Oct 23 19:10:21 2018 -0700
@@ -1,3 +1,3 @@
 #include "backupfile.h"
 #include <stdbool.h>
-extern char *backupfile_internal (char const *, enum backup_type, bool);
+extern char *backupfile_internal (int, char const *, enum backup_type, bool);
--- a/lib/backup-rename.c	Tue Oct 23 22:18:24 2018 +0200
+++ b/lib/backup-rename.c	Tue Oct 23 19:10:21 2018 -0700
@@ -19,13 +19,13 @@
 
 #include "backup-internal.h"
 
-/* Rename the existing file FILE to a backup name, allocated with
-   malloc, and return the backup name.  On failure return a null
-   pointer, setting errno.  Do not call this function if backup_type
-   == no_backups.  */
+/* Relative to DIR_FD, rename the existing file FILE to a backup name,
+   allocated with malloc, and return the backup name.  On failure
+   return a null pointer, setting errno.  Do not call this function if
+   backup_type == no_backups.  */
 
 char *
-backup_file_rename (char const *file, enum backup_type backup_type)
+backup_file_rename (int dir_fd, char const *file, enum backup_type backup_type)
 {
-  return backupfile_internal (file, backup_type, true);
+  return backupfile_internal (dir_fd, file, backup_type, true);
 }
--- a/lib/backupfile.c	Tue Oct 23 22:18:24 2018 +0200
+++ b/lib/backupfile.c	Tue Oct 23 19:10:21 2018 -0700
@@ -29,6 +29,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -36,7 +37,7 @@
 
 #include <unistd.h>
 
-#include "dirent--.h"
+#include "opendirat.h"
 #ifndef _D_EXACT_NAMLEN
 # define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
 #endif
@@ -48,9 +49,6 @@
 #ifndef _POSIX_NAME_MAX
 # define _POSIX_NAME_MAX 14
 #endif
-#ifndef SIZE_MAX
-# define SIZE_MAX ((size_t) -1)
-#endif
 
 #if defined _XOPEN_NAME_MAX
 # define NAME_MAX_MINIMUM _XOPEN_NAME_MAX
@@ -92,10 +90,15 @@
 /* If FILE (which was of length FILELEN before an extension was
    appended to it) is too long, replace the extension with the single
    char E.  If the result is still too long, remove the char just
-   before E.  */
+   before E.
+
+   If DIR_FD is nonnegative, it is a file descriptor for FILE's parent.
+   *NAME_MAX is either 0, or the cached result of a previous call for
+   FILE's parent's _PC_NAME_MAX.  */
 
 static void
-check_extension (char *file, size_t filelen, char e)
+check_extension (char *file, size_t filelen, char e,
+                 int dir_fd, size_t *base_max)
 {
   char *base = last_component (file);
   size_t baselen = base_len (base);
@@ -104,22 +107,34 @@
   if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen)
     {
       /* The new base name is long enough to require a pathconf check.  */
-      long name_max;
+      if (*base_max == 0)
+        {
+          long name_max;
+          if (dir_fd < 0)
+            {
+              /* Temporarily modify the buffer into its parent
+                 directory name, invoke pathconf on the directory, and
+                 then restore the buffer.  */
+              char tmp[sizeof "."];
+              memcpy (tmp, base, sizeof ".");
+              strcpy (base, ".");
+              errno = 0;
+              name_max = pathconf (file, _PC_NAME_MAX);
+              name_max -= !errno;
+              memcpy (base, tmp, sizeof ".");
+            }
+          else
+            {
+              errno = 0;
+              name_max = fpathconf (dir_fd, _PC_NAME_MAX);
+              name_max -= !errno;
+            }
 
-      /* Temporarily modify the buffer into its parent directory name,
-         invoke pathconf on the directory, and then restore the buffer.  */
-      char tmp[sizeof "."];
-      memcpy (tmp, base, sizeof ".");
-      strcpy (base, ".");
-      errno = 0;
-      name_max = pathconf (file, _PC_NAME_MAX);
-      if (0 <= name_max || errno == 0)
-        {
-          long size = baselen_max = name_max;
-          if (name_max != size)
-            baselen_max = SIZE_MAX;
+          *base_max = (0 <= name_max && name_max <= SIZE_MAX ? name_max
+                       : name_max < -1 ? NAME_MAX_MINIMUM : SIZE_MAX);
         }
-      memcpy (base, tmp, sizeof ".");
+
+      baselen_max = *base_max;
     }
 
   if (HAVE_DOS_FILE_NAMES && baselen_max <= 12)
@@ -167,8 +182,9 @@
     BACKUP_NOMEM
   };
 
-/* *BUFFER contains a file name.  Store into *BUFFER the next backup
-   name for the named file, with a version number greater than all the
+/* Relative to DIR_FD, *BUFFER contains a file name.
+   Store into *BUFFER the next backup name for the named file,
+   with a version number greater than all the
    existing numbered backups.  Reallocate *BUFFER as necessary; its
    initial allocated size is BUFFER_SIZE, which must be at least 4
    bytes longer than the file name to make room for the initially
@@ -180,11 +196,11 @@
 
    *DIRPP is the destination directory.  If *DIRPP is null, open the
    destination directory and store the resulting stream into *DIRPP
-   without closing the stream.  */
+   and its file descriptor into *PNEW_FD without closing the stream.  */
 
 static enum numbered_backup_result
-numbered_backup (char **buffer, size_t buffer_size, size_t filelen,
-                 ptrdiff_t base_offset, DIR **dirpp)
+numbered_backup (int dir_fd, char **buffer, size_t buffer_size, size_t filelen,
+                 ptrdiff_t base_offset, DIR **dirpp, int *pnew_fd)
 {
   enum numbered_backup_result result = BACKUP_IS_NEW;
   DIR *dirp = *dirpp;
@@ -203,7 +219,7 @@
       char tmp[sizeof "."];
       memcpy (tmp, base, sizeof ".");
       strcpy (base, ".");
-      dirp = opendir (buf);
+      dirp = opendirat (dir_fd, buf, 0, pnew_fd);
       if (!dirp && errno == ENOMEM)
         result = BACKUP_NOMEM;
       memcpy (base, tmp, sizeof ".");
@@ -283,13 +299,15 @@
   return result;
 }
 
-/* Return the name of the new backup file for the existing file FILE,
-   allocated with malloc.  If RENAME, also rename FILE to the new name.
+/* Relative to DIR_FD, return the name of the new backup file for the
+   existing file FILE, allocated with malloc.
+   If RENAME, also rename FILE to the new name.
    On failure, return NULL and set errno.
    Do not call this function if backup_type == no_backups.  */
 
 char *
-backupfile_internal (char const *file, enum backup_type backup_type, bool rename)
+backupfile_internal (int dir_fd, char const *file,
+                     enum backup_type backup_type, bool rename)
 {
   ptrdiff_t base_offset = last_component (file) - file;
   size_t filelen = base_offset + strlen (file + base_offset);
@@ -311,6 +329,8 @@
     return s;
 
   DIR *dirp = NULL;
+  int sdir = -1;
+  size_t base_max = 0;
   while (true)
     {
       memcpy (s, file, filelen + 1);
@@ -318,7 +338,8 @@
       if (backup_type == simple_backups)
         memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size);
       else
-        switch (numbered_backup (&s, ssize, filelen, base_offset, &dirp))
+        switch (numbered_backup (dir_fd, &s, ssize, filelen, base_offset,
+                                 &dirp, &sdir))
           {
           case BACKUP_IS_SAME_LENGTH:
             break;
@@ -330,11 +351,11 @@
                 memcpy (s + filelen, simple_backup_suffix,
                         simple_backup_suffix_size);
               }
-            check_extension (s, filelen, '~');
+            check_extension (s, filelen, '~', sdir, &base_max);
             break;
 
           case BACKUP_IS_LONGER:
-            check_extension (s, filelen, '~');
+            check_extension (s, filelen, '~', sdir, &base_max);
             break;
 
           case BACKUP_NOMEM:
@@ -346,7 +367,6 @@
       if (! rename)
         break;
 
-      int sdir = dirp ? dirfd (dirp) : -1;
       if (sdir < 0)
         {
           sdir = AT_FDCWD;
--- a/lib/backupfile.h	Tue Oct 23 22:18:24 2018 +0200
+++ b/lib/backupfile.h	Tue Oct 23 19:10:21 2018 -0700
@@ -17,11 +17,13 @@
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 #ifndef BACKUPFILE_H_
-# define BACKUPFILE_H_
+#define BACKUPFILE_H_
+
+#include <fcntl.h>
 
-# ifdef __cplusplus
+#ifdef __cplusplus
 extern "C" {
-# endif
+#endif
 
 
 /* When to make backup files. */
@@ -41,21 +43,20 @@
   numbered_backups
 };
 
-# define VALID_BACKUP_TYPE(Type)        \
+#define VALID_BACKUP_TYPE(Type) \
   ((unsigned int) (Type) <= numbered_backups)
 
 extern char const *simple_backup_suffix;
 
 void set_simple_backup_suffix (char const *);
-char *backup_file_rename (char const *, enum backup_type);
-char *find_backup_file_name (char const *, enum backup_type);
+char *backup_file_rename (int, char const *, enum backup_type);
+char *find_backup_file_name (int, char const *, enum backup_type);
 enum backup_type get_version (char const *context, char const *arg);
 enum backup_type xget_version (char const *context, char const *arg);
-void addext (char *, char const *, int);
 
 
-# ifdef __cplusplus
+#ifdef __cplusplus
 }
-# endif
+#endif
 
 #endif /* ! BACKUPFILE_H_ */
--- a/lib/fts.c	Tue Oct 23 22:18:24 2018 +0200
+++ b/lib/fts.c	Tue Oct 23 19:10:21 2018 -0700
@@ -73,6 +73,7 @@
 # include "fcntl--.h"
 # include "flexmember.h"
 # include "openat.h"
+# include "opendirat.h"
 # include "same-inode.h"
 #endif
 
@@ -294,31 +295,6 @@
                            : FTS_NO_STAT_REQUIRED);
 }
 
-/* file-descriptor-relative opendir.  */
-/* FIXME: if others need this function, move it into lib/openat.c */
-static DIR *
-internal_function
-opendirat (int fd, char const *dir, int extra_flags, int *pdir_fd)
-{
-  int open_flags = (O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY
-                    | O_NONBLOCK | extra_flags);
-  int new_fd = openat (fd, dir, open_flags);
-  DIR *dirp;
-
-  if (new_fd < 0)
-    return NULL;
-  dirp = fdopendir (new_fd);
-  if (dirp)
-    *pdir_fd = new_fd;
-  else
-    {
-      int saved_errno = errno;
-      close (new_fd);
-      errno = saved_errno;
-    }
-  return dirp;
-}
-
 /* Virtual fchdir.  Advance SP's working directory file descriptor,
    SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.
    CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/opendirat.c	Tue Oct 23 19:10:21 2018 -0700
@@ -0,0 +1,54 @@
+/* Open a directory relative to another directory.
+
+   Copyright 2006-2018 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
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+   Written by Jim Meyering and Paul Eggert.  */
+
+#include <config.h>
+
+#include <opendirat.h>
+
+#include <errno.h>
+#include <fcntl--.h>
+#include <unistd.h>
+
+/* Relative to DIR_FD, open the directory DIR, passing EXTRA_FLAGS to
+   the underlying openat call.  On success, store into *PNEW_FD the
+   underlying file descriptor of the newly opened directory and return
+   the directory stream.  On failure, return NULL and set errno.
+
+   On success, *PNEW_FD is at least 3, so this is a "safer" function.  */
+
+DIR *
+opendirat (int dir_fd, char const *dir, int extra_flags, int *pnew_fd)
+{
+  int open_flags = (O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY
+                    | O_NONBLOCK | extra_flags);
+  int new_fd = openat (dir_fd, dir, open_flags);
+
+  if (new_fd < 0)
+    return NULL;
+  DIR *dirp = fdopendir (new_fd);
+  if (dirp)
+    *pnew_fd = new_fd;
+  else
+    {
+      int fdopendir_errno = errno;
+      close (new_fd);
+      errno = fdopendir_errno;
+    }
+  return dirp;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/opendirat.h	Tue Oct 23 19:10:21 2018 -0700
@@ -0,0 +1,2 @@
+#include <dirent.h>
+DIR *opendirat (int, char const *, int, int *);
--- a/modules/backupfile	Tue Oct 23 22:18:24 2018 +0200
+++ b/modules/backupfile	Tue Oct 23 19:10:21 2018 -0700
@@ -13,14 +13,14 @@
 closedir
 d-ino
 dirent-safer
-dirfd
 dirname-lgpl
 fcntl
 memcmp
-opendir
+opendirat
 renameatu
 readdir
 stdbool
+stdint
 
 configure.ac:
 gl_BACKUPFILE
--- a/modules/fts	Tue Oct 23 22:18:24 2018 +0200
+++ b/modules/fts	Tue Oct 23 19:10:21 2018 -0700
@@ -15,7 +15,6 @@
 fchdir
 fcntl
 fcntl-h
-fdopendir
 flexmember
 fstat
 hash
@@ -23,8 +22,8 @@
 lstat
 memmove
 openat-h
-openat-safer
 opendir
+opendirat
 readdir
 stdalign
 stdbool
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/opendirat	Tue Oct 23 19:10:21 2018 -0700
@@ -0,0 +1,26 @@
+Description:
+Open a directory relative to another directory.
+
+Files:
+lib/opendirat.c
+lib/opendirat.h
+
+Depends-on:
+dirent
+fcntl-h
+fdopendir
+openat-safer
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += opendirat.c
+
+Include:
+"opendirat.h"
+
+License:
+GPL
+
+Maintainer:
+all