]> Dogcows Code - chaz/tar/commitdiff
tar: prefer openat-style functions
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 19 Sep 2010 06:37:45 +0000 (23:37 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 19 Sep 2010 06:42:54 +0000 (23:42 -0700)
This change replaces traditional functions like 'open' with the
POSIX.1-2008 functions like 'openat'.  Mostly this is an internal
refactoring change, in preparation for further changes to close
some races.
* gnulib.modules: Add faccessat, linkat, mkfifoat, renameat, symlinkat.
Remove save-cwd.
* src/Makefile.am (tar_LDADD): Add $(LIB_EACCESS).
* tests/Makefile.am (LDADD): Likewise.
* src/common.h (chdir_fd): New extern var.
* src/compare.c (diff_file, diff_multivol): Use openat instead of open.
* src/create.c (create_archive, restore_parent_fd): Likewise.
* src/extract.c (create_placeholder_file): Likewise.
* src/names.c (collect_and_sort_names): Likewise.
* src/update.c (append_file): Likewise.
* src/compare.c (diff_symlink): Use readlinkat instead of readlink.
* src/compare.c (diff_file): Use chdir_fd instead of AT_FDCWD.
* src/create.c (subfile_open, dump_file0): Likewise.
* src/extract.c (fd_chmod, fd_chown, fd_stat, set_stat):
(repair_delayed_set_stat, apply_nonancestor_delayed_set_stat):
Likewise.
* src/extract.c (mark_after_links, file_newer_p, extract_dir):
(extract_link, apply_delayed_links):
Use fstatat rather than stat or lstat.
* src/misc.c (maybe_backup_file, deref_stat): Likewise.
* src/extract.c (make_directories): Use mkdirat rather than mkdir.
Use faccessat rather than access.  This fixes a minor permissions
bug when tar is running setuid (who would want to do that?!).
(open_output_file): Use openat rather than open.
In the process, this removes support for Masscomp's O_CTG files,
which aren't compatible with openat's signature.  Masscomp!  Wow!
That's a blast from the past.  As far as I know, that operating
system hasn't been supported for more than 20 years.
(extract_link, apply_delayed_links):
Use linkat rather than link.
(extract_symlink, apply_delayed_links):
Use symlinkat rather than symlink.
(extract_node): Use mknodat rather than mknod.
(extract_fifo): Use mkfifoat rather than mkfifo.
(apply_delayed_links): Use unlinkat rather than unlink or rmdir.
* src/misc.c (safer_rmdir, remove_any_file): Likewise.
* src/unlink.c (flush_deferred_unlinks): Likewise.
* src/extract.c (rename_directory): Use renameat rather than rename.
* src/misc.c (maybe_backup_file, undo_last_backup): Likewise.
* src/misc.c: Don't include <save-cwd.h>; no longer needed now
that we're using openat etc.
(struct wd): Add member fd.  Remove members err and fd.  All uses
changed.
(CHDIR_CACHE_SIZE): New constant.
(wdcache, wdcache_count, chdir_fd): New vars.
(chdir_do): Use openat rather than save_cwd.  Keep the cache up
to date.  This code won't scale well, but is good enough for now.
* src/update.c (update_archive): Use openat + fdopendir +
streamsavedir rather than savedir.

This file is a placeholder. It will be replaced with the actual ChangeLog
by make dist.  Run make ChangeLog if you wish to create it earlier.

gnulib.modules
src/Makefile.am
src/common.h
src/compare.c
src/create.c
src/extract.c
src/misc.c
src/names.c
src/unlink.c
src/update.c
tests/Makefile.am

index 957d06589ee32d18cd0f3baea04baa3fcf9cee66..d33ee448a45f31b2d44873128253031b0b9e2304 100644 (file)
@@ -12,6 +12,7 @@ dirname
 error
 exclude
 exitfail
+faccessat
 fdopendir
 fdutimensat
 fileblocks
@@ -31,8 +32,10 @@ hash
 human
 inttypes
 lchown
+linkat
 localcharset
 mkdtemp
+mkfifoat
 modechange
 obstack
 openat
@@ -41,9 +44,9 @@ progname
 quote
 quotearg
 readlinkat
+renameat
 rpmatch
 safe-read
-save-cwd
 savedir
 setenv
 snprintf
@@ -55,6 +58,7 @@ strdup-posix
 strerror
 strtol
 strtoul
+symlinkat
 timespec
 unlinkdir
 unlocked-io
index fcbac33f08d8e74757195fb79daf3ceb916b0e3f..de310f43512a8dba61bd852aa2eb965a4dc49b91 100644 (file)
@@ -48,4 +48,4 @@ INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
 
 LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
 
-tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
+tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
index 2bc1d712ba22e6875e9d886b59982e9c31dec874..12a488919d754a1945a9c4693e096105abe57901 100644 (file)
@@ -614,6 +614,7 @@ void undo_last_backup (void);
 int deref_stat (bool deref, char const *name, struct stat *buf);
 
 extern int chdir_current;
+extern int chdir_fd;
 int chdir_arg (char const *dir);
 void chdir_do (int dir);
 int chdir_count (void);
index 204c5dc16dd38f204bea980e84bc4045e78387c5..1ee9bcb19d9f7e3d33ccc783f5b64cfba0cd6955 100644 (file)
@@ -222,9 +222,9 @@ diff_file (void)
             ? O_NOATIME
             : 0);
 
-         diff_handle = open (file_name,
-                             (O_RDONLY | O_BINARY | O_CLOEXEC | O_NOCTTY
-                              | O_NONBLOCK | atime_flag));
+         diff_handle = openat (chdir_fd, file_name,
+                               (O_RDONLY | O_BINARY | O_CLOEXEC | O_NOCTTY
+                                | O_NONBLOCK | atime_flag));
 
          if (diff_handle < 0)
            {
@@ -244,7 +244,7 @@ diff_file (void)
              if (atime_preserve_option == replace_atime_preserve)
                {
                  struct timespec atime = get_stat_atime (&stat_data);
-                 if (set_file_atime (diff_handle, AT_FDCWD, file_name,
+                 if (set_file_atime (diff_handle, chdir_fd, file_name,
                                      atime, 0)
                      != 0)
                    utime_error (file_name);
@@ -279,7 +279,8 @@ diff_symlink (void)
   size_t len = strlen (current_stat_info.link_name);
   char *linkbuf = alloca (len + 1);
 
-  int status = readlink (current_stat_info.file_name, linkbuf, len + 1);
+  int status = readlinkat (chdir_fd, current_stat_info.file_name,
+                          linkbuf, len + 1);
 
   if (status < 0)
     {
@@ -428,9 +429,9 @@ diff_multivol (void)
     }
 
 
-  fd = open (current_stat_info.file_name,
-            (O_RDONLY | O_BINARY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK
-             | atime_flag));
+  fd = openat (chdir_fd, current_stat_info.file_name,
+              (O_RDONLY | O_BINARY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK
+               | atime_flag));
 
   if (fd < 0)
     {
index 0d22e9601d07164ebbef67923a6090b738d5ffe6..26993c21e5792be036be572e930a4887556b9b2b 100644 (file)
@@ -1345,7 +1345,8 @@ create_archive (void)
                    {
                      if (! st.orig_file_name)
                        {
-                         int fd = open (p->name, open_searchdir_flags);
+                         int fd = openat (chdir_fd, p->name,
+                                          open_searchdir_flags);
                          if (fd < 0)
                            {
                              open_diag (p->name);
@@ -1549,7 +1550,7 @@ subfile_open (struct tar_stat_info const *dir, char const *file, int flags)
       gettext ("");
     }
 
-  while ((fd = openat (dir ? dir->fd : AT_FDCWD, file, flags)) < 0
+  while ((fd = openat (dir ? dir->fd : chdir_fd, file, flags)) < 0
         && open_failure_recover (dir))
     continue;
   return fd;
@@ -1580,7 +1581,8 @@ restore_parent_fd (struct tar_stat_info const *st)
 
       if (parentfd < 0)
        {
-         int origfd = open (parent->orig_file_name, open_searchdir_flags);
+         int origfd = openat (chdir_fd, parent->orig_file_name,
+                              open_searchdir_flags);
          if (0 <= origfd)
            {
              if (fstat (parentfd, &parentstat) == 0
@@ -1615,7 +1617,7 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
   bool is_dir;
   struct tar_stat_info const *parent = st->parent;
   bool top_level = ! parent;
-  int parentfd = top_level ? AT_FDCWD : parent->fd;
+  int parentfd = top_level ? chdir_fd : parent->fd;
   void (*diag) (char const *) = 0;
 
   if (interactive_option && !confirm ("add", p))
@@ -1723,7 +1725,7 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
          ok = dump_dir (st);
 
          fd = st->fd;
-         parentfd = top_level ? AT_FDCWD : parent->fd;
+         parentfd = top_level ? chdir_fd : parent->fd;
        }
       else
        {
index 750ca268e9b8944ec5e730dcb5466068b3eebea9..2730a03ab7129c824e42e316a66652ce6affb056 100644 (file)
@@ -178,7 +178,7 @@ fd_chmod (int fd, char const *file, mode_t mode, int atflag)
       if (result == 0 || implemented (errno))
        return result;
     }
-  return fchmodat (AT_FDCWD, file, mode, atflag);
+  return fchmodat (chdir_fd, file, mode, atflag);
 }
 
 /* Use fchown if possible, fchownat otherwise.  */
@@ -191,7 +191,7 @@ fd_chown (int fd, char const *file, uid_t uid, gid_t gid, int atflag)
       if (result == 0 || implemented (errno))
        return result;
     }
-  return fchownat (AT_FDCWD, file, uid, gid, atflag);
+  return fchownat (chdir_fd, file, uid, gid, atflag);
 }
 
 /* Use fstat if possible, fstatat otherwise.  */
@@ -200,7 +200,7 @@ fd_stat (int fd, char const *file, struct stat *st, int atflag)
 {
   return (0 <= fd
          ? fstat (fd, st)
-         : fstatat (AT_FDCWD, file, st, atflag));
+         : fstatat (chdir_fd, file, st, atflag));
 }
 
 /* Set the mode for FILE_NAME to MODE.
@@ -325,7 +325,7 @@ set_stat (char const *file_name,
        ts[0].tv_nsec = UTIME_OMIT;
       ts[1] = st->mtime;
 
-      if (fdutimensat (fd, AT_FDCWD, file_name, ts, atflag) == 0)
+      if (fdutimensat (fd, chdir_fd, file_name, ts, atflag) == 0)
        {
          if (incremental_option)
            check_time (file_name, ts[0]);
@@ -375,7 +375,7 @@ mark_after_links (struct delayed_set_stat *head)
       struct stat st;
       h->after_links = 1;
 
-      if (stat (h->file_name, &st) != 0)
+      if (fstatat (chdir_fd, h->file_name, &st, 0) != 0)
        stat_error (h->file_name);
       else
        {
@@ -449,7 +449,7 @@ repair_delayed_set_stat (char const *dir,
   for (data = delayed_set_stat_head;  data;  data = data->next)
     {
       struct stat st;
-      if (fstatat (AT_FDCWD, data->file_name, &st, data->atflag) != 0)
+      if (fstatat (chdir_fd, data->file_name, &st, data->atflag) != 0)
        {
          stat_error (data->file_name);
          return;
@@ -512,7 +512,7 @@ make_directories (char *file_name)
       *cursor = '\0';          /* truncate the name there */
       desired_mode = MODE_RWX & ~ newdir_umask;
       mode = desired_mode | (we_are_root ? 0 : MODE_WXUSR);
-      status = mkdir (file_name, mode);
+      status = mkdirat (chdir_fd, file_name, mode);
 
       if (status == 0)
        {
@@ -538,7 +538,7 @@ make_directories (char *file_name)
                                   this. Reported by Warren Hyde
                                   <Warren.Hyde@motorola.com> */
               || ERRNO_IS_EACCES)  /* Turbo C mkdir gives a funny errno.  */
-              && access (file_name, W_OK) == 0)
+              && faccessat (chdir_fd, file_name, W_OK, AT_EACCESS) == 0)
        continue;
 
       /* Some other error in the mkdir.  We return to the caller.  */
@@ -553,7 +553,7 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat)
 {
   struct stat st;
 
-  if (stat (file_name, &st))
+  if (fstatat (chdir_fd, file_name, &st, 0))
     {
       if (errno != ENOENT)
        {
@@ -671,7 +671,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
 
       if (check_for_renamed_directories)
        {
-         if (fstatat (AT_FDCWD, data->file_name, &st, data->atflag) != 0)
+         if (fstatat (chdir_fd, data->file_name, &st, data->atflag) != 0)
            {
              stat_error (data->file_name);
              skip_this_one = 1;
@@ -727,7 +727,7 @@ extract_dir (char *file_name, int typeflag)
     {
       struct stat st;
 
-      if (stat (".", &st) != 0)
+      if (fstatat (chdir_fd, ".", &st, 0) != 0)
        stat_diag (".");
       else
        root_device = st.st_dev;
@@ -769,7 +769,7 @@ extract_dir (char *file_name, int typeflag)
              || old_files_option == OVERWRITE_OLD_FILES))
        {
          struct stat st;
-         if (stat (file_name, &st) == 0)
+         if (fstatat (chdir_fd, file_name, &st, 0) == 0)
            {
              current_mode = st.st_mode;
              current_mode_mask = ALL_MODE_BITS;
@@ -826,16 +826,6 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
   int openflag = (O_WRONLY | O_BINARY | O_CREAT
                  | (overwriting_old_files ? O_TRUNC : O_EXCL));
 
-#if O_CTG
-  /* Contiguous files (on the Masscomp) have to specify the size in
-     the open call that creates them.  */
-
-  if (typeflag == CONTTYPE)
-    fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size);
-  else
-    fd = open (file_name, openflag, mode);
-
-#else /* not O_CTG */
   if (typeflag == CONTTYPE)
     {
       static int conttype_diagnosed;
@@ -847,10 +837,8 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
                   (0, 0, _("Extracting contiguous files as regular files")));
        }
     }
-  fd = open (file_name, openflag, mode);
-
-#endif /* not O_CTG */
 
+  fd = openat (chdir_fd, file_name, openflag, mode);
   if (0 <= fd)
     {
       if (overwriting_old_files)
@@ -1004,7 +992,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
   int fd;
   struct stat st;
 
-  while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
+  while ((fd = openat (chdir_fd, file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
     {
       switch (maybe_recoverable (file_name, interdir_made))
        {
@@ -1084,13 +1072,14 @@ extract_link (char *file_name, int typeflag)
     {
       struct stat st1, st2;
       int e;
-      int status = link (link_name, file_name);
+      int status = linkat (chdir_fd, link_name, chdir_fd, file_name, 0);
       e = errno;
 
       if (status == 0)
        {
          struct delayed_link *ds = delayed_link_head;
-         if (ds && lstat (link_name, &st1) == 0)
+         if (ds
+             && fstatat (chdir_fd, link_name, &st1, AT_SYMLINK_NOFOLLOW) == 0)
            for (; ds; ds = ds->next)
              if (ds->change_dir == chdir_current
                  && ds->dev == st1.st_dev
@@ -1107,8 +1096,10 @@ extract_link (char *file_name, int typeflag)
          return 0;
        }
       else if ((e == EEXIST && strcmp (link_name, file_name) == 0)
-              || (lstat (link_name, &st1) == 0
-                  && lstat (file_name, &st2) == 0
+              || ((fstatat (chdir_fd, link_name, &st1, AT_SYMLINK_NOFOLLOW)
+                   == 0)
+                  && (fstatat (chdir_fd, file_name, &st2, AT_SYMLINK_NOFOLLOW)
+                      == 0)
                   && st1.st_dev == st2.st_dev
                   && st1.st_ino == st2.st_ino))
        return 0;
@@ -1138,7 +1129,7 @@ extract_symlink (char *file_name, int typeflag)
          || contains_dot_dot (current_stat_info.link_name)))
     return create_placeholder_file (file_name, true, &interdir_made);
 
-  while (symlink (current_stat_info.link_name, file_name))
+  while (symlinkat (current_stat_info.link_name, chdir_fd, file_name) != 0)
     switch (maybe_recoverable (file_name, &interdir_made))
       {
       case RECOVER_OK:
@@ -1178,7 +1169,8 @@ extract_node (char *file_name, int typeflag)
   mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
                 & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
 
-  while (mknod (file_name, mode, current_stat_info.stat.st_rdev) != 0)
+  while (mknodat (chdir_fd, file_name, mode, current_stat_info.stat.st_rdev)
+        != 0)
     switch (maybe_recoverable (file_name, &interdir_made))
       {
       case RECOVER_OK:
@@ -1207,7 +1199,7 @@ extract_fifo (char *file_name, int typeflag)
   mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
                 & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
 
-  while (mkfifo (file_name, mode) != 0)
+  while (mkfifoat (chdir_fd, file_name, mode) != 0)
     switch (maybe_recoverable (file_name, &interdir_made))
       {
       case RECOVER_OK:
@@ -1454,23 +1446,25 @@ apply_delayed_links (void)
          /* Make sure the placeholder file is still there.  If not,
             don't create a link, as the placeholder was probably
             removed by a later extraction.  */
-         if (lstat (source, &st) == 0
+         if (fstatat (chdir_fd, source, &st, AT_SYMLINK_NOFOLLOW) == 0
              && st.st_dev == ds->dev
              && st.st_ino == ds->ino
              && timespec_cmp (get_stat_ctime (&st), ds->ctime) == 0)
            {
              /* Unlink the placeholder, then create a hard link if possible,
                 a symbolic link otherwise.  */
-             if (unlink (source) != 0)
+             if (unlinkat (chdir_fd, source, 0) != 0)
                unlink_error (source);
-             else if (valid_source && link (valid_source, source) == 0)
+             else if (valid_source
+                      && (linkat (chdir_fd, valid_source, chdir_fd, source, 0)
+                          == 0))
                ;
              else if (!ds->is_symlink)
                {
-                 if (link (ds->target, source) != 0)
+                 if (linkat (chdir_fd, ds->target, chdir_fd, source, 0) != 0)
                    link_error (ds->target, source);
                }
-             else if (symlink (ds->target, source) != 0)
+             else if (symlinkat (ds->target, chdir_fd, source) != 0)
                symlink_error (ds->target, source);
              else
                {
@@ -1523,7 +1517,7 @@ extract_finish (void)
 bool
 rename_directory (char *src, char *dst)
 {
-  if (rename (src, dst))
+  if (renameat (chdir_fd, src, chdir_fd, dst) != 0)
     {
       int e = errno;
 
@@ -1532,7 +1526,7 @@ rename_directory (char *src, char *dst)
        case ENOENT:
          if (make_directories (dst))
            {
-             if (rename (src, dst) == 0)
+             if (renameat (chdir_fd, src, chdir_fd, dst) == 0)
                return true;
              e = errno;
            }
index 15161ae97e2beffcc6d2fa092491430a9b335583..d94791f66a4f357d0fe542cc6dcd15b5bd197de2 100644 (file)
@@ -21,7 +21,6 @@
 #include <rmt.h>
 #include "common.h"
 #include <quotearg.h>
-#include <save-cwd.h>
 #include <xgetcwd.h>
 #include <unlinkdir.h>
 #include <utimens.h>
@@ -432,7 +431,7 @@ safer_rmdir (const char *file_name)
       return -1;
     }
 
-  return rmdir (file_name);
+  return unlinkat (chdir_fd, file_name, AT_REMOVEDIR);
 }
 
 /* Remove FILE_NAME, returning 1 on success.  If FILE_NAME is a directory,
@@ -452,7 +451,7 @@ remove_any_file (const char *file_name, enum remove_option option)
 
   if (try_unlink_first)
     {
-      if (unlink (file_name) == 0)
+      if (unlinkat (chdir_fd, file_name, 0) == 0)
        return 1;
 
       /* POSIX 1003.1-2001 requires EPERM when attempting to unlink a
@@ -468,7 +467,7 @@ remove_any_file (const char *file_name, enum remove_option option)
   switch (errno)
     {
     case ENOTDIR:
-      return !try_unlink_first && unlink (file_name) == 0;
+      return !try_unlink_first && unlinkat (chdir_fd, file_name, 0) == 0;
 
     case 0:
     case EEXIST:
@@ -545,7 +544,7 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive)
   if (this_is_the_archive && _remdev (file_name))
     return true;
 
-  if (stat (file_name, &file_stat))
+  if (fstatat (chdir_fd, file_name, &file_stat, 0))
     {
       if (errno == ENOENT)
        return true;
@@ -565,7 +564,8 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive)
   if (! after_backup_name)
     xalloc_die ();
 
-  if (rename (before_backup_name, after_backup_name) == 0)
+  if (renameat (chdir_fd, before_backup_name, chdir_fd, after_backup_name)
+      == 0)
     {
       if (verbose_option)
        fprintf (stdlis, _("Renaming %s to %s\n"),
@@ -592,7 +592,8 @@ undo_last_backup (void)
 {
   if (after_backup_name)
     {
-      if (rename (after_backup_name, before_backup_name) != 0)
+      if (renameat (chdir_fd, after_backup_name, chdir_fd, before_backup_name)
+         != 0)
        {
          int e = errno;
          ERROR ((0, e, _("%s: Cannot rename to %s"),
@@ -611,7 +612,7 @@ undo_last_backup (void)
 int
 deref_stat (bool deref, char const *name, struct stat *buf)
 {
-  return deref ? stat (name, buf) : lstat (name, buf);
+  return fstatat (chdir_fd, name, buf, deref ? 0 : AT_SYMLINK_NOFOLLOW);
 }
 
 /* Set FD's (i.e., assuming the working directory is PARENTFD, FILE's)
@@ -633,13 +634,10 @@ struct wd
   /* The directory's name.  */
   char const *name;
 
-  /* A negative value if no attempt has been made to save the
-     directory, 0 if it was saved successfully, and a positive errno
-     value if it was not saved successfully.  */
-  int err;
-
-  /* The saved version of the directory, if ERR == 0.  */
-  struct saved_cwd saved_cwd;
+  /* If nonzero, the file descriptor of the directory, or AT_FDCWD if
+     the working directory.  If zero, the directory needs to be opened
+     to be used.  */
+  int fd;
 };
 
 /* A vector of chdir targets.  wd[0] is the initial working directory.  */
@@ -651,6 +649,19 @@ static size_t wd_count;
 /* The allocated size of the vector.  */
 static size_t wd_alloc;
 
+/* The maximum number of chdir targets with open directories.
+   Don't make it too large, as many operating systems have a small
+   limit on the number of open file descriptors.  Also, the current
+   implementation does not scale well.  */
+enum { CHDIR_CACHE_SIZE = 16 };
+
+/* Indexes into WD of chdir targets with open file descriptors, sorted
+   most-recently used first.  Zero indexes are unused.  */
+static int wdcache[CHDIR_CACHE_SIZE];
+
+/* Number of nonzero entries in WDCACHE.  */
+static size_t wdcache_count;
+
 int
 chdir_count ()
 {
@@ -677,7 +688,7 @@ chdir_arg (char const *dir)
       if (! wd_count)
        {
          wd[wd_count].name = ".";
-         wd[wd_count].err = -1;
+         wd[wd_count].fd = AT_FDCWD;
          wd_count++;
        }
     }
@@ -694,66 +705,76 @@ chdir_arg (char const *dir)
     }
 
   wd[wd_count].name = dir;
-  wd[wd_count].err = -1;
+  wd[wd_count].fd = 0;
   return wd_count++;
 }
 
 /* Index of current directory.  */
 int chdir_current;
 
-/* Change to directory I.  If I is 0, change to the initial working
-   directory; otherwise, I must be a value returned by chdir_arg.  */
+/* Value suitable for use as the first argument to openat, and in
+   similar locations for fstatat, etc.  This is an open file
+   descriptor, or AT_FDCWD if the working directory is current.  It is
+   valid until the next invocation of chdir_do.  */
+int chdir_fd = AT_FDCWD;
+
+/* Change to directory I, in a virtual way.  This does not actually
+   invoke chdir; it merely sets chdir_fd to an int suitable as the
+   first argument for openat, etc.  If I is 0, change to the initial
+   working directory; otherwise, I must be a value returned by
+   chdir_arg.  */
 void
 chdir_do (int i)
 {
   if (chdir_current != i)
     {
-      struct wd *prev = &wd[chdir_current];
+      static size_t counter;
       struct wd *curr = &wd[i];
+      int fd = curr->fd;
 
-      if (prev->err < 0)
+      if (! fd)
        {
-         prev->err = 0;
-         if (save_cwd (&prev->saved_cwd) != 0)
-           prev->err = errno;
-         else if (0 <= prev->saved_cwd.desc)
+         if (! IS_ABSOLUTE_FILE_NAME (curr->name))
+           chdir_do (i - 1);
+         fd = openat (chdir_fd, curr->name, open_searchdir_flags);
+         if (fd < 0)
+           open_fatal (curr->name);
+
+         curr->fd = fd;
+
+         /* Add I to the cache, tossing out the lowest-ranking entry if the
+            cache is full.  */
+         if (wdcache_count < CHDIR_CACHE_SIZE)
+           wdcache[wdcache_count++] = i;
+         else
            {
-             /* Make sure we still have at least one descriptor available.  */
-             int fd1 = prev->saved_cwd.desc;
-             int fd2 = dup (fd1);
-             if (0 <= fd2)
-               close (fd2);
-             else if (errno == EMFILE)
-               {
-                 /* Force restore_cwd to use chdir_long.  */
-                 close (fd1);
-                 prev->saved_cwd.desc = -1;
-                 prev->saved_cwd.name = xgetcwd ();
-                 if (! prev->saved_cwd.name)
-                   prev->err = errno;
-               }
-             else
-               prev->err = errno;
+             struct wd *stale = &wd[wdcache[CHDIR_CACHE_SIZE - 1]];
+             if (close (stale->fd) != 0)
+               close_diag (stale->name);
+             stale->fd = 0;
+             wdcache[CHDIR_CACHE_SIZE - 1] = i;
            }
        }
 
-      if (0 <= curr->err)
+      if (0 < fd)
        {
-         int err = curr->err;
-         if (err == 0 && restore_cwd (&curr->saved_cwd) != 0)
-           err = errno;
-         if (err)
-           FATAL_ERROR ((0, err, _("Cannot restore working directory")));
-       }
-      else
-       {
-         if (i && ! ISSLASH (curr->name[0]))
-           chdir_do (i - 1);
-         if (chdir (curr->name) != 0)
-           chdir_fatal (curr->name);
+         /* Move the i value to the front of the cache.  This is
+            O(CHDIR_CACHE_SIZE), but the cache is small.  */
+         size_t ci;
+         int prev = wdcache[0];
+         for (ci = 1; prev != i; ci++)
+           {
+             int curr = wdcache[ci];
+             wdcache[ci] = prev;
+             if (curr == i)
+               break;
+             prev = curr;
+           }
+         wdcache[0] = i;
        }
 
       chdir_current = i;
+      chdir_fd = fd;
     }
 }
 \f
index c38ccb6ca2a25f18d65d219deaadcb194da3b178..88d8be2cfeccac8e3d9ade871733a8a82b412e8b 100644 (file)
@@ -988,7 +988,8 @@ collect_and_sort_names (void)
        }
       if (S_ISDIR (st.stat.st_mode))
        {
-         int dir_fd = open (name->name, open_read_flags | O_DIRECTORY);
+         int dir_fd = openat (chdir_fd, name->name,
+                              open_read_flags | O_DIRECTORY);
          if (dir_fd < 0)
            open_diag (name->name);
          else
index 817ab4a5ff300c8fe6668d8e61b5413c2df1afbc..b2816368569c85140f972fdc45bcb5664021c5d4 100644 (file)
@@ -77,7 +77,7 @@ flush_deferred_unlinks (bool force)
        {
          if (p->is_dir)
            {
-             if (rmdir (p->file_name) != 0)
+             if (unlinkat (chdir_fd, p->file_name, AT_REMOVEDIR) != 0)
                {
                  switch (errno)
                    {
@@ -101,7 +101,7 @@ flush_deferred_unlinks (bool force)
            }
          else
            {
-             if (unlink (p->file_name) != 0 && errno != ENOENT)
+             if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
                unlink_error (p->file_name);
            }
          dunlink_reclaim (p);
index 53ce553b0ca680944ae0a33b6d08126369c1752e..de2786df3e86efc748692f0f5a6d585f220fd674 100644 (file)
@@ -47,7 +47,7 @@ char *output_start;
 static void
 append_file (char *file_name)
 {
-  int handle = open (file_name, O_RDONLY | O_BINARY);
+  int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
   struct stat stat_data;
 
   if (handle < 0)
@@ -144,8 +144,13 @@ update_archive (void)
                    if (S_ISDIR (s.st_mode))
                      {
                        char *p, *dirp;
-                       dirp = savedir (name->name);
-                       if (!dirp)
+                       DIR *stream;
+                       int fd = openat (chdir_fd, name->name,
+                                        open_read_flags | O_DIRECTORY);
+                       if (fd < 0)
+                         open_error (name->name);
+                       else if (! ((stream = fdopendir (fd))
+                                   && (dirp = streamsavedir (stream))))
                          savedir_error (name->name);
                        else
                          {
@@ -160,6 +165,11 @@ update_archive (void)
 
                            remname (name);
                          }
+
+                       if (stream
+                           ? closedir (stream) != 0
+                           : 0 <= fd && close (fd) != 0)
+                         savedir_error (name->name);
                      }
                    else if (tar_timespec_cmp (get_stat_mtime (&s),
                                               current_stat_info.mtime)
index 5cfd847ef28c228367a13317925e0751c75cbbe7..d378a376e9894d93e19bb6856b6c75386d4cdc3d 100644 (file)
@@ -198,4 +198,4 @@ genfile_SOURCES = genfile.c argcv.c argcv.h
 localedir = $(datadir)/locale
 INCLUDES = -I$(top_srcdir)/gnu -I../gnu -I$(top_srcdir)/gnu -I$(top_srcdir)/lib
 AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\"
-LDADD = ../gnu/libgnu.a $(LIBINTL) $(LIB_CLOCK_GETTIME)
+LDADD = ../gnu/libgnu.a $(LIBINTL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
This page took 0.057649 seconds and 4 git commands to generate.