* NEWS: New option --atime-preserve=system, which uses O_NOATIME.

* configure.ac: Prefer AC_CHECK_HEADERS_ONCE to AC_CHECK_HEADERS.
Check for stropts.h and sys/filio.h too, for _FIOSATIME.
* doc/tar.texi: Change "modification time" to "data modification
time", "change time" to "status change time", and "filesystem" to
"file system", so that we use terminology consistent with POSIX.
Use American spacing rather than French for sentence ends.
"non-dependable" -> "undependable".
(Option Summary, Attributes): Explain better the pitfalls of the
--atime-preserve option, and suggest read-only mounts,loopback
mounts, and noatime mounts for older systems.
* doc/value.texi (op-atime-preserve-system): Renamed from
op-atime-preserver-system to fix a misspelling.
* src/common.h (enum atime_preserve): Use lower case for enum values.
* src/compare.c: Don't include utimens.h; no longer needed.
(diff_file): Use set_file_atime rather than utimens; avoid closing
diff_handle until after this, so that we can set the file time stamp
via the file descriptor rather than via its name.
* src/create.c: Don't include utimens.h; no longer needed.
(dump_regular_finish): Remove.  All callers now do its work inline.
(dump_dir): New arg FD.  All callers changed.
Use fdsavedir rather than savedir.
(unknown_file_error): Arg is a const pointer now.
(dump_file0): 2nd arg is a const pointer now.
Treat directories more like files, with respect to --atime-preserve.
For example, also warn if a directory changes while we are dumping it.
Prefer file descriptors to file names when retrieving/setting file
attributes; this saves path-resolution time and allows us to avoid
changing mtime/ctime on Solaris when restoring atime as root.
Use O_DIRECTORY when opening directories, to avoid some race conditions.
Do not reset atime if mtime has changed.  Report an error if
we cannot reset atime.

First cut at adding support for --atime-preserve=system.
* doc/tar.texi (Option Summary): First cut at documenting it.
All other uses of --atime-preserve changed to --atime-preserve=replace.
* doc/value.texi (op-atime-preserve-replace, op-atime-preserver-system):
New.
(op-atime-preserve): Mention METHOD.
* src/common.h (atime_preserve): New enum.
(atime_preserve_option): Now of the enum type rather than bool.
All uses changed.
* src/compare.c (diff_file): Read with O_NOATIME if asked for.
* src/create.c (dump_file0): Read regular and CTG files with O_NOATIME
if asked for.
* src/tar.c (usage): Mention new usage.
(parse_opt): Parse new usage.
This commit is contained in:
Paul Eggert
2005-11-29 19:14:21 +00:00
parent 7b00db5675
commit aa976a5170
12 changed files with 509 additions and 293 deletions
+7 -1
View File
@@ -114,7 +114,13 @@ GLOBAL bool utc_option;
than newer_mtime_option. */
GLOBAL int after_date_option;
GLOBAL bool atime_preserve_option;
enum atime_preserve
{
no_atime_preserve,
replace_atime_preserve,
system_atime_preserve
};
GLOBAL enum atime_preserve atime_preserve_option;
GLOBAL bool backup_option;
+20 -16
View File
@@ -25,10 +25,8 @@
# include <linux/fd.h>
#endif
#include <quotearg.h>
#include <utimens.h>
#include "common.h"
#include <quotearg.h>
#include <rmt.h>
#include <stdarg.h>
@@ -205,9 +203,10 @@ diff_dir (void)
static void
diff_file (void)
{
char const *file_name = current_stat_info.file_name;
struct stat stat_data;
if (!get_stat_data (current_stat_info.file_name, &stat_data))
if (!get_stat_data (file_name, &stat_data))
skip_member ();
else if (!S_ISREG (stat_data.st_mode))
{
@@ -228,19 +227,24 @@ diff_file (void)
if (tar_timespec_cmp (get_stat_mtime (&stat_data),
current_stat_info.mtime))
report_difference (&current_stat_info, _("Mod time differs"));
if (current_header->header.typeflag != GNUTYPE_SPARSE &&
stat_data.st_size != current_stat_info.stat.st_size)
if (current_header->header.typeflag != GNUTYPE_SPARSE
&& stat_data.st_size != current_stat_info.stat.st_size)
{
report_difference (&current_stat_info, _("Size differs"));
skip_member ();
}
else
{
diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
int atime_flag =
(atime_preserve_option == system_atime_preserve
? O_NOATIME
: 0);
diff_handle = open (file_name, O_RDONLY | O_BINARY | atime_flag);
if (diff_handle < 0)
{
open_error (current_stat_info.file_name);
open_error (file_name);
skip_member ();
report_difference (&current_stat_info, NULL);
}
@@ -253,18 +257,18 @@ diff_file (void)
else
read_and_process (&current_stat_info, process_rawdata);
status = close (diff_handle);
if (status != 0)
close_error (current_stat_info.file_name);
if (atime_preserve_option)
if (atime_preserve_option == replace_atime_preserve)
{
struct timespec ts[2];
ts[0] = get_stat_atime (&stat_data);
ts[1] = get_stat_mtime (&stat_data);
if (utimens (current_stat_info.file_name, ts) != 0)
utime_error (current_stat_info.file_name);
if (set_file_atime (diff_handle, file_name, ts) != 0)
utime_error (file_name);
}
status = close (diff_handle);
if (status != 0)
close_error (file_name);
}
}
}
@@ -357,7 +361,7 @@ diff_dumpdir (void)
}
else
dev = stat.st_dev;
dumpdir_buffer = get_directory_contents (current_stat_info.file_name, dev);
if (dumpdir_buffer)
+150 -134
View File
@@ -22,7 +22,6 @@
#include <system.h>
#include <quotearg.h>
#include <utimens.h>
#include "common.h"
#include <hash.h>
@@ -452,7 +451,7 @@ write_gnu_long_link (struct tar_stat_info *st, const char *p, char type)
finish_header (st, header, -1);
header = find_next_block ();
bufsize = available_space_after (header);
while (bufsize < size)
@@ -582,7 +581,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
union block *header, hp;
char *p;
int type;
if (extended_header.buffer || extended_header.stk == NULL)
return old_header;
@@ -950,35 +949,6 @@ dump_regular_file (int fd, struct tar_stat_info *st)
return dump_status_ok;
}
static void
dump_regular_finish (int fd, struct tar_stat_info *st,
struct timespec original_ctime)
{
if (fd >= 0)
{
struct stat final_stat;
if (fstat (fd, &final_stat) != 0)
{
stat_diag (st->orig_file_name);
}
else if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime)
!= 0)
{
WARN ((0, 0, _("%s: file changed as we read it"),
quotearg_colon (st->orig_file_name)));
}
if (close (fd) != 0)
{
close_diag (st->orig_file_name);
}
}
if (remove_files_option)
{
if (unlink (st->orig_file_name) == -1)
unlink_error (st->orig_file_name);
}
}
/* Look in directory DIRNAME for a cache directory tag file
with the magic name "CACHEDIR.TAG" and a standard header,
as described at:
@@ -1059,9 +1029,9 @@ dump_dir0 (char *directory,
size_t bufsize;
ssize_t count;
const char *buffer, *p_buffer;
block_ordinal = current_block_ordinal ();
buffer = gnu_list_name->dir_contents;
buffer = gnu_list_name->dir_contents;
if (buffer)
totsize = dumpdir_size (buffer);
else
@@ -1070,7 +1040,7 @@ dump_dir0 (char *directory,
finish_header (st, blk, block_ordinal);
p_buffer = buffer;
size_left = totsize;
mv_begin (st);
mv_total_size (totsize);
while (size_left > 0)
@@ -1161,11 +1131,9 @@ ensure_slash (char **pstr)
}
static bool
dump_dir (struct tar_stat_info *st, int top_level, dev_t parent_device)
dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
{
char *directory;
directory = savedir (st->orig_file_name);
char *directory = fdsavedir (fd);
if (!directory)
{
savedir_diag (st->orig_file_name);
@@ -1273,7 +1241,7 @@ compare_links (void const *entry1, void const *entry2)
}
static void
unknown_file_error (char *p)
unknown_file_error (char const *p)
{
WARN ((0, 0, _("%s: Unknown file type; file ignored"),
quotearg_colon (p)));
@@ -1289,8 +1257,8 @@ unknown_file_error (char *p)
again if we've done it once already. */
static Hash_table *link_table;
/* Try to dump stat as a hard link to another file in the archive. If
succeeded returns true */
/* Try to dump stat as a hard link to another file in the archive.
Return true if successful. */
static bool
dump_hard_link (struct tar_stat_info *st)
{
@@ -1392,7 +1360,7 @@ check_links (void)
exit_status to failure, a clear diagnostic has been issued. */
static void
dump_file0 (struct tar_stat_info *st, char *p,
dump_file0 (struct tar_stat_info *st, char const *p,
int top_level, dev_t parent_device)
{
union block *header;
@@ -1400,6 +1368,7 @@ dump_file0 (struct tar_stat_info *st, char *p,
struct timespec original_ctime;
struct timespec restore_times[2];
off_t block_ordinal = -1;
bool is_dir;
if (interactive_option && !confirm ("add", p))
return;
@@ -1458,43 +1427,48 @@ dump_file0 (struct tar_stat_info *st, char *p,
if (is_avoided_name (p))
return;
if (S_ISDIR (st->stat.st_mode))
{
dump_dir (st, top_level, parent_device);
if (atime_preserve_option)
utimens (p, restore_times);
return;
}
else
{
/* Check for multiple links. */
if (dump_hard_link (st))
return;
/* This is not a link to a previously dumped file, so dump it. */
is_dir = S_ISDIR (st->stat.st_mode) != 0;
if (S_ISREG (st->stat.st_mode)
|| S_ISCTG (st->stat.st_mode))
if (!is_dir && dump_hard_link (st))
return;
if (is_dir || S_ISREG (st->stat.st_mode) || S_ISCTG (st->stat.st_mode))
{
bool ok;
int fd = -1;
struct stat final_stat;
if (is_dir || file_dumpable_p (st))
{
int fd;
enum dump_status status;
if (file_dumpable_p (st))
fd = open (p,
(O_RDONLY | O_BINARY
| (is_dir ? O_DIRECTORY | O_NONBLOCK : 0)
| (atime_preserve_option == system_atime_preserve
? O_NOATIME
: 0)));
if (fd < 0)
{
fd = open (st->orig_file_name,
O_RDONLY | O_BINARY);
if (fd < 0)
{
if (!top_level && errno == ENOENT)
WARN ((0, 0, _("%s: File removed before we read it"),
quotearg_colon (st->orig_file_name)));
else
open_diag (st->orig_file_name);
return;
}
if (!top_level && errno == ENOENT)
WARN ((0, 0, _("%s: File removed before we read it"),
quotearg_colon (p)));
else
open_diag (p);
return;
}
else
}
if (is_dir)
{
ok = dump_dir (fd, st, top_level, parent_device);
/* dump_dir consumes FD if successful. */
if (ok)
fd = -1;
}
else
{
enum dump_status status;
if (fd != -1 && sparse_option && sparse_file_p (st))
{
@@ -1508,88 +1482,130 @@ dump_file0 (struct tar_stat_info *st, char *p,
switch (status)
{
case dump_status_ok:
mv_end ();
dump_regular_finish (fd, st, original_ctime);
break;
case dump_status_short:
mv_end ();
close (fd);
break;
case dump_status_fail:
close (fd);
return;
break;
case dump_status_not_implemented:
abort ();
}
if (atime_preserve_option)
utimens (st->orig_file_name, restore_times);
file_count_links (st);
return;
ok = status == dump_status_ok;
}
#ifdef HAVE_READLINK
else if (S_ISLNK (st->stat.st_mode))
if (ok)
{
char *buffer;
int size;
size_t linklen = st->stat.st_size;
if (linklen != st->stat.st_size || linklen + 1 == 0)
xalloc_die ();
buffer = (char *) alloca (linklen + 1);
size = readlink (p, buffer, linklen + 1);
if (size < 0)
/* If possible, reopen a directory if we are preserving
atimes, so that we can set just the atime on systems with
_FIOSATIME. */
if (fd < 0 && is_dir
&& atime_preserve_option == replace_atime_preserve)
fd = open (p, O_RDONLY | O_BINARY | O_DIRECTORY | O_NONBLOCK);
if ((fd < 0
? deref_stat (dereference_option, p, &final_stat)
: fstat (fd, &final_stat))
!= 0)
{
readlink_diag (p);
return;
stat_diag (p);
ok = false;
}
buffer[size] = '\0';
assign_string (&st->link_name, buffer);
if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
write_long_link (st);
}
block_ordinal = current_block_ordinal ();
st->stat.st_size = 0; /* force 0 size on symlink */
header = start_header (st);
if (!header)
return;
tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
header->header.typeflag = SYMTYPE;
finish_header (st, header, block_ordinal);
/* nothing more to do to it */
if (ok)
{
if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0)
WARN ((0, 0, _("%s: file changed as we read it"),
quotearg_colon (p)));
else if (atime_preserve_option == replace_atime_preserve
&& set_file_atime (fd, p, restore_times) != 0)
utime_error (p);
}
if (remove_files_option)
if (0 <= fd && close (fd) != 0)
{
close_diag (p);
ok = false;
}
if (ok && remove_files_option)
{
if (is_dir)
{
if (unlink (p) == -1)
if (rmdir (p) != 0 && errno != ENOTEMPTY)
rmdir_error (p);
}
else
{
if (unlink (p) != 0)
unlink_error (p);
}
file_count_links (st);
}
return;
}
#ifdef HAVE_READLINK
else if (S_ISLNK (st->stat.st_mode))
{
char *buffer;
int size;
size_t linklen = st->stat.st_size;
if (linklen != st->stat.st_size || linklen + 1 == 0)
xalloc_die ();
buffer = (char *) alloca (linklen + 1);
size = readlink (p, buffer, linklen + 1);
if (size < 0)
{
readlink_diag (p);
return;
}
buffer[size] = '\0';
assign_string (&st->link_name, buffer);
if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
write_long_link (st);
block_ordinal = current_block_ordinal ();
st->stat.st_size = 0; /* force 0 size on symlink */
header = start_header (st);
if (!header)
return;
tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
header->header.typeflag = SYMTYPE;
finish_header (st, header, block_ordinal);
/* nothing more to do to it */
if (remove_files_option)
{
if (unlink (p) == -1)
unlink_error (p);
}
file_count_links (st);
return;
}
#endif
else if (S_ISCHR (st->stat.st_mode))
type = CHRTYPE;
else if (S_ISBLK (st->stat.st_mode))
type = BLKTYPE;
else if (S_ISFIFO (st->stat.st_mode))
type = FIFOTYPE;
else if (S_ISSOCK (st->stat.st_mode))
{
WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
return;
}
else if (S_ISDOOR (st->stat.st_mode))
{
WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
return;
}
else
{
unknown_file_error (p);
return;
}
else if (S_ISCHR (st->stat.st_mode))
type = CHRTYPE;
else if (S_ISBLK (st->stat.st_mode))
type = BLKTYPE;
else if (S_ISFIFO (st->stat.st_mode))
type = FIFOTYPE;
else if (S_ISSOCK (st->stat.st_mode))
{
WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
return;
}
else if (S_ISDOOR (st->stat.st_mode))
{
WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
return;
}
else
{
unknown_file_error (p);
return;
}
if (archive_format == V7_FORMAT)
+28
View File
@@ -23,6 +23,14 @@
#include <quotearg.h>
#include <save-cwd.h>
#include <unlinkdir.h>
#include <utimens.h>
#if HAVE_STROPTS_H
# include <stropts.h>
#endif
#if HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
/* Handling strings. */
@@ -490,6 +498,26 @@ deref_stat (bool deref, char const *name, struct stat *buf)
return deref ? stat (name, buf) : lstat (name, buf);
}
/* Set FD's (i.e., FILE's) access time to TIMESPEC[0]. If that's not
possible to do by itself, set its access and data modification
times to TIMESPEC[0] and TIMESPEC[1], respectively. */
int
set_file_atime (int fd, char const *file, struct timespec const timespec[2])
{
#ifdef _FIOSATIME
if (0 <= fd)
{
struct timeval timeval;
timeval.tv_sec = timespec[0].tv_sec;
timeval.tv_usec = timespec[0].tv_nsec / 1000;
if (ioctl (fd, _FIOSATIME, &timeval) == 0)
return 0;
}
#endif
return futimens (fd, file, timespec);
}
/* A description of a working directory. */
struct wd
{
+29 -6
View File
@@ -36,6 +36,7 @@
#define GLOBAL
#include "common.h"
#include <argmatch.h>
#include <getdate.h>
#include <localedir.h>
#include <rmt.h>
@@ -113,7 +114,7 @@ confirm (const char *message_action, const char *message_name)
status = rpmatch (response) > 0;
free (response);
}
if (confirm_file_EOF)
{
fputc ('\n', stdlis);
@@ -267,7 +268,7 @@ The version control may be set with --backup or VERSION_CONTROL, values are:\n\n
[For Solaris tar compatibility =/= Is it important at all?]
e exit immediately with a nonzero exit status if unexpected errors occur
E use extended headers (--format=posix)
[q alias for --occurrence=1 =/= this would better be used for quiet?]
[I same as T =/= will harm star compatibility]
@@ -357,8 +358,11 @@ static struct argp_option options[] = {
N_("force NAME as group for added files"), 51 },
{"mode", MODE_OPTION, N_("CHANGES"), 0,
N_("force (symbolic) mode CHANGES for added files"), 51 },
{"atime-preserve", ATIME_PRESERVE_OPTION, 0, 0,
N_("don't change access times on dumped files"), 51 },
{"atime-preserve", ATIME_PRESERVE_OPTION,
N_("METHOD"), OPTION_ARG_OPTIONAL,
N_("preserve access times on dumped files, either by restoring the times"
" after reading (METHOD='replace'; default) or by not setting the times"
" in the first place (METHOD='system')"), 51 },
{"touch", 'm', 0, 0,
N_("don't extract file modified time"), 51 },
{"same-owner", SAME_OWNER_OPTION, 0, 0,
@@ -578,6 +582,17 @@ static struct argp_option options[] = {
{0, 0, 0, 0, 0, 0}
};
static char const *const atime_preserve_args[] =
{
"replace", "system", NULL
};
static enum atime_preserve const atime_preserve_types[] =
{
replace_atime_preserve, system_atime_preserve
};
ARGMATCH_VERIFY (atime_preserve_args, atime_preserve_types);
struct tar_args {
char const *textual_date_option;
int exclude_options;
@@ -1117,7 +1132,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
break;
case ATIME_PRESERVE_OPTION:
atime_preserve_option = true;
atime_preserve_option =
(arg
? XARGMATCH ("--atime-preserve", arg,
atime_preserve_args, atime_preserve_types)
: replace_atime_preserve);
if (! O_NOATIME && atime_preserve_option == system_atime_preserve)
FATAL_ERROR ((0, 0,
_("--atime-preserve='system' is not supported"
" on this platform\n")));
break;
case CHECKPOINT_OPTION:
@@ -1872,7 +1895,7 @@ main (int argc, char **argv)
/* Make sure we have first three descriptors available */
stdopen ();
/* Pre-allocate a few structures. */
allocated_archive_names = 10;