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.
fdopendir
fdutimensat
fileblocks
fdopendir
fdutimensat
fileblocks
modechange
obstack
openat
modechange
obstack
openat
quote
quotearg
readlinkat
quote
quotearg
readlinkat
timespec
unlinkdir
unlocked-io
timespec
unlinkdir
unlocked-io
LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
-tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
+tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
int deref_stat (bool deref, char const *name, struct stat *buf);
extern int chdir_current;
int deref_stat (bool deref, char const *name, struct stat *buf);
extern int chdir_current;
int chdir_arg (char const *dir);
void chdir_do (int dir);
int chdir_count (void);
int chdir_arg (char const *dir);
void chdir_do (int dir);
int chdir_count (void);
- 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 (atime_preserve_option == replace_atime_preserve)
{
struct timespec atime = get_stat_atime (&stat_data);
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);
atime, 0)
!= 0)
utime_error (file_name);
size_t len = strlen (current_stat_info.link_name);
char *linkbuf = alloca (len + 1);
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);
- 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 (! st.orig_file_name)
{
{
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);
if (fd < 0)
{
open_diag (p->name);
- 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;
&& open_failure_recover (dir))
continue;
return fd;
- 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
if (0 <= origfd)
{
if (fstat (parentfd, &parentstat) == 0
bool is_dir;
struct tar_stat_info const *parent = st->parent;
bool top_level = ! parent;
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))
void (*diag) (char const *) = 0;
if (interactive_option && !confirm ("add", p))
ok = dump_dir (st);
fd = st->fd;
ok = dump_dir (st);
fd = st->fd;
- parentfd = top_level ? AT_FDCWD : parent->fd;
+ parentfd = top_level ? chdir_fd : parent->fd;
if (result == 0 || implemented (errno))
return result;
}
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. */
}
/* Use fchown if possible, fchownat otherwise. */
if (result == 0 || implemented (errno))
return result;
}
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. */
}
/* Use fstat if possible, fstatat otherwise. */
{
return (0 <= fd
? fstat (fd, st)
{
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.
}
/* Set the mode for FILE_NAME to MODE.
ts[0].tv_nsec = UTIME_OMIT;
ts[1] = st->mtime;
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]);
{
if (incremental_option)
check_time (file_name, ts[0]);
struct stat st;
h->after_links = 1;
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
{
stat_error (h->file_name);
else
{
for (data = delayed_set_stat_head; data; data = data->next)
{
struct stat st;
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;
{
stat_error (data->file_name);
return;
*cursor = '\0'; /* truncate the name there */
desired_mode = MODE_RWX & ~ newdir_umask;
mode = desired_mode | (we_are_root ? 0 : MODE_WXUSR);
*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);
this. Reported by Warren Hyde
<Warren.Hyde@motorola.com> */
|| ERRNO_IS_EACCES) /* Turbo C mkdir gives a funny errno. */
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. */
continue;
/* Some other error in the mkdir. We return to the caller. */
- if (stat (file_name, &st))
+ if (fstatat (chdir_fd, file_name, &st, 0))
if (check_for_renamed_directories)
{
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;
{
stat_error (data->file_name);
skip_this_one = 1;
- if (stat (".", &st) != 0)
+ if (fstatat (chdir_fd, ".", &st, 0) != 0)
stat_diag (".");
else
root_device = st.st_dev;
stat_diag (".");
else
root_device = st.st_dev;
|| old_files_option == OVERWRITE_OLD_FILES))
{
struct stat st;
|| 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;
{
current_mode = st.st_mode;
current_mode_mask = ALL_MODE_BITS;
int openflag = (O_WRONLY | O_BINARY | O_CREAT
| (overwriting_old_files ? O_TRUNC : O_EXCL));
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;
if (typeflag == CONTTYPE)
{
static int conttype_diagnosed;
(0, 0, _("Extracting contiguous files as regular files")));
}
}
(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)
if (0 <= fd)
{
if (overwriting_old_files)
- 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))
{
{
switch (maybe_recoverable (file_name, interdir_made))
{
{
struct stat st1, st2;
int e;
{
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;
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
for (; ds; ds = ds->next)
if (ds->change_dir == chdir_current
&& ds->dev == st1.st_dev
return 0;
}
else if ((e == EEXIST && strcmp (link_name, file_name) == 0)
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;
&& st1.st_dev == st2.st_dev
&& st1.st_ino == st2.st_ino))
return 0;
|| contains_dot_dot (current_stat_info.link_name)))
return create_placeholder_file (file_name, true, &interdir_made);
|| 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:
switch (maybe_recoverable (file_name, &interdir_made))
{
case RECOVER_OK:
mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
& ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
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:
switch (maybe_recoverable (file_name, &interdir_made))
{
case RECOVER_OK:
mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
& ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
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:
switch (maybe_recoverable (file_name, &interdir_made))
{
case RECOVER_OK:
/* 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. */
/* 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. */
&& 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)
- 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)
{
;
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);
}
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
{
symlink_error (ds->target, source);
else
{
bool
rename_directory (char *src, char *dst)
{
bool
rename_directory (char *src, char *dst)
{
+ if (renameat (chdir_fd, src, chdir_fd, dst) != 0)
case ENOENT:
if (make_directories (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;
}
return true;
e = errno;
}
#include <rmt.h>
#include "common.h"
#include <quotearg.h>
#include <rmt.h>
#include "common.h"
#include <quotearg.h>
#include <xgetcwd.h>
#include <unlinkdir.h>
#include <utimens.h>
#include <xgetcwd.h>
#include <unlinkdir.h>
#include <utimens.h>
- 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,
}
/* Remove FILE_NAME, returning 1 on success. If FILE_NAME is a directory,
- 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
return 1;
/* POSIX 1003.1-2001 requires EPERM when attempting to unlink a
switch (errno)
{
case ENOTDIR:
switch (errno)
{
case ENOTDIR:
- return !try_unlink_first && unlink (file_name) == 0;
+ return !try_unlink_first && unlinkat (chdir_fd, file_name, 0) == 0;
if (this_is_the_archive && _remdev (file_name))
return true;
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;
{
if (errno == ENOENT)
return true;
if (! after_backup_name)
xalloc_die ();
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"),
{
if (verbose_option)
fprintf (stdlis, _("Renaming %s to %s\n"),
{
if (after_backup_name)
{
{
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"),
{
int e = errno;
ERROR ((0, e, _("%s: Cannot rename to %s"),
int
deref_stat (bool deref, char const *name, struct stat *buf)
{
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)
}
/* Set FD's (i.e., assuming the working directory is PARENTFD, FILE's)
/* The directory's name. */
char const *name;
/* 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. */
};
/* A vector of chdir targets. wd[0] is the initial working directory. */
/* The allocated size of the vector. */
static size_t wd_alloc;
/* 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;
+
if (! wd_count)
{
wd[wd_count].name = ".";
if (! wd_count)
{
wd[wd_count].name = ".";
+ wd[wd_count].fd = AT_FDCWD;
}
wd[wd_count].name = dir;
}
wd[wd_count].name = dir;
return wd_count++;
}
/* Index of current directory. */
int chdir_current;
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)
{
void
chdir_do (int i)
{
if (chdir_current != i)
{
- struct wd *prev = &wd[chdir_current];
struct wd *curr = &wd[i];
struct wd *curr = &wd[i];
- 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;
- 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;
}
if (S_ISDIR (st.stat.st_mode))
{
}
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
if (dir_fd < 0)
open_diag (name->name);
else
- if (rmdir (p->file_name) != 0)
+ if (unlinkat (chdir_fd, p->file_name, AT_REMOVEDIR) != 0)
- 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);
unlink_error (p->file_name);
}
dunlink_reclaim (p);
static void
append_file (char *file_name)
{
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)
struct stat stat_data;
if (handle < 0)
if (S_ISDIR (s.st_mode))
{
char *p, *dirp;
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
{
savedir_error (name->name);
else
{
+
+ 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)
}
else if (tar_timespec_cmp (get_stat_mtime (&s),
current_stat_info.mtime)
localedir = $(datadir)/locale
INCLUDES = -I$(top_srcdir)/gnu -I../gnu -I$(top_srcdir)/gnu -I$(top_srcdir)/lib
AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\"
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)