Compare commits
99 Commits
release_1_
...
release_1_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea443b9e8 | ||
|
|
881e5626c5 | ||
|
|
67ae04ba31 | ||
|
|
2807513841 | ||
|
|
bb0af96c54 | ||
|
|
ec586b37e0 | ||
|
|
a7fc5ecead | ||
|
|
649b747913 | ||
|
|
bb971a1e8a | ||
|
|
50a57a0147 | ||
|
|
1584b72ff2 | ||
|
|
065cf0958c | ||
|
|
2a55b4b037 | ||
|
|
27225be1a3 | ||
|
|
73d0d1a0f8 | ||
|
|
ff2bc5c0a1 | ||
|
|
24214ca5d5 | ||
|
|
777042e024 | ||
|
|
1801399830 | ||
|
|
ecd700fbfb | ||
|
|
f1fed3996a | ||
|
|
b32edff5aa | ||
|
|
3913675640 | ||
|
|
b8feb2b142 | ||
|
|
3fe59ed5ef | ||
|
|
5af29cb944 | ||
|
|
7dd57ebdfa | ||
|
|
e23d123b93 | ||
|
|
6398c7a79c | ||
|
|
28e91b48f6 | ||
|
|
3c0bedd494 | ||
|
|
c520964e84 | ||
|
|
acb77ac5bd | ||
|
|
983113b140 | ||
|
|
9447e799ab | ||
|
|
502abd93bd | ||
|
|
241b72ffad | ||
|
|
c5c196310e | ||
|
|
14efeb9f95 | ||
|
|
efe26f98ec | ||
|
|
4bde4f39d0 | ||
|
|
fc5e44c99c | ||
|
|
098ad10c71 | ||
|
|
6e08ab7694 | ||
|
|
cecb7ac8e6 | ||
|
|
ea964cce81 | ||
|
|
e3fdb78d4b | ||
|
|
d945888643 | ||
|
|
ecbcb7b6d7 | ||
|
|
59146768ef | ||
|
|
0fb3020da0 | ||
|
|
d7db30d0e8 | ||
|
|
8da503cad6 | ||
|
|
c743301494 | ||
|
|
de328a580a | ||
|
|
bad4b0006c | ||
|
|
e985feb292 | ||
|
|
fbc54fa337 | ||
|
|
45a574151a | ||
|
|
0adf4027f5 | ||
|
|
fce1c1c8cb | ||
|
|
372ac37d01 | ||
|
|
a9895fd20c | ||
|
|
a59c819beb | ||
|
|
57b11473b0 | ||
|
|
fbcadc9592 | ||
|
|
3b219f943e | ||
|
|
37ddfb0b7e | ||
|
|
09f039050e | ||
|
|
34795cedb7 | ||
|
|
9764a6b1d3 | ||
|
|
a1a15e3202 | ||
|
|
0ab5e64ac0 | ||
|
|
0a694a16e5 | ||
|
|
b3b15f7cbc | ||
|
|
8c662c9f29 | ||
|
|
427b3b8c79 | ||
|
|
c1b30c268f | ||
|
|
cc40c57a37 | ||
|
|
ab6dd4948d | ||
|
|
b4bcb97e38 | ||
|
|
9c194c9942 | ||
|
|
80a6ef7d94 | ||
|
|
9b31db388e | ||
|
|
f6edb92580 | ||
|
|
e21d54e8cd | ||
|
|
c79f0d06bc | ||
|
|
2c97cb7ea2 | ||
|
|
fc61c17410 | ||
|
|
2981fcc5cb | ||
|
|
40dea1ae7f | ||
|
|
b60e56fdb6 | ||
|
|
340dbf5aab | ||
|
|
c1d3d13493 | ||
|
|
67b4f3519d | ||
|
|
0ba8bdf5f3 | ||
|
|
8d3cc6c3cf | ||
|
|
3f4a6d83f0 | ||
|
|
a3f1d933cc |
18
COPYING
18
COPYING
@@ -1,12 +1,11 @@
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
@@ -69,7 +68,7 @@ patents cannot be used to render the program non-free.
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
@@ -77,7 +76,7 @@ modification follow.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
@@ -510,7 +509,7 @@ actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
@@ -619,9 +618,9 @@ an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -673,4 +672,3 @@ may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Currently there is just one ChangeLog file for tar, but
|
||||
Currently there is just one ChangeLog file for tar, but
|
||||
there used to be separate ChangeLog files for each subdirectory.
|
||||
This file records what used to be in those separate files.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
2009-03-04 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
Add xz support.
|
||||
|
||||
|
||||
* src/buffer.c, src/suffix.c: Add support for xz compression.
|
||||
* src/tar.c: New option --xz, for compression/decompression using xz.
|
||||
Re-assign -J as a short equivalent of --xz.
|
||||
@@ -34,8 +34,8 @@
|
||||
2008-11-25 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
Do not try to drain the input pipe before closing the
|
||||
archive.
|
||||
|
||||
archive.
|
||||
|
||||
* src/buffer.c (close_archive): Remove call to
|
||||
sys_drain_input_pipe. Pass hit_eof as the second
|
||||
argument to sys_wait_for_child.
|
||||
@@ -63,7 +63,7 @@
|
||||
* src/extract.c (extract_link, extract_symlink): Remove calls to
|
||||
transform_member_name. It is done in read_header.
|
||||
* src/list.c (decode_xform): Reflect change in data type of 2nd
|
||||
argument.
|
||||
argument.
|
||||
(transform_member_name): 2nd arg is int.
|
||||
(decode_header): Transform file name and link target names.
|
||||
* src/tar.c: Remove --transform-symlinks.
|
||||
@@ -73,7 +73,7 @@
|
||||
set global flags using `flags=' syntax.
|
||||
(_transform_name_to_obstack, transform_name_fp)
|
||||
(transform_name): Take an additional argument, specifying scope
|
||||
flags.
|
||||
flags.
|
||||
|
||||
2008-10-19 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
* tests/sparsemvp.at: Likewise.
|
||||
* tests/volsize.at: Likewise.
|
||||
* NEWS: Update.
|
||||
|
||||
|
||||
2008-10-16 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/common.h (transform_symlinks_option): New global.
|
||||
@@ -99,11 +99,11 @@
|
||||
* doc/tar.texi: Document --transform-symlinks
|
||||
* NEWS: Update.
|
||||
* THANKS: Update.
|
||||
|
||||
|
||||
* src/names.c (name_gather): Use xzalloc.
|
||||
* src/buffer.c (short_read): Move record size detection before
|
||||
the loop.
|
||||
|
||||
|
||||
2008-10-07 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/tar.c (options): Add --lzop option.
|
||||
@@ -111,7 +111,7 @@
|
||||
2008-10-05 Xavier Hienne <xavier.hienne@free.fr> (tiny change)
|
||||
|
||||
* src/checkpoint.c (checkpoint_compile_action): Add missing
|
||||
`else'.
|
||||
`else'.
|
||||
|
||||
2008-09-24 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
* tests/atlocal.in (decho): New function.
|
||||
* tests/multiv06.at: Use decho instead of echo2.
|
||||
* tests/incremental.at: Raise wait interval to 2 seconds.
|
||||
|
||||
|
||||
2008-07-24 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/tar.c (decode_options): Do not allow volume length less
|
||||
@@ -193,21 +193,21 @@
|
||||
tests/longv7.at, tests/lustar01.at, tests/lustar02.at,
|
||||
tests/shortfile.at: Update to match new diagnostic wording
|
||||
(see 2008-05-06).
|
||||
|
||||
|
||||
* NEWS: Update.
|
||||
|
||||
2008-06-14 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/tar.texi (exclude): Document support for new VCS.
|
||||
* THANKS: Update.
|
||||
* NEWS: Update.
|
||||
* NEWS: Update.
|
||||
* tests/multiv05.at: Fix typos.
|
||||
* tests/volsize.at: Remove a TZ dependency.
|
||||
|
||||
|
||||
2008-06-14 Dan Drake <dan@dandrake.org> (tiny change)
|
||||
|
||||
* src/tar.c (exclude_vcs_files): Support for Bazaar, Mercurial and
|
||||
Darcs.
|
||||
Darcs.
|
||||
|
||||
2008-05-06 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -231,23 +231,23 @@
|
||||
* tests/incr03.at, tests/incr04.at, tests/rename02.at,
|
||||
tests/rename03.at: Insert calls to sleep between creation of files
|
||||
and adding them to the archive.
|
||||
|
||||
|
||||
2008-03-31 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/create.c (dump_file0): Count links only for actually dumped
|
||||
files.
|
||||
files.
|
||||
|
||||
2008-03-27 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* NEWS: Document --no-check-device and --check-device.
|
||||
* doc/rendition.texi: Change the way FIXME-*refs are handled in
|
||||
!PROOF.
|
||||
!PROOF.
|
||||
* doc/intern.texi, doc/tar.texi: Update.
|
||||
* doc/untabify.el: New file.
|
||||
* doc/Makefile.am (EXTRA_DIST): Add untabify.el
|
||||
(untabify, final, check-format, check-refs, check-fixmes)
|
||||
(check-unrevised, all-check-docs, check-docs): New rules.
|
||||
|
||||
|
||||
* src/common.h (check_device_option): New global.
|
||||
* src/incremen.c (procdir): Use boolean and instead of bitwise
|
||||
one. Patch by Jean-Louis Martineau.
|
||||
@@ -256,7 +256,7 @@
|
||||
--check-device. Proposed by Jean-Louis Martineau.
|
||||
(parse_opt): Hanlde new options.
|
||||
(decode_options): Initialize check_device_option to true.
|
||||
|
||||
|
||||
* THANKS: Update
|
||||
|
||||
2008-03-06 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
@@ -267,7 +267,7 @@
|
||||
* po/.cvsignore: Update
|
||||
* src/system.c: Remove include setenv.h.
|
||||
* tests/atlocal.in (STAR_DATA_URL): Update.
|
||||
* tests/star/README: Update URL.
|
||||
* tests/star/README: Update URL.
|
||||
|
||||
2008-02-09 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
Exit with nonzero status if a close fails on an archive.
|
||||
Problem (and initial trivial fix)
|
||||
* src/buffer.c (close_archive, new_volume): close_error, not
|
||||
close_warn.
|
||||
close_warn.
|
||||
|
||||
2007-12-05 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
EXTRA_DIST = ChangeLog.1 Make.rules
|
||||
SUBDIRS = doc gnu lib rmt src scripts po tests
|
||||
|
||||
dist-hook:
|
||||
dist-hook:
|
||||
$(MAKE) changelog_dir=$(distdir) ChangeLog
|
||||
-rm -f $(distdir).cpio
|
||||
find $(distdir) | cpio -Hcrc -o | \
|
||||
@@ -36,5 +36,3 @@ include Make.rules
|
||||
gen_start_date = 2009-03-06
|
||||
prev_change_log = ChangeLog.CVS
|
||||
changelog_dir = .
|
||||
|
||||
|
||||
|
||||
169
NEWS
169
NEWS
@@ -1,6 +1,147 @@
|
||||
GNU tar NEWS - User visible changes. 2010-03-10
|
||||
GNU tar NEWS - User visible changes. 2011-03-12
|
||||
Please send GNU tar bug reports to <bug-tar@gnu.org>
|
||||
|
||||
|
||||
version 1.26 - Sergey Poznyakoff, 2011-03-12
|
||||
|
||||
* Bugfixes
|
||||
|
||||
** Fix the --verify option, which broke in version 1.24.
|
||||
|
||||
** Fix storing long sparse file names in PAX archives.
|
||||
|
||||
** Fix correctness of --atime-preserve=replace
|
||||
|
||||
tar --atime-preserve=replace no longer tries to restore atime of
|
||||
zero-sized files.
|
||||
|
||||
** Work around POSIX incompatibilities on FreeBSD, NetBSD and Tru64
|
||||
|
||||
** Fix bug with --one-file-system --listed-incremental
|
||||
|
||||
When invoked with these two options, tar 1.25 would add only the
|
||||
top-level directory to the archive, but not its contents.
|
||||
|
||||
|
||||
version 1.25 - Sergey Poznyakoff, 2010-11-07
|
||||
|
||||
* Fix extraction of empty directories with the -C option in effect.
|
||||
* Fix extraction of device nodes.
|
||||
* Make sure name matching occurs before eventual name transformation.
|
||||
|
||||
Tar 1.24 changed the ordering of name matching and name transformation
|
||||
so that the former saw already transformed file names. This made it
|
||||
impossible to match file names in certain cases. It is fixed now.
|
||||
|
||||
* Fix the behavior of tar -x --overwrite on hosts lacking O_NOFOLLOW.
|
||||
|
||||
* Improve the testsuite.
|
||||
|
||||
* Alternative decompression programs.
|
||||
|
||||
If extraction from a compressed archive fails because the corresponding
|
||||
compression program is not installed and the following two conditions
|
||||
are met, tar retries extraction using an alternative decompressor:
|
||||
|
||||
1. Another compression program supported by tar is able to handle this
|
||||
compression format.
|
||||
2. The compression program was not explicitly requested in the command
|
||||
line by the use of such options as -z, -j, etc.
|
||||
|
||||
For example, if `compress' is not available, tar will try `gzip'.
|
||||
|
||||
|
||||
version 1.24 - Sergey Poznyakoff, 2010-10-24
|
||||
|
||||
* The --full-time option.
|
||||
|
||||
New command line option `--full-time' instructs tar to output file
|
||||
time stamps to the full resolution.
|
||||
|
||||
* Bugfixes.
|
||||
|
||||
** More reliable directory traversal when creating archives
|
||||
|
||||
Tar now checks for inconsistencies caused when a file system is
|
||||
modified while tar is creating an archive. In the new approach, tar
|
||||
maintains a cache of file descriptors to directories, so it uses more
|
||||
file descriptors than before, but it adjusts to system limits on
|
||||
the number of file descriptors. Tar also takes more care when
|
||||
a file system is modified while tar is extracting from an archive.
|
||||
|
||||
The new checks are implemented via the openat and related calls
|
||||
standardized by POSIX.1-2008. On an older system where these calls do
|
||||
not exist or do not return useful results, tar emulates the calls at
|
||||
some cost in efficiency and reliability.
|
||||
|
||||
** Symbolic link attributes
|
||||
|
||||
When extracting symbolic links, tar now restores attributes such as
|
||||
last-modified time and link permissions, if the operating system
|
||||
supports this. For example, recent versions of the Linux kernel
|
||||
support setting times on symlinks, and some BSD kernels also support
|
||||
symlink permissions.
|
||||
|
||||
** --dereference consistency
|
||||
|
||||
The --dereference (-h) option now applies to files that are copied
|
||||
into or out of archives, independently of other options. For example,
|
||||
if F is a symbolic link and archive.tar contains a regular-file member
|
||||
also named F, "tar --overwrite -x -f archive.tar F" now overwrites F
|
||||
itself, rather than the file that F points to. (To overwrite the file
|
||||
that F points to, add the --dereference (-h) option.) Formerly,
|
||||
--dereference was intended to apply only when using the -c option, but
|
||||
the implementation was not consistent.
|
||||
|
||||
Also, the --dereference option no longer affects accesses to other
|
||||
files, such as archives and time stamp files. Symbolic links to these
|
||||
files are always followed. Previously, the links were usually but not
|
||||
always followed.
|
||||
|
||||
** Spurious error diagnostics on broken pipe.
|
||||
|
||||
When receiving SIGPIPE, tar would exit with error status and
|
||||
"write error" diagnostics. In particular, this occurred if
|
||||
invoked as in the example below:
|
||||
|
||||
tar tf archive.tar | head -n 1
|
||||
|
||||
** --remove-files
|
||||
|
||||
`Tar --remove-files' failed to remove a directory which contained
|
||||
symlinks to another files within that directory.
|
||||
|
||||
** --test-label behavior
|
||||
|
||||
In case of a mismatch, `tar --test-label LABEL' exits with code 1,
|
||||
not 2 as it did in previous versions.
|
||||
|
||||
The `--verbose' option used with `--test-label' provides additional
|
||||
diagnostics.
|
||||
|
||||
Several volume labels may be specified in a command line, e.g.:
|
||||
|
||||
tar --test-label -f archive 'My volume' 'New volume' 'Test volume'
|
||||
|
||||
In this case, tar exits with code 0 if any one of the arguments
|
||||
matches the actual volume label.
|
||||
|
||||
** --label used with --update
|
||||
|
||||
The `--label' option can be used with `--update' to prevent accidental
|
||||
update of an archive:
|
||||
|
||||
tar -rf archive --label 'My volume' .
|
||||
|
||||
This did not work in previous versions, in spite of what the docs said.
|
||||
|
||||
** --record-size and --tape-length (-L) options
|
||||
|
||||
Usual size suffixes are allowed for these options. For example,
|
||||
-L10k stands for a 10 kilobyte tape length.
|
||||
|
||||
** Fix dead loop on extracting existing symlinks with the -k option.
|
||||
|
||||
|
||||
version 1.23 - Sergey Poznyakoff, 2010-03-10
|
||||
|
||||
@@ -80,7 +221,7 @@ the Epoch or a `Time reference' (see below).
|
||||
* Time references in --pax-option argument.
|
||||
|
||||
Any value from the --pax-option argument that is enclosed in a pair
|
||||
of curly braces represents a time reference. The string between the
|
||||
of curly braces represents a time reference. The string between the
|
||||
braces is understood either as a textual time representation, as described in
|
||||
chapter 7, "Date input formats", of the Tar manual, or as a name of
|
||||
an existing file, starting with `/' or `.'. In the latter
|
||||
@@ -114,14 +255,14 @@ version 1.22 - Sergey Poznyakoff, 2009-03-05
|
||||
|
||||
* Support for xz compression
|
||||
|
||||
Tar uses xz for compression if one of the following conditions is met:
|
||||
Tar uses xz for compression if one of the following conditions is met:
|
||||
|
||||
1. The option --xz or -J (see below) is used.
|
||||
2. The xz binary is set as compressor using --use-compress-program option.
|
||||
3. The file name of the archive being created ends in `.xz' and
|
||||
auto-compress option (-a) is used.
|
||||
|
||||
Xz is used for decompression if one of the following conditions is met:
|
||||
Xz is used for decompression if one of the following conditions is met:
|
||||
|
||||
1. The option --xz or -J is used.
|
||||
2. The xz binary is set as compressor using --use-compress-program option.
|
||||
@@ -174,7 +315,7 @@ control type of archive members affected by them. The flags are:
|
||||
- s
|
||||
Apply transformation to symbolic link targets.
|
||||
|
||||
- h
|
||||
- h
|
||||
Apply transformation to hard link targets.
|
||||
|
||||
Corresponding upper-case letters negate the meaning, so that
|
||||
@@ -196,10 +337,10 @@ can be changed using `flags=' statement before the expressions, e.g.:
|
||||
|
||||
** The --null option disabled handling of tar options in list files. This
|
||||
is fixed.
|
||||
** Fixed record size autodetection. If detected record size differs from
|
||||
the expected value (either default, or set on the command line), tar
|
||||
always prints a warning if verbosity level is set to 1 or greater,
|
||||
i.e. if either -t or -v option is given.
|
||||
** Fixed record size autodetection. If the detected record size differs from
|
||||
the expected value (either default one, or the one set from the
|
||||
command line), tar always prints a warning if verbosity level is set
|
||||
to 1 or greater, i.e. if either -t or -v option is given.
|
||||
|
||||
|
||||
|
||||
@@ -253,7 +394,7 @@ during both creation and extraction. Tar 1.19 used them only
|
||||
during extraction.
|
||||
|
||||
For a detailed description, see chapter 6.7 "Modifying File and Member
|
||||
Names".
|
||||
Names".
|
||||
|
||||
* Info (end-of-volume) scripts
|
||||
|
||||
@@ -280,8 +421,8 @@ control systems, e.g. "CVS/", ".svn/", etc.
|
||||
|
||||
The following options now work with incremental archives as well:
|
||||
|
||||
--exclude-caches
|
||||
--exclude-caches-all
|
||||
--exclude-caches
|
||||
--exclude-caches-all
|
||||
--exclude-tag
|
||||
--exclude-tag-all
|
||||
--exclude-tag-under
|
||||
@@ -292,14 +433,14 @@ Previous versions always stored absolute file names in rename
|
||||
records, even if -P was not used. This is fixed: rename records
|
||||
contain file names processed in accordance with the command line
|
||||
settings.
|
||||
|
||||
|
||||
* Fix --version output.
|
||||
|
||||
* Recognition of broken archives.
|
||||
|
||||
When supplied an archive smaller than 512 bytes in reading mode (-x,
|
||||
-t), the previous version of tar silently ignored it, exiting with
|
||||
code 0. It is fixed. Tar now issues the following diagnostic message:
|
||||
code 0. It is fixed. Tar now issues the following diagnostic message:
|
||||
'This does not look like a tar archive', and exits with code 2.
|
||||
|
||||
* Fix double-dot recognition in archive member names in case of duplicate '/.'.
|
||||
|
||||
7
README
7
README
@@ -180,19 +180,19 @@ the `lchown' call will be used if available, but that's all.
|
||||
GNU `tar' is able to create archive in the following formats:
|
||||
|
||||
*** The format of UNIX version 7
|
||||
*** POSIX.1-1988 format, also known as "ustar format"
|
||||
*** POSIX.1-1988 format, also known as "ustar format"
|
||||
*** POSIX.1-2001 format, also known as "pax format"
|
||||
*** Old GNU format (described below)
|
||||
|
||||
In addition to those, GNU `tar' is also able to read archives
|
||||
produced by `star' archiver.
|
||||
produced by `star' archiver.
|
||||
|
||||
A so called `Old GNU' format is based on an early draft of the
|
||||
POSIX 1003.1 `ustar' standard which is different from the final
|
||||
standard. It defines its extensions (such as incremental backups
|
||||
and handling of the long file names) in a way incompatible with
|
||||
any existing tar archive format, therefore the use of old GNU
|
||||
format is strongly discouraged.
|
||||
format is strongly discouraged.
|
||||
|
||||
Please read the file NEWS for more information about POSIX compliance
|
||||
and new `tar' features.
|
||||
@@ -248,4 +248,3 @@ mode: outline
|
||||
paragraph-separate: "[ ]*$"
|
||||
version-control: never
|
||||
End:
|
||||
|
||||
|
||||
@@ -6,20 +6,20 @@ this package.
|
||||
You need the following packages to build the Git version of GNU
|
||||
tar. We do not make any efforts to accommodate older versions of
|
||||
these packages, so please make sure that you have the latest stable
|
||||
version.
|
||||
version.
|
||||
|
||||
- Automake <http://www.gnu.org/software/automake/>
|
||||
- Autoconf <http://www.gnu.org/software/autoconf/>
|
||||
- M4 <http://www.gnu.org/software/m4/>
|
||||
- Texinfo <http://www.gnu.org/software/texinfo>
|
||||
- Gnulib <http://www.gnu.org/software/gnulib>
|
||||
- Gnulib <http://www.gnu.org/software/gnulib>
|
||||
- Git <http://git.or.cz>
|
||||
|
||||
* Bootstrapping
|
||||
|
||||
Obviously, if you are reading these notes, you did manage to clone
|
||||
tar from Git. The next step is to get other files needed to build,
|
||||
which are extracted from other source packages:
|
||||
which are extracted from other source packages:
|
||||
|
||||
1. Change to the source tree directory
|
||||
|
||||
@@ -27,7 +27,7 @@ which are extracted from other source packages:
|
||||
|
||||
2. Run
|
||||
|
||||
./bootstrap
|
||||
./bootstrap
|
||||
|
||||
Once done, proceed as described in the file README (section
|
||||
INSTALLATION).
|
||||
@@ -43,7 +43,7 @@ contents:
|
||||
|
||||
Replace `$HOME/gnulib' with the actual directory where the Gnulib
|
||||
sources reside.
|
||||
|
||||
|
||||
For more information about `bootstrap', run `bootstrap --help'.
|
||||
|
||||
|
||||
@@ -67,5 +67,3 @@ mode: outline
|
||||
paragraph-separate: "[ ^L]*$"
|
||||
version-control: never
|
||||
End:
|
||||
|
||||
|
||||
|
||||
2
THANKS
2
THANKS
@@ -133,7 +133,7 @@ David Steiner dsteiner@ispa.uni-osnabrueck.de
|
||||
David Taylor taylor@think.com
|
||||
Dean Gaudet dgaudet@watdragon.uwaterloo.ca
|
||||
Demizu Noritoshi nori-d@is.aist-nara.ac.jp
|
||||
Denis Excoffier denis.excoffier@airbus.com
|
||||
Denis Excoffier denis.excoffier@free.fr
|
||||
Denis Fortin fortin@acm.org
|
||||
Dennis Pixton dennis@math.binghamton.edu
|
||||
Dick Streefland dicks@tasking.nl
|
||||
|
||||
509
bootstrap
509
bootstrap
@@ -1,8 +1,10 @@
|
||||
#! /bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-10-24.18; # UTC
|
||||
|
||||
# Bootstrap this package from checked-out sources.
|
||||
|
||||
# Copyright (C) 2003-2008, 2009 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003-2010 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
|
||||
@@ -17,7 +19,15 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Written by Paul Eggert and Sergey Poznyakoff.
|
||||
# Originally written by Paul Eggert. The canonical version of this
|
||||
# script is maintained as build-aux/bootstrap in gnulib, however, to
|
||||
# be useful to your project, you should place a copy of it under
|
||||
# version control in the top-level directory of your project. The
|
||||
# intent is that all customization can be done with a bootstrap.conf
|
||||
# file also maintained in your version control; gnulib comes with a
|
||||
# template build-aux/bootstrap.conf to get you started.
|
||||
|
||||
# Please report bugs or propose patches to bug-gnulib@gnu.org.
|
||||
|
||||
nl='
|
||||
'
|
||||
@@ -34,7 +44,7 @@ bt_regex=`echo "$bt"| sed 's/\./[.]/g'`
|
||||
bt2=${bt}2
|
||||
|
||||
usage() {
|
||||
echo >&2 "\
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION]...
|
||||
Bootstrap this package from the checked-out sources.
|
||||
|
||||
@@ -48,7 +58,7 @@ Options:
|
||||
sources reside. Use this if you already
|
||||
have gnulib sources on your machine, and
|
||||
do not want to waste your bandwidth downloading
|
||||
them again.
|
||||
them again. Defaults to \$GNULIB_SRCDIR.
|
||||
--copy Copy files instead of creating symbolic links.
|
||||
--force Attempt to bootstrap even if the sources seem
|
||||
not to have been checked out.
|
||||
@@ -57,8 +67,11 @@ Options:
|
||||
If the file $0.conf exists in the same directory as this script, its
|
||||
contents are read as shell variables to configure the bootstrap.
|
||||
|
||||
For build prerequisites, environment variables like \$AUTOCONF and \$AMTAR
|
||||
are honored.
|
||||
|
||||
Running without arguments will suffice in most cases.
|
||||
"
|
||||
EOF
|
||||
}
|
||||
|
||||
# Configuration.
|
||||
@@ -72,13 +85,22 @@ gnulib_modules=
|
||||
# Any gnulib files needed that are not in modules.
|
||||
gnulib_files=
|
||||
|
||||
# A function to be called to edit gnulib.mk right after it's created.
|
||||
# Override it via your own definition in bootstrap.conf.
|
||||
gnulib_mk_hook() { :; }
|
||||
|
||||
# A function to be called after everything else in this script.
|
||||
# Override it via your own definition in bootstrap.conf.
|
||||
bootstrap_epilogue() { :; }
|
||||
|
||||
# The command to download all .po files for a specified domain into
|
||||
# a specified directory. Fill in the first %s is the domain name, and
|
||||
# the second with the destination directory. Use rsync's -L and -r
|
||||
# options because the latest/%s directory and the .po files within are
|
||||
# all symlinks.
|
||||
po_download_command_format=\
|
||||
"rsync -Lrtvz 'translationproject.org::tp/latest/%s/' '%s'"
|
||||
"rsync --delete --exclude '*.s1' -Lrtvz \
|
||||
'translationproject.org::tp/latest/%s/' '%s'"
|
||||
|
||||
extract_package_name='
|
||||
/^AC_INIT(/{
|
||||
@@ -108,14 +130,14 @@ tests_base=tests
|
||||
|
||||
# Extra files from gnulib, which override files from other sources.
|
||||
gnulib_extra_files="
|
||||
$build_aux/install-sh
|
||||
$build_aux/missing
|
||||
$build_aux/mdate-sh
|
||||
$build_aux/texinfo.tex
|
||||
$build_aux/depcomp
|
||||
$build_aux/config.guess
|
||||
$build_aux/config.sub
|
||||
doc/INSTALL
|
||||
$build_aux/install-sh
|
||||
$build_aux/missing
|
||||
$build_aux/mdate-sh
|
||||
$build_aux/texinfo.tex
|
||||
$build_aux/depcomp
|
||||
$build_aux/config.guess
|
||||
$build_aux/config.sub
|
||||
doc/INSTALL
|
||||
"
|
||||
|
||||
# Additional gnulib-tool options to use. Use "\newline" to break lines.
|
||||
@@ -131,7 +153,8 @@ XGETTEXT_OPTIONS='\\\
|
||||
--flag=error:3:c-format --flag=error_at_line:5:c-format\\\
|
||||
'
|
||||
|
||||
# Package bug report address for gettext files
|
||||
# Package bug report address and copyright holder for gettext files
|
||||
COPYRIGHT_HOLDER='Free Software Foundation, Inc.'
|
||||
MSGID_BUGS_ADDRESS=bug-$package@gnu.org
|
||||
|
||||
# Files we don't want to import.
|
||||
@@ -150,6 +173,44 @@ copy=false
|
||||
# on which version control system (if any) is used in the source directory.
|
||||
vc_ignore=auto
|
||||
|
||||
# find_tool ENVVAR NAMES...
|
||||
# -------------------------
|
||||
# Search for a required program. Use the value of ENVVAR, if set,
|
||||
# otherwise find the first of the NAMES that can be run (i.e.,
|
||||
# supports --version). If found, set ENVVAR to the program name,
|
||||
# die otherwise.
|
||||
find_tool ()
|
||||
{
|
||||
find_tool_envvar=$1
|
||||
shift
|
||||
find_tool_names=$@
|
||||
eval "find_tool_res=\$$find_tool_envvar"
|
||||
if test x"$find_tool_res" = x; then
|
||||
for i
|
||||
do
|
||||
if ($i --version </dev/null) >/dev/null 2>&1; then
|
||||
find_tool_res=$i
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
find_tool_error_prefix="\$$find_tool_envvar: "
|
||||
fi
|
||||
if test x"$find_tool_res" = x; then
|
||||
echo >&2 "$0: one of these is required: $find_tool_names"
|
||||
exit 1
|
||||
fi
|
||||
($find_tool_res --version </dev/null) >/dev/null 2>&1 || {
|
||||
echo >&2 "$0: ${find_tool_error_prefix}cannot run $find_tool_res --version"
|
||||
exit 1
|
||||
}
|
||||
eval "$find_tool_envvar=\$find_tool_res"
|
||||
eval "export $find_tool_envvar"
|
||||
}
|
||||
|
||||
# Find sha1sum, named gsha1sum on MacPorts, and shasum on MacOS 10.6.
|
||||
find_tool SHA1SUM sha1sum gsha1sum shasum
|
||||
|
||||
# Override the default configuration, if necessary.
|
||||
# Make sure that bootstrap.conf is sourced from the current directory
|
||||
# if we were invoked as "sh bootstrap".
|
||||
@@ -202,7 +263,7 @@ insert_sorted_if_absent() {
|
||||
file=$1
|
||||
str=$2
|
||||
test -f $file || touch $file
|
||||
echo "$str" | sort -u - $file | cmp -s - $file \
|
||||
echo "$str" | sort -u - $file | cmp - $file > /dev/null \
|
||||
|| echo "$str" | sort -u - $file -o $file \
|
||||
|| exit 1
|
||||
}
|
||||
@@ -229,6 +290,128 @@ if test ! -d $build_aux; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Note this deviates from the version comparison in automake
|
||||
# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
|
||||
# but this should suffice as we won't be specifying old
|
||||
# version formats or redundant trailing .0 in bootstrap.conf.
|
||||
# If we did want full compatibility then we should probably
|
||||
# use m4_version_compare from autoconf.
|
||||
sort_ver() { # sort -V is not generally available
|
||||
ver1="$1"
|
||||
ver2="$2"
|
||||
|
||||
# split on '.' and compare each component
|
||||
i=1
|
||||
while : ; do
|
||||
p1=$(echo "$ver1" | cut -d. -f$i)
|
||||
p2=$(echo "$ver2" | cut -d. -f$i)
|
||||
if [ ! "$p1" ]; then
|
||||
echo "$1 $2"
|
||||
break
|
||||
elif [ ! "$p2" ]; then
|
||||
echo "$2 $1"
|
||||
break
|
||||
elif [ ! "$p1" = "$p2" ]; then
|
||||
if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison
|
||||
echo "$2 $1"
|
||||
elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison
|
||||
echo "$1 $2"
|
||||
else # numeric, then lexicographic comparison
|
||||
lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1)
|
||||
if [ "$lp" = "$p2" ]; then
|
||||
echo "$1 $2"
|
||||
else
|
||||
echo "$2 $1"
|
||||
fi
|
||||
fi
|
||||
break
|
||||
fi
|
||||
i=$(($i+1))
|
||||
done
|
||||
}
|
||||
|
||||
get_version() {
|
||||
app=$1
|
||||
|
||||
$app --version >/dev/null 2>&1 || return 1
|
||||
|
||||
$app --version 2>&1 |
|
||||
sed -n '# extract version within line
|
||||
s/.*[v ]\{1,\}\([0-9]\{1,\}\.[.a-z0-9-]*\).*/\1/
|
||||
t done
|
||||
|
||||
# extract version at start of line
|
||||
s/^\([0-9]\{1,\}\.[.a-z0-9-]*\).*/\1/
|
||||
t done
|
||||
|
||||
d
|
||||
|
||||
:done
|
||||
#the following essentially does s/5.005/5.5/
|
||||
s/\.0*\([1-9]\)/.\1/g
|
||||
p
|
||||
q'
|
||||
}
|
||||
|
||||
check_versions() {
|
||||
ret=0
|
||||
|
||||
while read app req_ver; do
|
||||
# We only need libtoolize from the libtool package.
|
||||
if test "$app" = libtool; then
|
||||
app=libtoolize
|
||||
fi
|
||||
# Honor $APP variables ($TAR, $AUTOCONF, etc.)
|
||||
appvar=`echo $app | tr '[a-z]-' '[A-Z]_'`
|
||||
test "$appvar" = TAR && appvar=AMTAR
|
||||
eval "app=\${$appvar-$app}"
|
||||
inst_ver=$(get_version $app)
|
||||
if [ ! "$inst_ver" ]; then
|
||||
echo "Error: '$app' not found" >&2
|
||||
ret=1
|
||||
elif [ ! "$req_ver" = "-" ]; then
|
||||
latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2)
|
||||
if [ ! "$latest_ver" = "$inst_ver" ]; then
|
||||
echo "Error: '$app' version == $inst_ver is too old" >&2
|
||||
echo " '$app' version >= $req_ver is required" >&2
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
print_versions() {
|
||||
echo "Program Min_version"
|
||||
echo "----------------------"
|
||||
printf %s "$buildreq"
|
||||
echo "----------------------"
|
||||
# can't depend on column -t
|
||||
}
|
||||
|
||||
use_libtool=0
|
||||
# We'd like to use grep -E, to see if any of LT_INIT,
|
||||
# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac,
|
||||
# but that's not portable enough (e.g., for Solaris).
|
||||
grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \
|
||||
&& use_libtool=1
|
||||
grep '^[ ]*LT_INIT' configure.ac >/dev/null \
|
||||
&& use_libtool=1
|
||||
if test $use_libtool = 1; then
|
||||
find_tool LIBTOOLIZE glibtoolize libtoolize
|
||||
fi
|
||||
|
||||
if ! printf "$buildreq" | check_versions; then
|
||||
echo >&2
|
||||
if test -f README-prereq; then
|
||||
echo "See README-prereq for how to get the prerequisite programs" >&2
|
||||
else
|
||||
echo "Please install the prerequisite programs" >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$0: Bootstrapping from checked-out $package sources..."
|
||||
|
||||
# See if we can use gnulib's git-merge-changelog merge driver.
|
||||
@@ -245,13 +428,13 @@ if test -d .git && (git --version) >/dev/null 2>/dev/null ; then
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
status=$?
|
||||
rm -fr $1
|
||||
exit $status
|
||||
status=$?
|
||||
rm -fr $1
|
||||
exit $status
|
||||
}
|
||||
|
||||
git_modules_config () {
|
||||
GIT_CONFIG_LOCAL=.gitmodules git config "$@"
|
||||
cleanup_gnulib() {
|
||||
cleanup "$gnulib_path"
|
||||
}
|
||||
|
||||
# Get paxutils files.
|
||||
@@ -276,6 +459,12 @@ if [ -r $PAXUTILS_SRCDIR/gnulib.modules ]; then
|
||||
`
|
||||
fi
|
||||
|
||||
git_modules_config () {
|
||||
test -f .gitmodules && git config --file .gitmodules "$@"
|
||||
}
|
||||
|
||||
gnulib_path=`git_modules_config submodule.gnulib.path`
|
||||
: ${gnulib_path:=gnulib}
|
||||
# Get gnulib files.
|
||||
|
||||
case ${GNULIB_SRCDIR--} in
|
||||
@@ -285,30 +474,44 @@ case ${GNULIB_SRCDIR--} in
|
||||
git submodule init || exit $?
|
||||
git submodule update || exit $?
|
||||
|
||||
elif [ ! -d gnulib ]; then
|
||||
elif [ ! -d "$gnulib_path" ]; then
|
||||
echo "$0: getting gnulib files..."
|
||||
|
||||
trap cleanup_gnulib 1 2 13 15
|
||||
|
||||
git clone --help|grep depth > /dev/null && shallow='--depth 2' || shallow=
|
||||
git clone $shallow git://git.sv.gnu.org/gnulib ||
|
||||
"cleanup $1"
|
||||
shallow=
|
||||
git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2'
|
||||
git clone $shallow git://git.sv.gnu.org/gnulib "$gnulib_path" ||
|
||||
cleanup_gnulib
|
||||
|
||||
trap - 1 2 13 15
|
||||
fi
|
||||
GNULIB_SRCDIR=gnulib
|
||||
GNULIB_SRCDIR=$gnulib_path
|
||||
;;
|
||||
*)
|
||||
# Redirect the gnulib submodule to the directory on the command line
|
||||
# if possible.
|
||||
# Use GNULIB_SRCDIR as a reference.
|
||||
if test -d "$GNULIB_SRCDIR"/.git && \
|
||||
git_modules_config submodule.gnulib.url >/dev/null; then
|
||||
git submodule init
|
||||
GNULIB_SRCDIR=`cd $GNULIB_SRCDIR && pwd`
|
||||
git config --replace-all submodule.gnulib.url $GNULIB_SRCDIR
|
||||
git_modules_config submodule.gnulib.url >/dev/null; then
|
||||
echo "$0: getting gnulib files..."
|
||||
git submodule update || exit $?
|
||||
GNULIB_SRCDIR=gnulib
|
||||
if git submodule -h|grep -- --reference > /dev/null; then
|
||||
# Prefer the one-liner available in git 1.6.4 or newer.
|
||||
git submodule update --init --reference "$GNULIB_SRCDIR" \
|
||||
"$gnulib_path" || exit $?
|
||||
else
|
||||
# This fallback allows at least git 1.5.5.
|
||||
if test -f "$gnulib_path"/gnulib-tool; then
|
||||
# Since file already exists, assume submodule init already complete.
|
||||
git submodule update || exit $?
|
||||
else
|
||||
# Older git can't clone into an empty directory.
|
||||
rmdir "$gnulib_path" 2>/dev/null
|
||||
git clone --reference "$GNULIB_SRCDIR" \
|
||||
"$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \
|
||||
&& git submodule init && git submodule update \
|
||||
|| exit $?
|
||||
fi
|
||||
fi
|
||||
GNULIB_SRCDIR=$gnulib_path
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
@@ -326,22 +529,24 @@ download_po_files() {
|
||||
eval "$cmd"
|
||||
}
|
||||
|
||||
# Download .po files to $po_dir/.reference and copy only the new
|
||||
# Mirror .po files to $po_dir/.reference and copy only the new
|
||||
# or modified ones into $po_dir. Also update $po_dir/LINGUAS.
|
||||
# Note po files that exist locally only are left in $po_dir but will
|
||||
# not be included in LINGUAS and hence will not be distributed.
|
||||
update_po_files() {
|
||||
# Directory containing primary .po files.
|
||||
# Overwrite them only when we're sure a .po file is new.
|
||||
po_dir=$1
|
||||
domain=$2
|
||||
|
||||
# Download *.po files into this dir.
|
||||
# Mirror *.po files into this dir.
|
||||
# Usually contains *.s1 checksum files.
|
||||
ref_po_dir="$po_dir/.reference"
|
||||
|
||||
test -d $ref_po_dir || mkdir $ref_po_dir || return
|
||||
download_po_files $ref_po_dir $domain \
|
||||
&& ls "$ref_po_dir"/*.po 2>/dev/null |
|
||||
sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS"
|
||||
sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return
|
||||
|
||||
langs=`cd $ref_po_dir && echo *.po|sed 's/\.po//g'`
|
||||
test "$langs" = '*' && langs=x
|
||||
@@ -350,10 +555,12 @@ update_po_files() {
|
||||
new_po="$ref_po_dir/$po.po"
|
||||
cksum_file="$ref_po_dir/$po.s1"
|
||||
if ! test -f "$cksum_file" ||
|
||||
! test -f "$po_dir/$po.po" ||
|
||||
! sha1sum -c --status "$cksum_file" < "$new_po" > /dev/null; then
|
||||
! test -f "$po_dir/$po.po" ||
|
||||
! $SHA1SUM -c --status "$cksum_file" \
|
||||
< "$new_po" > /dev/null; then
|
||||
echo "updated $po_dir/$po.po..."
|
||||
cp "$new_po" "$po_dir/$po.po" && sha1sum < "$new_po" > "$cksum_file"
|
||||
cp "$new_po" "$po_dir/$po.po" \
|
||||
&& $SHA1SUM < "$new_po" > "$cksum_file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -387,46 +594,46 @@ symlink_to_dir()
|
||||
# FIXME: for now, this does only one level
|
||||
parent=`dirname "$dst_dir"`
|
||||
for dot_ig in x $vc_ignore; do
|
||||
test $dot_ig = x && continue
|
||||
ig=$parent/$dot_ig
|
||||
insert_sorted_if_absent $ig `echo "$dst_dir"|sed 's,.*/,,'`
|
||||
test $dot_ig = x && continue
|
||||
ig=$parent/$dot_ig
|
||||
insert_sorted_if_absent $ig `echo "$dst_dir"|sed 's,.*/,,'`
|
||||
done
|
||||
fi
|
||||
|
||||
if $copy; then
|
||||
{
|
||||
test ! -h "$dst" || {
|
||||
echo "$0: rm -f $dst" &&
|
||||
rm -f "$dst"
|
||||
}
|
||||
test ! -h "$dst" || {
|
||||
echo "$0: rm -f $dst" &&
|
||||
rm -f "$dst"
|
||||
}
|
||||
} &&
|
||||
test -f "$dst" &&
|
||||
cmp -s "$src" "$dst" || {
|
||||
echo "$0: cp -fp $src $dst" &&
|
||||
cp -fp "$src" "$dst"
|
||||
echo "$0: cp -fp $src $dst" &&
|
||||
cp -fp "$src" "$dst"
|
||||
}
|
||||
else
|
||||
test -h "$dst" &&
|
||||
src_ls=`ls -diL "$src" 2>/dev/null` && set $src_ls && src_i=$1 &&
|
||||
dst_ls=`ls -diL "$dst" 2>/dev/null` && set $dst_ls && dst_i=$1 &&
|
||||
test "$src_i" = "$dst_i" || {
|
||||
dot_dots=
|
||||
case $src in
|
||||
/*) ;;
|
||||
*)
|
||||
case /$dst/ in
|
||||
/./*) ;;
|
||||
*//* | */../* | */./* | /*/*/*/*/*/)
|
||||
echo >&2 "$0: invalid symlink calculation: $src -> $dst"
|
||||
exit 1;;
|
||||
/*/*/*/*/) dot_dots=../../../;;
|
||||
/*/*/*/) dot_dots=../../;;
|
||||
/*/*/) dot_dots=../;;
|
||||
esac;;
|
||||
esac
|
||||
dot_dots=
|
||||
case $src in
|
||||
/*) ;;
|
||||
*)
|
||||
case /$dst/ in
|
||||
/./*) ;;
|
||||
*//* | */../* | */./* | /*/*/*/*/*/)
|
||||
echo >&2 "$0: invalid symlink calculation: $src -> $dst"
|
||||
exit 1;;
|
||||
/*/*/*/*/) dot_dots=../../../;;
|
||||
/*/*/*/) dot_dots=../../;;
|
||||
/*/*/) dot_dots=../;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
echo "$0: ln -fs $dot_dots$src $dst" &&
|
||||
ln -fs "$dot_dots$src" "$dst"
|
||||
echo "$0: ln -fs $dot_dots$src $dst" &&
|
||||
ln -fs "$dot_dots$src" "$dst"
|
||||
}
|
||||
fi
|
||||
}
|
||||
@@ -456,28 +663,28 @@ cp_mark_as_generated()
|
||||
|
||||
if test -z "$c1"; then
|
||||
cmp -s "$cp_src" "$cp_dst" || {
|
||||
# Copy the file first to get proper permissions if it
|
||||
# doesn't already exist. Then overwrite the copy.
|
||||
echo "$0: cp -f $cp_src $cp_dst" &&
|
||||
rm -f "$cp_dst" &&
|
||||
cp "$cp_src" "$cp_dst-t" &&
|
||||
sed "s!$bt_regex/!!g" "$cp_src" > "$cp_dst-t" &&
|
||||
mv -f "$cp_dst-t" "$cp_dst"
|
||||
# Copy the file first to get proper permissions if it
|
||||
# doesn't already exist. Then overwrite the copy.
|
||||
echo "$0: cp -f $cp_src $cp_dst" &&
|
||||
rm -f "$cp_dst" &&
|
||||
cp "$cp_src" "$cp_dst-t" &&
|
||||
sed "s!$bt_regex/!!g" "$cp_src" > "$cp_dst-t" &&
|
||||
mv -f "$cp_dst-t" "$cp_dst"
|
||||
}
|
||||
else
|
||||
# Copy the file first to get proper permissions if it
|
||||
# doesn't already exist. Then overwrite the copy.
|
||||
cp "$cp_src" "$cp_dst-t" &&
|
||||
(
|
||||
echo "$c1-*- buffer-read-only: t -*- vi: set ro:$c2" &&
|
||||
echo "${c1}DO NOT EDIT! GENERATED AUTOMATICALLY!$c2" &&
|
||||
sed "s!$bt_regex/!!g" "$cp_src"
|
||||
echo "$c1-*- buffer-read-only: t -*- vi: set ro:$c2" &&
|
||||
echo "${c1}DO NOT EDIT! GENERATED AUTOMATICALLY!$c2" &&
|
||||
sed "s!$bt_regex/!!g" "$cp_src"
|
||||
) > $cp_dst-t &&
|
||||
if cmp -s "$cp_dst-t" "$cp_dst"; then
|
||||
rm -f "$cp_dst-t"
|
||||
rm -f "$cp_dst-t"
|
||||
else
|
||||
echo "$0: cp $cp_src $cp_dst # with edits" &&
|
||||
mv -f "$cp_dst-t" "$cp_dst"
|
||||
echo "$0: cp $cp_src $cp_dst # with edits" &&
|
||||
mv -f "$cp_dst-t" "$cp_dst"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -489,7 +696,7 @@ version_controlled_file() {
|
||||
found=no
|
||||
if test -d CVS; then
|
||||
grep -F "/$file/" $dir/CVS/Entries 2>/dev/null |
|
||||
grep '^/[^/]*/[0-9]' > /dev/null && found=yes
|
||||
grep '^/[^/]*/[0-9]' > /dev/null && found=yes
|
||||
elif test -d .git; then
|
||||
git rm -n "$dir/$file" > /dev/null 2>&1 && found=yes
|
||||
elif test -d .svn; then
|
||||
@@ -507,39 +714,42 @@ slurp() {
|
||||
for file in `ls -a $1/$dir`; do
|
||||
case $file in
|
||||
.|..) continue;;
|
||||
.*) continue;; # FIXME: should all file names starting with "." be ignored?
|
||||
# FIXME: should all file names starting with "." be ignored?
|
||||
.*) continue;;
|
||||
esac
|
||||
test -d $1/$dir/$file && continue
|
||||
for excluded_file in $excluded_files; do
|
||||
test "$dir/$file" = "$excluded_file" && continue 2
|
||||
test "$dir/$file" = "$excluded_file" && continue 2
|
||||
done
|
||||
if test $file = Makefile.am; then
|
||||
if test $file = Makefile.am && test "X$gnulib_mk" != XMakefile.am; then
|
||||
copied=$copied${sep}$gnulib_mk; sep=$nl
|
||||
remove_intl='/^[^#].*\/intl/s/^/#/;'"s!$bt_regex/!!g"
|
||||
sed "$remove_intl" $1/$dir/$file | cmp -s - $dir/$gnulib_mk || {
|
||||
echo "$0: Copying $1/$dir/$file to $dir/$gnulib_mk ..." &&
|
||||
rm -f $dir/$gnulib_mk &&
|
||||
sed "$remove_intl" $1/$dir/$file >$dir/$gnulib_mk
|
||||
}
|
||||
remove_intl='/^[^#].*\/intl/s/^/#/;'"s!$bt_regex/!!g"
|
||||
sed "$remove_intl" $1/$dir/$file |
|
||||
cmp - $dir/$gnulib_mk > /dev/null || {
|
||||
echo "$0: Copying $1/$dir/$file to $dir/$gnulib_mk ..." &&
|
||||
rm -f $dir/$gnulib_mk &&
|
||||
sed "$remove_intl" $1/$dir/$file >$dir/$gnulib_mk &&
|
||||
gnulib_mk_hook $dir/$gnulib_mk
|
||||
}
|
||||
elif { test "${2+set}" = set && test -r $2/$dir/$file; } ||
|
||||
version_controlled_file $dir $file; then
|
||||
echo "$0: $dir/$file overrides $1/$dir/$file"
|
||||
version_controlled_file $dir $file; then
|
||||
echo "$0: $dir/$file overrides $1/$dir/$file"
|
||||
else
|
||||
copied=$copied$sep$file; sep=$nl
|
||||
if test $file = gettext.m4; then
|
||||
echo "$0: patching m4/gettext.m4 to remove need for intl/* ..."
|
||||
rm -f $dir/$file
|
||||
sed '
|
||||
/^AC_DEFUN(\[AM_INTL_SUBDIR],/,/^]/c\
|
||||
AC_DEFUN([AM_INTL_SUBDIR], [
|
||||
/^AC_DEFUN(\[gt_INTL_SUBDIR_CORE],/,/^]/c\
|
||||
AC_DEFUN([gt_INTL_SUBDIR_CORE], [])
|
||||
$a\
|
||||
AC_DEFUN([gl_LOCK_EARLY], [])
|
||||
' $1/$dir/$file >$dir/$file
|
||||
else
|
||||
cp_mark_as_generated $1/$dir/$file $dir/$file
|
||||
fi
|
||||
copied=$copied$sep$file; sep=$nl
|
||||
if test $file = gettext.m4; then
|
||||
echo "$0: patching m4/gettext.m4 to remove need for intl/* ..."
|
||||
rm -f $dir/$file
|
||||
sed '
|
||||
/^AC_DEFUN(\[AM_INTL_SUBDIR],/,/^]/c\
|
||||
AC_DEFUN([AM_INTL_SUBDIR], [])
|
||||
/^AC_DEFUN(\[gt_INTL_SUBDIR_CORE],/,/^]/c\
|
||||
AC_DEFUN([gt_INTL_SUBDIR_CORE], [])
|
||||
$a\
|
||||
AC_DEFUN([gl_LOCK_EARLY], [])
|
||||
' $1/$dir/$file >$dir/$file
|
||||
else
|
||||
cp_mark_as_generated $1/$dir/$file $dir/$file
|
||||
fi
|
||||
fi || exit
|
||||
done
|
||||
|
||||
@@ -547,18 +757,25 @@ slurp() {
|
||||
test $dot_ig = x && continue
|
||||
ig=$dir/$dot_ig
|
||||
if test -n "$copied"; then
|
||||
insert_sorted_if_absent $ig "$copied"
|
||||
# If an ignored file name ends with .in.h, then also add
|
||||
# the name with just ".h". Many gnulib headers are generated,
|
||||
# e.g., stdint.in.h -> stdint.h, dirent.in.h ->..., etc.
|
||||
# Likewise for .gperf -> .h, .y -> .c, and .sin -> .sed
|
||||
f=`echo "$copied"|sed 's/\.in\.h$/.h/;s/\.sin$/.sed/;s/\.y$/.c/;s/\.gperf$/.h/'`
|
||||
insert_sorted_if_absent $ig "$f"
|
||||
insert_sorted_if_absent $ig "$copied"
|
||||
# If an ignored file name ends with .in.h, then also add
|
||||
# the name with just ".h". Many gnulib headers are generated,
|
||||
# e.g., stdint.in.h -> stdint.h, dirent.in.h ->..., etc.
|
||||
# Likewise for .gperf -> .h, .y -> .c, and .sin -> .sed
|
||||
f=`echo "$copied" |
|
||||
sed '
|
||||
s/\.in\.h$/.h/
|
||||
s/\.sin$/.sed/
|
||||
s/\.y$/.c/
|
||||
s/\.gperf$/.h/
|
||||
'
|
||||
`
|
||||
insert_sorted_if_absent $ig "$f"
|
||||
|
||||
# For files like sys_stat.in.h and sys_time.in.h, record as
|
||||
# ignorable the directory we might eventually create: sys/.
|
||||
f=`echo "$copied"|sed 's/sys_.*\.in\.h$/sys/'`
|
||||
insert_sorted_if_absent $ig "$f"
|
||||
# For files like sys_stat.in.h and sys_time.in.h, record as
|
||||
# ignorable the directory we might eventually create: sys/.
|
||||
f=`echo "$copied"|sed 's/sys_.*\.in\.h$/sys/'`
|
||||
insert_sorted_if_absent $ig "$f"
|
||||
fi
|
||||
done
|
||||
done
|
||||
@@ -583,6 +800,12 @@ gnulib_tool_options="\
|
||||
--local-dir $local_gl_dir\
|
||||
$gnulib_tool_option_extras\
|
||||
"
|
||||
if test $use_libtool = 1; then
|
||||
case "$gnulib_tool_options " in
|
||||
*' --libtool '*) ;;
|
||||
*) gnulib_tool_options="$gnulib_tool_options --libtool" ;;
|
||||
esac
|
||||
fi
|
||||
echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
|
||||
$gnulib_tool $gnulib_tool_options --import $gnulib_modules &&
|
||||
slurp $bt || exit
|
||||
@@ -598,9 +821,9 @@ grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \
|
||||
with_gettext=no
|
||||
|
||||
if test $with_gettext = yes; then
|
||||
echo "$0: (cd $bt2; autopoint) ..."
|
||||
echo "$0: (cd $bt2; ${AUTOPOINT-autopoint}) ..."
|
||||
cp configure.ac $bt2 &&
|
||||
(cd $bt2 && autopoint && rm configure.ac) &&
|
||||
(cd $bt2 && ${AUTOPOINT-autopoint} && rm configure.ac) &&
|
||||
slurp $bt2 $bt || exit
|
||||
fi
|
||||
rm -fr $bt $bt2 || exit
|
||||
@@ -617,6 +840,8 @@ find "$m4_base" "$source_base" \
|
||||
-depth \( -name '*.m4' -o -name '*.[ch]' \) \
|
||||
-type l -xtype l -delete > /dev/null 2>&1
|
||||
|
||||
# Reconfigure, getting other files.
|
||||
|
||||
# copy_files srcdir dstdir
|
||||
copy_files() {
|
||||
for file in `cat $1/DISTFILES`
|
||||
@@ -661,27 +886,22 @@ done
|
||||
copy_files ${PAXUTILS_SRCDIR}/paxlib lib pax
|
||||
|
||||
|
||||
# Reconfigure, getting other files.
|
||||
|
||||
# Skip autoheader if it's not needed.
|
||||
grep -E '^[ ]*AC_CONFIG_HEADERS?\>' configure.ac >/dev/null ||
|
||||
AUTOHEADER=true
|
||||
|
||||
for command in \
|
||||
libtool \
|
||||
'aclocal --force -I m4' \
|
||||
'autoconf --force' \
|
||||
'autoheader --force' \
|
||||
'automake --add-missing --copy --force-missing';
|
||||
"${ACLOCAL-aclocal} --force -I m4 $ACLOCAL_FLAGS" \
|
||||
"${AUTOCONF-autoconf} --force" \
|
||||
"${AUTOHEADER-autoheader} --force" \
|
||||
"${AUTOMAKE-automake} --add-missing --copy --force-missing"
|
||||
do
|
||||
if test "$command" = libtool; then
|
||||
use_libtool=0
|
||||
# We'd like to use grep -E, to see if any of LT_INIT,
|
||||
# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac,
|
||||
# but that's not portable enough (e.g., for Solaris).
|
||||
grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \
|
||||
&& use_libtool=1
|
||||
grep '^[ ]*LT_INIT' configure.ac >/dev/null \
|
||||
&& use_libtool=1
|
||||
test $use_libtool = 0 \
|
||||
&& continue
|
||||
command='libtoolize -c -f'
|
||||
command="${LIBTOOLIZE-libtoolize} -c -f"
|
||||
fi
|
||||
echo "$0: $command ..."
|
||||
$command || exit
|
||||
@@ -704,13 +924,14 @@ if test $with_gettext = yes; then
|
||||
rm -f po/Makevars
|
||||
sed '
|
||||
/^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/
|
||||
/^MSGID_BUGS_ADDRESS *=/s/=.*/= '"$MSGID_BUGS_ADDRESS"'/
|
||||
/^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/
|
||||
/^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'|
|
||||
/^XGETTEXT_OPTIONS *=/{
|
||||
s/$/ \\/
|
||||
a\
|
||||
'"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+}
|
||||
'"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+}
|
||||
}
|
||||
' po/Makevars.template >po/Makevars
|
||||
' po/Makevars.template >po/Makevars || exit 1
|
||||
|
||||
if test -d runtime-po; then
|
||||
# Similarly for runtime-po/Makevars, but not quite the same.
|
||||
@@ -720,15 +941,25 @@ if test $with_gettext = yes; then
|
||||
/^subdir *=.*/s/=.*/= runtime-po/
|
||||
/^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/
|
||||
/^XGETTEXT_OPTIONS *=/{
|
||||
s/$/ \\/
|
||||
a\
|
||||
'"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+}
|
||||
s/$/ \\/
|
||||
a\
|
||||
'"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+}
|
||||
}
|
||||
' <po/Makevars.template >runtime-po/Makevars
|
||||
' po/Makevars.template >runtime-po/Makevars || exit 1
|
||||
|
||||
# Copy identical files from po to runtime-po.
|
||||
(cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po)
|
||||
fi
|
||||
fi
|
||||
|
||||
bootstrap_epilogue
|
||||
|
||||
echo "$0: done. Now you can run './configure'."
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
||||
10
configure.ac
10
configure.ac
@@ -19,12 +19,12 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AC_INIT([GNU tar], [1.23], [bug-tar@gnu.org])
|
||||
AC_INIT([GNU tar], [1.26], [bug-tar@gnu.org])
|
||||
AC_CONFIG_SRCDIR([src/tar.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_PREREQ([2.63])
|
||||
AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-bzip2 dist-shar std-options silent-rules])
|
||||
AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-bzip2 dist-xz dist-shar std-options silent-rules])
|
||||
|
||||
# Enable silent rules by default:
|
||||
AM_SILENT_RULES([yes])
|
||||
@@ -40,8 +40,8 @@ AC_ISC_POSIX
|
||||
AC_C_INLINE
|
||||
|
||||
AC_CHECK_HEADERS_ONCE(fcntl.h linux/fd.h memory.h net/errno.h \
|
||||
sgtty.h string.h stropts.h \
|
||||
sys/param.h sys/device.h sys/filio.h sys/gentape.h \
|
||||
sgtty.h string.h \
|
||||
sys/param.h sys/device.h sys/gentape.h \
|
||||
sys/inet.h sys/io/trioctl.h \
|
||||
sys/mtio.h sys/time.h sys/tprintf.h sys/tape.h \
|
||||
unistd.h locale.h)
|
||||
@@ -90,7 +90,7 @@ gl_INIT
|
||||
# paxutils modules
|
||||
tar_PAXUTILS
|
||||
|
||||
AC_CHECK_FUNCS(fsync getdtablesize lstat mkfifo readlink symlink setlocale utimes)
|
||||
AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
|
||||
AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
|
||||
AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
|
||||
AC_CHECK_DECLS([time],,, [#include <time.h>])
|
||||
|
||||
@@ -65,7 +65,7 @@ programs (using pipes); tar can even access remote devices or files
|
||||
%%contributors: Jay Fenlason,
|
||||
Joy Kendall,
|
||||
Francois Pinard <pinard@iro.umontreal.ca>
|
||||
|
||||
|
||||
%%source-tarball: ftp://ftp.gnu.org/pub/gnu/tar/tar-1.15.1.tar.gz
|
||||
%%source-info: http://savannah.gnu.org/projects/tar
|
||||
|
||||
@@ -84,4 +84,3 @@ programs (using pipes); tar can even access remote devices or files
|
||||
%%bug-list: bug-tar@gnu.org bug-tar@gnu.org http://mail.gnu.org/mailman/listinfo/bug-tar
|
||||
|
||||
%%entry-written-by: Sergey Poznyakoff <gray@gnu.org>
|
||||
|
||||
|
||||
2
doc/.gitignore
vendored
2
doc/.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
genfile.texi
|
||||
getdate.texi
|
||||
header.texi
|
||||
manual
|
||||
parse-datetime.texi
|
||||
stamp-vti
|
||||
tar.aux
|
||||
tar.cp
|
||||
|
||||
@@ -24,9 +24,9 @@ tar_TEXINFOS = \
|
||||
fdl.texi\
|
||||
freemanuals.texi\
|
||||
genfile.texi\
|
||||
getdate.texi\
|
||||
header.texi\
|
||||
intern.texi\
|
||||
parse-datetime.texi\
|
||||
rendition.texi\
|
||||
snapshot.texi\
|
||||
sparse.texi\
|
||||
@@ -124,7 +124,7 @@ check-unrevised:
|
||||
|
||||
all-check-docs: check-format check-options check-refs check-fixmes check-unrevised
|
||||
|
||||
check-docs:
|
||||
check-docs:
|
||||
$(MAKE) -k all-check-docs
|
||||
|
||||
#
|
||||
@@ -144,4 +144,3 @@ manual:
|
||||
MAKEINFO="$(MAKEINFO) $(MAKEINFOFLAGS)" \
|
||||
TEXI2DVI="$(TEXI2DVI) -t @finalout" \
|
||||
$(GENDOCS) --texi2html tar 'GNU tar manual'
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ dumped directory in special data blocks called @dfn{dumpdirs}.
|
||||
@noindent
|
||||
where @var{C} is one of the @dfn{control codes} described below,
|
||||
@var{filename} is the name of the file @var{C} operates upon, and
|
||||
@samp{\0} represents a nul character (ASCII 0). The white space
|
||||
@samp{\0} represents a nul character (ASCII 0). The white space
|
||||
characters were added for readability, real dumpdirs do not contain
|
||||
them.
|
||||
|
||||
@@ -68,7 +68,7 @@ directory}. For example, consider the following scenario:
|
||||
@enumerate 1
|
||||
@item
|
||||
Previous run dumped a directory @file{foo} which contained the
|
||||
following three directories:
|
||||
following three directories:
|
||||
|
||||
@smallexample
|
||||
a
|
||||
@@ -93,7 +93,7 @@ New incremental dump was made.
|
||||
renaming @file{a} to @file{b} will destroy the existing directory.
|
||||
To correctly process it, @GNUTAR{} needs a temporary directory, so
|
||||
it creates the following dumpdir (newlines have been added for
|
||||
readability):
|
||||
readability):
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
@@ -114,7 +114,7 @@ work as usual, and, finally, the last command, @samp{R\0Tfoo/a\0}
|
||||
tells tar to rename the temporary directory to @file{foo/a}.
|
||||
|
||||
The exact placement of a dumpdir in the archive depends on the
|
||||
archive format (@pxref{Formats}):
|
||||
archive format (@pxref{Formats}):
|
||||
|
||||
@itemize
|
||||
@item PAX archives
|
||||
|
||||
110
doc/fdl.texi
110
doc/fdl.texi
@@ -1,13 +1,12 @@
|
||||
@c The GNU Free Documentation License.
|
||||
@center Version 1.3, 3 November 2008
|
||||
|
||||
@node GNU Free Documentation License
|
||||
@appendixsec GNU Free Documentation License
|
||||
|
||||
@cindex FDL, GNU Free Documentation License
|
||||
@center Version 1.2, November 2002
|
||||
@c This file is intended to be included within another document,
|
||||
@c hence no sectioning command or @node.
|
||||
|
||||
@display
|
||||
Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
|
||||
@uref{http://fsf.org/}
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
@@ -93,16 +92,16 @@ An image format is not Transparent if used for any substantial amount
|
||||
of text. A copy that is not ``Transparent'' is called ``Opaque''.
|
||||
|
||||
Examples of suitable formats for Transparent copies include plain
|
||||
@sc{ascii} without markup, Texinfo input format, La@TeX{} input
|
||||
format, @acronym{SGML} or @acronym{XML} using a publicly available
|
||||
@acronym{DTD}, and standard-conforming simple @acronym{HTML},
|
||||
PostScript or @acronym{PDF} designed for human modification. Examples
|
||||
of transparent image formats include @acronym{PNG}, @acronym{XCF} and
|
||||
@acronym{JPG}. Opaque formats include proprietary formats that can be
|
||||
read and edited only by proprietary word processors, @acronym{SGML} or
|
||||
@acronym{XML} for which the @acronym{DTD} and/or processing tools are
|
||||
not generally available, and the machine-generated @acronym{HTML},
|
||||
PostScript or @acronym{PDF} produced by some word processors for
|
||||
ASCII without markup, Texinfo input format, La@TeX{} input
|
||||
format, SGML or XML using a publicly available
|
||||
DTD, and standard-conforming simple HTML,
|
||||
PostScript or PDF designed for human modification. Examples
|
||||
of transparent image formats include PNG, XCF and
|
||||
JPG. Opaque formats include proprietary formats that can be
|
||||
read and edited only by proprietary word processors, SGML or
|
||||
XML for which the DTD and/or processing tools are
|
||||
not generally available, and the machine-generated HTML,
|
||||
PostScript or PDF produced by some word processors for
|
||||
output purposes only.
|
||||
|
||||
The ``Title Page'' means, for a printed book, the title page itself,
|
||||
@@ -112,6 +111,9 @@ formats which do not have any title page as such, ``Title Page'' means
|
||||
the text near the most prominent appearance of the work's title,
|
||||
preceding the beginning of the body of the text.
|
||||
|
||||
The ``publisher'' means any person or entity that distributes copies
|
||||
of the Document to the public.
|
||||
|
||||
A section ``Entitled XYZ'' means a named subunit of the Document whose
|
||||
title either is precisely XYZ or contains XYZ in parentheses following
|
||||
text that translates XYZ in another language. (Here XYZ stands for a
|
||||
@@ -380,13 +382,30 @@ title.
|
||||
@item
|
||||
TERMINATION
|
||||
|
||||
You may not copy, modify, sublicense, or distribute the Document except
|
||||
as expressly provided for under this License. Any other attempt to
|
||||
copy, modify, sublicense or distribute the Document is void, and will
|
||||
automatically terminate your rights under this License. However,
|
||||
parties who have received copies, or rights, from you under this
|
||||
License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
You may not copy, modify, sublicense, or distribute the Document
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense, or distribute it is void, and
|
||||
will automatically terminate your rights under this License.
|
||||
|
||||
However, if you cease all violation of this License, then your license
|
||||
from a particular copyright holder is reinstated (a) provisionally,
|
||||
unless and until the copyright holder explicitly and finally
|
||||
terminates your license, and (b) permanently, if the copyright holder
|
||||
fails to notify you of the violation by some reasonable means prior to
|
||||
60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, receipt of a copy of some or all of the same material does
|
||||
not give you any rights to use it.
|
||||
|
||||
@item
|
||||
FUTURE REVISIONS OF THIS LICENSE
|
||||
@@ -404,11 +423,46 @@ following the terms and conditions either of that specified version or
|
||||
of any later version that has been published (not as a draft) by the
|
||||
Free Software Foundation. If the Document does not specify a version
|
||||
number of this License, you may choose any version ever published (not
|
||||
as a draft) by the Free Software Foundation.
|
||||
as a draft) by the Free Software Foundation. If the Document
|
||||
specifies that a proxy can decide which future versions of this
|
||||
License can be used, that proxy's public statement of acceptance of a
|
||||
version permanently authorizes you to choose that version for the
|
||||
Document.
|
||||
|
||||
@item
|
||||
RELICENSING
|
||||
|
||||
``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any
|
||||
World Wide Web server that publishes copyrightable works and also
|
||||
provides prominent facilities for anybody to edit those works. A
|
||||
public wiki that anybody can edit is an example of such a server. A
|
||||
``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the
|
||||
site means any set of copyrightable works thus published on the MMC
|
||||
site.
|
||||
|
||||
``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0
|
||||
license published by Creative Commons Corporation, a not-for-profit
|
||||
corporation with a principal place of business in San Francisco,
|
||||
California, as well as future copyleft versions of that license
|
||||
published by that same organization.
|
||||
|
||||
``Incorporate'' means to publish or republish a Document, in whole or
|
||||
in part, as part of another Document.
|
||||
|
||||
An MMC is ``eligible for relicensing'' if it is licensed under this
|
||||
License, and if all works that were first published under this License
|
||||
somewhere other than this MMC, and subsequently incorporated in whole
|
||||
or in part into the MMC, (1) had no cover texts or invariant sections,
|
||||
and (2) were thus incorporated prior to November 1, 2008.
|
||||
|
||||
The operator of an MMC Site may republish an MMC contained in the site
|
||||
under CC-BY-SA on the same site at any time before August 1, 2009,
|
||||
provided the MMC is eligible for relicensing.
|
||||
|
||||
@end enumerate
|
||||
|
||||
@page
|
||||
@appendixsubsec ADDENDUM: How to use this License for your documents
|
||||
@heading ADDENDUM: How to use this License for your documents
|
||||
|
||||
To use this License in a document you have written, include a copy of
|
||||
the License in the document and put the following copyright and
|
||||
@@ -418,7 +472,7 @@ license notices just after the title page:
|
||||
@group
|
||||
Copyright (C) @var{year} @var{your name}.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.2
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
|
||||
Texts. A copy of the license is included in the section entitled ``GNU
|
||||
@@ -427,7 +481,7 @@ license notices just after the title page:
|
||||
@end smallexample
|
||||
|
||||
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
|
||||
replace the ``with...Texts.'' line with this:
|
||||
replace the ``with@dots{}Texts.'' line with this:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<p>The manual for %%PACKAGE%% is available in the following formats:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="%%PACKAGE%%.html">HTML
|
||||
<li><a href="%%PACKAGE%%.html">HTML
|
||||
(%%HTML_MONO_SIZE%%K bytes)</a> - entirely on one web page.</li>
|
||||
<li><a href="html_node/index.html">HTML</a> - with one web page per
|
||||
node.</li>
|
||||
@@ -49,7 +49,7 @@
|
||||
chapter.</li>
|
||||
%%ENDIF HTML_CHAPTER%%
|
||||
<li><a href="%%PACKAGE%%.html.gz">HTML compressed
|
||||
(%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
|
||||
(%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
|
||||
one web page.</li>
|
||||
<li><a href="%%PACKAGE%%.html_node.tar.gz">HTML compressed
|
||||
(%%HTML_NODE_TGZ_SIZE%%K gzipped tar file)</a> -
|
||||
@@ -96,9 +96,9 @@ Return to the <a href="/home.html">GNU Project home page</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Please send FSF & GNU inquiries to
|
||||
Please send FSF & GNU inquiries to
|
||||
<a href="mailto:gnu@gnu.org"><em>gnu@gnu.org</em></a>.
|
||||
There are also <a href="/home.html#ContactInfo">other ways to contact</a>
|
||||
There are also <a href="/home.html#ContactInfo">other ways to contact</a>
|
||||
the FSF.
|
||||
<br />
|
||||
Please send broken links and other corrections (or suggestions) to
|
||||
|
||||
@@ -107,10 +107,10 @@ group permission could be copied from the @emph{other} permission.
|
||||
The @code{uid} and @code{gid} fields are the numeric user and group
|
||||
@acronym{ID} of the file owners, respectively. If the operating system does
|
||||
not support numeric user or group @acronym{ID}s, these fields should
|
||||
be ignored.
|
||||
be ignored.
|
||||
|
||||
The @code{size} field is the size of the file in bytes; linked files
|
||||
are archived with this field specified as zero.
|
||||
are archived with this field specified as zero.
|
||||
|
||||
The @code{mtime} field is the data modification time of the file at
|
||||
the time it was archived. It is the ASCII representation of the octal
|
||||
@@ -330,4 +330,3 @@ checksum error.
|
||||
@node Dumpdir
|
||||
@unnumberedsec Dumpdir
|
||||
@include dumpdir.texi
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
;; This file redefines texinfo-master-menu-list so that it takes into
|
||||
;; account included files.
|
||||
|
||||
;; Known bugs: @menu without previous sectioning command will inherit
|
||||
;; Known bugs: @menu without previous sectioning command will inherit
|
||||
;; documentation string from the previous menu. However, since such a
|
||||
;; menu is illegal in a texinfo file, we can live with it.
|
||||
|
||||
(require 'texinfo)
|
||||
(require 'texinfo)
|
||||
(require 'texnfo-upd)
|
||||
|
||||
(defun texinfo-master-menu-list-recursive (title)
|
||||
|
||||
@@ -137,4 +137,3 @@ previous versions it is not quoted;
|
||||
@end enumerate
|
||||
|
||||
@c End of snapshot.texi
|
||||
|
||||
|
||||
@@ -232,4 +232,3 @@ into a separate directory. Then, using a simple program it would be
|
||||
possible to expand the file to its original form even without @GNUTAR{}.
|
||||
@xref{Sparse Recovery}, for the detailed information on how to extract
|
||||
sparse members without @GNUTAR{}.
|
||||
|
||||
|
||||
@@ -55,4 +55,3 @@ file version 2
|
||||
$ @kbd{tar-snapshot-edit -b -r 0x0306-0x4500 /var/backup/snap.a}
|
||||
file version 2
|
||||
@end smallexample
|
||||
|
||||
|
||||
864
doc/tar.texi
864
doc/tar.texi
File diff suppressed because it is too large
Load Diff
@@ -18,5 +18,3 @@
|
||||
@end ifclear
|
||||
@xopindex{\option\, summary}
|
||||
@end macro
|
||||
|
||||
|
||||
|
||||
@@ -6,19 +6,19 @@ argmatch
|
||||
argp
|
||||
argp-version-etc
|
||||
backupfile
|
||||
canonicalize
|
||||
closeout
|
||||
configmake
|
||||
dirname
|
||||
error
|
||||
exclude
|
||||
exitfail
|
||||
fdopendir
|
||||
fdutimensat
|
||||
fileblocks
|
||||
fnmatch-gnu
|
||||
fseeko
|
||||
ftruncate
|
||||
full-write
|
||||
getdate
|
||||
futimens
|
||||
getline
|
||||
getopt-gnu
|
||||
getpagesize
|
||||
@@ -27,19 +27,25 @@ gettime
|
||||
gitlog-to-changelog
|
||||
hash
|
||||
human
|
||||
inttostr
|
||||
inttypes
|
||||
lchown
|
||||
linkat
|
||||
localcharset
|
||||
mkdtemp
|
||||
mkfifoat
|
||||
modechange
|
||||
obstack
|
||||
openat
|
||||
parse-datetime
|
||||
priv-set
|
||||
progname
|
||||
quote
|
||||
quotearg
|
||||
readlinkat
|
||||
renameat
|
||||
rpmatch
|
||||
safe-read
|
||||
save-cwd
|
||||
savedir
|
||||
setenv
|
||||
snprintf
|
||||
@@ -51,10 +57,11 @@ strdup-posix
|
||||
strerror
|
||||
strtol
|
||||
strtoul
|
||||
symlinkat
|
||||
timespec
|
||||
unlinkdir
|
||||
unlocked-io
|
||||
utimens
|
||||
utimensat
|
||||
version-etc-fsf
|
||||
xalloc
|
||||
xalloc-die
|
||||
|
||||
@@ -26,14 +26,13 @@ rmt-command.h : Makefile
|
||||
$(AM_V_at)mv $@-t $@
|
||||
BUILT_SOURCES = rmt-command.h
|
||||
CLEANFILES = rmt-command.h rmt-command.h-t
|
||||
INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu
|
||||
INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu
|
||||
|
||||
noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h
|
||||
libtar_a_SOURCES = \
|
||||
paxerror.c paxexit.c paxlib.h paxnames.c \
|
||||
paxerror.c paxexit-status.c paxlib.h paxnames.c \
|
||||
prepargs.c prepargs.h \
|
||||
rtapelib.c \
|
||||
rmt.h \
|
||||
stdopen.c stdopen.h \
|
||||
system.h system-ioctl.h
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ gnu/version-etc.c
|
||||
gnu/xalloc-die.c
|
||||
|
||||
lib/paxerror.c
|
||||
lib/paxexit.c
|
||||
lib/paxnames.c
|
||||
lib/rtapelib.c
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ XLIST=exclude_files
|
||||
SLEEP_TIME=15
|
||||
|
||||
# Script to be run when it's time to insert a new tape in for the next
|
||||
# volume. Administrators may want to tailor this script for their site.
|
||||
# volume. Administrators may want to tailor this script for their site.
|
||||
# If this variable isn't set, tar will use some default behavior which is
|
||||
# probably defined in the manual.
|
||||
# probably defined in the manual.
|
||||
#DUMP_REMIND_SCRIPT='rsh apple-gunkies /home/gd2/dump/dump-remind'
|
||||
|
||||
# Message to display on the terminal while waiting for dump time. Usually
|
||||
@@ -75,7 +75,7 @@ SLEEP_TIME=15
|
||||
# entertaining than this. The awk script here saves some redundant
|
||||
# repetition, but is not really all that desirable.
|
||||
SLEEP_MESSAGE="`awk '
|
||||
BEGIN {
|
||||
BEGIN {
|
||||
for (i = 0; i < 30; i++)
|
||||
print \" \" \
|
||||
\"D O N O T T O U C H T H I S T E R M I N A L !!!!!\"
|
||||
|
||||
@@ -72,8 +72,9 @@ do
|
||||
--l=*|--le=*|--lev=*|--leve=*|--level=*)
|
||||
DUMP_LEVEL=$optarg
|
||||
;;
|
||||
-l?*) DUMP_LEVEL=`expr $option : '-l\(.*\)'`;;
|
||||
-l|--l|--le|--lev|--leve|--level)
|
||||
prev=$option
|
||||
prev=--level
|
||||
;;
|
||||
--verb=*|--verbo=*|--verbos=*|--verbose=*)
|
||||
VERBOSE=$optarg
|
||||
@@ -81,14 +82,13 @@ do
|
||||
-v|--verb|--verbo|--verbos|--verbose)
|
||||
VERBOSE=100
|
||||
;;
|
||||
-v*) VERBOSE=`expr $option : "-v\(.*\)"`;;
|
||||
-v*) VERBOSE=`expr $option : '-v\(.*\)'`;;
|
||||
--t=*|--ti=*|--tim=*|--time=*)
|
||||
TIME=$optarg
|
||||
;;
|
||||
-t) prev=--t;;
|
||||
-t*) TIME=`expr $option : "-t\(.*\)"`;;
|
||||
--t|--ti|--tim|--time)
|
||||
prev=$option
|
||||
-t?*) TIME=`expr $option : '-t\(.*\)'`;;
|
||||
-t|--t|--ti|--tim|--time)
|
||||
prev=--time
|
||||
;;
|
||||
-V|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "backup (@PACKAGE_NAME@) @VERSION@"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# This file is included in the GNU tar distribution as an example. It is
|
||||
# not used by default unless the proper line is uncommented in backup-specs.
|
||||
# System administrators will probably want to customize this and
|
||||
# backup-specs for their site.
|
||||
# backup-specs for their site.
|
||||
#
|
||||
# This script should be run by tar with --info-script (-F) to inform
|
||||
# interested parties that a tape for the next volume of the backup needs to
|
||||
# be put in the tape drive.
|
||||
# be put in the tape drive.
|
||||
#
|
||||
|
||||
# Include location of `sendmail' and GNU finger.
|
||||
# Include location of `sendmail' and GNU finger.
|
||||
PATH="/usr/lib:/usr/local/gnubin:${PATH}"
|
||||
export PATH
|
||||
|
||||
@@ -27,7 +27,7 @@ MT_OFFLINE
|
||||
#
|
||||
# Certain users (like `root') aren't real users, and shouldn't be notified.
|
||||
# Neither should `zippy', `elvis', etc. (on the GNU machines) since they're
|
||||
# just test accounts.
|
||||
# just test accounts.
|
||||
recipients="`
|
||||
finger .clients 2> /dev/null \
|
||||
| sed -ne '
|
||||
@@ -69,8 +69,8 @@ Cc: ${ADMINISTRATOR}
|
||||
Subject: Backup needs new tape for volume ${TAR_VOLUME}
|
||||
Reply-To: ${ADMINISTRATOR}
|
||||
|
||||
This is an automated report from the backup script running on
|
||||
`hostname`.
|
||||
This is an automated report from the backup script running on
|
||||
`hostname`.
|
||||
|
||||
Volume ${TAR_VOLUME} of the backup needs to be put in the tape drive.
|
||||
Usually whoever prepared the backup leaves labeled tapes on top of the
|
||||
@@ -92,8 +92,8 @@ Cc: ${ADMINISTRATOR}
|
||||
Subject: Volume ${TAR_VOLUME} for backup has been added
|
||||
Reply-To: ${ADMINISTRATOR}
|
||||
|
||||
This is an automated report from the backup script running on
|
||||
`hostname`.
|
||||
This is an automated report from the backup script running on
|
||||
`hostname`.
|
||||
|
||||
The backup has been continued, so for now no further attention is required.
|
||||
__EOF__
|
||||
|
||||
@@ -60,8 +60,9 @@ do
|
||||
--l=*|--le=*|--lev=*|--leve=*|--level=*)
|
||||
DUMP_LEVEL=$optarg
|
||||
;;
|
||||
-l?*) DUMP_LEVEL=`expr $option : '-l\(.*\)'`;;
|
||||
-l|--l|--le|--lev|--leve|--level)
|
||||
prev=$option
|
||||
prev=--level
|
||||
;;
|
||||
--verb=*|--verbo=*|--verbos=*|--verbose=*)
|
||||
VERBOSE=$optarg
|
||||
@@ -69,7 +70,7 @@ do
|
||||
-v|--verb|--verbo|--verbos|--verbose)
|
||||
VERBOSE=100
|
||||
;;
|
||||
-v*) VERBOSE=`expr $option : "-v\(.*\)"`;;
|
||||
-v*) VERBOSE=`expr $option : '-v\(.*\)'`;;
|
||||
-V|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "restore (@PACKAGE_NAME@) @VERSION@"
|
||||
license
|
||||
|
||||
@@ -211,7 +211,7 @@ sub write_incr_db ($$) {
|
||||
sub write_incr_db_0 ($$) {
|
||||
my $info = shift;
|
||||
my $file = shift;
|
||||
|
||||
|
||||
my $timestamp_sec = $info->[1];
|
||||
print $file "$timestamp_sec\n";
|
||||
|
||||
@@ -226,7 +226,7 @@ sub write_incr_db_0 ($$) {
|
||||
sub write_incr_db_1 ($$) {
|
||||
my $info = shift;
|
||||
my $file = shift;
|
||||
|
||||
|
||||
print $file "GNU tar-1.15-1\n";
|
||||
|
||||
my $timestamp_sec = $info->[1];
|
||||
@@ -246,7 +246,7 @@ sub write_incr_db_1 ($$) {
|
||||
sub write_incr_db_2 ($$) {
|
||||
my $info = shift;
|
||||
my $file = shift;
|
||||
|
||||
|
||||
print $file "GNU tar-1.16-2\n";
|
||||
|
||||
my $timestamp_sec = $info->[1];
|
||||
|
||||
@@ -35,8 +35,7 @@ do
|
||||
fi
|
||||
if [ "$T" = "M" ]; then
|
||||
SKIP=$(($SKIP + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
dd skip=$SKIP if="$f"
|
||||
dd skip=$SKIP if="$f"
|
||||
done
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* xsparse - expands compressed sparse file images extracted from GNU tar
|
||||
archives.
|
||||
|
||||
Copyright (C) 2006, 2007 Free Software Foundation, Inc.
|
||||
Copyright (C) 2006, 2007, 2010 Free Software Foundation, Inc.
|
||||
|
||||
Written by Sergey Poznyakoff
|
||||
|
||||
|
||||
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, or (at your option) any later
|
||||
@@ -20,6 +20,7 @@
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
@@ -39,7 +40,7 @@
|
||||
struct sp_array
|
||||
{
|
||||
off_t offset;
|
||||
size_t numbytes;
|
||||
off_t numbytes;
|
||||
};
|
||||
|
||||
char *progname;
|
||||
@@ -129,12 +130,12 @@ get_var (FILE *fp, char **name, char **value)
|
||||
static char *buffer;
|
||||
static size_t bufsize = OFF_T_STRSIZE_BOUND;
|
||||
char *p, *q;
|
||||
|
||||
|
||||
buffer = emalloc (bufsize);
|
||||
do
|
||||
{
|
||||
size_t len, s;
|
||||
|
||||
|
||||
if (!fgets (buffer, bufsize, fp))
|
||||
return 0;
|
||||
len = strlen (buffer);
|
||||
@@ -186,12 +187,12 @@ read_xheader (char *name)
|
||||
|
||||
if (verbose)
|
||||
printf ("Reading extended header file\n");
|
||||
|
||||
|
||||
while (get_var (fp, &kw, &val))
|
||||
{
|
||||
if (verbose)
|
||||
printf ("Found variable GNU.sparse.%s = %s\n", kw, val);
|
||||
|
||||
|
||||
if (expect && strcmp (kw, expect))
|
||||
die (1, "bad keyword sequence: expected `%s' but found `%s'",
|
||||
expect, kw);
|
||||
@@ -226,7 +227,7 @@ read_xheader (char *name)
|
||||
}
|
||||
else if (strcmp (kw, "numbytes") == 0)
|
||||
{
|
||||
sparse_map[i++].numbytes = string_to_size (val, NULL);
|
||||
sparse_map[i++].numbytes = string_to_off (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "map") == 0)
|
||||
{
|
||||
@@ -236,7 +237,7 @@ read_xheader (char *name)
|
||||
if (*val != ',')
|
||||
die (1, "bad GNU.sparse.map: expected `,' but found `%c'",
|
||||
*val);
|
||||
sparse_map[i].numbytes = string_to_size (val+1, &val);
|
||||
sparse_map[i].numbytes = string_to_off (val+1, &val);
|
||||
if (*val != ',')
|
||||
{
|
||||
if (!(*val == 0 && i == sparse_map_size-1))
|
||||
@@ -267,7 +268,7 @@ read_map (FILE *ifp)
|
||||
|
||||
if (verbose)
|
||||
printf ("Reading v.1.0 sparse map\n");
|
||||
|
||||
|
||||
get_line (nbuf, sizeof nbuf, ifp);
|
||||
sparse_map_size = string_to_size (nbuf, NULL);
|
||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||
@@ -277,31 +278,34 @@ read_map (FILE *ifp)
|
||||
get_line (nbuf, sizeof nbuf, ifp);
|
||||
sparse_map[i].offset = string_to_off (nbuf, NULL);
|
||||
get_line (nbuf, sizeof nbuf, ifp);
|
||||
sparse_map[i].numbytes = string_to_size (nbuf, NULL);
|
||||
sparse_map[i].numbytes = string_to_off (nbuf, NULL);
|
||||
}
|
||||
|
||||
fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
|
||||
SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
expand_sparse (FILE *sfp, int ofd)
|
||||
{
|
||||
size_t i;
|
||||
size_t maxbytes = 0;
|
||||
off_t max_numbytes = 0;
|
||||
size_t maxbytes;
|
||||
char *buffer;
|
||||
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
if (maxbytes < sparse_map[i].numbytes)
|
||||
maxbytes = sparse_map[i].numbytes;
|
||||
|
||||
if (max_numbytes < sparse_map[i].numbytes)
|
||||
max_numbytes = sparse_map[i].numbytes;
|
||||
|
||||
maxbytes = max_numbytes < SIZE_MAX ? max_numbytes : SIZE_MAX;
|
||||
|
||||
for (buffer = malloc (maxbytes); !buffer; maxbytes /= 2)
|
||||
if (maxbytes == 0)
|
||||
die (1, "not enough memory");
|
||||
|
||||
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
{
|
||||
size_t size = sparse_map[i].numbytes;
|
||||
off_t size = sparse_map[i].numbytes;
|
||||
|
||||
if (size == 0)
|
||||
ftruncate (ofd, sparse_map[i].offset);
|
||||
@@ -342,13 +346,13 @@ guess_outname (char *name)
|
||||
{
|
||||
char *p;
|
||||
char *s;
|
||||
|
||||
|
||||
if (name[0] == '.' && name[1] == '/')
|
||||
name += 2;
|
||||
|
||||
p = name + strlen (name) - 1;
|
||||
s = NULL;
|
||||
|
||||
|
||||
for (; p > name && *p != '/'; p--)
|
||||
;
|
||||
if (*p == '/')
|
||||
@@ -358,7 +362,7 @@ guess_outname (char *name)
|
||||
for (p--; p > name && *p != '/'; p--)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
if (*p != '/')
|
||||
{
|
||||
if (s)
|
||||
@@ -389,7 +393,7 @@ main (int argc, char **argv)
|
||||
FILE *ifp;
|
||||
struct stat st;
|
||||
int ofd;
|
||||
|
||||
|
||||
progname = argv[0];
|
||||
while ((c = getopt (argc, argv, "hnvx:")) != EOF)
|
||||
{
|
||||
@@ -408,7 +412,7 @@ main (int argc, char **argv)
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
exit (1);
|
||||
}
|
||||
@@ -429,17 +433,17 @@ main (int argc, char **argv)
|
||||
|
||||
if (stat (inname, &st))
|
||||
die (1, "cannot stat %s (%d)", inname, errno);
|
||||
|
||||
|
||||
ifp = fopen (inname, "r");
|
||||
if (ifp == NULL)
|
||||
die (1, "cannot open file %s (%d)", inname, errno);
|
||||
|
||||
|
||||
if (!xheader_file || version_major == 1)
|
||||
read_map (ifp);
|
||||
|
||||
if (!outname)
|
||||
guess_outname (inname);
|
||||
|
||||
|
||||
ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
|
||||
if (ofd == -1)
|
||||
die (1, "cannot open file %s (%d)", outname, errno);
|
||||
@@ -452,7 +456,7 @@ main (int argc, char **argv)
|
||||
printf ("Finished dry run\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
expand_sparse (ifp, ofd);
|
||||
|
||||
fclose (ifp);
|
||||
@@ -460,7 +464,7 @@ main (int argc, char **argv)
|
||||
|
||||
if (verbose)
|
||||
printf ("Done\n");
|
||||
|
||||
|
||||
if (outsize)
|
||||
{
|
||||
if (stat (outname, &st))
|
||||
@@ -468,7 +472,6 @@ main (int argc, char **argv)
|
||||
if (st.st_size != outsize)
|
||||
die (1, "expanded file has wrong size");
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
blocked in 1 kB boundaries. We'll need arbitrary precision
|
||||
arithmetic anyway once we get into the 2**64 range, so there's no
|
||||
point doing anything fancy before then. */
|
||||
|
||||
|
||||
#define TARLONG_FORMAT "%.0f"
|
||||
typedef double tarlong;
|
||||
|
||||
444
src/buffer.c
444
src/buffer.c
@@ -54,7 +54,7 @@ enum access_mode access_mode; /* how do we handle the archive */
|
||||
off_t records_read; /* number of records read from this archive */
|
||||
off_t records_written; /* likewise, for records written */
|
||||
extern off_t records_skipped; /* number of records skipped at the start
|
||||
of the archive, defined in delete.c */
|
||||
of the archive, defined in delete.c */
|
||||
|
||||
static off_t record_start_block; /* block ordinal at record_start */
|
||||
|
||||
@@ -77,7 +77,6 @@ static bool read_full_records = false;
|
||||
/* We're reading, but we just read the last block and it's time to update.
|
||||
Declared in update.c
|
||||
|
||||
As least EXTERN like this one as possible. (?? --gray)
|
||||
FIXME: Either eliminate it or move it to common.h.
|
||||
*/
|
||||
extern bool time_to_start_writing;
|
||||
@@ -101,19 +100,94 @@ static int global_volno = 1; /* volume number to print in external
|
||||
|
||||
bool write_archive_to_stdout;
|
||||
|
||||
/* Used by flush_read and flush_write to store the real info about saved
|
||||
names. */
|
||||
static char *real_s_name;
|
||||
static off_t real_s_totsize;
|
||||
static off_t real_s_sizeleft;
|
||||
|
||||
|
||||
/* Multi-volume tracking support */
|
||||
static char *save_name; /* name of the file we are currently writing */
|
||||
static off_t save_totsize; /* total size of file we are writing, only
|
||||
valid if save_name is nonzero */
|
||||
static off_t save_sizeleft; /* where we are in the file we are writing,
|
||||
only valid if save_name is nonzero */
|
||||
|
||||
/* When creating a multi-volume archive, each `bufmap' represents
|
||||
a member stored (perhaps partly) in the current record buffer.
|
||||
After flushing the record to the output media, all bufmaps that
|
||||
represent fully written members are removed from the list, then
|
||||
the sizeleft and start numbers in the remaining bufmaps are updated.
|
||||
|
||||
When reading from a multi-volume archive, the list degrades to a
|
||||
single element, which keeps information about the member currently
|
||||
being read.
|
||||
*/
|
||||
|
||||
struct bufmap
|
||||
{
|
||||
struct bufmap *next; /* Pointer to the next map entry */
|
||||
size_t start; /* Offset of the first data block */
|
||||
char *file_name; /* Name of the stored file */
|
||||
off_t sizetotal; /* Size of the stored file */
|
||||
off_t sizeleft; /* Size left to read/write */
|
||||
};
|
||||
static struct bufmap *bufmap_head, *bufmap_tail;
|
||||
|
||||
/* This variable, when set, inhibits updating the bufmap chain after
|
||||
a write. This is necessary when writing extended POSIX headers. */
|
||||
static int inhibit_map;
|
||||
|
||||
void
|
||||
mv_begin_write (const char *file_name, off_t totsize, off_t sizeleft)
|
||||
{
|
||||
if (multi_volume_option)
|
||||
{
|
||||
struct bufmap *bp = xmalloc (sizeof bp[0]);
|
||||
if (bufmap_tail)
|
||||
bufmap_tail->next = bp;
|
||||
else
|
||||
bufmap_head = bp;
|
||||
bufmap_tail = bp;
|
||||
|
||||
bp->next = NULL;
|
||||
bp->start = current_block - record_start;
|
||||
bp->file_name = xstrdup (file_name);
|
||||
bp->sizetotal = totsize;
|
||||
bp->sizeleft = sizeleft;
|
||||
}
|
||||
}
|
||||
|
||||
static struct bufmap *
|
||||
bufmap_locate (size_t off)
|
||||
{
|
||||
struct bufmap *map;
|
||||
|
||||
for (map = bufmap_head; map; map = map->next)
|
||||
{
|
||||
if (!map->next
|
||||
|| off < map->next->start * BLOCKSIZE)
|
||||
break;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static void
|
||||
bufmap_free (struct bufmap *mark)
|
||||
{
|
||||
struct bufmap *map;
|
||||
for (map = bufmap_head; map && map != mark; )
|
||||
{
|
||||
struct bufmap *next = map->next;
|
||||
free (map->file_name);
|
||||
free (map);
|
||||
map = next;
|
||||
}
|
||||
bufmap_head = map;
|
||||
if (!bufmap_head)
|
||||
bufmap_tail = bufmap_head;
|
||||
}
|
||||
|
||||
static void
|
||||
bufmap_reset (struct bufmap *map, ssize_t fixup)
|
||||
{
|
||||
bufmap_free (map);
|
||||
if (map)
|
||||
{
|
||||
for (; map; map = map->next)
|
||||
map->start += fixup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct tar_stat_info dummy;
|
||||
@@ -125,32 +199,23 @@ buffer_write_global_xheader ()
|
||||
}
|
||||
|
||||
void
|
||||
mv_begin (struct tar_stat_info *st)
|
||||
mv_begin_read (struct tar_stat_info *st)
|
||||
{
|
||||
if (multi_volume_option)
|
||||
{
|
||||
assign_string (&save_name, st->orig_file_name);
|
||||
save_totsize = save_sizeleft = st->stat.st_size;
|
||||
}
|
||||
mv_begin_write (st->orig_file_name, st->stat.st_size, st->stat.st_size);
|
||||
}
|
||||
|
||||
void
|
||||
mv_end ()
|
||||
{
|
||||
if (multi_volume_option)
|
||||
assign_string (&save_name, 0);
|
||||
}
|
||||
|
||||
void
|
||||
mv_total_size (off_t size)
|
||||
{
|
||||
save_totsize = size;
|
||||
bufmap_free (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mv_size_left (off_t size)
|
||||
{
|
||||
save_sizeleft = size;
|
||||
if (bufmap_head)
|
||||
bufmap_head->sizeleft = size;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,8 +240,8 @@ set_start_time ()
|
||||
last_stat_time = start_time;
|
||||
}
|
||||
|
||||
void
|
||||
set_volume_start_time ()
|
||||
static void
|
||||
set_volume_start_time (void)
|
||||
{
|
||||
gettime (&volume_start_time);
|
||||
last_stat_time = volume_start_time;
|
||||
@@ -196,8 +261,8 @@ compute_duration ()
|
||||
/* Compression detection */
|
||||
|
||||
enum compress_type {
|
||||
ct_tar, /* Plain tar file */
|
||||
ct_none, /* Unknown compression type */
|
||||
ct_tar, /* Plain tar file */
|
||||
ct_compress,
|
||||
ct_gzip,
|
||||
ct_bzip2,
|
||||
@@ -207,34 +272,105 @@ enum compress_type {
|
||||
ct_xz
|
||||
};
|
||||
|
||||
static enum compress_type archive_compression_type = ct_none;
|
||||
|
||||
struct zip_magic
|
||||
{
|
||||
enum compress_type type;
|
||||
size_t length;
|
||||
char *magic;
|
||||
char *program;
|
||||
char *option;
|
||||
char const *magic;
|
||||
};
|
||||
|
||||
struct zip_program
|
||||
{
|
||||
enum compress_type type;
|
||||
char const *program;
|
||||
char const *option;
|
||||
};
|
||||
|
||||
static struct zip_magic const magic[] = {
|
||||
{ ct_tar },
|
||||
{ ct_none, },
|
||||
{ ct_compress, 2, "\037\235", COMPRESS_PROGRAM, "-Z" },
|
||||
{ ct_gzip, 2, "\037\213", GZIP_PROGRAM, "-z" },
|
||||
{ ct_bzip2, 3, "BZh", BZIP2_PROGRAM, "-j" },
|
||||
{ ct_lzip, 4, "LZIP", LZIP_PROGRAM, "--lzip" },
|
||||
{ ct_lzma, 6, "\xFFLZMA", LZMA_PROGRAM, "--lzma" },
|
||||
{ ct_lzop, 4, "\211LZO", LZOP_PROGRAM, "--lzop" },
|
||||
{ ct_xz, 6, "\0xFD7zXZ", XZ_PROGRAM, "-J" },
|
||||
{ ct_tar },
|
||||
{ ct_compress, 2, "\037\235" },
|
||||
{ ct_gzip, 2, "\037\213" },
|
||||
{ ct_bzip2, 3, "BZh" },
|
||||
{ ct_lzip, 4, "LZIP" },
|
||||
{ ct_lzma, 6, "\xFFLZMA" },
|
||||
{ ct_lzop, 4, "\211LZO" },
|
||||
{ ct_xz, 6, "\xFD" "7zXZ" },
|
||||
};
|
||||
|
||||
#define NMAGIC (sizeof(magic)/sizeof(magic[0]))
|
||||
|
||||
#define compress_option(t) magic[t].option
|
||||
#define compress_program(t) magic[t].program
|
||||
static struct zip_program zip_program[] = {
|
||||
{ ct_compress, COMPRESS_PROGRAM, "-Z" },
|
||||
{ ct_compress, GZIP_PROGRAM, "-z" },
|
||||
{ ct_gzip, GZIP_PROGRAM, "-z" },
|
||||
{ ct_bzip2, BZIP2_PROGRAM, "-j" },
|
||||
{ ct_bzip2, "lbzip2", "-j" },
|
||||
{ ct_lzip, LZIP_PROGRAM, "--lzip" },
|
||||
{ ct_lzma, LZMA_PROGRAM, "--lzma" },
|
||||
{ ct_lzma, XZ_PROGRAM, "-J" },
|
||||
{ ct_lzop, LZOP_PROGRAM, "--lzop" },
|
||||
{ ct_xz, XZ_PROGRAM, "-J" },
|
||||
{ ct_none }
|
||||
};
|
||||
|
||||
static struct zip_program const *
|
||||
find_zip_program (enum compress_type type, int *pstate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = *pstate; zip_program[i].type != ct_none; i++)
|
||||
{
|
||||
if (zip_program[i].type == type)
|
||||
{
|
||||
*pstate = i + 1;
|
||||
return zip_program + i;
|
||||
}
|
||||
}
|
||||
*pstate = i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
first_decompress_program (int *pstate)
|
||||
{
|
||||
struct zip_program const *zp;
|
||||
|
||||
if (use_compress_program_option)
|
||||
return use_compress_program_option;
|
||||
|
||||
if (archive_compression_type == ct_none)
|
||||
return NULL;
|
||||
|
||||
*pstate = 0;
|
||||
zp = find_zip_program (archive_compression_type, pstate);
|
||||
return zp ? zp->program : NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
next_decompress_program (int *pstate)
|
||||
{
|
||||
struct zip_program const *zp;
|
||||
|
||||
if (use_compress_program_option)
|
||||
return NULL;
|
||||
zp = find_zip_program (archive_compression_type, pstate);
|
||||
return zp ? zp->program : NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
compress_option (enum compress_type type)
|
||||
{
|
||||
struct zip_program const *zp;
|
||||
int i = 0;
|
||||
zp = find_zip_program (type, &i);
|
||||
return zp ? zp->option : NULL;
|
||||
}
|
||||
|
||||
/* Check if the file ARCHIVE is a compressed archive. */
|
||||
enum compress_type
|
||||
static enum compress_type
|
||||
check_compressed_archive (bool *pshort)
|
||||
{
|
||||
struct zip_magic const *p;
|
||||
@@ -243,14 +379,14 @@ check_compressed_archive (bool *pshort)
|
||||
|
||||
if (!pshort)
|
||||
pshort = &temp;
|
||||
|
||||
|
||||
/* Prepare global data needed for find_next_block: */
|
||||
record_end = record_start; /* set up for 1st record = # 0 */
|
||||
sfr = read_full_records;
|
||||
read_full_records = true; /* Suppress fatal error on reading a partial
|
||||
record */
|
||||
*pshort = find_next_block () == 0;
|
||||
|
||||
|
||||
/* Restore global values */
|
||||
read_full_records = sfr;
|
||||
|
||||
@@ -267,7 +403,7 @@ check_compressed_archive (bool *pshort)
|
||||
|
||||
/* Guess if the archive is seekable. */
|
||||
static void
|
||||
guess_seekable_archive ()
|
||||
guess_seekable_archive (void)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
@@ -288,7 +424,7 @@ guess_seekable_archive ()
|
||||
seekable_archive = !!seek_option;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!multi_volume_option && !use_compress_program_option
|
||||
&& fstat (archive, &st) == 0)
|
||||
seekable_archive = S_ISREG (st.st_mode);
|
||||
@@ -299,8 +435,8 @@ guess_seekable_archive ()
|
||||
/* Open an archive named archive_name_array[0]. Detect if it is
|
||||
a compressed archive of known type and use corresponding decompression
|
||||
program if so */
|
||||
int
|
||||
open_compressed_archive ()
|
||||
static int
|
||||
open_compressed_archive (void)
|
||||
{
|
||||
archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
|
||||
MODE_RW, rsh_command_option);
|
||||
@@ -320,24 +456,24 @@ open_compressed_archive ()
|
||||
if (shortfile)
|
||||
ERROR ((0, 0, _("This does not look like a tar archive")));
|
||||
return archive;
|
||||
|
||||
|
||||
case ct_none:
|
||||
if (shortfile)
|
||||
ERROR ((0, 0, _("This does not look like a tar archive")));
|
||||
set_comression_program_by_suffix (archive_name_array[0], NULL);
|
||||
set_compression_program_by_suffix (archive_name_array[0], NULL);
|
||||
if (!use_compress_program_option)
|
||||
return archive;
|
||||
break;
|
||||
|
||||
default:
|
||||
use_compress_program_option = compress_program (type);
|
||||
archive_compression_type = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* FD is not needed any more */
|
||||
rmtclose (archive);
|
||||
|
||||
|
||||
hit_eof = false; /* It might have been set by find_next_block in
|
||||
check_compressed_archive */
|
||||
|
||||
@@ -486,7 +622,7 @@ xclose (int fd)
|
||||
}
|
||||
|
||||
static void
|
||||
init_buffer ()
|
||||
init_buffer (void)
|
||||
{
|
||||
if (! record_buffer_aligned[record_index])
|
||||
record_buffer_aligned[record_index] =
|
||||
@@ -511,8 +647,6 @@ _open_archive (enum access_mode wanted_access)
|
||||
FATAL_ERROR ((0, 0, _("No archive name given")));
|
||||
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
save_name = 0;
|
||||
real_s_name = 0;
|
||||
|
||||
record_index = 0;
|
||||
init_buffer ();
|
||||
@@ -657,7 +791,7 @@ _open_archive (enum access_mode wanted_access)
|
||||
}
|
||||
|
||||
/* Perform a write to flush the buffer. */
|
||||
ssize_t
|
||||
static ssize_t
|
||||
_flush_write (void)
|
||||
{
|
||||
ssize_t status;
|
||||
@@ -672,7 +806,21 @@ _flush_write (void)
|
||||
status = record_size;
|
||||
else
|
||||
status = sys_write_archive_buffer ();
|
||||
|
||||
|
||||
if (status && multi_volume_option && !inhibit_map)
|
||||
{
|
||||
struct bufmap *map = bufmap_locate (status);
|
||||
if (map)
|
||||
{
|
||||
size_t delta = status - map->start * BLOCKSIZE;
|
||||
if (delta > map->sizeleft)
|
||||
delta = map->sizeleft;
|
||||
map->sizeleft -= delta;
|
||||
if (map->sizeleft == 0)
|
||||
map = map->next;
|
||||
bufmap_reset (map, map ? (- map->start) : 0);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -713,7 +861,7 @@ archive_read_error (void)
|
||||
}
|
||||
|
||||
static bool
|
||||
archive_is_dev ()
|
||||
archive_is_dev (void)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
@@ -861,7 +1009,7 @@ seek_archive (off_t size)
|
||||
|
||||
if (size <= skipped)
|
||||
return 0;
|
||||
|
||||
|
||||
/* Compute number of records to skip */
|
||||
nrec = (size - skipped) / record_size;
|
||||
if (nrec == 0)
|
||||
@@ -907,12 +1055,9 @@ close_archive (void)
|
||||
sys_wait_for_child (child_pid, hit_eof);
|
||||
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
if (save_name)
|
||||
free (save_name);
|
||||
if (real_s_name)
|
||||
free (real_s_name);
|
||||
free (record_buffer[0]);
|
||||
free (record_buffer[1]);
|
||||
bufmap_free (NULL);
|
||||
}
|
||||
|
||||
/* Called to initialize the global volume number. */
|
||||
@@ -956,7 +1101,7 @@ closeout_volume_number (void)
|
||||
|
||||
|
||||
static void
|
||||
increase_volume_number ()
|
||||
increase_volume_number (void)
|
||||
{
|
||||
global_volno++;
|
||||
if (global_volno < 0)
|
||||
@@ -964,13 +1109,13 @@ increase_volume_number ()
|
||||
volno++;
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
change_tape_menu (FILE *read_file)
|
||||
{
|
||||
char *input_buffer = NULL;
|
||||
size_t size = 0;
|
||||
bool stop = false;
|
||||
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
fputc ('\007', stderr);
|
||||
@@ -1088,7 +1233,7 @@ new_volume (enum access_mode mode)
|
||||
assign_string (&continued_file_name, NULL);
|
||||
continued_file_size = continued_file_offset = 0;
|
||||
current_block = record_start;
|
||||
|
||||
|
||||
if (rmtclose (archive) != 0)
|
||||
close_error (*archive_name_cursor);
|
||||
|
||||
@@ -1177,13 +1322,13 @@ read_header0 (struct tar_stat_info *info)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
try_new_volume ()
|
||||
static bool
|
||||
try_new_volume (void)
|
||||
{
|
||||
size_t status;
|
||||
union block *header;
|
||||
enum access_mode acc;
|
||||
|
||||
|
||||
switch (subcommand_option)
|
||||
{
|
||||
case APPEND_SUBCOMMAND:
|
||||
@@ -1199,7 +1344,7 @@ try_new_volume ()
|
||||
|
||||
if (!new_volume (acc))
|
||||
return true;
|
||||
|
||||
|
||||
while ((status = rmtread (archive, record_start->buffer, record_size))
|
||||
== SAFE_READ_ERROR)
|
||||
archive_read_error ();
|
||||
@@ -1222,10 +1367,10 @@ try_new_volume ()
|
||||
ERROR ((0, 0, _("This does not look like a tar archive")));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
xheader_decode (&dummy); /* decodes values from the global header */
|
||||
tar_stat_destroy (&dummy);
|
||||
|
||||
|
||||
/* The initial global header must be immediately followed by
|
||||
an extended PAX header for the first member in this volume.
|
||||
However, in some cases tar may split volumes in the middle
|
||||
@@ -1237,7 +1382,7 @@ try_new_volume ()
|
||||
HEADER_FAILURE, which is ignored.
|
||||
|
||||
See also tests/multiv07.at */
|
||||
|
||||
|
||||
switch (read_header (&header, &dummy, read_header_auto))
|
||||
{
|
||||
case HEADER_SUCCESS:
|
||||
@@ -1280,30 +1425,30 @@ try_new_volume ()
|
||||
break;
|
||||
}
|
||||
|
||||
if (real_s_name)
|
||||
if (bufmap_head)
|
||||
{
|
||||
uintmax_t s;
|
||||
if (!continued_file_name
|
||||
|| strcmp (continued_file_name, real_s_name))
|
||||
|| strcmp (continued_file_name, bufmap_head->file_name))
|
||||
{
|
||||
if ((archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
|
||||
&& strlen (real_s_name) >= NAME_FIELD_SIZE
|
||||
&& strncmp (continued_file_name, real_s_name,
|
||||
&& strlen (bufmap_head->file_name) >= NAME_FIELD_SIZE
|
||||
&& strncmp (continued_file_name, bufmap_head->file_name,
|
||||
NAME_FIELD_SIZE) == 0)
|
||||
WARN ((0, 0,
|
||||
_("%s is possibly continued on this volume: header contains truncated name"),
|
||||
quote (real_s_name)));
|
||||
quote (bufmap_head->file_name)));
|
||||
else
|
||||
{
|
||||
WARN ((0, 0, _("%s is not continued on this volume"),
|
||||
quote (real_s_name)));
|
||||
quote (bufmap_head->file_name)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
s = continued_file_size + continued_file_offset;
|
||||
|
||||
if (real_s_totsize != s || s < continued_file_offset)
|
||||
if (bufmap_head->sizetotal != s || s < continued_file_offset)
|
||||
{
|
||||
char totsizebuf[UINTMAX_STRSIZE_BOUND];
|
||||
char s1buf[UINTMAX_STRSIZE_BOUND];
|
||||
@@ -1311,23 +1456,24 @@ try_new_volume ()
|
||||
|
||||
WARN ((0, 0, _("%s is the wrong size (%s != %s + %s)"),
|
||||
quote (continued_file_name),
|
||||
STRINGIFY_BIGINT (save_totsize, totsizebuf),
|
||||
STRINGIFY_BIGINT (bufmap_head->sizetotal, totsizebuf),
|
||||
STRINGIFY_BIGINT (continued_file_size, s1buf),
|
||||
STRINGIFY_BIGINT (continued_file_offset, s2buf)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (real_s_totsize - real_s_sizeleft != continued_file_offset)
|
||||
if (bufmap_head->sizetotal - bufmap_head->sizeleft !=
|
||||
continued_file_offset)
|
||||
{
|
||||
char totsizebuf[UINTMAX_STRSIZE_BOUND];
|
||||
char s1buf[UINTMAX_STRSIZE_BOUND];
|
||||
char s2buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
WARN ((0, 0, _("This volume is out of sequence (%s - %s != %s)"),
|
||||
STRINGIFY_BIGINT (real_s_totsize, totsizebuf),
|
||||
STRINGIFY_BIGINT (real_s_sizeleft, s1buf),
|
||||
STRINGIFY_BIGINT (bufmap_head->sizetotal, totsizebuf),
|
||||
STRINGIFY_BIGINT (bufmap_head->sizeleft, s1buf),
|
||||
STRINGIFY_BIGINT (continued_file_offset, s2buf)));
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1348,7 +1494,7 @@ drop_volume_label_suffix (const char *label)
|
||||
|
||||
if (len < 1)
|
||||
return NULL;
|
||||
|
||||
|
||||
for (p = label + len - 1; p > label && isdigit ((unsigned char) *p); p--)
|
||||
;
|
||||
if (p > label && p - (VOLUME_TEXT_LEN - 1) > label)
|
||||
@@ -1365,7 +1511,7 @@ drop_volume_label_suffix (const char *label)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Check LABEL against the volume label, seen as a globbing
|
||||
pattern. Return true if the pattern matches. In case of failure,
|
||||
retry matching a volume sequence number before giving up in
|
||||
@@ -1374,7 +1520,7 @@ static bool
|
||||
check_label_pattern (const char *label)
|
||||
{
|
||||
char *string;
|
||||
bool result;
|
||||
bool result = false;
|
||||
|
||||
if (fnmatch (volume_label_option, label, 0) == 0)
|
||||
return true;
|
||||
@@ -1399,7 +1545,7 @@ match_volume_label (void)
|
||||
if (!volume_label)
|
||||
{
|
||||
union block *label = find_next_block ();
|
||||
|
||||
|
||||
if (!label)
|
||||
FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
|
||||
quote (volume_label_option)));
|
||||
@@ -1425,11 +1571,11 @@ match_volume_label (void)
|
||||
tar_stat_destroy (&st);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!volume_label)
|
||||
FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
|
||||
quote (volume_label_option)));
|
||||
|
||||
|
||||
if (!check_label_pattern (volume_label))
|
||||
FATAL_ERROR ((0, 0, _("Volume %s does not match %s"),
|
||||
quote_n (0, volume_label),
|
||||
@@ -1477,26 +1623,24 @@ add_volume_label (void)
|
||||
}
|
||||
|
||||
static void
|
||||
add_chunk_header ()
|
||||
add_chunk_header (struct bufmap *map)
|
||||
{
|
||||
if (archive_format == POSIX_FORMAT)
|
||||
{
|
||||
off_t block_ordinal;
|
||||
union block *blk;
|
||||
struct tar_stat_info st;
|
||||
static size_t real_s_part_no; /* FIXME */
|
||||
|
||||
real_s_part_no++;
|
||||
memset (&st, 0, sizeof st);
|
||||
st.orig_file_name = st.file_name = real_s_name;
|
||||
st.orig_file_name = st.file_name = map->file_name;
|
||||
st.stat.st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
|
||||
st.stat.st_uid = getuid ();
|
||||
st.stat.st_gid = getgid ();
|
||||
st.orig_file_name = xheader_format_name (&st,
|
||||
"%d/GNUFileParts.%p/%f.%n",
|
||||
real_s_part_no);
|
||||
volno);
|
||||
st.file_name = st.orig_file_name;
|
||||
st.archive_file_size = st.stat.st_size = real_s_sizeleft;
|
||||
st.archive_file_size = st.stat.st_size = map->sizeleft;
|
||||
|
||||
block_ordinal = current_block_ordinal ();
|
||||
blk = start_header (&st);
|
||||
@@ -1520,27 +1664,23 @@ write_volume_label (void)
|
||||
|
||||
/* Write GNU multi-volume header */
|
||||
static void
|
||||
gnu_add_multi_volume_header (void)
|
||||
gnu_add_multi_volume_header (struct bufmap *map)
|
||||
{
|
||||
int tmp;
|
||||
union block *block = find_next_block ();
|
||||
|
||||
if (strlen (real_s_name) > NAME_FIELD_SIZE)
|
||||
if (strlen (map->file_name) > NAME_FIELD_SIZE)
|
||||
WARN ((0, 0,
|
||||
_("%s: file name too long to be stored in a GNU multivolume header, truncated"),
|
||||
quotearg_colon (real_s_name)));
|
||||
quotearg_colon (map->file_name)));
|
||||
|
||||
memset (block, 0, BLOCKSIZE);
|
||||
|
||||
/* FIXME: Michael P Urban writes: [a long name file] is being written
|
||||
when a new volume rolls around [...] Looks like the wrong value is
|
||||
being preserved in real_s_name, though. */
|
||||
|
||||
strncpy (block->header.name, real_s_name, NAME_FIELD_SIZE);
|
||||
strncpy (block->header.name, map->file_name, NAME_FIELD_SIZE);
|
||||
block->header.typeflag = GNUTYPE_MULTIVOL;
|
||||
|
||||
OFF_TO_CHARS (real_s_sizeleft, block->header.size);
|
||||
OFF_TO_CHARS (real_s_totsize - real_s_sizeleft,
|
||||
OFF_TO_CHARS (map->sizeleft, block->header.size);
|
||||
OFF_TO_CHARS (map->sizetotal - map->sizeleft,
|
||||
block->oldgnu_header.offset);
|
||||
|
||||
tmp = verbose_option;
|
||||
@@ -1553,40 +1693,17 @@ gnu_add_multi_volume_header (void)
|
||||
/* Add a multi volume header to the current archive. The exact header format
|
||||
depends on the archive format. */
|
||||
static void
|
||||
add_multi_volume_header (void)
|
||||
add_multi_volume_header (struct bufmap *map)
|
||||
{
|
||||
if (archive_format == POSIX_FORMAT)
|
||||
{
|
||||
off_t d = real_s_totsize - real_s_sizeleft;
|
||||
xheader_store ("GNU.volume.filename", &dummy, real_s_name);
|
||||
xheader_store ("GNU.volume.size", &dummy, &real_s_sizeleft);
|
||||
off_t d = map->sizetotal - map->sizeleft;
|
||||
xheader_store ("GNU.volume.filename", &dummy, map->file_name);
|
||||
xheader_store ("GNU.volume.size", &dummy, &map->sizeleft);
|
||||
xheader_store ("GNU.volume.offset", &dummy, &d);
|
||||
}
|
||||
else
|
||||
gnu_add_multi_volume_header ();
|
||||
}
|
||||
|
||||
/* Synchronize multi-volume globals */
|
||||
static void
|
||||
multi_volume_sync ()
|
||||
{
|
||||
if (multi_volume_option)
|
||||
{
|
||||
if (save_name)
|
||||
{
|
||||
assign_string (&real_s_name,
|
||||
safer_name_suffix (save_name, false,
|
||||
absolute_names_option));
|
||||
real_s_totsize = save_totsize;
|
||||
real_s_sizeleft = save_sizeleft;
|
||||
}
|
||||
else
|
||||
{
|
||||
assign_string (&real_s_name, 0);
|
||||
real_s_totsize = 0;
|
||||
real_s_sizeleft = 0;
|
||||
}
|
||||
}
|
||||
gnu_add_multi_volume_header (map);
|
||||
}
|
||||
|
||||
|
||||
@@ -1599,7 +1716,7 @@ simple_flush_read (void)
|
||||
size_t status; /* result from system call */
|
||||
|
||||
checkpoint_run (false);
|
||||
|
||||
|
||||
/* Clear the count of errors. This only applies to a single call to
|
||||
flush_read. */
|
||||
|
||||
@@ -1658,7 +1775,7 @@ _gnu_flush_read (void)
|
||||
size_t status; /* result from system call */
|
||||
|
||||
checkpoint_run (false);
|
||||
|
||||
|
||||
/* Clear the count of errors. This only applies to a single call to
|
||||
flush_read. */
|
||||
|
||||
@@ -1673,8 +1790,6 @@ _gnu_flush_read (void)
|
||||
archive_write_error (status);
|
||||
}
|
||||
|
||||
multi_volume_sync ();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
status = rmtread (archive, record_start->buffer, record_size);
|
||||
@@ -1726,36 +1841,36 @@ _gnu_flush_write (size_t buffer_level)
|
||||
char *copy_ptr;
|
||||
size_t copy_size;
|
||||
size_t bufsize;
|
||||
tarlong wrt;
|
||||
|
||||
struct bufmap *map;
|
||||
|
||||
status = _flush_write ();
|
||||
if (status != record_size && !multi_volume_option)
|
||||
archive_write_error (status);
|
||||
else
|
||||
{
|
||||
if (status)
|
||||
records_written++;
|
||||
records_written++;
|
||||
bytes_written += status;
|
||||
}
|
||||
|
||||
if (status == record_size)
|
||||
{
|
||||
multi_volume_sync ();
|
||||
return;
|
||||
}
|
||||
|
||||
map = bufmap_locate (status);
|
||||
|
||||
if (status % BLOCKSIZE)
|
||||
{
|
||||
ERROR ((0, 0, _("write did not end on a block boundary")));
|
||||
archive_write_error (status);
|
||||
}
|
||||
|
||||
|
||||
/* In multi-volume mode. */
|
||||
/* ENXIO is for the UNIX PC. */
|
||||
if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
|
||||
archive_write_error (status);
|
||||
|
||||
real_s_sizeleft -= status;
|
||||
if (!new_volume (ACCESS_WRITE))
|
||||
return;
|
||||
|
||||
@@ -1767,25 +1882,28 @@ _gnu_flush_write (size_t buffer_level)
|
||||
|
||||
copy_ptr = record_start->buffer + status;
|
||||
copy_size = buffer_level - status;
|
||||
|
||||
|
||||
/* Switch to the next buffer */
|
||||
record_index = !record_index;
|
||||
init_buffer ();
|
||||
|
||||
inhibit_map = 1;
|
||||
|
||||
if (volume_label_option)
|
||||
add_volume_label ();
|
||||
|
||||
if (real_s_name)
|
||||
add_multi_volume_header ();
|
||||
if (map)
|
||||
add_multi_volume_header (map);
|
||||
|
||||
write_extended (true, &dummy, find_next_block ());
|
||||
tar_stat_destroy (&dummy);
|
||||
|
||||
if (real_s_name)
|
||||
add_chunk_header ();
|
||||
wrt = bytes_written;
|
||||
|
||||
if (map)
|
||||
add_chunk_header (map);
|
||||
header = find_next_block ();
|
||||
bufmap_reset (map, header - record_start);
|
||||
bufsize = available_space_after (header);
|
||||
inhibit_map = 0;
|
||||
while (bufsize < copy_size)
|
||||
{
|
||||
memcpy (header->buffer, copy_ptr, bufsize);
|
||||
@@ -1798,16 +1916,6 @@ _gnu_flush_write (size_t buffer_level)
|
||||
memcpy (header->buffer, copy_ptr, copy_size);
|
||||
memset (header->buffer + copy_size, 0, bufsize - copy_size);
|
||||
set_next_block_after (header + (copy_size - 1) / BLOCKSIZE);
|
||||
if (multi_volume_option && wrt < bytes_written)
|
||||
{
|
||||
/* The value of bytes_written has changed while moving data;
|
||||
that means that flush_archive was executed at least once in
|
||||
between, and, as a consequence, copy_size bytes were not written
|
||||
to disk. We need to update sizeleft variables to compensate for
|
||||
that. */
|
||||
save_sizeleft += copy_size;
|
||||
multi_volume_sync ();
|
||||
}
|
||||
find_next_block ();
|
||||
}
|
||||
|
||||
@@ -1841,6 +1949,7 @@ open_archive (enum access_mode wanted_access)
|
||||
switch (wanted_access)
|
||||
{
|
||||
case ACCESS_READ:
|
||||
case ACCESS_UPDATE:
|
||||
if (volume_label_option)
|
||||
match_volume_label ();
|
||||
break;
|
||||
@@ -1850,9 +1959,6 @@ open_archive (enum access_mode wanted_access)
|
||||
if (volume_label_option)
|
||||
write_volume_label ();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
set_volume_start_time ();
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ void
|
||||
checkpoint_compile_action (const char *str)
|
||||
{
|
||||
struct checkpoint_action *act;
|
||||
|
||||
|
||||
if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
|
||||
alloc_action (cop_dot);
|
||||
else if (strcmp (str, "bell") == 0)
|
||||
@@ -119,14 +119,14 @@ checkpoint_finish_compile ()
|
||||
{
|
||||
if (!checkpoint_action)
|
||||
/* Provide a historical default */
|
||||
checkpoint_compile_action ("echo");
|
||||
checkpoint_compile_action ("echo");
|
||||
}
|
||||
else if (checkpoint_action)
|
||||
/* Otherwise, set default checkpoint rate */
|
||||
checkpoint_option = DEFAULT_CHECKPOINT;
|
||||
}
|
||||
|
||||
char *
|
||||
static char *
|
||||
expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
|
||||
{
|
||||
const char *opstr = do_write ? gettext ("write") : gettext ("read");
|
||||
@@ -147,7 +147,7 @@ expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
|
||||
case 'u':
|
||||
outlen += cpslen - 2;
|
||||
break;
|
||||
|
||||
|
||||
case 's':
|
||||
outlen += opstrlen - 2;
|
||||
}
|
||||
@@ -164,11 +164,11 @@ expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
|
||||
case 'u':
|
||||
op = stpcpy (op, cps);
|
||||
break;
|
||||
|
||||
|
||||
case 's':
|
||||
op = stpcpy (op, opstr);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
*op++ = '%';
|
||||
*op++ = *ip;
|
||||
@@ -188,7 +188,7 @@ run_checkpoint_actions (bool do_write)
|
||||
{
|
||||
struct checkpoint_action *p;
|
||||
FILE *tty = NULL;
|
||||
|
||||
|
||||
for (p = checkpoint_action; p; p = p->next)
|
||||
{
|
||||
switch (p->opcode)
|
||||
@@ -207,7 +207,7 @@ run_checkpoint_actions (bool do_write)
|
||||
fflush (tty);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case cop_echo:
|
||||
{
|
||||
char *tmp;
|
||||
@@ -232,7 +232,7 @@ run_checkpoint_actions (bool do_write)
|
||||
free (tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case cop_ttyout:
|
||||
if (!tty)
|
||||
tty = fopen ("/dev/tty", "w");
|
||||
@@ -245,11 +245,11 @@ run_checkpoint_actions (bool do_write)
|
||||
free (tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case cop_sleep:
|
||||
sleep (p->v.time);
|
||||
break;
|
||||
|
||||
|
||||
case cop_exec:
|
||||
sys_exec_checkpoint_script (p->v.command,
|
||||
archive_name_cursor[0],
|
||||
@@ -266,5 +266,4 @@ checkpoint_run (bool do_write)
|
||||
{
|
||||
if (checkpoint_option && !(++checkpoint % checkpoint_option))
|
||||
run_checkpoint_actions (do_write);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
117
src/common.h
117
src/common.h
@@ -1,7 +1,7 @@
|
||||
/* Common declarations for the tar program.
|
||||
|
||||
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
|
||||
2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
|
||||
2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
@@ -105,6 +105,8 @@ GLOBAL bool absolute_names_option;
|
||||
|
||||
/* Display file times in UTC */
|
||||
GLOBAL bool utc_option;
|
||||
/* Output file timestamps to the full resolution */
|
||||
GLOBAL bool full_time_option;
|
||||
|
||||
/* This variable tells how to interpret newer_mtime_option, below. If zero,
|
||||
files get archived if their mtime is not less than newer_mtime_option.
|
||||
@@ -335,12 +337,12 @@ struct name
|
||||
int matching_flags; /* wildcard flags if name is a pattern */
|
||||
bool cmdline; /* true if this name was given in the
|
||||
command line */
|
||||
|
||||
|
||||
int change_dir; /* Number of the directory to change to.
|
||||
Set with the -C option. */
|
||||
uintmax_t found_count; /* number of times a matching file has
|
||||
been found */
|
||||
|
||||
|
||||
/* The following members are used for incremental dumps only,
|
||||
if this struct name represents a directory;
|
||||
see incremen.c */
|
||||
@@ -355,6 +357,11 @@ struct name
|
||||
GLOBAL dev_t ar_dev;
|
||||
GLOBAL ino_t ar_ino;
|
||||
|
||||
/* Flags for reading, searching, and fstatatting files. */
|
||||
GLOBAL int open_read_flags;
|
||||
GLOBAL int open_searchdir_flags;
|
||||
GLOBAL int fstatat_flags;
|
||||
|
||||
GLOBAL int seek_option;
|
||||
GLOBAL bool seekable_archive;
|
||||
|
||||
@@ -425,13 +432,17 @@ void archive_read_error (void);
|
||||
off_t seek_archive (off_t size);
|
||||
void set_start_time (void);
|
||||
|
||||
void mv_begin (struct tar_stat_info *st);
|
||||
void mv_begin_write (const char *file_name, off_t totsize, off_t sizeleft);
|
||||
|
||||
void mv_begin_read (struct tar_stat_info *st);
|
||||
void mv_end (void);
|
||||
void mv_total_size (off_t size);
|
||||
void mv_size_left (off_t size);
|
||||
|
||||
void buffer_write_global_xheader (void);
|
||||
|
||||
const char *first_decompress_program (int *pstate);
|
||||
const char *next_decompress_program (int *pstate);
|
||||
|
||||
/* Module create.c. */
|
||||
|
||||
enum dump_status
|
||||
@@ -443,13 +454,14 @@ enum dump_status
|
||||
};
|
||||
|
||||
void add_exclusion_tag (const char *name, enum exclusion_tag_type type,
|
||||
bool (*)(const char*));
|
||||
bool cachedir_file_p (const char *name);
|
||||
bool (*predicate) (int));
|
||||
bool cachedir_file_p (int fd);
|
||||
char *get_directory_entries (struct tar_stat_info *st);
|
||||
|
||||
bool file_dumpable_p (struct tar_stat_info *st);
|
||||
void create_archive (void);
|
||||
void pad_archive (off_t size_left);
|
||||
void dump_file (const char *st, bool top_level, dev_t parent_device);
|
||||
void dump_file (struct tar_stat_info *parent, char const *name,
|
||||
char const *fullname);
|
||||
union block *start_header (struct tar_stat_info *st);
|
||||
void finish_header (struct tar_stat_info *st, union block *header,
|
||||
off_t block_ordinal);
|
||||
@@ -459,33 +471,18 @@ union block * write_extended (bool global, struct tar_stat_info *st,
|
||||
union block *start_private_header (const char *name, size_t size, time_t t);
|
||||
void write_eot (void);
|
||||
void check_links (void);
|
||||
int subfile_open (struct tar_stat_info const *dir, char const *file, int flags);
|
||||
void restore_parent_fd (struct tar_stat_info const *st);
|
||||
void exclusion_tag_warning (const char *dirname, const char *tagname,
|
||||
const char *message);
|
||||
enum exclusion_tag_type check_exclusion_tags (const char *dirname,
|
||||
enum exclusion_tag_type check_exclusion_tags (struct tar_stat_info const *st,
|
||||
const char **tag_file_name);
|
||||
|
||||
#define GID_TO_CHARS(val, where) gid_to_chars (val, where, sizeof (where))
|
||||
#define MAJOR_TO_CHARS(val, where) major_to_chars (val, where, sizeof (where))
|
||||
#define MINOR_TO_CHARS(val, where) minor_to_chars (val, where, sizeof (where))
|
||||
#define MODE_TO_CHARS(val, where) mode_to_chars (val, where, sizeof (where))
|
||||
#define OFF_TO_CHARS(val, where) off_to_chars (val, where, sizeof (where))
|
||||
#define SIZE_TO_CHARS(val, where) size_to_chars (val, where, sizeof (where))
|
||||
#define TIME_TO_CHARS(val, where) time_to_chars (val, where, sizeof (where))
|
||||
#define UID_TO_CHARS(val, where) uid_to_chars (val, where, sizeof (where))
|
||||
#define UINTMAX_TO_CHARS(val, where) uintmax_to_chars (val, where, sizeof (where))
|
||||
#define UNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf))
|
||||
#define GNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf))
|
||||
|
||||
bool gid_to_chars (gid_t gid, char *buf, size_t size);
|
||||
bool major_to_chars (major_t m, char *buf, size_t size);
|
||||
bool minor_to_chars (minor_t m, char *buf, size_t size);
|
||||
bool mode_to_chars (mode_t m, char *buf, size_t size);
|
||||
#define OFF_TO_CHARS(val, where) off_to_chars (val, where, sizeof (where))
|
||||
#define TIME_TO_CHARS(val, where) time_to_chars (val, where, sizeof (where))
|
||||
|
||||
bool off_to_chars (off_t off, char *buf, size_t size);
|
||||
bool size_to_chars (size_t v, char *buf, size_t size);
|
||||
bool time_to_chars (time_t t, char *buf, size_t size);
|
||||
bool uid_to_chars (uid_t uid, char *buf, size_t size);
|
||||
bool uintmax_to_chars (uintmax_t v, char *buf, size_t size);
|
||||
void string_to_chars (char const *s, char *buf, size_t size);
|
||||
|
||||
/* Module diffarch.c. */
|
||||
|
||||
@@ -507,18 +504,8 @@ bool rename_directory (char *src, char *dst);
|
||||
void delete_archive_members (void);
|
||||
|
||||
/* Module incremen.c. */
|
||||
typedef struct dumpdir *dumpdir_t;
|
||||
typedef struct dumpdir_iter *dumpdir_iter_t;
|
||||
|
||||
dumpdir_t dumpdir_create0 (const char *contents, const char *cmask);
|
||||
dumpdir_t dumpdir_create (const char *contents);
|
||||
void dumpdir_free (dumpdir_t);
|
||||
char *dumpdir_locate (dumpdir_t dump, const char *name);
|
||||
char *dumpdir_next (dumpdir_iter_t itr);
|
||||
char *dumpdir_first (dumpdir_t dump, int all, dumpdir_iter_t *pitr);
|
||||
|
||||
struct directory *scan_directory (char *dir, dev_t device, bool cmdline);
|
||||
void name_fill_directory (struct name *name, dev_t device, bool cmdline);
|
||||
struct directory *scan_directory (struct tar_stat_info *st);
|
||||
const char *directory_contents (struct directory *dir);
|
||||
const char *safe_directory_contents (struct directory *dir);
|
||||
|
||||
@@ -531,7 +518,7 @@ void read_directory_file (void);
|
||||
void write_directory_file (void);
|
||||
void purge_directory (char const *directory_name);
|
||||
void list_dumpdir (char *buffer, size_t size);
|
||||
void update_parent_directory (const char *name);
|
||||
void update_parent_directory (struct tar_stat_info *st);
|
||||
|
||||
size_t dumpdir_size (const char *p);
|
||||
bool is_dumpdir (struct tar_stat_info *stat_info);
|
||||
@@ -566,27 +553,13 @@ extern size_t recent_long_link_blocks;
|
||||
|
||||
void decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
enum archive_format *format_pointer, int do_user_group);
|
||||
void transform_stat_info (int typeflag, struct tar_stat_info *stat_info);
|
||||
char const *tartime (struct timespec t, bool full_time);
|
||||
|
||||
#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where))
|
||||
#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where))
|
||||
#define MINOR_FROM_HEADER(where) minor_from_header (where, sizeof (where))
|
||||
#define MODE_FROM_HEADER(where, hbits) \
|
||||
mode_from_header (where, sizeof (where), hbits)
|
||||
#define OFF_FROM_HEADER(where) off_from_header (where, sizeof (where))
|
||||
#define SIZE_FROM_HEADER(where) size_from_header (where, sizeof (where))
|
||||
#define TIME_FROM_HEADER(where) time_from_header (where, sizeof (where))
|
||||
#define UID_FROM_HEADER(where) uid_from_header (where, sizeof (where))
|
||||
#define UINTMAX_FROM_HEADER(where) uintmax_from_header (where, sizeof (where))
|
||||
|
||||
gid_t gid_from_header (const char *buf, size_t size);
|
||||
major_t major_from_header (const char *buf, size_t size);
|
||||
minor_t minor_from_header (const char *buf, size_t size);
|
||||
mode_t mode_from_header (const char *buf, size_t size, unsigned *hbits);
|
||||
off_t off_from_header (const char *buf, size_t size);
|
||||
size_t size_from_header (const char *buf, size_t size);
|
||||
time_t time_from_header (const char *buf, size_t size);
|
||||
uid_t uid_from_header (const char *buf, size_t size);
|
||||
uintmax_t uintmax_from_header (const char *buf, size_t size);
|
||||
|
||||
void list_archive (void);
|
||||
@@ -605,7 +578,6 @@ void skip_member (void);
|
||||
/* Module misc.c. */
|
||||
|
||||
void assign_string (char **dest, const char *src);
|
||||
char *quote_copy_string (const char *str);
|
||||
int unquote_string (char *str);
|
||||
char *zap_slashes (char *name);
|
||||
char *normalize_filename (const char *name);
|
||||
@@ -623,6 +595,8 @@ enum { BILLION = 1000000000, LOG10_BILLION = 9 };
|
||||
enum { TIMESPEC_STRSIZE_BOUND =
|
||||
UINTMAX_STRSIZE_BOUND + LOG10_BILLION + sizeof "-." - 1 };
|
||||
|
||||
bool must_be_dot_or_slash (char const *);
|
||||
|
||||
enum remove_option
|
||||
{
|
||||
ORDINARY_REMOVE_OPTION,
|
||||
@@ -641,8 +615,10 @@ int remove_any_file (const char *file_name, enum remove_option option);
|
||||
bool maybe_backup_file (const char *file_name, bool this_is_the_archive);
|
||||
void undo_last_backup (void);
|
||||
|
||||
int deref_stat (bool deref, char const *name, struct stat *buf);
|
||||
int deref_stat (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);
|
||||
@@ -656,8 +632,6 @@ void seek_diag_details (char const *name, off_t offset);
|
||||
void stat_diag (char const *name);
|
||||
void file_removed_diag (const char *name, bool top_level,
|
||||
void (*diagfn) (char const *name));
|
||||
void dir_removed_diag (char const *name, bool top_level,
|
||||
void (*diagfn) (char const *name));
|
||||
void write_error_details (char const *name, size_t status, size_t size);
|
||||
void write_fatal (char const *name) __attribute__ ((noreturn));
|
||||
void write_fatal_details (char const *name, ssize_t status, size_t size)
|
||||
@@ -667,11 +641,12 @@ pid_t xfork (void);
|
||||
void xpipe (int fd[2]);
|
||||
|
||||
void *page_aligned_alloc (void **ptr, size_t size);
|
||||
int set_file_atime (int fd, char const *file,
|
||||
struct timespec const timespec[2]);
|
||||
int set_file_atime (int fd, int parentfd, char const *file,
|
||||
struct timespec atime);
|
||||
|
||||
/* Module names.c. */
|
||||
|
||||
extern size_t name_count;
|
||||
extern struct name *gnu_list_name;
|
||||
|
||||
void gid_to_gname (gid_t gid, char **gname);
|
||||
@@ -690,6 +665,7 @@ struct name *addname (char const *string, int change_dir,
|
||||
void remname (struct name *name);
|
||||
bool name_match (const char *name);
|
||||
void names_notfound (void);
|
||||
void label_notfound (void);
|
||||
void collect_and_sort_names (void);
|
||||
struct name *name_scan (const char *name);
|
||||
struct name const *name_from_list (void);
|
||||
@@ -702,7 +678,6 @@ bool excluded_name (char const *name);
|
||||
|
||||
void add_avoided_name (char const *name);
|
||||
bool is_avoided_name (char const *name);
|
||||
bool is_individual_file (char const *name);
|
||||
|
||||
bool contains_dot_dot (char const *name);
|
||||
|
||||
@@ -716,9 +691,9 @@ bool contains_dot_dot (char const *name);
|
||||
void usage (int);
|
||||
|
||||
int confirm (const char *message_action, const char *name);
|
||||
void request_stdin (const char *option);
|
||||
|
||||
void tar_stat_init (struct tar_stat_info *st);
|
||||
bool tar_stat_close (struct tar_stat_info *st);
|
||||
void tar_stat_destroy (struct tar_stat_info *st);
|
||||
void usage (int) __attribute__ ((noreturn));
|
||||
int tar_timespec_cmp (struct timespec a, struct timespec b);
|
||||
@@ -734,7 +709,6 @@ void update_archive (void);
|
||||
|
||||
/* Module xheader.c. */
|
||||
|
||||
void xheader_init (struct xheader *xhdr);
|
||||
void xheader_decode (struct tar_stat_info *stat);
|
||||
void xheader_decode_global (struct xheader *xhdr);
|
||||
void xheader_store (char const *keyword, struct tar_stat_info *st,
|
||||
@@ -800,12 +774,12 @@ bool utf8_convert (bool to_utf, char const *input, char **output);
|
||||
|
||||
void set_transform_expr (const char *expr);
|
||||
bool transform_name (char **pinput, int type);
|
||||
bool transform_member_name (char **pinput, int type);
|
||||
bool transform_name_fp (char **pinput, int type,
|
||||
char *(*fun)(char *, void *), void *);
|
||||
bool transform_program_p (void);
|
||||
|
||||
/* Module suffix.c */
|
||||
void set_comression_program_by_suffix (const char *name, const char *defprog);
|
||||
void set_compression_program_by_suffix (const char *name, const char *defprog);
|
||||
|
||||
/* Module checkpoint.c */
|
||||
void checkpoint_compile_action (const char *str);
|
||||
@@ -832,11 +806,13 @@ void checkpoint_run (bool do_write);
|
||||
#define WARN_UNKNOWN_CAST 0x00010000
|
||||
#define WARN_UNKNOWN_KEYWORD 0x00020000
|
||||
#define WARN_XDEV 0x00040000
|
||||
#define WARN_DECOMPRESS_PROGRAM 0x00080000
|
||||
|
||||
/* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default
|
||||
in verbose mode */
|
||||
#define WARN_VERBOSE_WARNINGS (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY)
|
||||
#define WARN_ALL (0xffffffff & ~WARN_VERBOSE_WARNINGS)
|
||||
#define WARN_VERBOSE_WARNINGS (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY|\
|
||||
WARN_DECOMPRESS_PROGRAM)
|
||||
#define WARN_ALL (~WARN_VERBOSE_WARNINGS)
|
||||
|
||||
void set_warning_option (const char *arg);
|
||||
|
||||
@@ -856,4 +832,3 @@ void finish_deferred_unlinks (void);
|
||||
|
||||
/* Module exit.c */
|
||||
extern void (*fatal_exit_hook) (void);
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
|
||||
size_t data_size;
|
||||
off_t size = st->stat.st_size;
|
||||
|
||||
mv_begin (st);
|
||||
mv_begin_read (st);
|
||||
while (size)
|
||||
{
|
||||
data_block = find_next_block ();
|
||||
@@ -151,7 +151,7 @@ read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
|
||||
static int
|
||||
get_stat_data (char const *file_name, struct stat *stat_data)
|
||||
{
|
||||
int status = deref_stat (dereference_option, file_name, stat_data);
|
||||
int status = deref_stat (file_name, stat_data);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
@@ -217,12 +217,7 @@ diff_file (void)
|
||||
}
|
||||
else
|
||||
{
|
||||
int atime_flag =
|
||||
(atime_preserve_option == system_atime_preserve
|
||||
? O_NOATIME
|
||||
: 0);
|
||||
|
||||
diff_handle = open (file_name, O_RDONLY | O_BINARY | atime_flag);
|
||||
diff_handle = openat (chdir_fd, file_name, open_read_flags);
|
||||
|
||||
if (diff_handle < 0)
|
||||
{
|
||||
@@ -239,12 +234,12 @@ diff_file (void)
|
||||
else
|
||||
read_and_process (¤t_stat_info, process_rawdata);
|
||||
|
||||
if (atime_preserve_option == replace_atime_preserve)
|
||||
if (atime_preserve_option == replace_atime_preserve
|
||||
&& stat_data.st_size != 0)
|
||||
{
|
||||
struct timespec ts[2];
|
||||
ts[0] = get_stat_atime (&stat_data);
|
||||
ts[1] = get_stat_mtime (&stat_data);
|
||||
if (set_file_atime (diff_handle, file_name, ts) != 0)
|
||||
struct timespec atime = get_stat_atime (&stat_data);
|
||||
if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
|
||||
!= 0)
|
||||
utime_error (file_name);
|
||||
}
|
||||
|
||||
@@ -277,7 +272,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)
|
||||
{
|
||||
@@ -331,7 +327,7 @@ static int
|
||||
dumpdir_cmp (const char *a, const char *b)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
|
||||
while (*a)
|
||||
switch (*a)
|
||||
{
|
||||
@@ -345,7 +341,7 @@ dumpdir_cmp (const char *a, const char *b)
|
||||
a += len;
|
||||
b += len;
|
||||
break;
|
||||
|
||||
|
||||
case 'D':
|
||||
if (strcmp(a, b))
|
||||
return 1;
|
||||
@@ -353,7 +349,7 @@ dumpdir_cmp (const char *a, const char *b)
|
||||
a += len;
|
||||
b += len;
|
||||
break;
|
||||
|
||||
|
||||
case 'R':
|
||||
case 'T':
|
||||
case 'X':
|
||||
@@ -369,7 +365,7 @@ diff_dumpdir (void)
|
||||
dev_t dev = 0;
|
||||
struct stat stat_data;
|
||||
|
||||
if (deref_stat (true, current_stat_info.file_name, &stat_data))
|
||||
if (deref_stat (current_stat_info.file_name, &stat_data) != 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
stat_warn (current_stat_info.file_name);
|
||||
@@ -379,8 +375,7 @@ diff_dumpdir (void)
|
||||
else
|
||||
dev = stat_data.st_dev;
|
||||
|
||||
dumpdir_buffer = directory_contents
|
||||
(scan_directory (current_stat_info.file_name, dev, false));
|
||||
dumpdir_buffer = directory_contents (scan_directory (¤t_stat_info));
|
||||
|
||||
if (dumpdir_buffer)
|
||||
{
|
||||
@@ -422,7 +417,8 @@ diff_multivol (void)
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
|
||||
|
||||
fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
@@ -452,7 +448,6 @@ diff_archive (void)
|
||||
{
|
||||
|
||||
set_next_block_after (current_header);
|
||||
decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
|
||||
|
||||
/* Print the block from current_header and current_stat_info. */
|
||||
|
||||
@@ -518,13 +513,22 @@ diff_archive (void)
|
||||
void
|
||||
verify_volume (void)
|
||||
{
|
||||
int may_fail = 0;
|
||||
if (removed_prefixes_p ())
|
||||
{
|
||||
WARN((0, 0,
|
||||
_("Archive contains file names with leading prefixes removed.")));
|
||||
WARN((0, 0,
|
||||
_("Verification may fail to locate original files.")));
|
||||
may_fail = 1;
|
||||
}
|
||||
if (transform_program_p ())
|
||||
{
|
||||
WARN((0, 0,
|
||||
_("Archive contains transformed file names.")));
|
||||
may_fail = 1;
|
||||
}
|
||||
if (may_fail)
|
||||
WARN((0, 0,
|
||||
_("Verification may fail to locate original files.")));
|
||||
|
||||
if (!diff_buffer)
|
||||
diff_init ();
|
||||
@@ -578,8 +582,8 @@ verify_volume (void)
|
||||
flush_read ();
|
||||
while (1)
|
||||
{
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info,
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info,
|
||||
read_header_auto);
|
||||
|
||||
if (status == HEADER_FAILURE)
|
||||
@@ -609,7 +613,7 @@ verify_volume (void)
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
if (status == HEADER_ZERO_BLOCK)
|
||||
break;
|
||||
@@ -617,8 +621,10 @@ verify_volume (void)
|
||||
(0, 0, _("A lone zero block at %s"),
|
||||
STRINGIFY_BIGINT (current_block_ordinal (), buf)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
|
||||
diff_archive ();
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
}
|
||||
|
||||
480
src/create.c
480
src/create.c
@@ -26,11 +26,15 @@
|
||||
#include "common.h"
|
||||
#include <hash.h>
|
||||
|
||||
/* Error number to use when an impostor is discovered.
|
||||
Pretend the impostor isn't there. */
|
||||
enum { IMPOSTOR_ERRNO = ENOENT };
|
||||
|
||||
struct link
|
||||
{
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
size_t nlink;
|
||||
nlink_t nlink;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
@@ -39,7 +43,7 @@ struct exclusion_tag
|
||||
const char *name;
|
||||
size_t length;
|
||||
enum exclusion_tag_type type;
|
||||
bool (*predicate) (const char *name);
|
||||
bool (*predicate) (int fd);
|
||||
struct exclusion_tag *next;
|
||||
};
|
||||
|
||||
@@ -47,7 +51,7 @@ static struct exclusion_tag *exclusion_tags;
|
||||
|
||||
void
|
||||
add_exclusion_tag (const char *name, enum exclusion_tag_type type,
|
||||
bool (*predicate) (const char *name))
|
||||
bool (*predicate) (int fd))
|
||||
{
|
||||
struct exclusion_tag *tag = xmalloc (sizeof tag[0]);
|
||||
tag->next = exclusion_tags;
|
||||
@@ -71,39 +75,24 @@ exclusion_tag_warning (const char *dirname, const char *tagname,
|
||||
message));
|
||||
}
|
||||
|
||||
enum exclusion_tag_type
|
||||
check_exclusion_tags (const char *dirname, const char **tag_file_name)
|
||||
enum exclusion_tag_type
|
||||
check_exclusion_tags (struct tar_stat_info const *st, char const **tag_file_name)
|
||||
{
|
||||
static char *tagname;
|
||||
static size_t tagsize;
|
||||
struct exclusion_tag *tag;
|
||||
size_t dlen = strlen (dirname);
|
||||
int addslash = !ISSLASH (dirname[dlen-1]);
|
||||
size_t noff = 0;
|
||||
|
||||
|
||||
for (tag = exclusion_tags; tag; tag = tag->next)
|
||||
{
|
||||
size_t size = dlen + addslash + tag->length + 1;
|
||||
if (size > tagsize)
|
||||
int tagfd = subfile_open (st, tag->name, open_read_flags);
|
||||
if (0 <= tagfd)
|
||||
{
|
||||
tagsize = size;
|
||||
tagname = xrealloc (tagname, tagsize);
|
||||
}
|
||||
|
||||
if (noff == 0)
|
||||
{
|
||||
strcpy (tagname, dirname);
|
||||
noff = dlen;
|
||||
if (addslash)
|
||||
tagname[noff++] = '/';
|
||||
}
|
||||
strcpy (tagname + noff, tag->name);
|
||||
if (access (tagname, F_OK) == 0
|
||||
&& (!tag->predicate || tag->predicate (tagname)))
|
||||
{
|
||||
if (tag_file_name)
|
||||
*tag_file_name = tag->name;
|
||||
return tag->type;
|
||||
bool satisfied = !tag->predicate || tag->predicate (tagfd);
|
||||
close (tagfd);
|
||||
if (satisfied)
|
||||
{
|
||||
if (tag_file_name)
|
||||
*tag_file_name = tag->name;
|
||||
return tag->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,22 +110,13 @@ check_exclusion_tags (const char *dirname, const char **tag_file_name)
|
||||
#define CACHEDIR_SIGNATURE_SIZE (sizeof CACHEDIR_SIGNATURE - 1)
|
||||
|
||||
bool
|
||||
cachedir_file_p (const char *name)
|
||||
cachedir_file_p (int fd)
|
||||
{
|
||||
bool tag_present = false;
|
||||
int fd = open (name, O_RDONLY);
|
||||
if (fd >= 0)
|
||||
{
|
||||
static char tagbuf[CACHEDIR_SIGNATURE_SIZE];
|
||||
char tagbuf[CACHEDIR_SIGNATURE_SIZE];
|
||||
|
||||
if (read (fd, tagbuf, CACHEDIR_SIGNATURE_SIZE)
|
||||
== CACHEDIR_SIGNATURE_SIZE
|
||||
&& memcmp (tagbuf, CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE) == 0)
|
||||
tag_present = true;
|
||||
|
||||
close (fd);
|
||||
}
|
||||
return tag_present;
|
||||
return
|
||||
(read (fd, tagbuf, CACHEDIR_SIGNATURE_SIZE) == CACHEDIR_SIGNATURE_SIZE
|
||||
&& memcmp (tagbuf, CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE) == 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +194,14 @@ to_base256 (int negative, uintmax_t value, char *where, size_t size)
|
||||
while (i);
|
||||
}
|
||||
|
||||
#define GID_TO_CHARS(val, where) gid_to_chars (val, where, sizeof (where))
|
||||
#define MAJOR_TO_CHARS(val, where) major_to_chars (val, where, sizeof (where))
|
||||
#define MINOR_TO_CHARS(val, where) minor_to_chars (val, where, sizeof (where))
|
||||
#define MODE_TO_CHARS(val, where) mode_to_chars (val, where, sizeof (where))
|
||||
#define UID_TO_CHARS(val, where) uid_to_chars (val, where, sizeof (where))
|
||||
|
||||
#define UNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf))
|
||||
#define GNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf))
|
||||
|
||||
static bool
|
||||
to_chars (int negative, uintmax_t value, size_t valsize,
|
||||
@@ -263,7 +251,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
|
||||
|
||||
1. In OLDGNU_FORMAT all strings in a tar header end in \0
|
||||
2. Incremental archives use oldgnu_header.
|
||||
|
||||
|
||||
Apart from this they are completely identical. */
|
||||
uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
|
||||
char subbuf[UINTMAX_STRSIZE_BOUND + 1];
|
||||
@@ -368,25 +356,25 @@ gid_substitute (int *negative)
|
||||
return r;
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
gid_to_chars (gid_t v, char *p, size_t s)
|
||||
{
|
||||
return to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
major_to_chars (major_t v, char *p, size_t s)
|
||||
{
|
||||
return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t");
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
minor_to_chars (minor_t v, char *p, size_t s)
|
||||
{
|
||||
return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t");
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
mode_to_chars (mode_t v, char *p, size_t s)
|
||||
{
|
||||
/* In the common case where the internal and external mode bits are the same,
|
||||
@@ -432,12 +420,6 @@ off_to_chars (off_t v, char *p, size_t s)
|
||||
return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");
|
||||
}
|
||||
|
||||
bool
|
||||
size_to_chars (size_t v, char *p, size_t s)
|
||||
{
|
||||
return to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");
|
||||
}
|
||||
|
||||
bool
|
||||
time_to_chars (time_t v, char *p, size_t s)
|
||||
{
|
||||
@@ -460,19 +442,19 @@ uid_substitute (int *negative)
|
||||
return r;
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
uid_to_chars (uid_t v, char *p, size_t s)
|
||||
{
|
||||
return to_chars (v < 0, (uintmax_t) v, sizeof v, uid_substitute, p, s, "uid_t");
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
uintmax_to_chars (uintmax_t v, char *p, size_t s)
|
||||
{
|
||||
return to_chars (0, v, sizeof v, 0, p, s, "uintmax_t");
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
string_to_chars (char const *str, char *p, size_t s)
|
||||
{
|
||||
tar_copy_str (p, str, s);
|
||||
@@ -480,20 +462,25 @@ string_to_chars (char const *str, char *p, size_t s)
|
||||
}
|
||||
|
||||
|
||||
/* A file is considered dumpable if it is sparse and both --sparse and --totals
|
||||
/* A directory is always considered dumpable.
|
||||
Otherwise, only regular and contiguous files are considered dumpable.
|
||||
Such a file is dumpable if it is sparse and both --sparse and --totals
|
||||
are specified.
|
||||
Otherwise, it is dumpable unless any of the following conditions occur:
|
||||
|
||||
a) it is empty *and* world-readable, or
|
||||
b) current archive is /dev/null */
|
||||
|
||||
bool
|
||||
file_dumpable_p (struct tar_stat_info *st)
|
||||
static bool
|
||||
file_dumpable_p (struct stat const *st)
|
||||
{
|
||||
if (S_ISDIR (st->st_mode))
|
||||
return true;
|
||||
if (! (S_ISREG (st->st_mode) || S_ISCTG (st->st_mode)))
|
||||
return false;
|
||||
if (dev_null_output)
|
||||
return totals_option && sparse_option && ST_IS_SPARSE (st->stat);
|
||||
return !(st->archive_file_size == 0
|
||||
&& (st->stat.st_mode & MODE_R) == MODE_R);
|
||||
return totals_option && sparse_option && ST_IS_SPARSE (*st);
|
||||
return ! (st->st_size == 0 && (st->st_mode & MODE_R) == MODE_R);
|
||||
}
|
||||
|
||||
|
||||
@@ -575,7 +562,8 @@ write_gnu_long_link (struct tar_stat_info *st, const char *p, char type)
|
||||
GNAME_TO_CHARS (tmpname, header->header.gname);
|
||||
free (tmpname);
|
||||
|
||||
strcpy (header->header.magic, OLDGNU_MAGIC);
|
||||
strcpy (header->buffer + offsetof (struct posix_header, magic),
|
||||
OLDGNU_MAGIC);
|
||||
header->header.typeflag = type;
|
||||
finish_header (st, header, -1);
|
||||
|
||||
@@ -618,7 +606,7 @@ write_ustar_long_name (const char *name)
|
||||
size_t length = strlen (name);
|
||||
size_t i, nlen;
|
||||
union block *header;
|
||||
|
||||
|
||||
if (length > PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)
|
||||
{
|
||||
ERROR ((0, 0, _("%s: file name is too long (max %d); not dumped"),
|
||||
@@ -713,7 +701,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
|
||||
char *p;
|
||||
int type;
|
||||
time_t t;
|
||||
|
||||
|
||||
if (st->xhdr.buffer || st->xhdr.stk == NULL)
|
||||
return old_header;
|
||||
|
||||
@@ -912,7 +900,8 @@ start_header (struct tar_stat_info *st)
|
||||
case OLDGNU_FORMAT:
|
||||
case GNU_FORMAT: /*FIXME?*/
|
||||
/* Overwrite header->header.magic and header.version in one blow. */
|
||||
strcpy (header->header.magic, OLDGNU_MAGIC);
|
||||
strcpy (header->buffer + offsetof (struct posix_header, magic),
|
||||
OLDGNU_MAGIC);
|
||||
break;
|
||||
|
||||
case POSIX_FORMAT:
|
||||
@@ -1012,7 +1001,6 @@ pad_archive (off_t size_left)
|
||||
union block *blk;
|
||||
while (size_left > 0)
|
||||
{
|
||||
mv_size_left (size_left);
|
||||
blk = find_next_block ();
|
||||
memset (blk->buffer, 0, BLOCKSIZE);
|
||||
set_next_block_after (blk);
|
||||
@@ -1038,12 +1026,10 @@ dump_regular_file (int fd, struct tar_stat_info *st)
|
||||
|
||||
finish_header (st, blk, block_ordinal);
|
||||
|
||||
mv_begin (st);
|
||||
mv_begin_write (st->file_name, st->stat.st_size, st->stat.st_size);
|
||||
while (size_left > 0)
|
||||
{
|
||||
size_t bufsize, count;
|
||||
|
||||
mv_size_left (size_left);
|
||||
|
||||
blk = find_next_block ();
|
||||
|
||||
@@ -1058,7 +1044,7 @@ dump_regular_file (int fd, struct tar_stat_info *st)
|
||||
memset (blk->buffer + size_left, 0, BLOCKSIZE - count);
|
||||
}
|
||||
|
||||
count = (fd < 0) ? bufsize : safe_read (fd, blk->buffer, bufsize);
|
||||
count = (fd <= 0) ? bufsize : safe_read (fd, blk->buffer, bufsize);
|
||||
if (count == SAFE_READ_ERROR)
|
||||
{
|
||||
read_diag_details (st->orig_file_name,
|
||||
@@ -1080,7 +1066,7 @@ dump_regular_file (int fd, struct tar_stat_info *st)
|
||||
size_left),
|
||||
quotearg_colon (st->orig_file_name),
|
||||
STRINGIFY_BIGINT (size_left, buf)));
|
||||
if (! ignore_failed_read_option)
|
||||
if (! ignore_failed_read_option)
|
||||
set_exit_status (TAREXIT_DIFFERS);
|
||||
pad_archive (size_left - (bufsize - count));
|
||||
return dump_status_short;
|
||||
@@ -1090,11 +1076,13 @@ dump_regular_file (int fd, struct tar_stat_info *st)
|
||||
}
|
||||
|
||||
|
||||
/* Copy info from the directory identified by ST into the archive.
|
||||
DIRECTORY contains the directory's entries. */
|
||||
|
||||
static void
|
||||
dump_dir0 (char *directory,
|
||||
struct tar_stat_info *st, bool top_level, dev_t parent_device)
|
||||
dump_dir0 (struct tar_stat_info *st, char const *directory)
|
||||
{
|
||||
dev_t our_device = st->stat.st_dev;
|
||||
bool top_level = ! st->parent;
|
||||
const char *tag_file_name;
|
||||
union block *blk = NULL;
|
||||
off_t block_ordinal = current_block_ordinal ();
|
||||
@@ -1129,7 +1117,7 @@ dump_dir0 (char *directory,
|
||||
size_t bufsize;
|
||||
ssize_t count;
|
||||
const char *buffer, *p_buffer;
|
||||
|
||||
|
||||
block_ordinal = current_block_ordinal ();
|
||||
buffer = safe_directory_contents (gnu_list_name->directory);
|
||||
totsize = dumpdir_size (buffer);
|
||||
@@ -1137,12 +1125,10 @@ dump_dir0 (char *directory,
|
||||
finish_header (st, blk, block_ordinal);
|
||||
p_buffer = buffer;
|
||||
size_left = totsize;
|
||||
|
||||
mv_begin (st);
|
||||
mv_total_size (totsize);
|
||||
|
||||
mv_begin_write (st->file_name, totsize, totsize);
|
||||
while (size_left > 0)
|
||||
{
|
||||
mv_size_left (size_left);
|
||||
blk = find_next_block ();
|
||||
bufsize = available_space_after (blk);
|
||||
if (size_left < bufsize)
|
||||
@@ -1157,7 +1143,6 @@ dump_dir0 (char *directory,
|
||||
p_buffer += bufsize;
|
||||
set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
|
||||
}
|
||||
mv_end ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1167,7 +1152,7 @@ dump_dir0 (char *directory,
|
||||
|
||||
if (one_file_system_option
|
||||
&& !top_level
|
||||
&& parent_device != st->stat.st_dev)
|
||||
&& st->parent->stat.st_dev != st->stat.st_dev)
|
||||
{
|
||||
if (verbose_option)
|
||||
WARNOPT (WARN_XDEV,
|
||||
@@ -1179,13 +1164,13 @@ dump_dir0 (char *directory,
|
||||
{
|
||||
char *name_buf;
|
||||
size_t name_size;
|
||||
|
||||
switch (check_exclusion_tags (st->orig_file_name, &tag_file_name))
|
||||
|
||||
switch (check_exclusion_tags (st, &tag_file_name))
|
||||
{
|
||||
case exclusion_tag_all:
|
||||
/* Handled in dump_file0 */
|
||||
break;
|
||||
|
||||
|
||||
case exclusion_tag_none:
|
||||
{
|
||||
char const *entry;
|
||||
@@ -1196,7 +1181,6 @@ dump_dir0 (char *directory,
|
||||
name_size = name_len = strlen (name_buf);
|
||||
|
||||
/* Now output all the files in the directory. */
|
||||
/* FIXME: Should speed this up by cd-ing into the dir. */
|
||||
for (entry = directory; (entry_len = strlen (entry)) != 0;
|
||||
entry += entry_len + 1)
|
||||
{
|
||||
@@ -1207,9 +1191,9 @@ dump_dir0 (char *directory,
|
||||
}
|
||||
strcpy (name_buf + name_len, entry);
|
||||
if (!excluded_name (name_buf))
|
||||
dump_file (name_buf, false, our_device);
|
||||
dump_file (st, entry, name_buf);
|
||||
}
|
||||
|
||||
|
||||
free (name_buf);
|
||||
}
|
||||
break;
|
||||
@@ -1221,10 +1205,10 @@ dump_dir0 (char *directory,
|
||||
name_buf = xmalloc (name_size);
|
||||
strcpy (name_buf, st->orig_file_name);
|
||||
strcat (name_buf, tag_file_name);
|
||||
dump_file (name_buf, false, our_device);
|
||||
dump_file (st, tag_file_name, name_buf);
|
||||
free (name_buf);
|
||||
break;
|
||||
|
||||
|
||||
case exclusion_tag_under:
|
||||
exclusion_tag_warning (st->orig_file_name, tag_file_name,
|
||||
_("contents not dumped"));
|
||||
@@ -1246,23 +1230,73 @@ ensure_slash (char **pstr)
|
||||
(*pstr)[len] = '\0';
|
||||
}
|
||||
|
||||
/* If we just ran out of file descriptors, release a file descriptor
|
||||
in the directory chain somewhere leading from DIR->parent->parent
|
||||
up through the root. Return true if successful, false (preserving
|
||||
errno == EMFILE) otherwise.
|
||||
|
||||
Do not release DIR's file descriptor, or DIR's parent, as other
|
||||
code assumes that they work. On some operating systems, another
|
||||
process can claim file descriptor resources as we release them, and
|
||||
some calls or their emulations require multiple file descriptors,
|
||||
so callers should not give up if a single release doesn't work. */
|
||||
|
||||
static bool
|
||||
dump_dir (int fd, struct tar_stat_info *st, bool top_level,
|
||||
dev_t parent_device)
|
||||
open_failure_recover (struct tar_stat_info const *dir)
|
||||
{
|
||||
char *directory = fdsavedir (fd);
|
||||
if (!directory)
|
||||
if (errno == EMFILE && dir && dir->parent)
|
||||
{
|
||||
struct tar_stat_info *p;
|
||||
for (p = dir->parent->parent; p; p = p->parent)
|
||||
if (0 < p->fd && (! p->parent || p->parent->fd <= 0))
|
||||
{
|
||||
tar_stat_close (p);
|
||||
return true;
|
||||
}
|
||||
errno = EMFILE;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return the directory entries of ST, in a dynamically allocated buffer,
|
||||
each entry followed by '\0' and the last followed by an extra '\0'.
|
||||
Return null on failure, setting errno. */
|
||||
char *
|
||||
get_directory_entries (struct tar_stat_info *st)
|
||||
{
|
||||
while (! (st->dirstream = fdopendir (st->fd)))
|
||||
if (! open_failure_recover (st))
|
||||
return 0;
|
||||
return streamsavedir (st->dirstream);
|
||||
}
|
||||
|
||||
/* Dump the directory ST. Return true if successful, false (emitting
|
||||
diagnostics) otherwise. Get ST's entries, recurse through its
|
||||
subdirectories, and clean up file descriptors afterwards. */
|
||||
static bool
|
||||
dump_dir (struct tar_stat_info *st)
|
||||
{
|
||||
char *directory = get_directory_entries (st);
|
||||
if (! directory)
|
||||
{
|
||||
savedir_diag (st->orig_file_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
dump_dir0 (directory, st, top_level, parent_device);
|
||||
dump_dir0 (st, directory);
|
||||
|
||||
restore_parent_fd (st);
|
||||
free (directory);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Number of links a file can have without having to be entered into
|
||||
the link table. Typically this is 1, but in trickier circumstances
|
||||
it is 0. */
|
||||
static nlink_t trivial_link_count;
|
||||
|
||||
|
||||
/* Main functions of this module. */
|
||||
|
||||
@@ -1271,6 +1305,8 @@ create_archive (void)
|
||||
{
|
||||
struct name const *p;
|
||||
|
||||
trivial_link_count = name_count <= 1 && ! dereference_option;
|
||||
|
||||
open_archive (ACCESS_WRITE);
|
||||
buffer_write_global_xheader ();
|
||||
|
||||
@@ -1284,12 +1320,13 @@ create_archive (void)
|
||||
|
||||
while ((p = name_from_list ()) != NULL)
|
||||
if (!excluded_name (p->name))
|
||||
dump_file (p->name, p->cmdline, (dev_t) 0);
|
||||
dump_file (0, p->name, p->name);
|
||||
|
||||
blank_name_list ();
|
||||
while ((p = name_from_list ()) != NULL)
|
||||
if (!excluded_name (p->name))
|
||||
{
|
||||
struct tar_stat_info st;
|
||||
size_t plen = strlen (p->name);
|
||||
if (buffer_size <= plen)
|
||||
{
|
||||
@@ -1300,6 +1337,7 @@ create_archive (void)
|
||||
memcpy (buffer, p->name, plen);
|
||||
if (! ISSLASH (buffer[plen - 1]))
|
||||
buffer[plen++] = DIRECTORY_SEPARATOR;
|
||||
tar_stat_init (&st);
|
||||
q = directory_contents (gnu_list_name->directory);
|
||||
if (q)
|
||||
while (*q)
|
||||
@@ -1307,6 +1345,23 @@ create_archive (void)
|
||||
size_t qlen = strlen (q);
|
||||
if (*q == 'Y')
|
||||
{
|
||||
if (! st.orig_file_name)
|
||||
{
|
||||
int fd = openat (chdir_fd, p->name,
|
||||
open_searchdir_flags);
|
||||
if (fd < 0)
|
||||
{
|
||||
open_diag (p->name);
|
||||
break;
|
||||
}
|
||||
st.fd = fd;
|
||||
if (fstat (fd, &st.stat) != 0)
|
||||
{
|
||||
stat_diag (p->name);
|
||||
break;
|
||||
}
|
||||
st.orig_file_name = xstrdup (p->name);
|
||||
}
|
||||
if (buffer_size < plen + qlen)
|
||||
{
|
||||
while ((buffer_size *=2 ) < plen + qlen)
|
||||
@@ -1314,10 +1369,11 @@ create_archive (void)
|
||||
buffer = xrealloc (buffer, buffer_size);
|
||||
}
|
||||
strcpy (buffer + plen, q + 1);
|
||||
dump_file (buffer, false, (dev_t) 0);
|
||||
dump_file (&st, q + 1, buffer);
|
||||
}
|
||||
q += qlen + 1;
|
||||
}
|
||||
tar_stat_destroy (&st);
|
||||
}
|
||||
free (buffer);
|
||||
}
|
||||
@@ -1326,7 +1382,7 @@ create_archive (void)
|
||||
const char *name;
|
||||
while ((name = name_next (1)) != NULL)
|
||||
if (!excluded_name (name))
|
||||
dump_file (name, true, (dev_t) 0);
|
||||
dump_file (0, name, name);
|
||||
}
|
||||
|
||||
write_eot ();
|
||||
@@ -1378,7 +1434,8 @@ static Hash_table *link_table;
|
||||
static bool
|
||||
dump_hard_link (struct tar_stat_info *st)
|
||||
{
|
||||
if (link_table && (st->stat.st_nlink > 1 || remove_files_option))
|
||||
if (link_table
|
||||
&& (trivial_link_count < st->stat.st_nlink || remove_files_option))
|
||||
{
|
||||
struct link lp;
|
||||
struct link *duplicate;
|
||||
@@ -1425,7 +1482,7 @@ file_count_links (struct tar_stat_info *st)
|
||||
{
|
||||
if (hard_dereference_option)
|
||||
return;
|
||||
if (st->stat.st_nlink > 1)
|
||||
if (trivial_link_count < st->stat.st_nlink)
|
||||
{
|
||||
struct link *duplicate;
|
||||
char *linkname = NULL;
|
||||
@@ -1433,7 +1490,7 @@ file_count_links (struct tar_stat_info *st)
|
||||
|
||||
assign_string (&linkname, st->orig_file_name);
|
||||
transform_name (&linkname, XFORM_LINK);
|
||||
|
||||
|
||||
lp = xmalloc (offsetof (struct link, name)
|
||||
+ strlen (linkname) + 1);
|
||||
lp->ino = st->stat.st_ino;
|
||||
@@ -1441,13 +1498,13 @@ file_count_links (struct tar_stat_info *st)
|
||||
lp->nlink = st->stat.st_nlink;
|
||||
strcpy (lp->name, linkname);
|
||||
free (linkname);
|
||||
|
||||
|
||||
if (! ((link_table
|
||||
|| (link_table = hash_initialize (0, 0, hash_link,
|
||||
compare_links, 0)))
|
||||
&& (duplicate = hash_insert (link_table, lp))))
|
||||
xalloc_die ();
|
||||
|
||||
|
||||
if (duplicate != lp)
|
||||
abort ();
|
||||
lp->nlink--;
|
||||
@@ -1474,26 +1531,96 @@ check_links (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump a single file, recursing on directories. P is the file name
|
||||
to dump. TOP_LEVEL tells whether this is a top-level call; zero
|
||||
means no, positive means yes, and negative means the top level
|
||||
of an incremental dump. PARENT_DEVICE is the device of P's
|
||||
parent directory; it is examined only if TOP_LEVEL is zero. */
|
||||
/* Assuming DIR is the working directory, open FILE, using FLAGS to
|
||||
control the open. A null DIR means to use ".". If we are low on
|
||||
file descriptors, try to release one or more from DIR's parents to
|
||||
reuse it. */
|
||||
int
|
||||
subfile_open (struct tar_stat_info const *dir, char const *file, int flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
static bool initialized;
|
||||
if (! initialized)
|
||||
{
|
||||
/* Initialize any tables that might be needed when file
|
||||
descriptors are exhausted, and whose initialization might
|
||||
require a file descriptor. This includes the system message
|
||||
catalog and tar's message catalog. */
|
||||
initialized = true;
|
||||
strerror (ENOENT);
|
||||
gettext ("");
|
||||
}
|
||||
|
||||
while ((fd = openat (dir ? dir->fd : chdir_fd, file, flags)) < 0
|
||||
&& open_failure_recover (dir))
|
||||
continue;
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Restore the file descriptor for ST->parent, if it was temporarily
|
||||
closed to conserve file descriptors. On failure, set the file
|
||||
descriptor to the negative of the corresponding errno value. Call
|
||||
this every time a subdirectory is ascended from. */
|
||||
void
|
||||
restore_parent_fd (struct tar_stat_info const *st)
|
||||
{
|
||||
struct tar_stat_info *parent = st->parent;
|
||||
if (parent && ! parent->fd)
|
||||
{
|
||||
int parentfd = openat (st->fd, "..", open_searchdir_flags);
|
||||
struct stat parentstat;
|
||||
|
||||
if (parentfd < 0)
|
||||
parentfd = - errno;
|
||||
else if (! (fstat (parentfd, &parentstat) == 0
|
||||
&& parent->stat.st_ino == parentstat.st_ino
|
||||
&& parent->stat.st_dev == parentstat.st_dev))
|
||||
{
|
||||
close (parentfd);
|
||||
parentfd = IMPOSTOR_ERRNO;
|
||||
}
|
||||
|
||||
if (parentfd < 0)
|
||||
{
|
||||
int origfd = openat (chdir_fd, parent->orig_file_name,
|
||||
open_searchdir_flags);
|
||||
if (0 <= origfd)
|
||||
{
|
||||
if (fstat (parentfd, &parentstat) == 0
|
||||
&& parent->stat.st_ino == parentstat.st_ino
|
||||
&& parent->stat.st_dev == parentstat.st_dev)
|
||||
parentfd = origfd;
|
||||
else
|
||||
close (origfd);
|
||||
}
|
||||
}
|
||||
|
||||
parent->fd = parentfd;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump a single file, recursing on directories. ST is the file's
|
||||
status info, NAME its name relative to the parent directory, and P
|
||||
its full name (which may be relative to the working directory). */
|
||||
|
||||
/* FIXME: One should make sure that for *every* path leading to setting
|
||||
exit_status to failure, a clear diagnostic has been issued. */
|
||||
|
||||
static void
|
||||
dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
bool top_level, dev_t parent_device)
|
||||
dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
|
||||
{
|
||||
union block *header;
|
||||
char type;
|
||||
off_t original_size;
|
||||
struct timespec original_ctime;
|
||||
struct timespec restore_times[2];
|
||||
off_t block_ordinal = -1;
|
||||
int fd = 0;
|
||||
bool is_dir;
|
||||
struct tar_stat_info const *parent = st->parent;
|
||||
bool top_level = ! parent;
|
||||
int parentfd = top_level ? chdir_fd : parent->fd;
|
||||
void (*diag) (char const *) = 0;
|
||||
|
||||
if (interactive_option && !confirm ("add", p))
|
||||
return;
|
||||
@@ -1504,14 +1631,34 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
|
||||
transform_name (&st->file_name, XFORM_REGFILE);
|
||||
|
||||
if (deref_stat (dereference_option, p, &st->stat) != 0)
|
||||
if (parentfd < 0 && ! top_level)
|
||||
{
|
||||
file_removed_diag (p, top_level, stat_diag);
|
||||
errno = - parentfd;
|
||||
diag = open_diag;
|
||||
}
|
||||
else if (fstatat (parentfd, name, &st->stat, fstatat_flags) != 0)
|
||||
diag = stat_diag;
|
||||
else if (file_dumpable_p (&st->stat))
|
||||
{
|
||||
fd = subfile_open (parent, name, open_read_flags);
|
||||
if (fd < 0)
|
||||
diag = open_diag;
|
||||
else
|
||||
{
|
||||
st->fd = fd;
|
||||
if (fstat (fd, &st->stat) != 0)
|
||||
diag = stat_diag;
|
||||
}
|
||||
}
|
||||
if (diag)
|
||||
{
|
||||
file_removed_diag (p, top_level, diag);
|
||||
return;
|
||||
}
|
||||
|
||||
st->archive_file_size = original_size = st->stat.st_size;
|
||||
st->atime = restore_times[0] = get_stat_atime (&st->stat);
|
||||
st->mtime = restore_times[1] = get_stat_mtime (&st->stat);
|
||||
st->atime = get_stat_atime (&st->stat);
|
||||
st->mtime = get_stat_mtime (&st->stat);
|
||||
st->ctime = original_ctime = get_stat_ctime (&st->stat);
|
||||
|
||||
#ifdef S_ISHIDDEN
|
||||
@@ -1529,11 +1676,11 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
|
||||
/* See if we want only new files, and check if this one is too old to
|
||||
put in the archive.
|
||||
|
||||
|
||||
This check is omitted if incremental_option is set *and* the
|
||||
requested file is not explicitely listed in the command line. */
|
||||
|
||||
if (!(incremental_option && !is_individual_file (p))
|
||||
requested file is not explicitly listed in the command line. */
|
||||
|
||||
if (! (incremental_option && ! top_level)
|
||||
&& !S_ISDIR (st->stat.st_mode)
|
||||
&& OLDER_TAR_STAT_TIME (*st, m)
|
||||
&& (!after_date_option || OLDER_TAR_STAT_TIME (*st, c)))
|
||||
@@ -1562,51 +1709,31 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
file_removed_diag (p, top_level, open_diag);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir)
|
||||
{
|
||||
const char *tag_file_name;
|
||||
ensure_slash (&st->orig_file_name);
|
||||
ensure_slash (&st->file_name);
|
||||
|
||||
if (check_exclusion_tags (st->orig_file_name, &tag_file_name)
|
||||
== exclusion_tag_all)
|
||||
if (check_exclusion_tags (st, &tag_file_name) == exclusion_tag_all)
|
||||
{
|
||||
exclusion_tag_warning (st->orig_file_name, tag_file_name,
|
||||
_("directory not dumped"));
|
||||
if (fd >= 0)
|
||||
close (fd);
|
||||
return;
|
||||
}
|
||||
|
||||
ok = dump_dir (fd, st, top_level, parent_device);
|
||||
|
||||
/* dump_dir consumes FD if successful. */
|
||||
if (ok)
|
||||
fd = -1;
|
||||
ok = dump_dir (st);
|
||||
|
||||
fd = st->fd;
|
||||
parentfd = top_level ? chdir_fd : parent->fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum dump_status status;
|
||||
|
||||
if (fd != -1 && sparse_option && ST_IS_SPARSE (st->stat))
|
||||
if (fd && sparse_option && ST_IS_SPARSE (st->stat))
|
||||
{
|
||||
status = sparse_dump_file (fd, st);
|
||||
if (status == dump_status_not_implemented)
|
||||
@@ -1619,7 +1746,6 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
{
|
||||
case dump_status_ok:
|
||||
case dump_status_short:
|
||||
mv_end ();
|
||||
file_count_links (st);
|
||||
break;
|
||||
|
||||
@@ -1635,21 +1761,26 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
|
||||
if (ok)
|
||||
{
|
||||
/* 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)
|
||||
if (fd < 0)
|
||||
{
|
||||
file_removed_diag (p, top_level, stat_diag);
|
||||
errno = - fd;
|
||||
ok = false;
|
||||
}
|
||||
else if (fd == 0)
|
||||
{
|
||||
if (parentfd < 0 && ! top_level)
|
||||
{
|
||||
errno = - parentfd;
|
||||
ok = false;
|
||||
}
|
||||
else
|
||||
ok = fstatat (parentfd, name, &final_stat, fstatat_flags) == 0;
|
||||
}
|
||||
else
|
||||
ok = fstat (fd, &final_stat) == 0;
|
||||
|
||||
if (! ok)
|
||||
file_removed_diag (p, top_level, stat_diag);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
@@ -1666,16 +1797,12 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
set_exit_status (TAREXIT_DIFFERS);
|
||||
}
|
||||
else if (atime_preserve_option == replace_atime_preserve
|
||||
&& set_file_atime (fd, p, restore_times) != 0)
|
||||
&& fd && (is_dir || original_size != 0)
|
||||
&& set_file_atime (fd, parentfd, name, st->atime) != 0)
|
||||
utime_error (p);
|
||||
}
|
||||
|
||||
if (0 <= fd && close (fd) != 0)
|
||||
{
|
||||
close_diag (p);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
ok &= tar_stat_close (st);
|
||||
if (ok && remove_files_option)
|
||||
queue_deferred_unlink (p, is_dir);
|
||||
|
||||
@@ -1690,7 +1817,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
if (linklen != st->stat.st_size || linklen + 1 == 0)
|
||||
xalloc_die ();
|
||||
buffer = (char *) alloca (linklen + 1);
|
||||
size = readlink (p, buffer, linklen + 1);
|
||||
size = readlinkat (parentfd, name, buffer, linklen + 1);
|
||||
if (size < 0)
|
||||
{
|
||||
file_removed_diag (p, top_level, readlink_diag);
|
||||
@@ -1769,13 +1896,20 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
queue_deferred_unlink (p, false);
|
||||
}
|
||||
|
||||
/* Dump a file, recursively. PARENT describes the file's parent
|
||||
directory, NAME is the file's name relative to PARENT, and FULLNAME
|
||||
its full name, possibly relative to the working directory. NAME
|
||||
may contain slashes at the top level of invocation. */
|
||||
|
||||
void
|
||||
dump_file (const char *p, bool top_level, dev_t parent_device)
|
||||
dump_file (struct tar_stat_info *parent, char const *name,
|
||||
char const *fullname)
|
||||
{
|
||||
struct tar_stat_info st;
|
||||
tar_stat_init (&st);
|
||||
dump_file0 (&st, p, top_level, parent_device);
|
||||
if (listed_incremental_option)
|
||||
update_parent_directory (p);
|
||||
st.parent = parent;
|
||||
dump_file0 (&st, name, fullname);
|
||||
if (parent && listed_incremental_option)
|
||||
update_parent_directory (parent);
|
||||
tar_stat_destroy (&st);
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ delete_archive_members (void)
|
||||
|
||||
if (current_block == record_end)
|
||||
flush_archive ();
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
|
||||
xheader_decode (¤t_stat_info);
|
||||
@@ -296,7 +296,7 @@ delete_archive_members (void)
|
||||
set_next_block_after (current_header);
|
||||
blocks_to_skip = (current_stat_info.stat.st_size
|
||||
+ BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
|
||||
|
||||
while (record_end - current_block <= blocks_to_skip)
|
||||
{
|
||||
blocks_to_skip -= (record_end - current_block);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* This file is part of GNU tar.
|
||||
/* This file is part of GNU tar.
|
||||
Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
988
src/extract.c
988
src/extract.c
File diff suppressed because it is too large
Load Diff
193
src/incremen.c
193
src/incremen.c
@@ -43,7 +43,7 @@ enum children
|
||||
#define DIR_IS_INITED(d) ((d)->flags & DIRF_INIT)
|
||||
#define DIR_IS_NFS(d) ((d)->flags & DIRF_NFS)
|
||||
#define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
|
||||
#define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
|
||||
/* #define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW) FIXME: not used */
|
||||
#define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
|
||||
|
||||
#define DIR_SET_FLAG(d,f) (d)->flags |= (f)
|
||||
@@ -77,14 +77,14 @@ struct directory
|
||||
char *name; /* file name of directory */
|
||||
};
|
||||
|
||||
struct dumpdir *
|
||||
static struct dumpdir *
|
||||
dumpdir_create0 (const char *contents, const char *cmask)
|
||||
{
|
||||
struct dumpdir *dump;
|
||||
size_t i, total, ctsize, len;
|
||||
char *p;
|
||||
const char *q;
|
||||
|
||||
|
||||
for (i = 0, total = 0, ctsize = 1, q = contents; *q; total++, q += len)
|
||||
{
|
||||
len = strlen (q) + 1;
|
||||
@@ -108,13 +108,13 @@ dumpdir_create0 (const char *contents, const char *cmask)
|
||||
return dump;
|
||||
}
|
||||
|
||||
struct dumpdir *
|
||||
static struct dumpdir *
|
||||
dumpdir_create (const char *contents)
|
||||
{
|
||||
return dumpdir_create0 (contents, "YND");
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
dumpdir_free (struct dumpdir *dump)
|
||||
{
|
||||
free (dump->elv);
|
||||
@@ -131,7 +131,7 @@ compare_dirnames (const void *first, const void *second)
|
||||
|
||||
/* Locate NAME in the dumpdir array DUMP.
|
||||
Return pointer to the slot in DUMP->contents, or NULL if not found */
|
||||
char *
|
||||
static char *
|
||||
dumpdir_locate (struct dumpdir *dump, const char *name)
|
||||
{
|
||||
char **ptr;
|
||||
@@ -146,16 +146,16 @@ dumpdir_locate (struct dumpdir *dump, const char *name)
|
||||
struct dumpdir_iter
|
||||
{
|
||||
struct dumpdir *dump; /* Dumpdir being iterated */
|
||||
int all; /* Iterate over all entries, not only D/N/Y */
|
||||
int all; /* Iterate over all entries, not only D/N/Y */
|
||||
size_t next; /* Index of the next element */
|
||||
};
|
||||
|
||||
char *
|
||||
static char *
|
||||
dumpdir_next (struct dumpdir_iter *itr)
|
||||
{
|
||||
size_t cur = itr->next;
|
||||
char *ret = NULL;
|
||||
|
||||
|
||||
if (itr->all)
|
||||
{
|
||||
ret = itr->dump->contents + cur;
|
||||
@@ -172,7 +172,7 @@ dumpdir_next (struct dumpdir_iter *itr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
static char *
|
||||
dumpdir_first (struct dumpdir *dump, int all, struct dumpdir_iter **pitr)
|
||||
{
|
||||
struct dumpdir_iter *itr = xmalloc (sizeof (*itr));
|
||||
@@ -258,7 +258,7 @@ make_directory (const char *name, char *caname)
|
||||
directory->dump = directory->idump = NULL;
|
||||
directory->orig = NULL;
|
||||
directory->flags = false;
|
||||
if (namelen && ISSLASH (name[namelen - 1]))
|
||||
if (namelen > 1 && ISSLASH (name[namelen - 1]))
|
||||
namelen--;
|
||||
directory->name = xmalloc (namelen + 1);
|
||||
memcpy (directory->name, name, namelen);
|
||||
@@ -288,9 +288,9 @@ attach_directory (const char *name)
|
||||
dirtail = dir;
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
static void
|
||||
dirlist_replace_prefix (const char *pref, const char *repl)
|
||||
{
|
||||
struct directory *dp;
|
||||
@@ -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 (fstat (parent->fd, &st) != 0)
|
||||
stat_diag (directory->name);
|
||||
else
|
||||
directory->mtime = get_stat_mtime (&st);
|
||||
}
|
||||
free (p);
|
||||
}
|
||||
|
||||
#define PD_FORCE_CHILDREN 0x10
|
||||
@@ -429,12 +420,12 @@ 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;
|
||||
bool nfs = NFS_FILE_STAT (*stat_data);
|
||||
|
||||
if ((directory = find_directory (name_buffer)) != NULL)
|
||||
@@ -457,14 +448,14 @@ procdir (const char *name_buffer, struct stat *stat_data,
|
||||
*entry = 'N';
|
||||
return directory;
|
||||
}
|
||||
|
||||
|
||||
/* With NFS, the same file can have two different devices
|
||||
if an NFS directory is mounted in multiple locations,
|
||||
which is relatively common when automounting.
|
||||
To avoid spurious incremental redumping of
|
||||
directories, consider all NFS devices as equal,
|
||||
relying on the i-node to establish differences. */
|
||||
|
||||
|
||||
if (! ((!check_device_option
|
||||
|| (DIR_IS_NFS (directory) && nfs)
|
||||
|| directory->device_number == stat_data->st_dev)
|
||||
@@ -502,14 +493,14 @@ procdir (const char *name_buffer, struct stat *stat_data,
|
||||
}
|
||||
else
|
||||
directory->children = CHANGED_CHILDREN;
|
||||
|
||||
|
||||
DIR_SET_FLAG (directory, DIRF_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct directory *d = find_directory_meta (stat_data->st_dev,
|
||||
stat_data->st_ino);
|
||||
|
||||
|
||||
directory = note_directory (name_buffer,
|
||||
get_stat_mtime(stat_data),
|
||||
stat_data->st_dev,
|
||||
@@ -548,12 +539,9 @@ procdir (const char *name_buffer, struct stat *stat_data,
|
||||
}
|
||||
}
|
||||
|
||||
/* If the directory is on another device and --one-file-system was given,
|
||||
omit it... */
|
||||
if (one_file_system_option && device != stat_data->st_dev
|
||||
/* ... except if it was explicitely given in the command line */
|
||||
&& !is_individual_file (name_buffer))
|
||||
/* FIXME:
|
||||
if (one_file_system_option && st->parent
|
||||
&& stat_data->st_dev != st->parent->stat.st_dev)
|
||||
/* FIXME:
|
||||
WARNOPT (WARN_XDEV,
|
||||
(0, 0,
|
||||
_("%s: directory is on a different filesystem; not dumped"),
|
||||
@@ -566,14 +554,14 @@ procdir (const char *name_buffer, struct stat *stat_data,
|
||||
if (directory->children == NO_CHILDREN)
|
||||
*entry = 'N';
|
||||
}
|
||||
|
||||
|
||||
DIR_SET_FLAG (directory, DIRF_INIT);
|
||||
|
||||
if (directory->children != NO_CHILDREN)
|
||||
{
|
||||
const char *tag_file_name;
|
||||
|
||||
switch (check_exclusion_tags (name_buffer, &tag_file_name))
|
||||
switch (check_exclusion_tags (st, &tag_file_name))
|
||||
{
|
||||
case exclusion_tag_all:
|
||||
/* This warning can be duplicated by code in dump_file0, but only
|
||||
@@ -590,13 +578,13 @@ procdir (const char *name_buffer, struct stat *stat_data,
|
||||
_("contents not dumped"));
|
||||
directory->children = NO_CHILDREN;
|
||||
break;
|
||||
|
||||
|
||||
case exclusion_tag_under:
|
||||
exclusion_tag_warning (name_buffer, tag_file_name,
|
||||
_("contents not dumped"));
|
||||
directory->tagfile = tag_file_name;
|
||||
break;
|
||||
|
||||
|
||||
case exclusion_tag_none:
|
||||
break;
|
||||
}
|
||||
@@ -616,7 +604,7 @@ procdir (const char *name_buffer, struct stat *stat_data,
|
||||
DIRECTORY->dump is replaced with the created template. Each entry is
|
||||
prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
|
||||
|
||||
void
|
||||
static void
|
||||
makedumpdir (struct directory *directory, const char *dir)
|
||||
{
|
||||
size_t i,
|
||||
@@ -682,40 +670,29 @@ 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;
|
||||
char *dirp = get_directory_entries (st);
|
||||
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;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
free (tmp);
|
||||
|
||||
nbuf = namebuf_create (dir);
|
||||
@@ -723,7 +700,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
|
||||
if (dirp && directory->children != NO_CHILDREN)
|
||||
{
|
||||
char *entry; /* directory entry being scanned */
|
||||
dumpdir_iter_t itr;
|
||||
struct dumpdir_iter *itr;
|
||||
|
||||
makedumpdir (directory, dirp);
|
||||
|
||||
@@ -739,14 +716,37 @@ scan_directory (char *dir, dev_t device, bool cmdline)
|
||||
*entry = 'N';
|
||||
else
|
||||
{
|
||||
if (deref_stat (dereference_option, full_name, &stat_data))
|
||||
int fd = st->fd;
|
||||
void (*diag) (char const *) = 0;
|
||||
struct tar_stat_info stsub;
|
||||
tar_stat_init (&stsub);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
file_removed_diag (full_name, false, stat_diag);
|
||||
*entry = 'N';
|
||||
continue;
|
||||
errno = - fd;
|
||||
diag = open_diag;
|
||||
}
|
||||
else if (fstatat (fd, entry + 1, &stsub.stat, fstatat_flags) != 0)
|
||||
diag = stat_diag;
|
||||
else if (S_ISDIR (stsub.stat.st_mode))
|
||||
{
|
||||
int subfd = subfile_open (st, entry + 1, open_read_flags);
|
||||
if (subfd < 0)
|
||||
diag = open_diag;
|
||||
else
|
||||
{
|
||||
stsub.fd = subfd;
|
||||
if (fstat (subfd, &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 +754,24 @@ 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);
|
||||
|
||||
stsub.parent = st;
|
||||
procdir (full_name, &stsub, pd_flag, entry);
|
||||
restore_parent_fd (&stsub);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -778,8 +779,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
|
||||
|
||||
namebuf_free (nbuf);
|
||||
|
||||
if (dirp)
|
||||
free (dirp);
|
||||
free (dirp);
|
||||
|
||||
return directory;
|
||||
}
|
||||
@@ -801,17 +801,11 @@ 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 *from, char *to)
|
||||
obstack_code_rename (struct obstack *stk, char const *from, char const *to)
|
||||
{
|
||||
char *s;
|
||||
char const *s;
|
||||
|
||||
s = from[0] == 0 ? from :
|
||||
safer_name_suffix (from, false, absolute_names_option);
|
||||
@@ -874,7 +868,7 @@ append_incremental_renames (struct directory *dir)
|
||||
size_t size;
|
||||
struct directory *dp;
|
||||
const char *dump;
|
||||
|
||||
|
||||
if (dirhead == NULL)
|
||||
return;
|
||||
|
||||
@@ -891,7 +885,8 @@ append_incremental_renames (struct directory *dir)
|
||||
for (dp = dirhead; dp; dp = dp->next)
|
||||
store_rename (dp, &stk);
|
||||
|
||||
if (obstack_object_size (&stk) != size)
|
||||
/* FIXME: Is this the right thing to do when DIR is null? */
|
||||
if (dir && obstack_object_size (&stk) != size)
|
||||
{
|
||||
obstack_1grow (&stk, 0);
|
||||
dumpdir_free (dir->dump);
|
||||
@@ -1217,7 +1212,7 @@ read_timespec (FILE *fp, struct timespec *pval)
|
||||
|
||||
/* Read incremental snapshot format 2 */
|
||||
static void
|
||||
read_incr_db_2 ()
|
||||
read_incr_db_2 (void)
|
||||
{
|
||||
uintmax_t u;
|
||||
struct obstack stk;
|
||||
@@ -1312,7 +1307,7 @@ read_directory_file (void)
|
||||
which is necessary to recreate absolute file names. */
|
||||
name_from_list ();
|
||||
blank_name_list ();
|
||||
|
||||
|
||||
if (0 < getline (&buf, &bufsize, listed_incremental_stream))
|
||||
{
|
||||
char *ebuf;
|
||||
@@ -1352,8 +1347,7 @@ read_directory_file (void)
|
||||
|
||||
if (ferror (listed_incremental_stream))
|
||||
read_error (listed_incremental_option);
|
||||
if (buf)
|
||||
free (buf);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
/* Output incremental data for the directory ENTRY to the file DATA.
|
||||
@@ -1367,7 +1361,7 @@ write_directory_file_entry (void *entry, void *data)
|
||||
if (DIR_IS_FOUND (directory))
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
char *s;
|
||||
char const *s;
|
||||
|
||||
s = DIR_IS_NFS (directory) ? "1" : "0";
|
||||
fwrite (s, 2, 1, fp);
|
||||
@@ -1386,7 +1380,7 @@ write_directory_file_entry (void *entry, void *data)
|
||||
if (directory->dump)
|
||||
{
|
||||
const char *p;
|
||||
dumpdir_iter_t itr;
|
||||
struct dumpdir_iter *itr;
|
||||
|
||||
for (p = dumpdir_first (directory->dump, 0, &itr);
|
||||
p;
|
||||
@@ -1452,7 +1446,7 @@ get_gnu_dumpdir (struct tar_stat_info *stat_info)
|
||||
to = archive_dir;
|
||||
|
||||
set_next_block_after (current_header);
|
||||
mv_begin (stat_info);
|
||||
mv_begin_read (stat_info);
|
||||
|
||||
for (; size > 0; size -= copied)
|
||||
{
|
||||
@@ -1664,11 +1658,10 @@ try_purge_directory (char const *directory_name)
|
||||
{
|
||||
const char *entry;
|
||||
struct stat st;
|
||||
if (p)
|
||||
free (p);
|
||||
free (p);
|
||||
p = new_name (directory_name, cur);
|
||||
|
||||
if (deref_stat (false, p, &st))
|
||||
if (deref_stat (p, &st) != 0)
|
||||
{
|
||||
if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
|
||||
dirs and check it here? */
|
||||
@@ -1707,7 +1700,7 @@ try_purge_directory (char const *directory_name)
|
||||
}
|
||||
free (p);
|
||||
dumpdir_free (dump);
|
||||
|
||||
|
||||
free (current_dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
211
src/list.c
211
src/list.c
@@ -35,6 +35,20 @@ size_t recent_long_name_blocks; /* number of blocks in recent_long_name */
|
||||
size_t recent_long_link_blocks; /* likewise, for long link */
|
||||
union block *recent_global_header; /* Recent global header block */
|
||||
|
||||
#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where))
|
||||
#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where))
|
||||
#define MINOR_FROM_HEADER(where) minor_from_header (where, sizeof (where))
|
||||
#define MODE_FROM_HEADER(where, hbits) \
|
||||
mode_from_header (where, sizeof (where), hbits)
|
||||
#define TIME_FROM_HEADER(where) time_from_header (where, sizeof (where))
|
||||
#define UID_FROM_HEADER(where) uid_from_header (where, sizeof (where))
|
||||
|
||||
static gid_t gid_from_header (const char *buf, size_t size);
|
||||
static major_t major_from_header (const char *buf, size_t size);
|
||||
static minor_t minor_from_header (const char *buf, size_t size);
|
||||
static mode_t mode_from_header (const char *buf, size_t size, unsigned *hbits);
|
||||
static time_t time_from_header (const char *buf, size_t size);
|
||||
static uid_t uid_from_header (const char *buf, size_t size);
|
||||
static uintmax_t from_header (const char *, size_t, const char *,
|
||||
uintmax_t, uintmax_t, bool, bool);
|
||||
|
||||
@@ -61,6 +75,66 @@ base64_init (void)
|
||||
base64_map[(int) base_64_digits[i]] = i;
|
||||
}
|
||||
|
||||
static char *
|
||||
decode_xform (char *file_name, void *data)
|
||||
{
|
||||
int type = *(int*)data;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case XFORM_SYMLINK:
|
||||
/* FIXME: It is not quite clear how and to which extent are the symbolic
|
||||
links subject to filename transformation. In the absence of another
|
||||
solution, symbolic links are exempt from component stripping and
|
||||
name suffix normalization, but subject to filename transformation
|
||||
proper. */
|
||||
return file_name;
|
||||
|
||||
case XFORM_LINK:
|
||||
file_name = safer_name_suffix (file_name, true, absolute_names_option);
|
||||
break;
|
||||
|
||||
case XFORM_REGFILE:
|
||||
file_name = safer_name_suffix (file_name, false, absolute_names_option);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strip_name_components)
|
||||
{
|
||||
size_t prefix_len = stripped_prefix_len (file_name,
|
||||
strip_name_components);
|
||||
if (prefix_len == (size_t) -1)
|
||||
prefix_len = strlen (file_name);
|
||||
file_name += prefix_len;
|
||||
}
|
||||
return file_name;
|
||||
}
|
||||
|
||||
static bool
|
||||
transform_member_name (char **pinput, int type)
|
||||
{
|
||||
return transform_name_fp (pinput, type, decode_xform, &type);
|
||||
}
|
||||
|
||||
void
|
||||
transform_stat_info (int typeflag, struct tar_stat_info *stat_info)
|
||||
{
|
||||
if (typeflag == GNUTYPE_VOLHDR)
|
||||
/* Name transformations don't apply to volume headers. */
|
||||
return;
|
||||
|
||||
transform_member_name (&stat_info->file_name, XFORM_REGFILE);
|
||||
switch (typeflag)
|
||||
{
|
||||
case SYMTYPE:
|
||||
transform_member_name (&stat_info->link_name, XFORM_SYMLINK);
|
||||
break;
|
||||
|
||||
case LNKTYPE:
|
||||
transform_member_name (&stat_info->link_name, XFORM_LINK);
|
||||
}
|
||||
}
|
||||
|
||||
/* Main loop for reading an archive. */
|
||||
void
|
||||
read_and (void (*do_something) (void))
|
||||
@@ -78,7 +152,7 @@ read_and (void (*do_something) (void))
|
||||
prev_status = status;
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
switch (status)
|
||||
{
|
||||
@@ -90,7 +164,8 @@ read_and (void (*do_something) (void))
|
||||
|
||||
/* Valid header. We should decode next field (mode) first.
|
||||
Ensure incoming names are null terminated. */
|
||||
|
||||
decode_header (current_header, ¤t_stat_info,
|
||||
¤t_format, 1);
|
||||
if (! name_match (current_stat_info.file_name)
|
||||
|| (NEWER_OPTION_INITIALIZED (newer_mtime_option)
|
||||
/* FIXME: We get mtime now, and again later; this causes
|
||||
@@ -116,13 +191,12 @@ read_and (void (*do_something) (void))
|
||||
quotearg_colon (current_stat_info.file_name)));
|
||||
/* Fall through. */
|
||||
default:
|
||||
decode_header (current_header,
|
||||
¤t_stat_info, ¤t_format, 0);
|
||||
skip_member ();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
transform_stat_info (current_header->header.typeflag,
|
||||
¤t_stat_info);
|
||||
(*do_something) ();
|
||||
continue;
|
||||
|
||||
@@ -140,7 +214,7 @@ read_and (void (*do_something) (void))
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
if (status == HEADER_ZERO_BLOCK)
|
||||
break;
|
||||
@@ -210,8 +284,6 @@ list_archive (void)
|
||||
off_t block_ordinal = current_block_ordinal ();
|
||||
|
||||
/* Print the header block. */
|
||||
|
||||
decode_header (current_header, ¤t_stat_info, ¤t_format, 0);
|
||||
if (verbose_option)
|
||||
print_header (¤t_stat_info, current_header, block_ordinal);
|
||||
|
||||
@@ -361,15 +433,13 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
|
||||
if (header->header.typeflag == GNUTYPE_LONGNAME)
|
||||
{
|
||||
if (next_long_name)
|
||||
free (next_long_name);
|
||||
free (next_long_name);
|
||||
next_long_name = header_copy;
|
||||
next_long_name_blocks = size / BLOCKSIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (next_long_link)
|
||||
free (next_long_link);
|
||||
free (next_long_link);
|
||||
next_long_link = header_copy;
|
||||
next_long_link_blocks = size / BLOCKSIZE;
|
||||
}
|
||||
@@ -428,8 +498,7 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
struct posix_header const *h = &header->header;
|
||||
char namebuf[sizeof h->prefix + 1 + NAME_FIELD_SIZE + 1];
|
||||
|
||||
if (recent_long_name)
|
||||
free (recent_long_name);
|
||||
free (recent_long_name);
|
||||
|
||||
if (next_long_name)
|
||||
{
|
||||
@@ -460,8 +529,7 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
assign_string (&info->file_name, name);
|
||||
info->had_trailing_slash = strip_trailing_slashes (info->file_name);
|
||||
|
||||
if (recent_long_link)
|
||||
free (recent_long_link);
|
||||
free (recent_long_link);
|
||||
|
||||
if (next_long_link)
|
||||
{
|
||||
@@ -484,47 +552,6 @@ read_header (union block **return_block, struct tar_stat_info *info,
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
decode_xform (char *file_name, void *data)
|
||||
{
|
||||
int type = *(int*)data;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case XFORM_SYMLINK:
|
||||
/* FIXME: It is not quite clear how and to which extent are the symbolic
|
||||
links subject to filename transformation. In the absence of another
|
||||
solution, symbolic links are exempt from component stripping and
|
||||
name suffix normalization, but subject to filename transformation
|
||||
proper. */
|
||||
return file_name;
|
||||
|
||||
case XFORM_LINK:
|
||||
file_name = safer_name_suffix (file_name, true, absolute_names_option);
|
||||
break;
|
||||
|
||||
case XFORM_REGFILE:
|
||||
file_name = safer_name_suffix (file_name, false, absolute_names_option);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strip_name_components)
|
||||
{
|
||||
size_t prefix_len = stripped_prefix_len (file_name,
|
||||
strip_name_components);
|
||||
if (prefix_len == (size_t) -1)
|
||||
prefix_len = strlen (file_name);
|
||||
file_name += prefix_len;
|
||||
}
|
||||
return file_name;
|
||||
}
|
||||
|
||||
bool
|
||||
transform_member_name (char **pinput, int type)
|
||||
{
|
||||
return transform_name_fp (pinput, type, decode_xform, &type);
|
||||
}
|
||||
|
||||
#define ISOCTAL(c) ((c)>='0'&&(c)<='7')
|
||||
|
||||
/* Decode things from a file HEADER block into STAT_INFO, also setting
|
||||
@@ -547,7 +574,7 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
enum archive_format format;
|
||||
unsigned hbits; /* high bits of the file mode. */
|
||||
mode_t mode = MODE_FROM_HEADER (header->header.mode, &hbits);
|
||||
|
||||
|
||||
if (strcmp (header->header.magic, TMAGIC) == 0)
|
||||
{
|
||||
if (header->star_header.prefix[130] == 0
|
||||
@@ -561,7 +588,9 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
else
|
||||
format = USTAR_FORMAT;
|
||||
}
|
||||
else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
|
||||
else if (strcmp (header->buffer + offsetof (struct posix_header, magic),
|
||||
OLDGNU_MAGIC)
|
||||
== 0)
|
||||
format = hbits ? OLDGNU_FORMAT : GNU_FORMAT;
|
||||
else
|
||||
format = V7_FORMAT;
|
||||
@@ -644,19 +673,9 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
|| stat_info->dumpdir)
|
||||
stat_info->is_dumpdir = true;
|
||||
}
|
||||
|
||||
transform_member_name (&stat_info->file_name, XFORM_REGFILE);
|
||||
switch (header->header.typeflag)
|
||||
{
|
||||
case SYMTYPE:
|
||||
transform_member_name (&stat_info->link_name, XFORM_SYMLINK);
|
||||
break;
|
||||
|
||||
case LNKTYPE:
|
||||
transform_member_name (&stat_info->link_name, XFORM_LINK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Convert buffer at WHERE0 of size DIGS from external format to
|
||||
uintmax_t. DIGS must be positive. If TYPE is nonnull, the data
|
||||
are of type TYPE. The buffer must represent a value in the range
|
||||
@@ -877,7 +896,7 @@ from_header (char const *where0, size_t digs, char const *type,
|
||||
return -1;
|
||||
}
|
||||
|
||||
gid_t
|
||||
static gid_t
|
||||
gid_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "gid_t",
|
||||
@@ -886,7 +905,7 @@ gid_from_header (const char *p, size_t s)
|
||||
false, false);
|
||||
}
|
||||
|
||||
major_t
|
||||
static major_t
|
||||
major_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "major_t",
|
||||
@@ -894,7 +913,7 @@ major_from_header (const char *p, size_t s)
|
||||
(uintmax_t) TYPE_MAXIMUM (major_t), false, false);
|
||||
}
|
||||
|
||||
minor_t
|
||||
static minor_t
|
||||
minor_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "minor_t",
|
||||
@@ -904,7 +923,7 @@ minor_from_header (const char *p, size_t s)
|
||||
|
||||
/* Convert P to the file mode, as understood by tar.
|
||||
Store unrecognized mode bits (from 10th up) in HBITS. */
|
||||
mode_t
|
||||
static mode_t
|
||||
mode_from_header (const char *p, size_t s, unsigned *hbits)
|
||||
{
|
||||
unsigned u = from_header (p, s, "mode_t",
|
||||
@@ -935,14 +954,7 @@ off_from_header (const char *p, size_t s)
|
||||
(uintmax_t) TYPE_MAXIMUM (off_t), false, false);
|
||||
}
|
||||
|
||||
size_t
|
||||
size_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "size_t", (uintmax_t) 0,
|
||||
(uintmax_t) TYPE_MAXIMUM (size_t), false, false);
|
||||
}
|
||||
|
||||
time_t
|
||||
static time_t
|
||||
time_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "time_t",
|
||||
@@ -950,7 +962,7 @@ time_from_header (const char *p, size_t s)
|
||||
(uintmax_t) TYPE_MAXIMUM (time_t), false, false);
|
||||
}
|
||||
|
||||
uid_t
|
||||
static uid_t
|
||||
uid_from_header (const char *p, size_t s)
|
||||
{
|
||||
return from_header (p, s, "uid_t",
|
||||
@@ -1146,7 +1158,7 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
|
||||
|
||||
/* Time stamp. */
|
||||
|
||||
time_stamp = tartime (st->mtime, false);
|
||||
time_stamp = tartime (st->mtime, full_time_option);
|
||||
time_stamp_len = strlen (time_stamp);
|
||||
if (datewidth < time_stamp_len)
|
||||
datewidth = time_stamp_len;
|
||||
@@ -1292,8 +1304,8 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_volume_label ()
|
||||
static void
|
||||
print_volume_label (void)
|
||||
{
|
||||
struct tar_stat_info vstat;
|
||||
union block vblk;
|
||||
@@ -1356,7 +1368,7 @@ skip_file (off_t size)
|
||||
{
|
||||
union block *x;
|
||||
|
||||
/* FIXME: Make sure mv_begin is always called before it */
|
||||
/* FIXME: Make sure mv_begin_read is always called before it */
|
||||
|
||||
if (seekable_archive)
|
||||
{
|
||||
@@ -1391,7 +1403,7 @@ skip_member (void)
|
||||
char save_typeflag = current_header->header.typeflag;
|
||||
set_next_block_after (current_header);
|
||||
|
||||
mv_begin (¤t_stat_info);
|
||||
mv_begin_read (¤t_stat_info);
|
||||
|
||||
if (current_stat_info.is_sparse)
|
||||
sparse_skip_file (¤t_stat_info);
|
||||
@@ -1412,22 +1424,23 @@ test_archive_label ()
|
||||
if (read_header (¤t_header, ¤t_stat_info, read_header_auto)
|
||||
== HEADER_SUCCESS)
|
||||
{
|
||||
char *s = NULL;
|
||||
|
||||
decode_header (current_header,
|
||||
¤t_stat_info, ¤t_format, 0);
|
||||
if (current_header->header.typeflag == GNUTYPE_VOLHDR)
|
||||
assign_string (&volume_label, current_header->header.name);
|
||||
|
||||
if (volume_label
|
||||
&& (name_match (volume_label)
|
||||
|| (multi_volume_option
|
||||
&& (s = drop_volume_label_suffix (volume_label))
|
||||
&& name_match (s))))
|
||||
if (verbose_option)
|
||||
print_volume_label ();
|
||||
free (s);
|
||||
if (volume_label)
|
||||
{
|
||||
if (verbose_option)
|
||||
print_volume_label ();
|
||||
if (!name_match (volume_label) && multi_volume_option)
|
||||
{
|
||||
char *s = drop_volume_label_suffix (volume_label);
|
||||
name_match (s);
|
||||
free (s);
|
||||
}
|
||||
}
|
||||
}
|
||||
close_archive ();
|
||||
names_notfound ();
|
||||
label_notfound ();
|
||||
}
|
||||
|
||||
283
src/misc.c
283
src/misc.c
@@ -1,7 +1,7 @@
|
||||
/* Miscellaneous functions, not really specific to GNU tar.
|
||||
|
||||
Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
|
||||
2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
|
||||
2003, 2004, 2005, 2006, 2007, 2009, 2010 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
|
||||
@@ -21,17 +21,12 @@
|
||||
#include <rmt.h>
|
||||
#include "common.h"
|
||||
#include <quotearg.h>
|
||||
#include <save-cwd.h>
|
||||
#include <xgetcwd.h>
|
||||
#include <unlinkdir.h>
|
||||
#include <utimens.h>
|
||||
#include <canonicalize.h>
|
||||
|
||||
#if HAVE_STROPTS_H
|
||||
# include <stropts.h>
|
||||
#endif
|
||||
#if HAVE_SYS_FILIO_H
|
||||
# include <sys/filio.h>
|
||||
#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
|
||||
# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
|
||||
#endif
|
||||
|
||||
|
||||
@@ -42,11 +37,13 @@
|
||||
void
|
||||
assign_string (char **string, const char *value)
|
||||
{
|
||||
if (*string)
|
||||
free (*string);
|
||||
free (*string);
|
||||
*string = value ? xstrdup (value) : 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* This function is currently unused; perhaps it should be removed? */
|
||||
|
||||
/* Allocate a copy of the string quoted as in C, and returns that. If
|
||||
the string does not have to be quoted, it returns a null pointer.
|
||||
The allocated copy should normally be freed with free() after the
|
||||
@@ -59,7 +56,7 @@ assign_string (char **string, const char *value)
|
||||
when reading directory files. This means that we can't use
|
||||
quotearg, as quotearg is locale-dependent and is meant for human
|
||||
consumption. */
|
||||
char *
|
||||
static char *
|
||||
quote_copy_string (const char *string)
|
||||
{
|
||||
const char *source = string;
|
||||
@@ -100,6 +97,7 @@ quote_copy_string (const char *string)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Takes a quoted C string (like those produced by quote_copy_string)
|
||||
and turns it back into the un-quoted original. This is done in
|
||||
@@ -230,10 +228,79 @@ zap_slashes (char *name)
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Normalize FILE_NAME by removing redundant slashes and "."
|
||||
components, including redundant trailing slashes. Leave ".."
|
||||
alone, as it may be significant in the presence of symlinks and on
|
||||
platforms where "/.." != "/". Destructive version: modifies its
|
||||
argument. */
|
||||
static void
|
||||
normalize_filename_x (char *file_name)
|
||||
{
|
||||
char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
|
||||
char *p;
|
||||
char const *q;
|
||||
char c;
|
||||
|
||||
/* Don't squeeze leading "//" to "/", on hosts where they're distinct. */
|
||||
name += (DOUBLE_SLASH_IS_DISTINCT_ROOT
|
||||
&& ISSLASH (*name) && ISSLASH (name[1]) && ! ISSLASH (name[2]));
|
||||
|
||||
/* Omit redundant leading "." components. */
|
||||
for (q = p = name; (*p = *q) == '.' && ISSLASH (q[1]); p += !*q)
|
||||
for (q += 2; ISSLASH (*q); q++)
|
||||
continue;
|
||||
|
||||
/* Copy components from Q to P, omitting redundant slashes and
|
||||
internal "." components. */
|
||||
while ((*p++ = c = *q++) != '\0')
|
||||
if (ISSLASH (c))
|
||||
while (ISSLASH (q[*q == '.']))
|
||||
q += (*q == '.') + 1;
|
||||
|
||||
/* Omit redundant trailing "." component and slash. */
|
||||
if (2 < p - name)
|
||||
{
|
||||
p -= p[-2] == '.' && ISSLASH (p[-3]);
|
||||
p -= 2 < p - name && ISSLASH (p[-2]);
|
||||
p[-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Normalize NAME by removing redundant slashes and "." components,
|
||||
including redundant trailing slashes. Return a normalized
|
||||
newly-allocated copy. */
|
||||
|
||||
char *
|
||||
normalize_filename (const char *name)
|
||||
{
|
||||
return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING));
|
||||
char *copy = NULL;
|
||||
|
||||
if (IS_RELATIVE_FILE_NAME (name))
|
||||
{
|
||||
/* Set COPY to the absolute file name if possible.
|
||||
|
||||
FIXME: There should be no need to get the absolute file name.
|
||||
getcwd is slow, it might fail, and it does not necessarily
|
||||
return a canonical name even when it succeeds. Perhaps we
|
||||
can use dev+ino pairs instead of names? */
|
||||
copy = xgetcwd ();
|
||||
if (copy)
|
||||
{
|
||||
size_t copylen = strlen (copy);
|
||||
bool need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
|
||||
&& copylen == 2 && ISSLASH (copy[1]));
|
||||
copy = xrealloc (copy, copylen + need_separator + strlen (name) + 1);
|
||||
copy[copylen] = DIRECTORY_SEPARATOR;
|
||||
strcpy (copy + copylen + need_separator, name);
|
||||
}
|
||||
else
|
||||
WARN ((0, errno, _("Cannot get working directory")));
|
||||
}
|
||||
|
||||
if (! copy)
|
||||
copy = xstrdup (name);
|
||||
normalize_filename_x (copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +368,7 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
|
||||
/* ignore invalid values of ns */
|
||||
if (BILLION <= ns || ns < 0)
|
||||
ns = 0;
|
||||
|
||||
|
||||
if (negative && ns != 0)
|
||||
{
|
||||
s++;
|
||||
@@ -322,7 +389,7 @@ static char *before_backup_name;
|
||||
static char *after_backup_name;
|
||||
|
||||
/* Return 1 if FILE_NAME is obviously "." or "/". */
|
||||
static bool
|
||||
bool
|
||||
must_be_dot_or_slash (char const *file_name)
|
||||
{
|
||||
file_name += FILE_SYSTEM_PREFIX_LEN (file_name);
|
||||
@@ -363,7 +430,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,
|
||||
@@ -383,7 +450,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
|
||||
@@ -399,7 +466,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:
|
||||
@@ -476,7 +543,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 (deref_stat (file_name, &file_stat) != 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
return true;
|
||||
@@ -496,7 +563,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"),
|
||||
@@ -523,7 +591,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"),
|
||||
@@ -538,39 +607,36 @@ undo_last_backup (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Depending on DEREF, apply either stat or lstat to (NAME, BUF). */
|
||||
/* Apply either stat or lstat to (NAME, BUF), depending on the
|
||||
presence of the --dereference option. NAME is relative to the
|
||||
most-recent argument to chdir_do. */
|
||||
int
|
||||
deref_stat (bool deref, char const *name, struct stat *buf)
|
||||
deref_stat (char const *name, struct stat *buf)
|
||||
{
|
||||
return deref ? stat (name, buf) : lstat (name, buf);
|
||||
return fstatat (chdir_fd, name, buf, fstatat_flags);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
/* Set FD's (i.e., assuming the working directory is PARENTFD, FILE's)
|
||||
access time to ATIME. */
|
||||
int
|
||||
set_file_atime (int fd, char const *file, struct timespec const timespec[2])
|
||||
set_file_atime (int fd, int parentfd, char const *file, struct timespec atime)
|
||||
{
|
||||
#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 gl_futimens (fd, file, timespec);
|
||||
struct timespec ts[2];
|
||||
ts[0] = atime;
|
||||
ts[1].tv_nsec = UTIME_OMIT;
|
||||
return fdutimensat (fd, parentfd, file, ts, fstatat_flags);
|
||||
}
|
||||
|
||||
/* A description of a working directory. */
|
||||
struct wd
|
||||
{
|
||||
/* The directory's name. */
|
||||
char const *name;
|
||||
int saved;
|
||||
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. */
|
||||
@@ -582,6 +648,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 ()
|
||||
{
|
||||
@@ -608,7 +687,7 @@ chdir_arg (char const *dir)
|
||||
if (! wd_count)
|
||||
{
|
||||
wd[wd_count].name = ".";
|
||||
wd[wd_count].saved = 0;
|
||||
wd[wd_count].fd = AT_FDCWD;
|
||||
wd_count++;
|
||||
}
|
||||
}
|
||||
@@ -625,64 +704,76 @@ chdir_arg (char const *dir)
|
||||
}
|
||||
|
||||
wd[wd_count].name = dir;
|
||||
wd[wd_count].saved = 0;
|
||||
wd[wd_count].fd = 0;
|
||||
return wd_count++;
|
||||
}
|
||||
|
||||
/* Change to directory I. If I is 0, change to the initial working
|
||||
directory; otherwise, I must be a value returned by chdir_arg. */
|
||||
/* Index of current directory. */
|
||||
int chdir_current;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
static int previous;
|
||||
|
||||
if (previous != i)
|
||||
if (chdir_current != i)
|
||||
{
|
||||
struct wd *prev = &wd[previous];
|
||||
struct wd *curr = &wd[i];
|
||||
int fd = curr->fd;
|
||||
|
||||
if (! prev->saved)
|
||||
if (! fd)
|
||||
{
|
||||
int err = 0;
|
||||
prev->saved = 1;
|
||||
if (save_cwd (&prev->saved_cwd) != 0)
|
||||
err = errno;
|
||||
else if (0 <= prev->saved_cwd.desc)
|
||||
{
|
||||
/* 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 ();
|
||||
}
|
||||
else
|
||||
err = errno;
|
||||
}
|
||||
|
||||
if (err)
|
||||
FATAL_ERROR ((0, err, _("Cannot save working directory")));
|
||||
}
|
||||
|
||||
if (curr->saved)
|
||||
{
|
||||
if (restore_cwd (&curr->saved_cwd))
|
||||
FATAL_ERROR ((0, 0, _("Cannot change working directory")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i && ! ISSLASH (curr->name[0]))
|
||||
if (! IS_ABSOLUTE_FILE_NAME (curr->name))
|
||||
chdir_do (i - 1);
|
||||
if (chdir (curr->name) != 0)
|
||||
chdir_fatal (curr->name);
|
||||
fd = openat (chdir_fd, curr->name,
|
||||
open_searchdir_flags & ~ O_NOFOLLOW);
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
previous = i;
|
||||
if (0 < fd)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,21 +850,6 @@ file_removed_diag (const char *name, bool top_level,
|
||||
(0, 0, _("%s: File removed before we read it"),
|
||||
quotearg_colon (name)));
|
||||
set_exit_status (TAREXIT_DIFFERS);
|
||||
}
|
||||
else
|
||||
diagfn (name);
|
||||
}
|
||||
|
||||
void
|
||||
dir_removed_diag (const char *name, bool top_level,
|
||||
void (*diagfn) (char const *name))
|
||||
{
|
||||
if (!top_level && errno == ENOENT)
|
||||
{
|
||||
WARNOPT (WARN_FILE_REMOVED,
|
||||
(0, 0, _("%s: Directory removed before we read it"),
|
||||
quotearg_colon (name)));
|
||||
set_exit_status (TAREXIT_DIFFERS);
|
||||
}
|
||||
else
|
||||
diagfn (name);
|
||||
@@ -869,6 +945,3 @@ namebuf_name (namebuf_t buf, const char *name)
|
||||
strcpy (buf->buffer + buf->dir_length, name);
|
||||
return buf->buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
226
src/names.c
226
src/names.c
@@ -27,15 +27,6 @@
|
||||
|
||||
/* User and group names. */
|
||||
|
||||
struct group *getgrnam ();
|
||||
struct passwd *getpwnam ();
|
||||
#if ! HAVE_DECL_GETPWUID
|
||||
struct passwd *getpwuid ();
|
||||
#endif
|
||||
#if ! HAVE_DECL_GETGRGID
|
||||
struct group *getgrgid ();
|
||||
#endif
|
||||
|
||||
/* Make sure you link with the proper libraries if you are running the
|
||||
Yellow Peril (thanks for the good laugh, Ian J.!), or, euh... NIS.
|
||||
This code should also be modified for non-UNIX systems to do something
|
||||
@@ -56,8 +47,6 @@ static char *cached_no_such_gname;
|
||||
static uid_t cached_no_such_uid;
|
||||
static gid_t cached_no_such_gid;
|
||||
|
||||
static void register_individual_file (char const *name);
|
||||
|
||||
/* Given UID, find the corresponding UNAME. */
|
||||
void
|
||||
uid_to_uname (uid_t uid, char **uname)
|
||||
@@ -179,7 +168,7 @@ gname_to_gid (char const *gname, gid_t *gidp)
|
||||
}
|
||||
|
||||
|
||||
struct name *
|
||||
static struct name *
|
||||
make_name (const char *file_name)
|
||||
{
|
||||
struct name *p = xzalloc (sizeof (*p));
|
||||
@@ -190,7 +179,7 @@ make_name (const char *file_name)
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
free_name (struct name *p)
|
||||
{
|
||||
if (p)
|
||||
@@ -207,7 +196,7 @@ free_name (struct name *p)
|
||||
static struct name *namelist; /* first name in list, if any */
|
||||
static struct name *nametail; /* end of name list */
|
||||
|
||||
/* File name arguments are processed in two stages: first a
|
||||
/* File name arguments are processed in two stages: first a
|
||||
name_array (see below) is filled, then the names from it
|
||||
are moved into the namelist.
|
||||
|
||||
@@ -215,7 +204,7 @@ static struct name *nametail; /* end of name list */
|
||||
which is meant to help process large archives on machines with
|
||||
limited memory. With this option on, namelist contains at most one
|
||||
entry, which diminishes the memory consumption.
|
||||
|
||||
|
||||
However, I very much doubt if we still need this -- Sergey */
|
||||
|
||||
/* A name_array element contains entries of three types: */
|
||||
@@ -230,24 +219,25 @@ struct name_elt /* A name_array element. */
|
||||
union
|
||||
{
|
||||
const char *name; /* File or directory name */
|
||||
int matching_flags;/* fnmatch options if type == NELT_FMASK */
|
||||
int matching_flags;/* fnmatch options if type == NELT_FMASK */
|
||||
} v;
|
||||
};
|
||||
|
||||
static struct name_elt *name_array; /* store an array of names */
|
||||
static size_t allocated_names; /* how big is the array? */
|
||||
static size_t names; /* how many entries does it have? */
|
||||
static size_t name_index; /* how many of the entries have we scanned? */
|
||||
static size_t allocated_entries; /* how big is the array? */
|
||||
static size_t entries; /* how many entries does it have? */
|
||||
static size_t scanned; /* how many of the entries have we scanned? */
|
||||
size_t name_count; /* how many of the entries are names? */
|
||||
|
||||
/* Check the size of name_array, reallocating it as necessary. */
|
||||
static void
|
||||
check_name_alloc ()
|
||||
check_name_alloc (void)
|
||||
{
|
||||
if (names == allocated_names)
|
||||
if (entries == allocated_entries)
|
||||
{
|
||||
if (allocated_names == 0)
|
||||
allocated_names = 10; /* Set initial allocation */
|
||||
name_array = x2nrealloc (name_array, &allocated_names,
|
||||
if (allocated_entries == 0)
|
||||
allocated_entries = 10; /* Set initial allocation */
|
||||
name_array = x2nrealloc (name_array, &allocated_entries,
|
||||
sizeof (name_array[0]));
|
||||
}
|
||||
}
|
||||
@@ -260,17 +250,18 @@ name_add_name (const char *name, int matching_flags)
|
||||
struct name_elt *ep;
|
||||
|
||||
check_name_alloc ();
|
||||
ep = &name_array[names++];
|
||||
ep = &name_array[entries++];
|
||||
if (prev_flags != matching_flags)
|
||||
{
|
||||
ep->type = NELT_FMASK;
|
||||
ep->v.matching_flags = matching_flags;
|
||||
prev_flags = matching_flags;
|
||||
check_name_alloc ();
|
||||
ep = &name_array[names++];
|
||||
ep = &name_array[entries++];
|
||||
}
|
||||
ep->type = NELT_NAME;
|
||||
ep->v.name = name;
|
||||
name_count++;
|
||||
}
|
||||
|
||||
/* Add to name_array a chdir request for the directory NAME */
|
||||
@@ -279,10 +270,10 @@ name_add_dir (const char *name)
|
||||
{
|
||||
struct name_elt *ep;
|
||||
check_name_alloc ();
|
||||
ep = &name_array[names++];
|
||||
ep = &name_array[entries++];
|
||||
ep->type = NELT_CHDIR;
|
||||
ep->v.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Names from external name file. */
|
||||
@@ -313,28 +304,28 @@ static int matching_flags; /* exclude_fnmatch options */
|
||||
|
||||
If CHANGE_DIRS is true, treat any entries of type NELT_CHDIR as
|
||||
the request to change to the given directory.
|
||||
|
||||
|
||||
Entries of type NELT_FMASK cause updates of the matching_flags
|
||||
value. */
|
||||
struct name_elt *
|
||||
static struct name_elt *
|
||||
name_next_elt (int change_dirs)
|
||||
{
|
||||
static struct name_elt entry;
|
||||
const char *source;
|
||||
char *cursor;
|
||||
|
||||
while (name_index != names)
|
||||
while (scanned != entries)
|
||||
{
|
||||
struct name_elt *ep;
|
||||
size_t source_len;
|
||||
|
||||
ep = &name_array[name_index++];
|
||||
|
||||
ep = &name_array[scanned++];
|
||||
if (ep->type == NELT_FMASK)
|
||||
{
|
||||
matching_flags = ep->v.matching_flags;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
source = ep->v.name;
|
||||
source_len = strlen (source);
|
||||
if (name_buffer_length < source_len)
|
||||
@@ -367,8 +358,6 @@ name_next_elt (int change_dirs)
|
||||
{
|
||||
if (unquote_option)
|
||||
unquote_string (name_buffer);
|
||||
if (incremental_option)
|
||||
register_individual_file (name_buffer);
|
||||
entry.type = ep->type;
|
||||
entry.v.name = name_buffer;
|
||||
return &entry;
|
||||
@@ -421,7 +410,7 @@ name_gather (void)
|
||||
buffer->directory = NULL;
|
||||
buffer->parent = NULL;
|
||||
buffer->cmdline = true;
|
||||
|
||||
|
||||
namelist = nametail = buffer;
|
||||
}
|
||||
else if (change_dir)
|
||||
@@ -519,7 +508,7 @@ name_match (const char *file_name)
|
||||
|
||||
if (!cursor)
|
||||
return true;
|
||||
|
||||
|
||||
if (cursor->name[0] == 0)
|
||||
{
|
||||
chdir_do (cursor->change_dir);
|
||||
@@ -589,7 +578,7 @@ all_names_found (struct tar_stat_info *p)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
regex_usage_warning (const char *name)
|
||||
{
|
||||
static int warned_once = 0;
|
||||
@@ -603,6 +592,7 @@ regex_usage_warning (const char *name)
|
||||
_("Use --wildcards to enable pattern matching,"
|
||||
" or --no-wildcards to suppress this warning")));
|
||||
}
|
||||
return warned_once;
|
||||
}
|
||||
|
||||
/* Print the names of things in the namelist that were not matched. */
|
||||
@@ -615,12 +605,11 @@ names_notfound (void)
|
||||
if (!WASFOUND (cursor) && cursor->name[0])
|
||||
{
|
||||
regex_usage_warning (cursor->name);
|
||||
if (cursor->found_count == 0)
|
||||
ERROR ((0, 0, _("%s: Not found in archive"),
|
||||
quotearg_colon (cursor->name)));
|
||||
else
|
||||
ERROR ((0, 0, _("%s: Required occurrence not found in archive"),
|
||||
quotearg_colon (cursor->name)));
|
||||
ERROR ((0, 0,
|
||||
(cursor->found_count == 0) ?
|
||||
_("%s: Not found in archive") :
|
||||
_("%s: Required occurrence not found in archive"),
|
||||
quotearg_colon (cursor->name)));
|
||||
}
|
||||
|
||||
/* Don't bother freeing the name list; we're about to exit. */
|
||||
@@ -639,13 +628,49 @@ names_notfound (void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
label_notfound (void)
|
||||
{
|
||||
struct name const *cursor;
|
||||
|
||||
if (!namelist)
|
||||
return;
|
||||
|
||||
for (cursor = namelist; cursor; cursor = cursor->next)
|
||||
if (WASFOUND (cursor))
|
||||
return;
|
||||
|
||||
if (verbose_option)
|
||||
error (0, 0, _("Archive label mismatch"));
|
||||
set_exit_status (TAREXIT_DIFFERS);
|
||||
|
||||
for (cursor = namelist; cursor; cursor = cursor->next)
|
||||
{
|
||||
if (regex_usage_warning (cursor->name))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Don't bother freeing the name list; we're about to exit. */
|
||||
namelist = NULL;
|
||||
nametail = NULL;
|
||||
|
||||
if (same_order_option)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
while ((name = name_next (1)) != NULL
|
||||
&& regex_usage_warning (name) == 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorting name lists. */
|
||||
|
||||
/* Sort *singly* linked LIST of names, of given LENGTH, using COMPARE
|
||||
to order names. Return the sorted list. Note that after calling
|
||||
this function, the `prev' links in list elements are messed up.
|
||||
|
||||
|
||||
Apart from the type `struct name' and the definition of SUCCESSOR,
|
||||
this is a generic list-sorting function, but it's too painful to
|
||||
make it both generic and portable
|
||||
@@ -752,17 +777,15 @@ compare_names (struct name const *n1, struct name const *n2)
|
||||
}
|
||||
|
||||
|
||||
/* Add all the dirs under NAME, which names a directory, to the namelist.
|
||||
If any of the files is a directory, recurse on the subdirectory.
|
||||
DEVICE is the device not to leave, if the -l option is specified.
|
||||
CMDLINE is true, if the NAME appeared on the command line. */
|
||||
/* Add all the dirs under ST to the namelist NAME, descending the
|
||||
directory hierarchy recursively. */
|
||||
|
||||
static void
|
||||
add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
|
||||
add_hierarchy_to_namelist (struct tar_stat_info *st, struct name *name)
|
||||
{
|
||||
const char *buffer;
|
||||
|
||||
name_fill_directory (name, device, cmdline);
|
||||
|
||||
name->directory = scan_directory (st);
|
||||
buffer = directory_contents (name->directory);
|
||||
if (buffer)
|
||||
{
|
||||
@@ -790,6 +813,8 @@ add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
|
||||
if (*string == 'D')
|
||||
{
|
||||
struct name *np;
|
||||
struct tar_stat_info subdir;
|
||||
int subfd;
|
||||
|
||||
if (allocated_length <= name_length + string_length)
|
||||
{
|
||||
@@ -810,7 +835,38 @@ add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
|
||||
else
|
||||
child_tail->sibling = np;
|
||||
child_tail = np;
|
||||
add_hierarchy_to_namelist (np, device, false);
|
||||
|
||||
tar_stat_init (&subdir);
|
||||
subdir.parent = st;
|
||||
if (st->fd < 0)
|
||||
{
|
||||
subfd = -1;
|
||||
errno = - st->fd;
|
||||
}
|
||||
else
|
||||
subfd = subfile_open (st, string + 1,
|
||||
open_read_flags | O_DIRECTORY);
|
||||
if (subfd < 0)
|
||||
open_diag (namebuf);
|
||||
else
|
||||
{
|
||||
subdir.fd = subfd;
|
||||
if (fstat (subfd, &subdir.stat) != 0)
|
||||
stat_diag (namebuf);
|
||||
else if (! (O_DIRECTORY || S_ISDIR (subdir.stat.st_mode)))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
open_diag (namebuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
subdir.orig_file_name = xstrdup (namebuf);
|
||||
add_hierarchy_to_namelist (&subdir, np);
|
||||
restore_parent_fd (&subdir);
|
||||
}
|
||||
}
|
||||
|
||||
tar_stat_destroy (&subdir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,7 +902,7 @@ rebase_child_list (struct name *child, struct name *parent)
|
||||
size_t old_prefix_len = child->parent->length;
|
||||
size_t new_prefix_len = parent->length;
|
||||
char *new_prefix = parent->name;
|
||||
|
||||
|
||||
for (; child; child = child->sibling)
|
||||
{
|
||||
size_t size = child->length - old_prefix_len + new_prefix_len;
|
||||
@@ -858,7 +914,7 @@ rebase_child_list (struct name *child, struct name *parent)
|
||||
child->length = size;
|
||||
|
||||
rebase_directory (child->directory,
|
||||
child->parent->name, old_prefix_len,
|
||||
child->parent->name, old_prefix_len,
|
||||
new_prefix, new_prefix_len);
|
||||
}
|
||||
}
|
||||
@@ -871,11 +927,10 @@ void
|
||||
collect_and_sort_names (void)
|
||||
{
|
||||
struct name *name;
|
||||
struct name *next_name, *prev_name;
|
||||
struct name *next_name, *prev_name = NULL;
|
||||
int num_names;
|
||||
struct stat statbuf;
|
||||
Hash_table *nametab;
|
||||
|
||||
|
||||
name_gather ();
|
||||
|
||||
if (!namelist)
|
||||
@@ -903,10 +958,12 @@ collect_and_sort_names (void)
|
||||
|
||||
read_directory_file ();
|
||||
}
|
||||
|
||||
|
||||
num_names = 0;
|
||||
for (name = namelist; name; name = name->next, num_names++)
|
||||
{
|
||||
struct tar_stat_info st;
|
||||
|
||||
if (name->found_count || name->directory)
|
||||
continue;
|
||||
if (name->matching_flags & EXCLUDE_WILDCARDS)
|
||||
@@ -918,16 +975,34 @@ collect_and_sort_names (void)
|
||||
if (name->name[0] == 0)
|
||||
continue;
|
||||
|
||||
if (deref_stat (dereference_option, name->name, &statbuf) != 0)
|
||||
tar_stat_init (&st);
|
||||
|
||||
if (deref_stat (name->name, &st.stat) != 0)
|
||||
{
|
||||
stat_diag (name->name);
|
||||
continue;
|
||||
}
|
||||
if (S_ISDIR (statbuf.st_mode))
|
||||
if (S_ISDIR (st.stat.st_mode))
|
||||
{
|
||||
name->found_count++;
|
||||
add_hierarchy_to_namelist (name, statbuf.st_dev, true);
|
||||
int dir_fd = openat (chdir_fd, name->name,
|
||||
open_read_flags | O_DIRECTORY);
|
||||
if (dir_fd < 0)
|
||||
open_diag (name->name);
|
||||
else
|
||||
{
|
||||
st.fd = dir_fd;
|
||||
if (fstat (dir_fd, &st.stat) != 0)
|
||||
stat_diag (name->name);
|
||||
else if (O_DIRECTORY || S_ISDIR (st.stat.st_mode))
|
||||
{
|
||||
st.orig_file_name = xstrdup (name->name);
|
||||
name->found_count++;
|
||||
add_hierarchy_to_namelist (&st, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tar_stat_destroy (&st);
|
||||
}
|
||||
|
||||
namelist = merge_sort (namelist, num_names, compare_names);
|
||||
@@ -950,6 +1025,7 @@ collect_and_sort_names (void)
|
||||
{
|
||||
if (p->child)
|
||||
rebase_child_list (p->child, name);
|
||||
hash_delete (nametab, name);
|
||||
/* FIXME: remove_directory (p->caname); ? */
|
||||
remname (p);
|
||||
free_name (p);
|
||||
@@ -1071,28 +1147,6 @@ excluded_name (char const *name)
|
||||
{
|
||||
return excluded_file_name (excluded, name + FILE_SYSTEM_PREFIX_LEN (name));
|
||||
}
|
||||
|
||||
static Hash_table *individual_file_table;
|
||||
|
||||
static void
|
||||
register_individual_file (char const *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (deref_stat (dereference_option, name, &st) != 0)
|
||||
return; /* Will be complained about later */
|
||||
if (S_ISDIR (st.st_mode))
|
||||
return;
|
||||
|
||||
hash_string_insert (&individual_file_table, name);
|
||||
}
|
||||
|
||||
bool
|
||||
is_individual_file (char const *name)
|
||||
{
|
||||
return hash_string_lookup (individual_file_table, name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the size of the prefix of FILE_NAME that is removed after
|
||||
|
||||
133
src/sparse.c
133
src/sparse.c
@@ -1,6 +1,7 @@
|
||||
/* Functions for dealing with sparse files
|
||||
|
||||
Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
||||
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 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
|
||||
@@ -216,43 +217,45 @@ sparse_scan_file (struct tar_sparse_file *file)
|
||||
struct tar_stat_info *st = file->stat_info;
|
||||
int fd = file->fd;
|
||||
char buffer[BLOCKSIZE];
|
||||
size_t count;
|
||||
size_t count = 0;
|
||||
off_t offset = 0;
|
||||
struct sp_array sp = {0, 0};
|
||||
|
||||
if (!lseek_or_error (file, 0))
|
||||
return false;
|
||||
|
||||
st->archive_file_size = 0;
|
||||
|
||||
if (!tar_sparse_scan (file, scan_begin, NULL))
|
||||
return false;
|
||||
|
||||
while ((count = safe_read (fd, buffer, sizeof buffer)) != 0
|
||||
&& count != SAFE_READ_ERROR)
|
||||
if (ST_NBLOCKS (st->stat) == 0)
|
||||
offset = st->stat.st_size;
|
||||
else
|
||||
{
|
||||
/* Analyze the block. */
|
||||
if (zero_block_p (buffer, count))
|
||||
if (!tar_sparse_scan (file, scan_begin, NULL))
|
||||
return false;
|
||||
|
||||
while ((count = safe_read (fd, buffer, sizeof buffer)) != 0
|
||||
&& count != SAFE_READ_ERROR)
|
||||
{
|
||||
if (sp.numbytes)
|
||||
/* Analyze the block. */
|
||||
if (zero_block_p (buffer, count))
|
||||
{
|
||||
sparse_add_map (st, &sp);
|
||||
sp.numbytes = 0;
|
||||
if (!tar_sparse_scan (file, scan_block, NULL))
|
||||
if (sp.numbytes)
|
||||
{
|
||||
sparse_add_map (st, &sp);
|
||||
sp.numbytes = 0;
|
||||
if (!tar_sparse_scan (file, scan_block, NULL))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sp.numbytes == 0)
|
||||
sp.offset = offset;
|
||||
sp.numbytes += count;
|
||||
st->archive_file_size += count;
|
||||
if (!tar_sparse_scan (file, scan_block, buffer))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sp.numbytes == 0)
|
||||
sp.offset = offset;
|
||||
sp.numbytes += count;
|
||||
st->archive_file_size += count;
|
||||
if (!tar_sparse_scan (file, scan_block, buffer))
|
||||
return false;
|
||||
}
|
||||
|
||||
offset += count;
|
||||
offset += count;
|
||||
}
|
||||
}
|
||||
|
||||
if (sp.numbytes == 0)
|
||||
@@ -324,7 +327,6 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
|
||||
memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
|
||||
bytes_left -= bytes_read;
|
||||
file->dumped_size += bytes_read;
|
||||
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
|
||||
set_next_block_after (blk);
|
||||
}
|
||||
|
||||
@@ -334,7 +336,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
|
||||
static bool
|
||||
sparse_extract_region (struct tar_sparse_file *file, size_t i)
|
||||
{
|
||||
size_t write_size;
|
||||
off_t write_size;
|
||||
|
||||
if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
|
||||
return false;
|
||||
@@ -398,10 +400,11 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
mv_begin (file.stat_info);
|
||||
mv_begin_write (file.stat_info->file_name,
|
||||
file.stat_info->stat.st_size,
|
||||
file.stat_info->archive_file_size - file.dumped_size);
|
||||
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
rc = tar_sparse_dump_region (&file, i);
|
||||
mv_end ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,13 +511,13 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
|
||||
static bool
|
||||
check_data_region (struct tar_sparse_file *file, size_t i)
|
||||
{
|
||||
size_t size_left;
|
||||
off_t size_left;
|
||||
|
||||
if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
|
||||
return false;
|
||||
size_left = file->stat_info->sparse_map[i].numbytes;
|
||||
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
|
||||
|
||||
|
||||
while (size_left > 0)
|
||||
{
|
||||
size_t bytes_read;
|
||||
@@ -564,9 +567,9 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
|
||||
file.stat_info = st;
|
||||
file.fd = fd;
|
||||
file.seekable = true; /* File *must* be seekable for compare to work */
|
||||
|
||||
|
||||
rc = tar_sparse_decode_header (&file);
|
||||
mv_begin (st);
|
||||
mv_begin_read (st);
|
||||
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
rc = check_sparse_region (&file,
|
||||
@@ -579,7 +582,7 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
|
||||
if (!rc)
|
||||
skip_file (file.stat_info->archive_file_size - file.dumped_size);
|
||||
mv_end ();
|
||||
|
||||
|
||||
tar_sparse_done (&file);
|
||||
return rc;
|
||||
}
|
||||
@@ -625,8 +628,9 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
|
||||
if (s->numbytes[0] == '\0')
|
||||
return add_finish;
|
||||
sp.offset = OFF_FROM_HEADER (s->offset);
|
||||
sp.numbytes = SIZE_FROM_HEADER (s->numbytes);
|
||||
sp.numbytes = OFF_FROM_HEADER (s->numbytes);
|
||||
if (sp.offset < 0
|
||||
|| sp.offset + sp.numbytes < 0
|
||||
|| file->stat_info->stat.st_size < sp.offset + sp.numbytes
|
||||
|| file->stat_info->archive_file_size < 0)
|
||||
return add_fail;
|
||||
@@ -695,8 +699,8 @@ oldgnu_store_sparse_info (struct tar_sparse_file *file, size_t *pindex,
|
||||
{
|
||||
OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].offset,
|
||||
sp->offset);
|
||||
SIZE_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
|
||||
sp->numbytes);
|
||||
OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
|
||||
sp->numbytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,13 +855,13 @@ static struct tar_sparse_optab const star_optab = {
|
||||
instances of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas
|
||||
POSIX requires the latest occurrence of the variable to override all
|
||||
previous occurrences.
|
||||
|
||||
|
||||
To avoid this incompatibility two following versions were introduced.
|
||||
|
||||
* 0.1
|
||||
|
||||
Used by tar 1.15.2 -- 1.15.91 (alpha releases).
|
||||
|
||||
|
||||
The sparse file map is stored in
|
||||
x header:
|
||||
|
||||
@@ -875,18 +879,18 @@ static struct tar_sparse_optab const star_optab = {
|
||||
Starting from this version, the exact sparse format version is specified
|
||||
explicitely in the header using the following variables:
|
||||
|
||||
GNU.sparse.major Major version
|
||||
GNU.sparse.major Major version
|
||||
GNU.sparse.minor Minor version
|
||||
|
||||
X header keeps the following variables:
|
||||
|
||||
|
||||
GNU.sparse.name Real file name of the sparse file
|
||||
GNU.sparse.realsize Real size of the stored file (corresponds to the old
|
||||
GNU.sparse.size variable)
|
||||
|
||||
The name field of the ustar header is constructed using the pattern
|
||||
"%d/GNUSparseFile.%p/%f".
|
||||
|
||||
|
||||
The sparse map itself is stored in the file data block, preceding the actual
|
||||
file data. It consists of a series of octal numbers of arbitrary length,
|
||||
delimited by newlines. The map is padded with nulls to the nearest block
|
||||
@@ -924,11 +928,11 @@ pax_dump_header_0 (struct tar_sparse_file *file)
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
struct sp_array *map = file->stat_info->sparse_map;
|
||||
char *save_file_name = NULL;
|
||||
|
||||
|
||||
/* Store the real file size */
|
||||
xheader_store ("GNU.sparse.size", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
|
||||
|
||||
|
||||
if (xheader_keyword_deleted_p ("GNU.sparse.map")
|
||||
|| tar_sparse_minor == 0)
|
||||
{
|
||||
@@ -987,11 +991,11 @@ pax_dump_header_1 (struct tar_sparse_file *file)
|
||||
off_t size = 0;
|
||||
struct sp_array *map = file->stat_info->sparse_map;
|
||||
char *save_file_name = file->stat_info->file_name;
|
||||
|
||||
|
||||
#define COPY_STRING(b,dst,src) do \
|
||||
{ \
|
||||
char *endp = b->buffer + BLOCKSIZE; \
|
||||
char *srcp = src; \
|
||||
char const *srcp = src; \
|
||||
while (*srcp) \
|
||||
{ \
|
||||
if (dst == endp) \
|
||||
@@ -1003,7 +1007,7 @@ pax_dump_header_1 (struct tar_sparse_file *file)
|
||||
} \
|
||||
*dst++ = *srcp++; \
|
||||
} \
|
||||
} while (0)
|
||||
} while (0)
|
||||
|
||||
/* Compute stored file size */
|
||||
p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
|
||||
@@ -1018,15 +1022,18 @@ pax_dump_header_1 (struct tar_sparse_file *file)
|
||||
size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
file->stat_info->archive_file_size += size * BLOCKSIZE;
|
||||
file->dumped_size += size * BLOCKSIZE;
|
||||
|
||||
|
||||
/* Store sparse file identification */
|
||||
xheader_store ("GNU.sparse.major", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.minor", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.name", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.realsize", file->stat_info, NULL);
|
||||
|
||||
file->stat_info->file_name = xheader_format_name (file->stat_info,
|
||||
"%d/GNUSparseFile.%p/%f", 0);
|
||||
|
||||
file->stat_info->file_name =
|
||||
xheader_format_name (file->stat_info, "%d/GNUSparseFile.%p/%f", 0);
|
||||
/* Make sure the created header name is shorter than NAME_FIELD_SIZE: */
|
||||
if (strlen (file->stat_info->file_name) > NAME_FIELD_SIZE)
|
||||
file->stat_info->file_name[NAME_FIELD_SIZE] = 0;
|
||||
|
||||
blk = start_header (file->stat_info);
|
||||
/* Store the effective (shrunken) file size */
|
||||
@@ -1072,12 +1079,12 @@ decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
|
||||
|
||||
if (!ISDIGIT (*arg))
|
||||
return false;
|
||||
|
||||
|
||||
u = strtoumax (arg, &arg_lim, 10);
|
||||
|
||||
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
|
||||
return false;
|
||||
|
||||
|
||||
*num = u;
|
||||
return true;
|
||||
}
|
||||
@@ -1117,7 +1124,7 @@ pax_decode_header (struct tar_sparse_file *file)
|
||||
} \
|
||||
while (*dst++ != '\n'); \
|
||||
dst[-1] = 0; \
|
||||
} while (0)
|
||||
} while (0)
|
||||
|
||||
set_next_block_after (current_header);
|
||||
file->dumped_size += BLOCKSIZE;
|
||||
@@ -1126,7 +1133,7 @@ pax_decode_header (struct tar_sparse_file *file)
|
||||
COPY_BUF (blk,nbuf,p);
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
|
||||
{
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
return false;
|
||||
}
|
||||
@@ -1137,19 +1144,19 @@ pax_decode_header (struct tar_sparse_file *file)
|
||||
for (i = 0; i < file->stat_info->sparse_map_size; i++)
|
||||
{
|
||||
struct sp_array sp;
|
||||
|
||||
|
||||
COPY_BUF (blk,nbuf,p);
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
|
||||
{
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
return false;
|
||||
}
|
||||
sp.offset = u;
|
||||
COPY_BUF (blk,nbuf,p);
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
|
||||
{
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
return false;
|
||||
}
|
||||
@@ -1158,7 +1165,7 @@ pax_decode_header (struct tar_sparse_file *file)
|
||||
}
|
||||
set_next_block_after (blk);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1168,7 +1175,7 @@ static struct tar_sparse_optab const pax_optab = {
|
||||
pax_sparse_member_p,
|
||||
pax_dump_header,
|
||||
NULL,
|
||||
pax_decode_header,
|
||||
pax_decode_header,
|
||||
NULL, /* No scan_block function */
|
||||
sparse_dump_region,
|
||||
sparse_extract_region,
|
||||
|
||||
@@ -27,7 +27,7 @@ struct compression_suffix
|
||||
};
|
||||
|
||||
static struct compression_suffix compression_suffixes[] = {
|
||||
#define __CAT2__(a,b) a ## b
|
||||
#define __CAT2__(a,b) a ## b
|
||||
#define S(s,p) #s, sizeof (#s) - 1, __CAT2__(p,_PROGRAM)
|
||||
{ S(gz, GZIP) },
|
||||
{ S(tgz, GZIP) },
|
||||
@@ -54,7 +54,7 @@ static const char *
|
||||
find_compression_program (const char *name, const char *defprog)
|
||||
{
|
||||
char *suf = strrchr (name, '.');
|
||||
|
||||
|
||||
if (suf)
|
||||
{
|
||||
int i;
|
||||
@@ -74,10 +74,9 @@ find_compression_program (const char *name, const char *defprog)
|
||||
}
|
||||
|
||||
void
|
||||
set_comression_program_by_suffix (const char *name, const char *defprog)
|
||||
set_compression_program_by_suffix (const char *name, const char *defprog)
|
||||
{
|
||||
const char *program = find_compression_program (name, defprog);
|
||||
if (program)
|
||||
use_compress_program_option = program;
|
||||
}
|
||||
|
||||
|
||||
72
src/system.c
72
src/system.c
@@ -1,7 +1,7 @@
|
||||
/* System-dependent calls for tar.
|
||||
|
||||
Copyright (C) 2003, 2004, 2005, 2006, 2007,
|
||||
2008 Free Software Foundation, Inc.
|
||||
2008, 2010 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
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <system.h>
|
||||
|
||||
#include "common.h"
|
||||
#include <priv-set.h>
|
||||
#include <rmt.h>
|
||||
#include <signal.h>
|
||||
|
||||
@@ -192,6 +193,7 @@ sys_spawn_shell (void)
|
||||
child = xfork ();
|
||||
if (child == 0)
|
||||
{
|
||||
priv_set_restore_linkdir ();
|
||||
execlp (shell, "-sh", "-i", (char *) 0);
|
||||
exec_fatal (shell);
|
||||
}
|
||||
@@ -283,15 +285,15 @@ xdup2 (int from, int into)
|
||||
}
|
||||
}
|
||||
|
||||
void wait_for_grandchild (pid_t pid) __attribute__ ((__noreturn__));
|
||||
static void wait_for_grandchild (pid_t pid) __attribute__ ((__noreturn__));
|
||||
|
||||
/* Propagate any failure of the grandchild back to the parent. */
|
||||
void
|
||||
static void
|
||||
wait_for_grandchild (pid_t pid)
|
||||
{
|
||||
int wait_status;
|
||||
int exit_code = 0;
|
||||
|
||||
|
||||
while (waitpid (pid, &wait_status, 0) == -1)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
@@ -303,7 +305,7 @@ wait_for_grandchild (pid_t pid)
|
||||
raise (WTERMSIG (wait_status));
|
||||
else if (WEXITSTATUS (wait_status) != 0)
|
||||
exit_code = WEXITSTATUS (wait_status);
|
||||
|
||||
|
||||
exit (exit_code);
|
||||
}
|
||||
|
||||
@@ -332,7 +334,7 @@ sys_child_open_for_compress (void)
|
||||
|
||||
set_program_name (_("tar (child)"));
|
||||
signal (SIGPIPE, SIG_DFL);
|
||||
|
||||
|
||||
xdup2 (parent_pipe[PREAD], STDIN_FILENO);
|
||||
xclose (parent_pipe[PWRITE]);
|
||||
|
||||
@@ -362,6 +364,7 @@ sys_child_open_for_compress (void)
|
||||
}
|
||||
xdup2 (archive, STDOUT_FILENO);
|
||||
}
|
||||
priv_set_restore_linkdir ();
|
||||
execlp (use_compress_program_option, use_compress_program_option, NULL);
|
||||
exec_fatal (use_compress_program_option);
|
||||
}
|
||||
@@ -379,6 +382,7 @@ sys_child_open_for_compress (void)
|
||||
|
||||
xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
|
||||
xclose (child_pipe[PREAD]);
|
||||
priv_set_restore_linkdir ();
|
||||
execlp (use_compress_program_option, use_compress_program_option,
|
||||
(char *) 0);
|
||||
exec_fatal (use_compress_program_option);
|
||||
@@ -451,6 +455,29 @@ sys_child_open_for_compress (void)
|
||||
wait_for_grandchild (grandchild_pid);
|
||||
}
|
||||
|
||||
static void
|
||||
run_decompress_program (void)
|
||||
{
|
||||
int i;
|
||||
const char *p, *prog = NULL;
|
||||
|
||||
for (p = first_decompress_program (&i); p; p = next_decompress_program (&i))
|
||||
{
|
||||
if (prog)
|
||||
{
|
||||
WARNOPT (WARN_DECOMPRESS_PROGRAM,
|
||||
(0, errno, _("cannot run %s"), prog));
|
||||
WARNOPT (WARN_DECOMPRESS_PROGRAM,
|
||||
(0, 0, _("trying %s"), p));
|
||||
}
|
||||
prog = p;
|
||||
execlp (p, p, "-d", NULL);
|
||||
}
|
||||
if (!prog)
|
||||
FATAL_ERROR ((0, 0, _("unable to run decompression program")));
|
||||
exec_fatal (prog);
|
||||
}
|
||||
|
||||
/* Set ARCHIVE for uncompressing, then reading an archive. */
|
||||
pid_t
|
||||
sys_child_open_for_uncompress (void)
|
||||
@@ -476,7 +503,7 @@ sys_child_open_for_uncompress (void)
|
||||
|
||||
set_program_name (_("tar (child)"));
|
||||
signal (SIGPIPE, SIG_DFL);
|
||||
|
||||
|
||||
xdup2 (parent_pipe[PWRITE], STDOUT_FILENO);
|
||||
xclose (parent_pipe[PREAD]);
|
||||
|
||||
@@ -496,9 +523,8 @@ sys_child_open_for_uncompress (void)
|
||||
if (archive < 0)
|
||||
open_fatal (archive_name_array[0]);
|
||||
xdup2 (archive, STDIN_FILENO);
|
||||
execlp (use_compress_program_option, use_compress_program_option,
|
||||
"-d", (char *) 0);
|
||||
exec_fatal (use_compress_program_option);
|
||||
priv_set_restore_linkdir ();
|
||||
run_decompress_program ();
|
||||
}
|
||||
|
||||
/* We do need a grandchild tar. */
|
||||
@@ -514,9 +540,8 @@ sys_child_open_for_uncompress (void)
|
||||
|
||||
xdup2 (child_pipe[PREAD], STDIN_FILENO);
|
||||
xclose (child_pipe[PWRITE]);
|
||||
execlp (use_compress_program_option, use_compress_program_option,
|
||||
"-d", (char *) 0);
|
||||
exec_fatal (use_compress_program_option);
|
||||
priv_set_restore_linkdir ();
|
||||
run_decompress_program ();
|
||||
}
|
||||
|
||||
/* The child tar is still here! */
|
||||
@@ -575,7 +600,7 @@ sys_child_open_for_uncompress (void)
|
||||
|
||||
|
||||
static void
|
||||
dec_to_env (char *envar, uintmax_t num)
|
||||
dec_to_env (char const *envar, uintmax_t num)
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
char *numstr;
|
||||
@@ -586,7 +611,7 @@ dec_to_env (char *envar, uintmax_t num)
|
||||
}
|
||||
|
||||
static void
|
||||
time_to_env (char *envar, struct timespec t)
|
||||
time_to_env (char const *envar, struct timespec t)
|
||||
{
|
||||
char buf[TIMESPEC_STRSIZE_BOUND];
|
||||
if (setenv (envar, code_timespec (t, buf), 1) != 0)
|
||||
@@ -594,7 +619,7 @@ time_to_env (char *envar, struct timespec t)
|
||||
}
|
||||
|
||||
static void
|
||||
oct_to_env (char *envar, unsigned long num)
|
||||
oct_to_env (char const *envar, unsigned long num)
|
||||
{
|
||||
char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
|
||||
|
||||
@@ -604,7 +629,7 @@ oct_to_env (char *envar, unsigned long num)
|
||||
}
|
||||
|
||||
static void
|
||||
str_to_env (char *envar, char const *str)
|
||||
str_to_env (char const *envar, char const *str)
|
||||
{
|
||||
if (str)
|
||||
{
|
||||
@@ -616,7 +641,7 @@ str_to_env (char *envar, char const *str)
|
||||
}
|
||||
|
||||
static void
|
||||
chr_to_env (char *envar, char c)
|
||||
chr_to_env (char const *envar, char c)
|
||||
{
|
||||
char buf[2];
|
||||
buf[0] = c;
|
||||
@@ -702,6 +727,7 @@ sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
|
||||
argv[2] = to_command_option;
|
||||
argv[3] = NULL;
|
||||
|
||||
priv_set_restore_linkdir ();
|
||||
execv ("/bin/sh", argv);
|
||||
|
||||
exec_fatal (file_name);
|
||||
@@ -750,7 +776,7 @@ sys_exec_info_script (const char **archive_name, int volume_number)
|
||||
char uintbuf[UINTMAX_STRSIZE_BOUND];
|
||||
int p[2];
|
||||
static RETSIGTYPE (*saved_handler) (int sig);
|
||||
|
||||
|
||||
xpipe (p);
|
||||
saved_handler = signal (SIGPIPE, SIG_IGN);
|
||||
|
||||
@@ -783,7 +809,7 @@ sys_exec_info_script (const char **archive_name, int volume_number)
|
||||
}
|
||||
|
||||
signal (SIGPIPE, saved_handler);
|
||||
|
||||
|
||||
if (WIFEXITED (status))
|
||||
{
|
||||
if (WEXITSTATUS (status) == 0 && rc > 0)
|
||||
@@ -813,9 +839,10 @@ sys_exec_info_script (const char **archive_name, int volume_number)
|
||||
|
||||
argv[0] = "/bin/sh";
|
||||
argv[1] = "-c";
|
||||
argv[2] = (char*) info_script_option;
|
||||
argv[2] = (char *) info_script_option;
|
||||
argv[3] = NULL;
|
||||
|
||||
priv_set_restore_linkdir ();
|
||||
execv (argv[0], argv);
|
||||
|
||||
exec_fatal (info_script_option);
|
||||
@@ -860,9 +887,10 @@ sys_exec_checkpoint_script (const char *script_name,
|
||||
archive_format : current_format), 1);
|
||||
argv[0] = "/bin/sh";
|
||||
argv[1] = "-c";
|
||||
argv[2] = (char*) script_name;
|
||||
argv[2] = (char *) script_name;
|
||||
argv[3] = NULL;
|
||||
|
||||
priv_set_restore_linkdir ();
|
||||
execv (argv[0], argv);
|
||||
|
||||
exec_fatal (script_name);
|
||||
|
||||
24
src/tar.h
24
src/tar.h
@@ -265,7 +265,7 @@ enum archive_format
|
||||
struct sp_array
|
||||
{
|
||||
off_t offset;
|
||||
size_t numbytes;
|
||||
off_t numbytes;
|
||||
};
|
||||
|
||||
struct xheader
|
||||
@@ -311,12 +311,32 @@ struct tar_stat_info
|
||||
|
||||
/* Extended headers */
|
||||
struct xheader xhdr;
|
||||
|
||||
|
||||
/* For dumpdirs */
|
||||
bool is_dumpdir; /* Is the member a dumpdir? */
|
||||
bool skipped; /* The member contents is already read
|
||||
(for GNUTYPE_DUMPDIR) */
|
||||
char *dumpdir; /* Contents of the dump directory */
|
||||
|
||||
/* Parent directory, if creating an archive. This is null if the
|
||||
file is at the top level. */
|
||||
struct tar_stat_info *parent;
|
||||
|
||||
/* Directory stream. If this is not null, it is in control of FD,
|
||||
and should be closed instead of FD. */
|
||||
DIR *dirstream;
|
||||
|
||||
/* File descriptor, if creating an archive, and if a directory or a
|
||||
regular file or a contiguous file.
|
||||
|
||||
It is zero if no file descriptor is available, either because it
|
||||
was never needed or because it was open and then closed to
|
||||
conserve on file descriptors. (Standard input is never used
|
||||
here, so zero cannot be a valid file descriptor.)
|
||||
|
||||
It is negative if it could not be reopened after it was closed.
|
||||
Negate it to find out what errno was when the reopen failed. */
|
||||
int fd;
|
||||
};
|
||||
|
||||
union block
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* This file is part of GNU tar.
|
||||
/* This file is part of GNU tar.
|
||||
Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
@@ -34,8 +34,8 @@ enum replace_segm_type
|
||||
|
||||
enum case_ctl_type
|
||||
{
|
||||
ctl_stop, /* Stop case conversion */
|
||||
ctl_upcase_next,/* Turn the next character to uppercase */
|
||||
ctl_stop, /* Stop case conversion */
|
||||
ctl_upcase_next,/* Turn the next character to uppercase */
|
||||
ctl_locase_next,/* Turn the next character to lowercase */
|
||||
ctl_upcase, /* Turn the replacement to uppercase until ctl_stop */
|
||||
ctl_locase /* Turn the replacement to lowercase until ctl_stop */
|
||||
@@ -51,9 +51,9 @@ struct replace_segm
|
||||
{
|
||||
char *ptr;
|
||||
size_t size;
|
||||
} literal; /* type == segm_literal */
|
||||
} literal; /* type == segm_literal */
|
||||
size_t ref; /* type == segm_backref */
|
||||
enum case_ctl_type ctl; /* type == segm_case_ctl */
|
||||
enum case_ctl_type ctl; /* type == segm_case_ctl */
|
||||
} v;
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ int transform_flags = XFORM_ALL;
|
||||
static struct transform *transform_head, *transform_tail;
|
||||
|
||||
static struct transform *
|
||||
new_transform ()
|
||||
new_transform (void)
|
||||
{
|
||||
struct transform *p = xzalloc (sizeof *p);
|
||||
if (transform_tail)
|
||||
@@ -146,7 +146,7 @@ parse_xform_flags (int *pflags, int c)
|
||||
case 'R':
|
||||
*pflags &= ~XFORM_REGFILE;
|
||||
break;
|
||||
|
||||
|
||||
case 'h':
|
||||
*pflags |= XFORM_LINK;
|
||||
break;
|
||||
@@ -154,7 +154,7 @@ parse_xform_flags (int *pflags, int c)
|
||||
case 'H':
|
||||
*pflags &= ~XFORM_LINK;
|
||||
break;
|
||||
|
||||
|
||||
case 's':
|
||||
*pflags |= XFORM_SYMLINK;
|
||||
break;
|
||||
@@ -204,10 +204,10 @@ parse_transform_expr (const char *expr)
|
||||
*expr));
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
|
||||
}
|
||||
|
||||
|
||||
delim = expr[1];
|
||||
|
||||
/* Scan regular expression */
|
||||
@@ -258,14 +258,14 @@ parse_transform_expr (const char *expr)
|
||||
|
||||
if (*p == ';')
|
||||
p++;
|
||||
|
||||
|
||||
/* Extract and compile regex */
|
||||
str = xmalloc (i - 1);
|
||||
memcpy (str, expr + 2, i - 2);
|
||||
str[i - 2] = 0;
|
||||
|
||||
rc = regcomp (&tf->regex, str, cflags);
|
||||
|
||||
|
||||
if (rc)
|
||||
{
|
||||
char errbuf[512];
|
||||
@@ -275,7 +275,7 @@ parse_transform_expr (const char *expr)
|
||||
|
||||
if (str[0] == '^' || str[strlen (str) - 1] == '$')
|
||||
tf->transform_type = transform_first;
|
||||
|
||||
|
||||
free (str);
|
||||
|
||||
/* Extract and compile replacement expr */
|
||||
@@ -289,7 +289,7 @@ parse_transform_expr (const char *expr)
|
||||
if (*cur == '\\')
|
||||
{
|
||||
size_t n;
|
||||
|
||||
|
||||
add_literal_segment (tf, beg, cur);
|
||||
switch (*++cur)
|
||||
{
|
||||
@@ -310,32 +310,32 @@ parse_transform_expr (const char *expr)
|
||||
add_char_segment (tf, '\a');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'b':
|
||||
add_char_segment (tf, '\b');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'f':
|
||||
add_char_segment (tf, '\f');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'n':
|
||||
add_char_segment (tf, '\n');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'r':
|
||||
add_char_segment (tf, '\r');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 't':
|
||||
add_char_segment (tf, '\t');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'v':
|
||||
add_char_segment (tf, '\v');
|
||||
cur++;
|
||||
@@ -345,39 +345,39 @@ parse_transform_expr (const char *expr)
|
||||
add_char_segment (tf, '&');
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'L':
|
||||
/* Turn the replacement to lowercase until a `\U' or `\E'
|
||||
is found, */
|
||||
add_case_ctl_segment (tf, ctl_locase);
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'l':
|
||||
/* Turn the next character to lowercase, */
|
||||
add_case_ctl_segment (tf, ctl_locase_next);
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'U':
|
||||
/* Turn the replacement to uppercase until a `\L' or `\E'
|
||||
is found, */
|
||||
add_case_ctl_segment (tf, ctl_upcase);
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'u':
|
||||
/* Turn the next character to uppercase, */
|
||||
add_case_ctl_segment (tf, ctl_upcase_next);
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
case 'E':
|
||||
/* Stop case conversion started by `\L' or `\U'. */
|
||||
add_case_ctl_segment (tf, ctl_stop);
|
||||
cur++;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
/* Try to be nice */
|
||||
{
|
||||
@@ -420,7 +420,7 @@ run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
|
||||
static char *case_ctl_buffer;
|
||||
static size_t case_ctl_bufsize;
|
||||
char *p;
|
||||
|
||||
|
||||
if (case_ctl_bufsize < size)
|
||||
{
|
||||
case_ctl_bufsize = size;
|
||||
@@ -432,16 +432,16 @@ run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
|
||||
case ctl_upcase_next:
|
||||
case_ctl_buffer[0] = toupper ((unsigned char) case_ctl_buffer[0]);
|
||||
break;
|
||||
|
||||
|
||||
case ctl_locase_next:
|
||||
case_ctl_buffer[0] = tolower ((unsigned char) case_ctl_buffer[0]);
|
||||
break;
|
||||
|
||||
|
||||
case ctl_upcase:
|
||||
for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
|
||||
*p = toupper ((unsigned char) *p);
|
||||
break;
|
||||
|
||||
|
||||
case ctl_locase:
|
||||
for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
|
||||
*p = tolower ((unsigned char) *p);
|
||||
@@ -457,7 +457,7 @@ run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
|
||||
static struct obstack stk;
|
||||
static bool stk_init;
|
||||
|
||||
void
|
||||
static void
|
||||
_single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
{
|
||||
regmatch_t *rmp;
|
||||
@@ -465,7 +465,7 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
size_t nmatches = 0;
|
||||
enum case_ctl_type case_ctl = ctl_stop, /* Current case conversion op */
|
||||
save_ctl = ctl_stop; /* Saved case_ctl for \u and \l */
|
||||
|
||||
|
||||
/* Reset case conversion after a single-char operation */
|
||||
#define CASE_CTL_RESET() if (case_ctl == ctl_upcase_next \
|
||||
|| case_ctl == ctl_locase_next) \
|
||||
@@ -473,20 +473,20 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
case_ctl = save_ctl; \
|
||||
save_ctl = ctl_stop; \
|
||||
}
|
||||
|
||||
|
||||
rmp = xmalloc ((tf->regex.re_nsub + 1) * sizeof (*rmp));
|
||||
|
||||
while (*input)
|
||||
{
|
||||
size_t disp;
|
||||
char *ptr;
|
||||
|
||||
|
||||
rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0);
|
||||
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
struct replace_segm *segm;
|
||||
|
||||
|
||||
disp = rmp[0].rm_eo;
|
||||
|
||||
if (rmp[0].rm_so)
|
||||
@@ -516,7 +516,7 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
}
|
||||
obstack_grow (&stk, ptr, segm->v.literal.size);
|
||||
break;
|
||||
|
||||
|
||||
case segm_backref: /* Back-reference segment */
|
||||
if (rmp[segm->v.ref].rm_so != -1
|
||||
&& rmp[segm->v.ref].rm_eo != -1)
|
||||
@@ -529,7 +529,7 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
ptr = run_case_conv (case_ctl, ptr, size);
|
||||
CASE_CTL_RESET();
|
||||
}
|
||||
|
||||
|
||||
obstack_grow (&stk, ptr, size);
|
||||
}
|
||||
break;
|
||||
@@ -549,7 +549,7 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
break;
|
||||
}
|
||||
/*FALL THROUGH*/
|
||||
|
||||
|
||||
case ctl_upcase:
|
||||
case ctl_locase:
|
||||
case ctl_stop:
|
||||
@@ -577,18 +577,18 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
free (rmp);
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
_transform_name_to_obstack (int flags, char *input, char **output)
|
||||
{
|
||||
struct transform *tf;
|
||||
bool alloced = false;
|
||||
|
||||
|
||||
if (!stk_init)
|
||||
{
|
||||
obstack_init (&stk);
|
||||
stk_init = true;
|
||||
}
|
||||
|
||||
|
||||
for (tf = transform_head; tf; tf = tf->next)
|
||||
{
|
||||
if (tf->flags & flags)
|
||||
@@ -601,7 +601,7 @@ _transform_name_to_obstack (int flags, char *input, char **output)
|
||||
*output = input;
|
||||
return alloced;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
transform_name_fp (char **pinput, int flags,
|
||||
char *(*fun)(char *, void *), void *dat)
|
||||
@@ -628,3 +628,9 @@ transform_name (char **pinput, int type)
|
||||
{
|
||||
return transform_name_fp (pinput, type, NULL, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
transform_program_p (void)
|
||||
{
|
||||
return transform_head != NULL;
|
||||
}
|
||||
|
||||
14
src/unlink.c
14
src/unlink.c
@@ -1,4 +1,4 @@
|
||||
/* This file is part of GNU tar.
|
||||
/* This file is part of GNU tar.
|
||||
Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
@@ -42,7 +42,7 @@ static struct deferred_unlink *dunlink_avail;
|
||||
size_t deferred_unlink_delay = 0;
|
||||
|
||||
static struct deferred_unlink *
|
||||
dunlink_alloc ()
|
||||
dunlink_alloc (void)
|
||||
{
|
||||
struct deferred_unlink *p;
|
||||
if (dunlink_avail)
|
||||
@@ -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);
|
||||
@@ -116,7 +116,7 @@ flush_deferred_unlinks (bool force)
|
||||
{
|
||||
prev = p;
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dunlink_head)
|
||||
dunlink_tail = NULL;
|
||||
@@ -142,13 +142,13 @@ queue_deferred_unlink (const char *name, bool is_dir)
|
||||
if (dunlink_head
|
||||
&& records_written > dunlink_head->records_written + deferred_unlink_delay)
|
||||
flush_deferred_unlinks (false);
|
||||
|
||||
|
||||
p = dunlink_alloc ();
|
||||
p->next = NULL;
|
||||
p->file_name = normalize_filename (name);
|
||||
p->is_dir = is_dir;
|
||||
p->records_written = records_written;
|
||||
|
||||
|
||||
if (dunlink_tail)
|
||||
dunlink_tail->next = p;
|
||||
else
|
||||
|
||||
36
src/update.c
36
src/update.c
@@ -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)
|
||||
@@ -114,8 +114,8 @@ update_archive (void)
|
||||
|
||||
while (!found_end)
|
||||
{
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info,
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info,
|
||||
read_header_auto);
|
||||
|
||||
switch (status)
|
||||
@@ -130,6 +130,8 @@ update_archive (void)
|
||||
|
||||
decode_header (current_header, ¤t_stat_info,
|
||||
¤t_format, 0);
|
||||
transform_stat_info (current_header->header.typeflag,
|
||||
¤t_stat_info);
|
||||
archive_format = current_format;
|
||||
|
||||
if (subcommand_option == UPDATE_SUBCOMMAND
|
||||
@@ -138,28 +140,37 @@ update_archive (void)
|
||||
struct stat s;
|
||||
|
||||
chdir_do (name->change_dir);
|
||||
if (deref_stat (dereference_option,
|
||||
current_stat_info.file_name, &s) == 0)
|
||||
if (deref_stat (current_stat_info.file_name, &s) == 0)
|
||||
{
|
||||
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
|
||||
{
|
||||
namebuf_t nbuf = namebuf_create (name->name);
|
||||
|
||||
|
||||
for (p = dirp; *p; p += strlen (p) + 1)
|
||||
addname (namebuf_name (nbuf, p),
|
||||
0, false, NULL);
|
||||
|
||||
|
||||
namebuf_free (nbuf);
|
||||
free (dirp);
|
||||
|
||||
|
||||
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)
|
||||
@@ -167,7 +178,7 @@ update_archive (void)
|
||||
remname (name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
skip_member ();
|
||||
break;
|
||||
}
|
||||
@@ -224,11 +235,12 @@ update_archive (void)
|
||||
if (subcommand_option == CAT_SUBCOMMAND)
|
||||
append_file (file_name);
|
||||
else
|
||||
dump_file (file_name, 1, (dev_t) 0);
|
||||
dump_file (0, file_name, file_name);
|
||||
}
|
||||
}
|
||||
|
||||
write_eot ();
|
||||
close_archive ();
|
||||
finish_deferred_unlinks ();
|
||||
names_notfound ();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ static char const *const warning_args[] = {
|
||||
"unknown-cast",
|
||||
"unknown-keyword",
|
||||
"xdev",
|
||||
"decompress-program",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -64,7 +65,8 @@ static int warning_types[] = {
|
||||
WARN_TIMESTAMP,
|
||||
WARN_UNKNOWN_CAST,
|
||||
WARN_UNKNOWN_KEYWORD,
|
||||
WARN_XDEV
|
||||
WARN_XDEV,
|
||||
WARN_DECOMPRESS_PROGRAM
|
||||
};
|
||||
|
||||
ARGMATCH_VERIFY (warning_args, warning_types);
|
||||
@@ -76,7 +78,7 @@ set_warning_option (const char *arg)
|
||||
{
|
||||
int negate = 0;
|
||||
int option;
|
||||
|
||||
|
||||
if (strcmp (arg, "none") == 0)
|
||||
{
|
||||
warning_option = 0;
|
||||
@@ -88,11 +90,10 @@ set_warning_option (const char *arg)
|
||||
arg += 3;
|
||||
}
|
||||
|
||||
option = XARGMATCH ("--warning", arg,
|
||||
option = XARGMATCH ("--warning", arg,
|
||||
warning_args, warning_types);
|
||||
if (negate)
|
||||
warning_option &= ~option;
|
||||
else
|
||||
warning_option |= option;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static void xheader_init (struct xheader *xhdr);
|
||||
static bool xheader_protected_pattern_p (char const *pattern);
|
||||
static bool xheader_protected_keyword_p (char const *keyword);
|
||||
static void xheader_set_single_keyword (char *) __attribute__ ((noreturn));
|
||||
@@ -452,7 +453,7 @@ xheader_write_global (struct xheader *xhdr)
|
||||
if (xhdr->stk)
|
||||
{
|
||||
char *name;
|
||||
|
||||
|
||||
xheader_finish (xhdr);
|
||||
xheader_write (XGLTYPE, name = xheader_ghdr_name (), time (NULL), xhdr);
|
||||
free (name);
|
||||
@@ -661,7 +662,7 @@ xheader_decode_global (struct xheader *xhdr)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
xheader_init (struct xheader *xhdr)
|
||||
{
|
||||
if (!xhdr->stk)
|
||||
@@ -708,7 +709,7 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size)
|
||||
|
||||
if (!p)
|
||||
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
|
||||
|
||||
memcpy (&xhdr->buffer[j], p->buffer, len);
|
||||
set_next_block_after (p);
|
||||
|
||||
@@ -1307,7 +1308,7 @@ sparse_numbytes_decoder (struct tar_stat_info *st,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, SIZE_MAX, keyword))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
{
|
||||
if (st->sparse_map_avail < st->sparse_map_size)
|
||||
st->sparse_map[st->sparse_map_avail++].numbytes = u;
|
||||
@@ -1355,7 +1356,7 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
e.numbytes = u;
|
||||
if (!(u == e.numbytes && errno != ERANGE))
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (size_t));
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
}
|
||||
if (st->sparse_map_avail < st->sparse_map_size)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar regression tests.
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2001, 2003, 2004, 2005,
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2001, 2003, 2004, 2005,
|
||||
# 2006, 2007, 2009 Free Software Foundation, Inc.
|
||||
|
||||
# François Pinard <pinard@iro.umontreal.ca>, 1988.
|
||||
@@ -52,6 +52,7 @@ TESTSUITE_AT = \
|
||||
append.at\
|
||||
append01.at\
|
||||
append02.at\
|
||||
append03.at\
|
||||
backup01.at\
|
||||
chtype.at\
|
||||
comprec.at\
|
||||
@@ -66,6 +67,7 @@ TESTSUITE_AT = \
|
||||
exclude03.at\
|
||||
exclude04.at\
|
||||
exclude05.at\
|
||||
exclude06.at\
|
||||
extrac01.at\
|
||||
extrac02.at\
|
||||
extrac03.at\
|
||||
@@ -74,6 +76,15 @@ TESTSUITE_AT = \
|
||||
extrac06.at\
|
||||
extrac07.at\
|
||||
extrac08.at\
|
||||
extrac09.at\
|
||||
extrac10.at\
|
||||
extrac11.at\
|
||||
extrac12.at\
|
||||
extrac13.at\
|
||||
extrac14.at\
|
||||
extrac15.at\
|
||||
extrac16.at\
|
||||
extrac17.at\
|
||||
filerem01.at\
|
||||
filerem02.at\
|
||||
gzip.at\
|
||||
@@ -89,11 +100,17 @@ TESTSUITE_AT = \
|
||||
ignfail.at\
|
||||
label01.at\
|
||||
label02.at\
|
||||
label03.at\
|
||||
label04.at\
|
||||
label05.at\
|
||||
link01.at\
|
||||
link02.at\
|
||||
link03.at\
|
||||
link04.at\
|
||||
listed01.at\
|
||||
listed02.at\
|
||||
listed03.at\
|
||||
listed04.at\
|
||||
long01.at\
|
||||
longv7.at\
|
||||
lustar01.at\
|
||||
@@ -106,6 +123,7 @@ TESTSUITE_AT = \
|
||||
multiv05.at\
|
||||
multiv06.at\
|
||||
multiv07.at\
|
||||
multiv08.at\
|
||||
old.at\
|
||||
options.at\
|
||||
options02.at\
|
||||
@@ -118,14 +136,17 @@ TESTSUITE_AT = \
|
||||
rename05.at\
|
||||
remfiles01.at\
|
||||
remfiles02.at\
|
||||
remfiles03.at\
|
||||
same-order01.at\
|
||||
same-order02.at\
|
||||
shortfile.at\
|
||||
shortupd.at\
|
||||
shortrec.at\
|
||||
sigpipe.at\
|
||||
sparse01.at\
|
||||
sparse02.at\
|
||||
sparse03.at\
|
||||
sparse04.at\
|
||||
sparsemv.at\
|
||||
sparsemvp.at\
|
||||
spmvp00.at\
|
||||
@@ -138,8 +159,10 @@ TESTSUITE_AT = \
|
||||
volsize.at\
|
||||
volume.at\
|
||||
verbose.at\
|
||||
verify.at\
|
||||
version.at\
|
||||
xform-h.at\
|
||||
xform01.at\
|
||||
star/gtarfail.at\
|
||||
star/gtarfail2.at\
|
||||
star/multi-fail.at\
|
||||
@@ -184,4 +207,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)
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
# When decoding a header tar was assigning 0 to oldgnu_header.isextended,
|
||||
# which destroyed name prefix. When updating archive, modified prefix
|
||||
# could have been written to disk thus producing invalid archive member.
|
||||
# Reported by Adye, TJ (Tim), <T.J.Adye@rl.ac.uk>
|
||||
# Reported by Adye, TJ (Tim), <T.J.Adye@rl.ac.uk>
|
||||
# References:
|
||||
# <7231C15EAC2F164CA6DC326D97493C8B36C25D@exchange35.fed.cclrc.ac.uk>
|
||||
# http://lists.gnu.org/archive/html/bug-tar/2005-02/msg00032.html
|
||||
|
||||
|
||||
AT_SETUP([appending files with long names])
|
||||
AT_KEYWORDS([append append01])
|
||||
|
||||
@@ -45,4 +45,3 @@ PREFIX/file2
|
||||
[],[],[],[oldgnu, ustar, posix, gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
# Using tar 1.15.x the following equivalent command sets:
|
||||
#
|
||||
# 1. tar cf archive file1 file2
|
||||
# 1. tar cf archive file1 file2
|
||||
# and
|
||||
# 2. tar cfT archive /dev/null
|
||||
# tar rf archive file1
|
||||
@@ -62,7 +62,7 @@ genfile --file file2
|
||||
MTIME="--mtime=@0"
|
||||
|
||||
# For PAX archives, we need to make sure extended header names are
|
||||
# reproducible and that their contents won't change with time
|
||||
# reproducible and that their contents won't change with time
|
||||
if test $[]TEST_TAR_FORMAT = posix; then
|
||||
TAR_OPTIONS="$TAR_OPTIONS --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=mtime,delete=atime,delete=ctime"
|
||||
fi
|
||||
|
||||
43
tests/append03.at
Normal file
43
tests/append03.at
Normal file
@@ -0,0 +1,43 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
AT_SETUP([append with name transformation])
|
||||
AT_KEYWORDS([append append03])
|
||||
|
||||
# Description: Make sure filenames are transformed during append.
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile --file file.1
|
||||
genfile --file file.2
|
||||
|
||||
tar -c -f archive --transform 's/file/plik/' file.*
|
||||
echo Appending
|
||||
tar -r -f archive --transform 's/file/plik/' -v --show-transformed-names file.1
|
||||
echo Testing
|
||||
tar tf archive
|
||||
],
|
||||
[0],
|
||||
[Appending
|
||||
plik.1
|
||||
Testing
|
||||
plik.1
|
||||
plik.2
|
||||
plik.1
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -16,17 +16,17 @@ fi
|
||||
STAR_DATA_URL=ftp://ftp.berlios.de/pub/star/testscripts
|
||||
if test -z "$STAR_TESTSCRIPTS"; then
|
||||
STAR_TESTSCRIPTS=$TEST_DATA_DIR
|
||||
fi
|
||||
fi
|
||||
|
||||
# tarball_prereq file sum dir url
|
||||
tarball_prereq() {
|
||||
if test -d "$3"; then
|
||||
if test -d "$3"; then
|
||||
if test -r $3/$1; then
|
||||
:
|
||||
elif test -n "$FULL_TEST"; then
|
||||
wget -q --directory-prefix=$3 $4/$1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo "$2 $3/$1" | md5sum --status --check - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
@@ -34,4 +34,3 @@ decho() {
|
||||
echo $*
|
||||
echo >&2 $*
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2009-2010 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
|
||||
@@ -34,10 +34,11 @@ AT_SETUP([extracting existing dir with --backup])
|
||||
AT_KEYWORDS([extract backup backup01])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
unset VERSION_CONTROL
|
||||
mkdir dir1 dir2
|
||||
echo bla > dir1/file1
|
||||
tar cf test.tar dir1 dir2
|
||||
tar xfv test.tar --backup
|
||||
tar xfv test.tar --backup --warning=no-timestamp
|
||||
],
|
||||
[0],
|
||||
[dir1/
|
||||
@@ -46,4 +47,4 @@ Renaming `dir1/file1' to `dir1/file1~'
|
||||
dir2/
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
AT_CLEANUP
|
||||
|
||||
@@ -51,9 +51,9 @@ tar --create --file=archive.2 --listed-incremental=db.2 directory
|
||||
rm -r directory
|
||||
|
||||
echo Restore archive.1
|
||||
tar -xf archive.1 --listed-incremental=/dev/null
|
||||
tar -xf archive.1 --listed-incremental=/dev/null --warning=no-timestamp
|
||||
echo Restore archive.2
|
||||
tar -xf archive.2 --listed-incremental=/dev/null
|
||||
tar -xf archive.2 --listed-incremental=/dev/null --warning=no-timestamp
|
||||
find directory | sort
|
||||
],
|
||||
[0],
|
||||
|
||||
@@ -29,7 +29,7 @@ echo "separator"
|
||||
tar cfz archive file1
|
||||
echo "separator"
|
||||
mv file1 orig
|
||||
tar xfv archive
|
||||
tar xfv archive --warning=no-timestamp
|
||||
cmp orig file1
|
||||
],
|
||||
[0],
|
||||
|
||||
@@ -31,8 +31,8 @@ for i in 1 2 3 4 5 6 7 8 9
|
||||
do touch $prefix$i
|
||||
done
|
||||
tar -cf archive ./$prefix* &&
|
||||
tar --delete -f archive ./${prefix}5 &&
|
||||
tar -tf archive
|
||||
tar --delete -f archive ./${prefix}5 &&
|
||||
tar -tf archive
|
||||
],
|
||||
[0],
|
||||
[./PREFIX[]1
|
||||
|
||||
@@ -36,7 +36,7 @@ mkdir dir/rock
|
||||
echo "Signature: 8a477f597d28d172789f06886806bc55" > dir/rock/CACHEDIR.TAG
|
||||
echo "test" > dir/rock/file
|
||||
|
||||
for option in exclude-caches exclude-caches-under exclude-caches-all
|
||||
for option in exclude-caches exclude-caches-under exclude-caches-all
|
||||
do
|
||||
echo OPTION $option
|
||||
tar -cf archive.tar --$option -v dir 2>err | sort
|
||||
@@ -45,7 +45,7 @@ do
|
||||
tar tf archive.tar | sort
|
||||
done
|
||||
|
||||
for option in exclude-tag exclude-tag-under exclude-tag-all
|
||||
for option in exclude-tag exclude-tag-under exclude-tag-all
|
||||
do
|
||||
echo OPTION $option
|
||||
tar -cf archive.tar --${option}=tagfile -v dir 2>err | sort
|
||||
|
||||
47
tests/exclude06.at
Normal file
47
tests/exclude06.at
Normal file
@@ -0,0 +1,47 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Tar 1.23 would fail to exclude names longer that 100 characters from
|
||||
# pax format archives.
|
||||
#
|
||||
# Reported-by: Matthew Peterson <mrpeterson2@gmail.com>
|
||||
# References: <AANLkTin0teb1dcl0HCNquHxvN4HQnJmP6aK7CJCqy0sd@mail.gmail.com>
|
||||
# http://lists.gnu.org/archive/html/help-tar/2010-06/msg00000.html
|
||||
|
||||
AT_SETUP([exclude: long files in pax archives])
|
||||
AT_KEYWORDS([exclude exclude06])
|
||||
|
||||
m4_define([test_base_dir],[one/two/three/four/five/six/seven/eight/nine/ten/eleven/twelve/thirteen/fourteen/fifteen/sixteen/seventeen])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_TAR_MKHIER(test_base_dir)
|
||||
genfile --length 20 -f test_base_dir[/1.txt]
|
||||
genfile --length 20 -f test_base_dir[/1.c]
|
||||
|
||||
tar cf archive.tar test_base_dir
|
||||
mkdir out
|
||||
tar -C out -xf archive.tar --exclude='*.txt' --warning=no-timestamp
|
||||
find out -type f
|
||||
],
|
||||
[0],
|
||||
[[out/]test_base_dir[/1.c]
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[pax])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -27,7 +27,7 @@ AT_TAR_CHECK([
|
||||
mkdir directory
|
||||
touch directory/file
|
||||
tar cf archive directory || exit 1
|
||||
tar xf archive || exit 1
|
||||
tar xf archive --warning=no-timestamp || exit 1
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
@@ -27,7 +27,7 @@ AT_TAR_CHECK([
|
||||
mkdir directory
|
||||
tar -cPvf archive directory/../directory
|
||||
echo separator
|
||||
tar -xPvf archive],
|
||||
tar -xPvf archive --warning=no-timestamp],
|
||||
[0],
|
||||
[directory/../directory/
|
||||
separator
|
||||
|
||||
@@ -36,7 +36,7 @@ tar -cf archive ./file1 directory
|
||||
tar -tf archive \
|
||||
--exclude='./*1' \
|
||||
--exclude='d*/*1' \
|
||||
--exclude='d*/s*/*2' | sort
|
||||
--exclude='d*/s*/*2' | sort
|
||||
],
|
||||
[0],
|
||||
[directory/
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
AT_SETUP([extracting selected members from pax])
|
||||
AT_KEYWORDS([extract extract05])
|
||||
|
||||
AT_DATA([list],
|
||||
AT_DATA([list],
|
||||
[jeden
|
||||
cztery
|
||||
])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile --sparse --file sparsefile 0 ABCD 1M EFGH 2000K IJKL || AT_SKIP_TEST
|
||||
genfile --sparse --file sparsefile 0 ABCD 1M EFGH 2000K IJKL || AT_SKIP_TEST
|
||||
genfile --length 118 --file jeden
|
||||
genfile --length 223 --file dwa
|
||||
genfile --length 517 --file trzy
|
||||
@@ -47,7 +47,7 @@ tar cf archive jeden dwa trzy cztery || exit 1
|
||||
mkdir dir
|
||||
cd dir
|
||||
|
||||
tar xvfT ../archive ../../list || exit 1
|
||||
tar xvfT ../archive ../../list --warning=no-timestamp || exit 1
|
||||
|
||||
cd ..
|
||||
],
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
# permissions than your umask. In this case, the permissions of the
|
||||
# existing directory will toggle between the version which complies with
|
||||
# your umask (which would be correct, without -p) and the version from the
|
||||
# tarfile.
|
||||
# tarfile.
|
||||
#
|
||||
# Reported by: Ian Jackson <iwj@ubuntu.com>
|
||||
#
|
||||
@@ -41,7 +41,7 @@ umask 022
|
||||
# Make sure user's umask is honored, even if we are superuser
|
||||
TAR_OPTIONS="$TAR_OPTIONS --no-same-permissions"
|
||||
|
||||
# Create a directory
|
||||
# Create a directory
|
||||
mkdir directory
|
||||
chmod 777 directory
|
||||
genfile --stat=mode:777 directory
|
||||
@@ -54,10 +54,10 @@ chmod 755 directory
|
||||
genfile --stat=mode:777 directory
|
||||
|
||||
# ... and attempt to restore it twice
|
||||
tar xf arc directory
|
||||
tar xf arc directory --warning=no-timestamp
|
||||
genfile --stat=mode:777 directory
|
||||
|
||||
tar xf arc directory
|
||||
tar xf arc directory --warning=no-timestamp
|
||||
genfile --stat=mode:777 directory
|
||||
|
||||
# After both restores, the directory mode should be 755
|
||||
|
||||
@@ -28,13 +28,15 @@ AT_SETUP([extracting symlinks to a read-only dir])
|
||||
AT_KEYWORDS([extract extract07 read-only symlink])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_UNPRIVILEGED_PREREQ
|
||||
|
||||
echo Prepare the directory
|
||||
mkdir dir
|
||||
genfile -f foo
|
||||
cd dir
|
||||
ln -s ../foo .
|
||||
cd ..
|
||||
chmod -w dir
|
||||
chmod a-w dir
|
||||
|
||||
echo Create the archive
|
||||
tar cf archive dir || exit 1
|
||||
@@ -52,8 +54,6 @@ Extract
|
||||
dir/
|
||||
dir/foo
|
||||
],
|
||||
[],[],[ustar]) # Testing one format is enough
|
||||
[],[],[],[ustar]) # Testing one format is enough
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ chmod 755 dir
|
||||
echo bla > dir/file
|
||||
tar cf test.tar dir
|
||||
chmod 700 dir
|
||||
tar xfv test.tar
|
||||
tar xfv test.tar --warning=no-timestamp
|
||||
genfile --stat=mode.777 dir
|
||||
],
|
||||
[0],
|
||||
@@ -49,4 +49,4 @@ dir/file
|
||||
755
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
AT_CLEANUP
|
||||
|
||||
48
tests/extrac09.at
Normal file
48
tests/extrac09.at
Normal file
@@ -0,0 +1,48 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This checks for the --listed-incremental bug reported by J Chapman Flack at
|
||||
# http://lists.gnu.org/archive/html/bug-tar/2010-06/msg00000.html
|
||||
|
||||
AT_SETUP([no need to save dir with unreadable . and ..])
|
||||
AT_KEYWORDS([extract extrac09])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_UNPRIVILEGED_PREREQ
|
||||
|
||||
mkdir dir
|
||||
mkdir dir/sub
|
||||
mkdir dir/sub/extract
|
||||
genfile --file dir/sub/f
|
||||
cd dir/sub
|
||||
|
||||
tar -cf archive.tar f
|
||||
|
||||
chmod a-r . ..
|
||||
tar -xvf archive.tar -C extract f
|
||||
status=$?
|
||||
chmod a+r . ..
|
||||
cmp f extract/f || status=$?
|
||||
exit $status
|
||||
],
|
||||
[0],
|
||||
[f
|
||||
],
|
||||
[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
44
tests/extrac10.at
Normal file
44
tests/extrac10.at
Normal file
@@ -0,0 +1,44 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Check that delayed setting of directory metadata does not collide
|
||||
# with the -C option. When setting a directory's permissions, time
|
||||
# stamps, etc., tar should apply the -C option that was in effect when
|
||||
# the directory was extracted, not the -C option that happens to be in
|
||||
# effect when the metadata are later set.
|
||||
|
||||
AT_SETUP([-C and delayed setting of metadata])
|
||||
AT_KEYWORDS([extract extrac10])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir d x x/y
|
||||
echo foo >d/d1
|
||||
echo bar >e
|
||||
|
||||
tar -cf archive.tar d e &&
|
||||
tar -xf archive.tar -C x d -C y e &&
|
||||
diff -r d x/d &&
|
||||
diff e x/y/e
|
||||
],
|
||||
[0],
|
||||
[],
|
||||
[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
84
tests/extrac11.at
Normal file
84
tests/extrac11.at
Normal file
@@ -0,0 +1,84 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Check that 'tar' works even in a file-descriptor-limited environment.
|
||||
|
||||
AT_SETUP([scarce file descriptors])
|
||||
AT_KEYWORDS([extract extrac11])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
exec </dev/null
|
||||
dirs='a
|
||||
a/b
|
||||
a/b/c
|
||||
a/b/c/d
|
||||
a/b/c/d/e
|
||||
a/b/c/d/e/f
|
||||
a/b/c/d/e/f/g
|
||||
a/b/c/d/e/f/g/h
|
||||
a/b/c/d/e/f/g/h/i
|
||||
a/b/c/d/e/f/g/h/i/j
|
||||
a/b/c/d/e/f/g/h/i/j/k
|
||||
'
|
||||
files=
|
||||
mkdir $dirs dest1 dest2 dest3 || exit
|
||||
for dir in $dirs; do
|
||||
for file in X Y Z; do
|
||||
echo $file >$dir/$file || exit
|
||||
files="$files $file"
|
||||
done
|
||||
done
|
||||
|
||||
# Check that "ulimit" itself works. Close file descriptors before
|
||||
# invoking ulimit, to work around a bug (or a "feature") in some shells,
|
||||
# where they squirrel away dups of file descriptors into FD 10 and up
|
||||
# before closing the originals.
|
||||
( (exec 3<&- 4<&- 5<&- 6<&- 7<&- 8<&- 9<&- &&
|
||||
ulimit -n 100 &&
|
||||
tar -cf archive1.tar a &&
|
||||
tar -xf archive1.tar -C dest1 a
|
||||
) &&
|
||||
diff -r a dest1/a
|
||||
) >/dev/null 2>&1 ||
|
||||
AT_SKIP_TEST
|
||||
|
||||
# Another test that "ulimit" itself works:
|
||||
# tar should fail when completely starved of file descriptors.
|
||||
( (exec 3<&- 4<&- 5<&- 6<&- 7<&- 8<&- 9<&- &&
|
||||
ulimit -n 4 &&
|
||||
tar -cf archive2.tar a &&
|
||||
tar -xf archive2.tar -C dest2 a
|
||||
) &&
|
||||
diff -r a dest2/a
|
||||
) >/dev/null 2>&1 &&
|
||||
AT_SKIP_TEST
|
||||
|
||||
# Tar should work when there are few, but enough, file descriptors.
|
||||
( (exec 3<&- 4<&- 5<&- 6<&- 7<&- 8<&- 9<&- &&
|
||||
ulimit -n 10 &&
|
||||
tar -cf archive3.tar a &&
|
||||
tar -xf archive3.tar -C dest3 a
|
||||
) &&
|
||||
diff -r a dest3/a >/dev/null 2>&1
|
||||
) || { diff -r a dest3/a; exit 1; }
|
||||
],
|
||||
[0],[],[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
39
tests/extrac12.at
Normal file
39
tests/extrac12.at
Normal file
@@ -0,0 +1,39 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Check that 'tar' extracts permissions on the working directory last.
|
||||
|
||||
AT_SETUP([extract dot permissions])
|
||||
AT_KEYWORDS([extract extrac12])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir src dst
|
||||
echo file1 >src/file1
|
||||
echo file2 >src/file2
|
||||
chmod a-w src
|
||||
|
||||
tar --no-recursion -cf archive.tar -C src . ./file1 file2 &&
|
||||
tar -xf archive.tar -C dst &&
|
||||
cmp src/file1 dst/file1 &&
|
||||
cmp src/file2 dst/file2
|
||||
],
|
||||
[0],[],[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
53
tests/extrac13.at
Normal file
53
tests/extrac13.at
Normal file
@@ -0,0 +1,53 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Check that 'tar' normally does follow symbolic links when extracting,
|
||||
# unless --dereference is specified.
|
||||
|
||||
AT_SETUP([extract over symlinks])
|
||||
AT_KEYWORDS([extract extrac13])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir src dst1 dst2 dst3
|
||||
echo file1 >src/file1
|
||||
ln -s target1 dst1/file1
|
||||
echo target1 >dst1/target1
|
||||
echo target1 >target1
|
||||
|
||||
tar -cf archive.tar -C src . &&
|
||||
tar -xf archive.tar -C dst1 --warning=no-timestamp &&
|
||||
diff src/file1 dst1/file1 &&
|
||||
diff target1 dst1/target1
|
||||
|
||||
ln -s target1 dst2/file1
|
||||
echo target1 >dst2/target1
|
||||
tar --overwrite -xf archive.tar -C dst2 --warning=no-timestamp &&
|
||||
diff src/file1 dst2/file1 &&
|
||||
diff target1 dst2/target1
|
||||
|
||||
ln -s target1 dst3/file1
|
||||
echo target1 >dst3/target1
|
||||
tar --overwrite -xhf archive.tar -C dst3 --warning=no-timestamp &&
|
||||
diff src/file1 dst3/file1 &&
|
||||
diff src/file1 dst3/target1
|
||||
],
|
||||
[0],[],[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
36
tests/extrac14.at
Normal file
36
tests/extrac14.at
Normal file
@@ -0,0 +1,36 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Check that 'tar -x -C FOO' follows FOO if FOO is a symbolic link.
|
||||
|
||||
AT_SETUP([extract -C symlink])
|
||||
AT_KEYWORDS([extract extrac14])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir dest
|
||||
ln -s dest symlink
|
||||
echo foo >foo
|
||||
tar -cf archive.tar foo &&
|
||||
tar -xf archive.tar -C symlink --warning=no-timestamp &&
|
||||
cmp foo dest/foo
|
||||
],
|
||||
[0],[],[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
44
tests/extrac15.at
Normal file
44
tests/extrac15.at
Normal file
@@ -0,0 +1,44 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Check diagnostic of 'tar -x a/b/c' when b cannot be created.
|
||||
|
||||
AT_SETUP([extract parent mkdir failure])
|
||||
AT_KEYWORDS([extract extrac15])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_UNPRIVILEGED_PREREQ
|
||||
|
||||
mkdir src src/a src/a/b dest dest/a
|
||||
touch src/a/b/c
|
||||
chmod a-w dest/a
|
||||
|
||||
tar -cf archive.tar -C src a/b/c &&
|
||||
if tar -xf archive.tar -C dest a/b/c
|
||||
then (exit 1)
|
||||
else (exit 0)
|
||||
fi
|
||||
],
|
||||
[0],[],[tar: a/b: Cannot mkdir: Permission denied
|
||||
tar: a/b/c: Cannot open: No such file or directory
|
||||
tar: Exiting with failure status due to previous errors
|
||||
],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
36
tests/extrac16.at
Normal file
36
tests/extrac16.at
Normal file
@@ -0,0 +1,36 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# written by Paul Eggert from a bug report by Denis Excoffier
|
||||
# <http://lists.gnu.org/archive/html/bug-tar/2010-10/msg00034.html>
|
||||
|
||||
# Check extraction of empty directory with -C.
|
||||
|
||||
AT_SETUP([extract empty directory with -C])
|
||||
AT_KEYWORDS([extract extrac16])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir src src/a src/a/b dest
|
||||
touch src/a/c
|
||||
|
||||
tar -cf archive.tar -C src a &&
|
||||
tar -xf archive.tar -C dest
|
||||
],
|
||||
[0],[],[],[],[],[gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
46
tests/extrac17.at
Normal file
46
tests/extrac17.at
Normal file
@@ -0,0 +1,46 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
AT_SETUP([name matching/transformation ordering])
|
||||
AT_KEYWORDS([extract extrac17])
|
||||
|
||||
# Description: Tar 1.24 changed the ordering of name matching and
|
||||
# name transformation so that the former saw already transformed
|
||||
# file names (see commit 9c194c99 and exclude06.at). This reverted
|
||||
# ordering made it impossible to match file names in certain cases.
|
||||
# In particular, the testcase below would not extract anything.
|
||||
#
|
||||
# Reported-by: "Gabor Z. Papp" <gzp@papp.hu>
|
||||
# References: <x6r5fd9jye@gzp>, <20101026175126.29028@Pirx.gnu.org.ua>
|
||||
# http://lists.gnu.org/archive/html/bug-tar/2010-10/msg00047.html
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir dir dir/subdir1 dir/subdir2 out
|
||||
genfile --file dir/subdir1/file1
|
||||
genfile --file dir/subdir2/file2
|
||||
|
||||
tar cf dir.tar dir
|
||||
|
||||
tar -x -v -f dir.tar -C out --strip-components=2 --warning=no-timestamp \
|
||||
dir/subdir1/
|
||||
],
|
||||
[0],
|
||||
[dir/subdir1/file1
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -26,7 +26,7 @@
|
||||
# instead.
|
||||
#
|
||||
# Reported by: Solar Designer <solar@openwall.com>
|
||||
#
|
||||
#
|
||||
# References: <20090228235820.GA13362@openwall.com>
|
||||
# http://lists.gnu.org/archive/html/bug-tar/2009-03/msg00000.html
|
||||
#
|
||||
@@ -98,4 +98,3 @@ tar: dir/file1: File removed before we read it
|
||||
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
@@ -39,12 +39,11 @@ genfile --run --checkpoint=3 --exec 'rm -rf dir2' -- \
|
||||
],
|
||||
[2],
|
||||
[ignore],
|
||||
[tar: dir2: Cannot stat: No such file or directory
|
||||
tar: dir2/file1: File removed before we read it
|
||||
tar: Exiting with failure status due to previous errors
|
||||
],[],[],[gnu, posix])
|
||||
[ignore],[],[],[gnu, posix])
|
||||
|
||||
# Ignore stdout and stderr because their contents depend on
|
||||
# the file system implementation.
|
||||
|
||||
# Timing information: see filerem01.at
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2004, 2007, 2009 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2004, 2007, 2009, 2010 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
|
||||
@@ -27,13 +27,14 @@ unset TAR_OPTIONS
|
||||
|
||||
AT_CHECK([
|
||||
AT_GZIP_PREREQ
|
||||
tar xfvz /dev/null
|
||||
tar xfvz /dev/null 2>err
|
||||
RC=$?
|
||||
sed -n '/^tar:/p' err >&2
|
||||
exit $RC
|
||||
],
|
||||
[2],
|
||||
[],
|
||||
[
|
||||
gzip: stdin: unexpected end of file
|
||||
tar: Child returned status 1
|
||||
[tar: Child returned status 1
|
||||
tar: Error is not recoverable: exiting now
|
||||
],
|
||||
[],[])
|
||||
|
||||
@@ -37,9 +37,9 @@ tar -cf archive.1 -g db directory
|
||||
|
||||
mv directory orig
|
||||
|
||||
tar xvfg archive.0 /dev/null
|
||||
tar xvfg archive.0 /dev/null --warning=no-timestamp
|
||||
echo separator
|
||||
tar xvfg archive.1 /dev/null
|
||||
tar xvfg archive.1 /dev/null --warning=no-timestamp
|
||||
],
|
||||
[0],
|
||||
[directory/
|
||||
@@ -51,5 +51,3 @@ tar: Deleting `directory/bar'
|
||||
[],[],[],[gnu, oldgnu, posix])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
# if the archive has normal member ordering, i.e. each directory
|
||||
# member is immediately followed by members located under that directory.
|
||||
# This is not true for incremental archives, where directory members
|
||||
# precede the non-directory ones. Due to this, GNU tar up to version 1.15.2
|
||||
# precede the non-directory ones. Due to this, GNU tar up to version 1.15.2
|
||||
# failed to correctly restore directory timestamps from an incremental
|
||||
# archive if this directory contained some files in it.
|
||||
# archive if this directory contained some files in it.
|
||||
#
|
||||
# References: <200511291228.47081.karaman@dssgmbh.de>
|
||||
|
||||
|
||||
@@ -45,17 +45,17 @@ tar -cf archive.2 -g db directory
|
||||
mv directory orig
|
||||
|
||||
echo Listing of archive.1
|
||||
tar -tf archive.1 | sort
|
||||
tar -tf archive.1 | sort
|
||||
echo Listing of archive.2
|
||||
tar -tf archive.2 | sort
|
||||
tar -tf archive.2 | sort
|
||||
|
||||
echo Directory after first restore
|
||||
tar -xf archive.1 -g db
|
||||
tar -xf archive.1 -g db --warning=no-timestamp
|
||||
find directory | sort
|
||||
|
||||
echo Directory after second restore
|
||||
tar -xf archive.2 -g db
|
||||
find directory | sort
|
||||
tar -xf archive.2 -g db --warning=no-timestamp
|
||||
find directory | sort
|
||||
],
|
||||
[0],
|
||||
[Listing of archive.1
|
||||
@@ -78,4 +78,3 @@ directory/z
|
||||
AT_CLEANUP
|
||||
|
||||
# End of incr03.at
|
||||
|
||||
|
||||
@@ -75,5 +75,3 @@ sub/b/file4
|
||||
],[],[],[],[gnu, oldgnu, posix])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ genfile --length 0 --file foo
|
||||
genfile --length 12288 --file bar
|
||||
genfile --length 12288 --file baz
|
||||
tar --label=Test -cM -L10 -f 1.tar -f 2.tar -f 3.tar -f 4.tar foo bar baz
|
||||
tar -Mt -f 1.tar -f 2.tar -f 3.tar -f 4.tar
|
||||
tar -Mt -f 1.tar -f 2.tar -f 3.tar -f 4.tar
|
||||
],
|
||||
[0],
|
||||
[Test Volume 1
|
||||
|
||||
87
tests/label03.at
Normal file
87
tests/label03.at
Normal file
@@ -0,0 +1,87 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Description: Test the functionality of the --test-label option.
|
||||
# In versions up to 1.23 it did not match the documentation. This
|
||||
# test case follows the examples from "9.7 Including a Label in the Archive".
|
||||
# References: <15929_1268069389_4B95340D_15929_35_1_D621E31C29598A43AF7B4BBD30CCDDFD0838294A@fr0-mailmb04.res.airbus.corp>
|
||||
#
|
||||
|
||||
AT_SETUP([test-label option])
|
||||
AT_KEYWORDS([label label03 test-label])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
exec <&-
|
||||
genfile --file file
|
||||
tar -c --label='iamalabel' --file iamanarchive file
|
||||
tar -c --file unlabeled.tar file
|
||||
decho "# Display label"
|
||||
tar --test-label --file=iamanarchive; echo $?
|
||||
decho "# Display label: unlabeled"
|
||||
tar --test-label --file=unlabeled.tar; echo $?
|
||||
decho "# Test label: success"
|
||||
tar --test-label --file=iamanarchive 'iamalabel'; echo $?
|
||||
decho "# Test label: failure"
|
||||
tar --test-label --file=iamanarchive 'amalabel'; echo $?
|
||||
decho "# Test label: unlabeled"
|
||||
tar --test-label --file=unlabeled.tar 'amalabel'; echo $?
|
||||
decho "# Test label, verbose: success"
|
||||
tar --test-label --verbose --file=iamanarchive 'iamalabel'; echo $?
|
||||
decho "# Test label, verbose: failure"
|
||||
tar --test-label --verbose --file=iamanarchive 'amalabel'; echo $?
|
||||
decho "# Test label: multiple arguments"
|
||||
tar --test-label --file=iamanarchive a iamalabel b; echo $?
|
||||
decho "# Test label: wildcards"
|
||||
tar --test-label --file=iamanarchive --wildcards '*label'; echo $?
|
||||
],
|
||||
[0],
|
||||
[# Display label
|
||||
iamalabel
|
||||
0
|
||||
# Display label: unlabeled
|
||||
0
|
||||
# Test label: success
|
||||
0
|
||||
# Test label: failure
|
||||
1
|
||||
# Test label: unlabeled
|
||||
1
|
||||
# Test label, verbose: success
|
||||
iamalabel
|
||||
0
|
||||
# Test label, verbose: failure
|
||||
iamalabel
|
||||
1
|
||||
# Test label: multiple arguments
|
||||
0
|
||||
# Test label: wildcards
|
||||
0
|
||||
],
|
||||
[# Display label
|
||||
# Display label: unlabeled
|
||||
# Test label: success
|
||||
# Test label: failure
|
||||
# Test label: unlabeled
|
||||
# Test label, verbose: success
|
||||
# Test label, verbose: failure
|
||||
tar: Archive label mismatch
|
||||
# Test label: multiple arguments
|
||||
# Test label: wildcards
|
||||
],[],[],[gnu,oldgnu,posix])
|
||||
|
||||
AT_CLEANUP
|
||||
51
tests/label04.at
Normal file
51
tests/label04.at
Normal file
@@ -0,0 +1,51 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Description: Test the functionality of the --label option used in
|
||||
# conjunction with an operation, other than create. It was broken
|
||||
# in versions up to 1.23.
|
||||
# References: <15929_1268069389_4B95340D_15929_35_1_D621E31C29598A43AF7B4BBD30CCDDFD0838294A@fr0-mailmb04.res.airbus.corp>
|
||||
#
|
||||
|
||||
AT_SETUP([label with non-create option])
|
||||
AT_KEYWORDS([label label04])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
exec <&-
|
||||
genfile --file file
|
||||
decho "# Create volume"
|
||||
tar -c -f archive --label='New volume' file
|
||||
decho "# Update: wrong label"
|
||||
tar -rf archive --label='My volume' file; echo $?
|
||||
decho "# Update: right label"
|
||||
tar -rf archive --label='New volume' file
|
||||
],
|
||||
[0],
|
||||
[# Create volume
|
||||
# Update: wrong label
|
||||
2
|
||||
# Update: right label
|
||||
],
|
||||
[# Create volume
|
||||
# Update: wrong label
|
||||
tar: Volume `New volume' does not match `My volume'
|
||||
tar: Error is not recoverable: exiting now
|
||||
# Update: right label
|
||||
],[],[],[gnu,oldgnu,posix])
|
||||
|
||||
AT_CLEANUP
|
||||
48
tests/label05.at
Normal file
48
tests/label05.at
Normal file
@@ -0,0 +1,48 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Description: See label04. This testcase uses an unlabeled archive
|
||||
# volume.
|
||||
|
||||
AT_SETUP([label with non-create option])
|
||||
AT_KEYWORDS([label label05])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
exec <&-
|
||||
genfile --file file
|
||||
decho "# Create volume"
|
||||
tar -c -f archive file
|
||||
decho "# Update: wrong label"
|
||||
tar -rf archive --label='My volume' file; echo $?
|
||||
decho "# Update: right label"
|
||||
tar -rf archive file
|
||||
],
|
||||
[0],
|
||||
[# Create volume
|
||||
# Update: wrong label
|
||||
2
|
||||
# Update: right label
|
||||
],
|
||||
[# Create volume
|
||||
# Update: wrong label
|
||||
tar: Archive not labeled to match `My volume'
|
||||
tar: Error is not recoverable: exiting now
|
||||
# Update: right label
|
||||
],[],[],[gnu,oldgnu,posix])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -44,7 +44,7 @@ ln directory/test1/test.txt directory/test2/test.txt || AT_SKIP_TEST
|
||||
tar cf archive directory/test1/test.txt directory/test1/test.txt
|
||||
|
||||
rm -r directory
|
||||
tar xf archive
|
||||
tar xf archive --warning=no-timestamp
|
||||
|
||||
ls directory/test1
|
||||
],
|
||||
|
||||
@@ -34,9 +34,9 @@ AT_KEYWORDS([hardlinks link02])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile -l 64 -f file1
|
||||
link file1 file2
|
||||
link file2 file3
|
||||
link file3 file4
|
||||
ln file1 file2
|
||||
ln file2 file3
|
||||
ln file3 file4
|
||||
tar -c -f archive --remove-files file1 file2 file3 file4
|
||||
tar tfv archive | sed -n 's/.*file[[2-4]] link to //p'
|
||||
],
|
||||
@@ -49,4 +49,3 @@ file1
|
||||
AT_CLEANUP
|
||||
|
||||
# End of link02.at
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ AT_KEYWORDS([hardlinks link03])
|
||||
|
||||
m4_define([create_files],[
|
||||
genfile -l 64 -f file1
|
||||
link file1 file2
|
||||
link file2 file3
|
||||
link file3 file4
|
||||
ln file1 file2
|
||||
ln file2 file3
|
||||
ln file3 file4
|
||||
])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
@@ -52,5 +52,3 @@ file1
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
|
||||
64
tests/link04.at
Normal file
64
tests/link04.at
Normal file
@@ -0,0 +1,64 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2010 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, 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# written by Paul Eggert
|
||||
|
||||
# Make sure that tar -c correctly handles the case where a file is
|
||||
# encountered multiple times, even though it has a link count of 1.
|
||||
# This can occur when "tar -c FOO FOO" is used; it can also occur when
|
||||
# "tar -ch FOO" is used, if FOO contains symbolic links that point to
|
||||
# the same file.
|
||||
|
||||
AT_SETUP([link count is 1 but multiple occurrences])
|
||||
AT_KEYWORDS([hardlinks link04])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
mkdir dir
|
||||
echo TEST > dir/file
|
||||
ln -s file dir/symlink || AT_SKIP_TEST
|
||||
|
||||
tar cf archive dir dir
|
||||
tar tvf archive | sed '
|
||||
s,.*[[0-9]] dir/,dir/,
|
||||
' | sort
|
||||
|
||||
echo ==
|
||||
|
||||
tar chf archive dir
|
||||
tar tvf archive | sed '
|
||||
s,.*[[0-9]] dir/,dir/,
|
||||
s,file,FOO,g
|
||||
s,symlink,FOO,g
|
||||
' | sort
|
||||
],
|
||||
[0],
|
||||
[dir/
|
||||
dir/
|
||||
dir/file
|
||||
dir/file link to dir/file
|
||||
dir/symlink -> file
|
||||
dir/symlink link to dir/symlink
|
||||
==
|
||||
dir/
|
||||
dir/FOO
|
||||
dir/FOO link to dir/FOO
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -60,4 +60,3 @@ directory/file2
|
||||
[],[],[],[gnu, oldgnu])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user