summaryrefslogtreecommitdiff
path: root/src/incremen.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2010-09-06 13:38:44 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2010-09-06 13:39:21 -0700
commitde328a580ab6f5ff4a3237ce21f1ef0b7dd12984 (patch)
tree367c41040b9998ae40aeb740b9a12dd8a8d96b63 /src/incremen.c
parentbad4b0006c872e09129cc1128ab24157af38b0cd (diff)
downloadtar-de328a580ab6f5ff4a3237ce21f1ef0b7dd12984.tar.gz
tar: more reliable directory traversal when creating archives
* NEWS: Document this. * gnulib.modules: Add openat, readlinkat. * src/common.h (open_read_flags, fstatat_flags): New global variables. (cachedir_file_p, dump_file, check_exclusion_tags, scan_directory): Adjust to new signatures, described below. (name_fill_directory): Remove. * src/compare.c (diff_file, diff_multivol): Use open_read_flags. * src/create.c (struct exclusion_tag): Exclusion predicates now take a file descriptor, not a file name. (add_exclusion_tag): Likewise. All uses changed. (cachedir_file_p): Likewise. (check_exclusion_tags): The directory is now a file descriptor, not a file name. All uses changed. Use openat for better traversal. (file_dumpable_p): Arg is now a struct stat, not a struct tar_stat_info. All uses changed. Check the arg's file types too. (dump_dir0, dump_dir, dump_file0, dump_file): Omit top_level and parent_device args, since st->parent tells us that now. All uses changed. (dump_dir): Likewise. Also, omit fd arg for similar reasons. Apply fdsavedir to a dup of the file descriptor, since we need a file descriptor for openat etc. as well, and fdsavedir (perhaps unwisely) consumes its file descriptor when successful. Do not consume st->fd when successful; this simplifies the caller. (create_archive): Allocate a file descriptor when retraversing a directory, during incremental dumps. (dump_file0): Use fstatat, openat, and readlinkat for better traversal. When opening a file, use the result of fstat on the file descriptor rather than the fstatat on the directory entry, to avoid some race conditions. No need to reopen the directory since we now no longer close it. Change "did we open the file?" test from 0 <= fd to 0 < fd since fd == 0 now represents uninitialized. (dump_file): Now accepts struct tar_stat_info describing parent, not parent_device. Also, accept basename and fullname of entry. All uses changed. * src/incremen.c (update_parent_directory): Accept struct tar_stat_info for parent, not name. All callers changed. Use fstatat for safer directory traversal. (procdir): Accept struct tar_stat_info, not struct stat and dev_t, for info about directory. All callers changed. (scan_directory): Accept struct tar_stat_info, not name, device, and cmdline, for info about directory. All callers changed. Do not consume the file descriptor, since caller might need it. Use fstatat and openat for safer directory traversal; also, use fstat after opening to double-check. (name_fill_directory): Remove. * src/names.c (add_hierarchy_to_namelist): Accept struct tar_stat_info instead of device and cmdline. All callers changed. When descending into a subdirectory, use openat and fstat for safer directory traversal. (collect_and_sort_names): Use open and fstat for safer directory traversal. Set up struct tar_stat_info for callee's new API. * src/tar.c (decode_options): Initialize open_read_flags and fstatat_flags. (tar_stat_destroy): Close st->fd if it is positive (not zero!). * src/tar.h (struct tar_stat_info): New members parent, fd. * src/update.c (update_archive): Adjust to dump_file's API change. * tests/filerem02.at: Ignore stderr since its contents now depend on the file system implementation.
Diffstat (limited to 'src/incremen.c')
-rw-r--r--src/incremen.c106
1 files changed, 53 insertions, 53 deletions
diff --git a/src/incremen.c b/src/incremen.c
index 96d0e40a..afd19af6 100644
--- a/src/incremen.c
+++ b/src/incremen.c
@@ -402,26 +402,17 @@ find_directory_meta (dev_t dev, ino_t ino)
}
void
-update_parent_directory (const char *name)
+update_parent_directory (struct tar_stat_info *parent)
{
- struct directory *directory;
- char *p;
-
- p = dir_name (name);
- directory = find_directory (p);
+ struct directory *directory = find_directory (parent->orig_file_name);
if (directory)
{
struct stat st;
- if (deref_stat (dereference_option, p, &st) != 0)
- {
- if (errno != ENOENT)
- stat_diag (directory->name);
- /* else: should have been already reported */
- }
+ if (fstatat (parent->fd, ".", &st, fstatat_flags) != 0)
+ stat_diag (directory->name);
else
directory->mtime = get_stat_mtime (&st);
}
- free (p);
}
#define PD_FORCE_CHILDREN 0x10
@@ -429,12 +420,14 @@ update_parent_directory (const char *name)
#define PD_CHILDREN(f) ((f) & 3)
static struct directory *
-procdir (const char *name_buffer, struct stat *stat_data,
- dev_t device,
+procdir (const char *name_buffer, struct tar_stat_info *st,
int flag,
char *entry)
{
struct directory *directory;
+ struct stat *stat_data = &st->stat;
+ int fd = st->fd;
+ dev_t device = st->parent ? st->parent->stat.st_dev : 0;
bool nfs = NFS_FILE_STAT (*stat_data);
if ((directory = find_directory (name_buffer)) != NULL)
@@ -573,7 +566,7 @@ procdir (const char *name_buffer, struct stat *stat_data,
{
const char *tag_file_name;
- switch (check_exclusion_tags (name_buffer, &tag_file_name))
+ switch (check_exclusion_tags (fd, &tag_file_name))
{
case exclusion_tag_all:
/* This warning can be duplicated by code in dump_file0, but only
@@ -682,37 +675,39 @@ makedumpdir (struct directory *directory, const char *dir)
free (array);
}
-/* Recursively scan the given directory DIR.
- DEVICE is the device number where DIR resides (for --one-file-system).
- If CMDLINE is true, the directory name was explicitly listed in the
- command line.
- Unless *PDIR is NULL, store there a pointer to the struct directory
- describing DIR. */
+/* Recursively scan the directory identified by ST. */
struct directory *
-scan_directory (char *dir, dev_t device, bool cmdline)
+scan_directory (struct tar_stat_info *st)
{
- char *dirp = savedir (dir); /* for scanning directory */
+ char const *dir = st->orig_file_name;
+ int fd = st->fd;
+ char *dirp = 0;
+ dev_t device = st->stat.st_dev;
+ bool cmdline = ! st->parent;
namebuf_t nbuf;
char *tmp;
- struct stat stat_data;
struct directory *directory;
char ch;
+ int dupfd = dup (fd);
+ if (0 <= dupfd)
+ {
+ dirp = fdsavedir (dupfd);
+ if (! dirp)
+ {
+ int e = errno;
+ close (dupfd);
+ errno = e;
+ }
+ }
+
if (! dirp)
savedir_error (dir);
tmp = xstrdup (dir);
zap_slashes (tmp);
- if (deref_stat (dereference_option, tmp, &stat_data))
- {
- dir_removed_diag (tmp, cmdline, stat_diag);
- free (tmp);
- free (dirp);
- return NULL;
- }
-
- directory = procdir (tmp, &stat_data, device,
+ directory = procdir (tmp, st,
(cmdline ? PD_FORCE_INIT : 0),
&ch);
@@ -739,14 +734,27 @@ scan_directory (char *dir, dev_t device, bool cmdline)
*entry = 'N';
else
{
- if (deref_stat (dereference_option, full_name, &stat_data))
+ void (*diag) (char const *) = 0;
+ struct tar_stat_info stsub;
+ tar_stat_init (&stsub);
+
+ if (fstatat (fd, entry + 1, &stsub.stat, fstatat_flags) != 0)
+ diag = stat_diag;
+ else if (S_ISDIR (stsub.stat.st_mode))
{
- file_removed_diag (full_name, false, stat_diag);
- *entry = 'N';
- continue;
+ stsub.fd = openat (fd, entry + 1, open_read_flags);
+ if (stsub.fd < 0)
+ diag = open_diag;
+ else if (fstat (stsub.fd, &stsub.stat) != 0)
+ diag = stat_diag;
}
- if (S_ISDIR (stat_data.st_mode))
+ if (diag)
+ {
+ file_removed_diag (full_name, false, diag);
+ *entry = 'N';
+ }
+ else if (S_ISDIR (stsub.stat.st_mode))
{
int pd_flag = 0;
if (!recursion_option)
@@ -754,23 +762,21 @@ scan_directory (char *dir, dev_t device, bool cmdline)
else if (directory->children == ALL_CHILDREN)
pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN;
*entry = 'D';
- procdir (full_name, &stat_data, device, pd_flag, entry);
+ procdir (full_name, &stsub, pd_flag, entry);
}
-
- else if (one_file_system_option && device != stat_data.st_dev)
+ else if (one_file_system_option && device != stsub.stat.st_dev)
*entry = 'N';
-
else if (*entry == 'Y')
/* New entry, skip further checks */;
-
/* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
-
- else if (OLDER_STAT_TIME (stat_data, m)
+ else if (OLDER_STAT_TIME (stsub.stat, m)
&& (!after_date_option
- || OLDER_STAT_TIME (stat_data, c)))
+ || OLDER_STAT_TIME (stsub.stat, c)))
*entry = 'N';
else
*entry = 'Y';
+
+ tar_stat_destroy (&stsub);
}
}
free (itr);
@@ -801,12 +807,6 @@ safe_directory_contents (struct directory *dir)
return ret ? ret : "\0\0\0\0";
}
-void
-name_fill_directory (struct name *name, dev_t device, bool cmdline)
-{
- name->directory = scan_directory (name->name, device, cmdline);
-}
-
static void
obstack_code_rename (struct obstack *stk, char const *from, char const *to)