Initial commit for bsdtar.
This commit is contained in:
parent
29ae923f44
commit
d7fe3b4f52
21
usr.bin/tar/Makefile
Normal file
21
usr.bin/tar/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# Makefile for bsdtar
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
DEBUG_FLAGS= -g
|
||||
|
||||
PROG= bsdtar
|
||||
SRCS= bsdtar.c matching.c read.c util.c write.c
|
||||
MAN = bsdtar.1
|
||||
|
||||
BINDIR?= /usr/bin
|
||||
WARNS?= 6
|
||||
LDADD += -larchive -lz -lbz2
|
||||
|
||||
.if defined(DMALLOC)
|
||||
CFLAGS += -DDMALLOC -I/usr/local/include
|
||||
LDADD += -L/usr/local/lib -ldmalloc
|
||||
.endif
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
577
usr.bin/tar/bsdtar.1
Normal file
577
usr.bin/tar/bsdtar.1
Normal file
@ -0,0 +1,577 @@
|
||||
.\" Copyright (c) 2003 Tim Kientzle
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 5, 2004
|
||||
.Dt BSDTAR 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm bsdtar
|
||||
.Nd manipulate tape archives
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Ar bundled-flags Ao args Ac
|
||||
.Op Ao Ar file Ac | Ao Ar pattern Ac ...
|
||||
.Nm
|
||||
.Brq Fl c | Fl t | Fl x
|
||||
.Op Ar options
|
||||
.Op Ar files | patterns | directories
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
creates and manipulates streaming archive files.
|
||||
.Pp
|
||||
The first synopsis form shows a
|
||||
.Dq bundled
|
||||
option word.
|
||||
This usage is provided for compatibility with historical implementations.
|
||||
See COMPATIBILITY below for details.
|
||||
.Pp
|
||||
The preferred usage is illustrated in the second synopsis.
|
||||
The first option to
|
||||
.Nm
|
||||
must be a mode indicator from the following list:
|
||||
.Bl -tag -compact -width indent
|
||||
.It Fl c
|
||||
Create a new archive containing the specified items.
|
||||
.It Fl r
|
||||
Like
|
||||
.Fl c ,
|
||||
but new entries are appended to the archive specified with the
|
||||
.Fl f
|
||||
option, which is required.
|
||||
If a new entry has the same name as an existing entry, it will normally
|
||||
overwrite (replace) that entry on extraction.
|
||||
Note that this only works on uncompressed archives stored in regular files.
|
||||
.It Fl t
|
||||
List archive contents to stdout.
|
||||
.It Fl u
|
||||
Like
|
||||
.Fl r ,
|
||||
but new entries are written only if they have a modification date
|
||||
newer than the corresponding entry in the archive.
|
||||
Note that this only works on uncompressed archives stored in regular files.
|
||||
.It Fl x
|
||||
Extract to disk from the archive.
|
||||
.El
|
||||
.Pp
|
||||
In
|
||||
.Fl c ,
|
||||
.Fl r ,
|
||||
or
|
||||
.Fl u
|
||||
mode, each specified file or directory is added to the
|
||||
archive in the order specified on the command line.
|
||||
By default, the contents of each directory are also archived.
|
||||
.Pp
|
||||
In extract or list mode, the entire command line
|
||||
is read and parsed before the archive is opened.
|
||||
The pathnames or patterns on the command line indicate
|
||||
which items in the archive should be processed.
|
||||
Patterns are shell-style globbing patterns as
|
||||
documented in XXXX.
|
||||
.Sh OPTIONS
|
||||
Unless specifically stated otherwise, options are applicable in
|
||||
all operating modes.
|
||||
.Bl -tag -width indent
|
||||
.It Cm @ Ns Pa archive
|
||||
(c and r mode only)
|
||||
The specified archive is opened and the entries
|
||||
in it will be appended to the current archive.
|
||||
As a simple example,
|
||||
.Dl Nm Fl c Fl f Pa - Pa newfile Cm @ Ns Pa original.tar
|
||||
writes a new archive to standard output containing a file
|
||||
.Pa newfile
|
||||
and all of the entries from
|
||||
.Pa original.tar .
|
||||
In contrast,
|
||||
.Dl Nm Fl c Fl f Pa - Pa newfile Pa original.tar
|
||||
creates a new archive with only two entries.
|
||||
Similarly,
|
||||
.Dl Nm Fl czf Pa - Fl F Cm pax Cm @ Ns Pa -
|
||||
reads an archive from standard input (whose format will be determined
|
||||
automatically) and converts it into a gzip-compressed
|
||||
pax-format archive on stdout.
|
||||
In this way,
|
||||
.Nm
|
||||
can be used to convert archives from one format to another.
|
||||
.It Fl b Ar blocksize
|
||||
Specify the block size, in 512-byte records, for tape drive I/O.
|
||||
As a rule, this argument is only needed when reading from or writing
|
||||
to tape drives, and usually not even then as the default block size of
|
||||
20 records (10240 bytes) is very common.
|
||||
.It Fl C Ar directory
|
||||
Change directories.
|
||||
The directory is changed after the archive
|
||||
is opened, but before any entries are extracted or written.
|
||||
(In particular, it does not affect the interpretation of the
|
||||
.Fl f
|
||||
option.)
|
||||
In create mode, note that
|
||||
.Fl C
|
||||
options are all processed before any files are read.
|
||||
To change directories between files, use
|
||||
.Cm C=
|
||||
instead.
|
||||
.It Cm C= Ns Pa dir
|
||||
(c and r mode only)
|
||||
Change to the specified directory before adding the following files.
|
||||
(Note that this is not an option in the sense of
|
||||
.Xr getopt 3 ,
|
||||
and is therefore processed as the files are processed.)
|
||||
.It Fl -exclude Ar pattern
|
||||
Do not process files or directories that match the
|
||||
specified pattern.
|
||||
Note that exclusions take precedence over patterns or filenames
|
||||
specified on the command line.
|
||||
.It Fl F Ar format
|
||||
(c mode only)
|
||||
Use the specified format for the created archive.
|
||||
Supported formats include
|
||||
.Dq cpio ,
|
||||
.Dq pax ,
|
||||
.Dq shar ,
|
||||
.Dq shardump ,
|
||||
and
|
||||
.Dq ustar .
|
||||
.It Fl f Ar file
|
||||
Read the archive from or write the archive to the specified file.
|
||||
The filename can be
|
||||
.Pa -
|
||||
for standard input or standard output.
|
||||
.It Fl -fast-read
|
||||
(x and t mode only)
|
||||
Extract or list only the first archive entry that matches each pattern
|
||||
or filename operand.
|
||||
Exit as soon as each specified pattern or filename has been matched.
|
||||
By default, the archive is always read to the very end, since
|
||||
there can be multiple entries with the same name and, by convention,
|
||||
later entries overwrite earlier entries.
|
||||
This option is provided as a performance optimization.
|
||||
.It Fl H
|
||||
(c and r mode only)
|
||||
Symbolic links named on the command line will be followed; the
|
||||
target of the link will be archived, not the link itself.
|
||||
.It Fl j
|
||||
(c mode only)
|
||||
Compress the resulting archive with
|
||||
.Xr bzip2 1 .
|
||||
Note that, unlike other
|
||||
.Nm tar
|
||||
implementations, this implementation recognizes bzip2 compression
|
||||
automatically when reading archives.
|
||||
This option is ignored in extract or list modes.
|
||||
.It Fl k
|
||||
(x mode only)
|
||||
Do not overwrite existing files.
|
||||
.It Fl L
|
||||
(c and r mode only)
|
||||
All symbolic links will be followed.
|
||||
Normally, symbolic links are archived as such.
|
||||
With this option, the target of the link will be archived instead.
|
||||
.It Fl l
|
||||
(c mode only)
|
||||
Issue a warning message unless all links to each file are archived.
|
||||
.It Fl m
|
||||
(x mode only)
|
||||
Do not extract modification time.
|
||||
By default, the modification time is set to the time stored in the archive.
|
||||
.It Fl n
|
||||
(c, r, u modes only)
|
||||
Do not recursively archive the contents of directories.
|
||||
.It Fl -nodump
|
||||
(c and r modes only)
|
||||
Honor the nodump file flag by skipping this file.
|
||||
.It Fl O
|
||||
(x mode only)
|
||||
Extracted files are written to standard out rather than
|
||||
being extracted to disk.
|
||||
.It Fl o
|
||||
(x mode only)
|
||||
Use the user and group of the user running the program rather
|
||||
than those specified in the archive.
|
||||
Note that this has no significance unless
|
||||
.Fl p
|
||||
is specified, and the program is being run by the root user.
|
||||
In this case, the file modes and flags from
|
||||
the archive will be restored, but ACLs or owner information in
|
||||
the archive will be discarded.
|
||||
(not yet implemented)
|
||||
.It Fl P
|
||||
Preserve leading slashes.
|
||||
By default, absolute pathnames (those that begin with a / character)
|
||||
have the leading slash removed.
|
||||
This option suppresses that behavior.
|
||||
.It Fl p
|
||||
(x mode only)
|
||||
Preserve file permissions.
|
||||
Attempt to restore the full permissions, including owner, file modes, file
|
||||
flags and ACLs, if available, for each item extracted from the archive.
|
||||
By default, newly-created regular files have the file mode restored and
|
||||
all other types of entries receive default permissions.
|
||||
.It Fl U
|
||||
(x mode only)
|
||||
Unlink files before creating them.
|
||||
(Not yet implemented.)
|
||||
.It Fl v
|
||||
Produce verbose output.
|
||||
In create and extract modes,
|
||||
.Nm
|
||||
will list each file name as it is read from or written to
|
||||
the archive.
|
||||
In list mode,
|
||||
.Nm
|
||||
will produce output similar to that of
|
||||
.Xr ls 1 .
|
||||
Additional
|
||||
.Fl v
|
||||
options will provide additional detail.
|
||||
.It Fl w
|
||||
Ask for confirmation for every action.
|
||||
.It Fl X
|
||||
(c, r, u modes)
|
||||
When visiting subdirectories, ignore any that are on different devices.
|
||||
.It Fl y
|
||||
(c mode only)
|
||||
Compress the resulting archive with
|
||||
.Xr bzip2 1 .
|
||||
.It Fl z
|
||||
(c mode only)
|
||||
Compress the resulting archive with
|
||||
.Xr gzip 1 .
|
||||
Note that, unlike other
|
||||
.Nm tar
|
||||
implementations, this implementation recognizes gzip
|
||||
and bzip2 compression automatically when reading archives.
|
||||
The
|
||||
.Fl j , y , No and Fl z
|
||||
options are ignored for extract or list mode.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
The following creates a new archive
|
||||
called
|
||||
.Ar file.tar
|
||||
that contains two files
|
||||
.Ar source.c
|
||||
and
|
||||
.Ar source.h :
|
||||
.Dl Nm Fl czf Pa file.tar Pa source.c Pa source.h
|
||||
.Pp
|
||||
To view a detailed table of contents for this
|
||||
archive:
|
||||
.Dl Nm Fl tvf Pa file.tar
|
||||
.Pp
|
||||
To extract all entries from the archive on
|
||||
the default tape drive:
|
||||
.Dl Nm Fl x
|
||||
.Pp
|
||||
In create mode, the list of files and directories to be archived
|
||||
can also include directory change instructions of the form
|
||||
.Cm C= Ns Pa foo/baz
|
||||
and archive inclusions of the form
|
||||
.Cm @ Ns Pa archive-file .
|
||||
For example, the command line
|
||||
.Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm C= Ns Pa /tmp Pa foo2
|
||||
will create a new archive
|
||||
.Pa new.tar .
|
||||
.Nm
|
||||
will read the file
|
||||
.Pa foo1
|
||||
from the current directory and add it to the output archive.
|
||||
It will then read each entry from
|
||||
.Pa old.tgz
|
||||
and add those entries to the output archive.
|
||||
Finally, it will switch to the
|
||||
.Pa /tmp
|
||||
directory and add
|
||||
.Pa foo2
|
||||
to the output archive.
|
||||
.Sh DIAGNOSTICS
|
||||
.Ex -std
|
||||
.Sh ENVIRONMENT
|
||||
The following environment variables affect the execution of
|
||||
.Nm :
|
||||
.Bl -tag -width ".Ev BLOCKSIZE"
|
||||
.It Ev LANG
|
||||
The locale to use.
|
||||
See
|
||||
.Xr environ 7
|
||||
for more information.
|
||||
.It Ev TZ
|
||||
The timezone to use when displaying dates.
|
||||
See
|
||||
.Xr environ 7
|
||||
for more information.
|
||||
.El
|
||||
.Sh COMPATIBILITY
|
||||
The bundled-arguments format is supported for compatibility
|
||||
with historic implementations.
|
||||
It consists of an initial word (with no leading - character) in which
|
||||
each character indicates an option.
|
||||
Arguments follow as separate words.
|
||||
The order of the arguments must match the order
|
||||
of the corresponding characters in the bundled command word.
|
||||
For example,
|
||||
.Dl Nm Cm tbf 32 Pa file.tar
|
||||
specifies three flags
|
||||
.Cm t ,
|
||||
.Cm b ,
|
||||
and
|
||||
.Cm f .
|
||||
The
|
||||
.Cm b
|
||||
and
|
||||
.Cm f
|
||||
flags both require arguments,
|
||||
so there must be two additional items
|
||||
on the command line. The
|
||||
.Ar 32
|
||||
is the argument to the
|
||||
.Cm b
|
||||
flag, and
|
||||
.Ar file.tar
|
||||
is the argument to the
|
||||
.Cm f
|
||||
flag.
|
||||
.Pp
|
||||
The mode options c, r, t, u, and x and the options
|
||||
b, f, l, m, o, v, and w are implemented to be compatible
|
||||
with SUSv2.
|
||||
.Pp
|
||||
On systems that support getopt_long(), additional long options
|
||||
are available to improve compatibility with other tar implementations.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
program reads and writes a variety of streaming archive formats, including:
|
||||
.Bl -tag -width indent
|
||||
.It Cm cpio
|
||||
The octet-oriented cpio format standardized by POSIX.
|
||||
.It Cm gnutar
|
||||
.Nm
|
||||
has limited read support for GNU-format tar archives.
|
||||
.It Cm pax interchange
|
||||
The pax interchange format is a POSIX-standard tar format that removes
|
||||
essentially all of the historic limitations in a standard-conforming fashion.
|
||||
This format is supported by standard implementations of
|
||||
.Xr pax 1
|
||||
as well as by some
|
||||
.Nm tar
|
||||
programs, including
|
||||
.Nm star .
|
||||
.It Cm shar
|
||||
A
|
||||
.Dq shar
|
||||
format archive is a shell script that, when executed on a POSIX-compliant
|
||||
system, will recreate the specified files.
|
||||
Note that shar-format archives will be plain text files only if all of the
|
||||
files being archived are themselves plain text files.
|
||||
.It Cm shardump
|
||||
This format is similar to shar but encodes binary files so that the result
|
||||
will be a plain text file regardless of the file contents.
|
||||
It also includes additional shell commands that attempt to reproduce as
|
||||
many file attributes as possible, including owner, mode, and flags.
|
||||
.It Cm tar
|
||||
.Nm
|
||||
can read most older tar archives, including many that violate
|
||||
the POSIX standard.
|
||||
.It Cm ustar
|
||||
The format first standardized by POSIX.
|
||||
It has the following limitations:
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
Device major and minor numbers are limited to 21 bits.
|
||||
Nodes with larger numbers will not be added to the archive.
|
||||
.It
|
||||
Path names in the archive are limited to 255 bytes.
|
||||
(Shorter if there is no / character in exactly the right place.)
|
||||
.It
|
||||
Symbolic links and hard links are stored in the archive with
|
||||
the name of the referenced file.
|
||||
This name is limited to 100 bytes.
|
||||
.It
|
||||
Extended attributes, file flags, and other extended
|
||||
security information cannot be stored.
|
||||
.It
|
||||
Archive entries are limited to 2 gigabytes in size.
|
||||
.El
|
||||
Note that the pax interchange has none of these restrictions.
|
||||
.Nm
|
||||
also supports a variety of extensions to this format
|
||||
used by particular archivers.
|
||||
In particular, it supports base-256 values in certain numeric fields.
|
||||
This essentially removes the limitations on file size, modification time,
|
||||
and device numbers.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr ar 1 ,
|
||||
.Xr bzip2 1 ,
|
||||
.Xr gzip 1 ,
|
||||
.Xr mt 1 ,
|
||||
.Xr pax 1 ,
|
||||
.Xr shar 1 ,
|
||||
.Xr libarchive 3 ,
|
||||
.Xr tar 5 .
|
||||
.Sh STANDARDS
|
||||
There is no current POSIX standard for the tar command; it appeared
|
||||
in SUSv2 but was dropped from SUSv3.
|
||||
The options used by this implementation were developed by surveying a
|
||||
number of existing tar implementations as well as the old SUSv2 specification
|
||||
for tar and the current SUSv3 specification for pax.
|
||||
.Pp
|
||||
The ustar and pax interchange file formats are defined by
|
||||
.St -p1003.1-2001
|
||||
for the pax command.
|
||||
.Sh BUGS
|
||||
The
|
||||
.Fl l
|
||||
and
|
||||
.Fl o
|
||||
options follow POSIX.
|
||||
GNU tar's
|
||||
.Fl l
|
||||
and
|
||||
.Fl o
|
||||
options do not.
|
||||
(This is, of course, a bug in GNU tar and not bsdtar.)
|
||||
.Pp
|
||||
The distinction between the
|
||||
.Fl C Pa dir
|
||||
option and the
|
||||
.Cm C= Ns Pa dir
|
||||
operation is prompted by the use of
|
||||
.Xr getopt_long 3
|
||||
for parsing the command line.
|
||||
Recall that
|
||||
.Xr getopt_long 3
|
||||
processes all options before all non-options.
|
||||
In particular,
|
||||
.Cm C= Ns Pa dir
|
||||
is not an option, and is therefore processed in the order it appears
|
||||
on the command line.
|
||||
In contrast,
|
||||
.Fl C Pa dir
|
||||
is an option, and therefore, in accordance with POSIX
|
||||
conventions, is handled in a manner that does not
|
||||
depend on the order of command-line options.
|
||||
This behavior differs from that of implementations that do
|
||||
not follow standard getopt argument parsing conventions.
|
||||
.Pp
|
||||
Since many options depend on the particular operating mode,
|
||||
the mode option itself must be specified first on the command line.
|
||||
This allows for more accurate detection and reporting of
|
||||
incorrect option usage.
|
||||
.Pp
|
||||
All archive output is written in correctly-sized blocks, even
|
||||
if the output is being compressed.
|
||||
Whether or not the last output block is padded to a full
|
||||
block size varies depending on the format and the
|
||||
output device.
|
||||
For tar and cpio formats, the last block of output is padded
|
||||
to a full block size if the output is being
|
||||
written to standard output or to a character or block device such as
|
||||
a tape drive.
|
||||
If the output is being written to a regular file, the last block
|
||||
will not be padded.
|
||||
Many compressors, including
|
||||
.Xr gzip 1
|
||||
and
|
||||
.Xr bzip2 1 ,
|
||||
complain about the null padding when decompressing an archive created by
|
||||
.Nm ,
|
||||
although they still extract it correctly.
|
||||
.Pp
|
||||
The compression and decompression is implemented internally, so
|
||||
there may be insignificant differences between the compressed output
|
||||
generated by
|
||||
.Dl Nm Fl czf Pa - file
|
||||
and that generated by
|
||||
.Dl Nm Fl cf Pa - file | Nm gzip
|
||||
.Pp
|
||||
The default should be to read and write archives to the standard I/O paths,
|
||||
but tradition dictates otherwise.
|
||||
.Pp
|
||||
The
|
||||
.Cm r
|
||||
and
|
||||
.Cm u
|
||||
modes require that the archive be uncompressed
|
||||
and located in a regular file on disk.
|
||||
Other archives can be modified using
|
||||
.Cm c
|
||||
mode with the
|
||||
.Pa @archive-file
|
||||
extension.
|
||||
.Pp
|
||||
To archive a file called
|
||||
.Pa C=foo ,
|
||||
you must specify it as
|
||||
.Pa ./C=foo
|
||||
on the command line.
|
||||
Similarly, to archive a file called
|
||||
.Pa @foo
|
||||
or
|
||||
.Pa -foo
|
||||
you must specify it as
|
||||
.Pa ./@foo
|
||||
or
|
||||
.Pa ./-foo ,
|
||||
respectively.
|
||||
.Pp
|
||||
In create mode, a leading
|
||||
.Pa ./
|
||||
is always removed.
|
||||
A leading
|
||||
.Pa /
|
||||
is stripped unless the
|
||||
.Fl P
|
||||
option is specified.
|
||||
.Pp
|
||||
There needs to be better support for file selection on both create
|
||||
and extract.
|
||||
.Pp
|
||||
There is not yet any support for multi-volume archives or sparse files.
|
||||
.Pp
|
||||
All features should be available using only short options in order
|
||||
to enhance portability to platforms that lack
|
||||
.Fn getopt_long .
|
||||
.Pp
|
||||
There are alternative long options for many of the short options that
|
||||
are deliberately not documented.
|
||||
.Sh HISTORY
|
||||
A
|
||||
.Nm tar
|
||||
command appeared in Sixth Edition Unix.
|
||||
There have been numerous other implementations,
|
||||
many of which extended the file format.
|
||||
John Gilmore's
|
||||
.Nm pdtar
|
||||
public-domain implementation (circa November, 1987)
|
||||
was quite influential, and formed the basis of GNU tar.
|
||||
GNU tar was included as the standard system tar
|
||||
in FreeBSD beginning with FreeBSD 1.0.
|
||||
.Pp
|
||||
This is a complete re-implementation based on the
|
||||
.Xr libarchive 3
|
||||
library.
|
501
usr.bin/tar/bsdtar.c
Normal file
501
usr.bin/tar/bsdtar.c
Normal file
@ -0,0 +1,501 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "bsdtar_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <dirent.h>
|
||||
#ifdef DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <locale.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
static void long_help(void);
|
||||
static void only_mode(char mode, char opt, const char *valid);
|
||||
static const char *progname;
|
||||
static char ** rewrite_argv(int *argc, char ** src_argv,
|
||||
const char *optstring);
|
||||
|
||||
const char *tar_opts = "b:C:cF:f:HhjkLlmnOoPprtUuvwXxyZz";
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
/*
|
||||
* These long options are deliberately not documented. They are
|
||||
* provided only to make life easier for people using GNU tar. The
|
||||
* only long options documented in the manual page are the ones with
|
||||
* no corresponding short option (currently, --exclude, --nodump, and
|
||||
* --fast-read).
|
||||
*
|
||||
* XXX TODO: Provide short options for --exclude, --nodump and --fast-read
|
||||
* so that bsdtar is usable on systems that do not have (or do not want
|
||||
* to use) getopt_long().
|
||||
*/
|
||||
|
||||
#define OPTION_EXCLUDE 1
|
||||
#define OPTION_FAST_READ 2
|
||||
#define OPTION_NODUMP 3
|
||||
|
||||
const struct option tar_longopts[] = {
|
||||
{ "absolute-paths", no_argument, NULL, 'P' },
|
||||
{ "append", no_argument, NULL, 'r' },
|
||||
{ "block-size", required_argument, NULL, 'b' },
|
||||
{ "bunzip2", no_argument, NULL, 'j' },
|
||||
{ "bzip", no_argument, NULL, 'j' },
|
||||
{ "bzip2", no_argument, NULL, 'j' },
|
||||
{ "cd", required_argument, NULL, 'C' },
|
||||
{ "confirmation", no_argument, NULL, 'w' },
|
||||
{ "create", no_argument, NULL, 'c' },
|
||||
{ "directory", required_argument, NULL, 'C' },
|
||||
{ "exclude", required_argument, NULL, OPTION_EXCLUDE },
|
||||
{ "extract", no_argument, NULL, 'x' },
|
||||
{ "fast-read", no_argument, NULL, OPTION_FAST_READ },
|
||||
{ "file", required_argument, NULL, 'f' },
|
||||
{ "format", required_argument, NULL, 'F' },
|
||||
{ "gunzip", no_argument, NULL, 'z' },
|
||||
{ "gzip", no_argument, NULL, 'z' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "interactive", no_argument, NULL, 'w' },
|
||||
{ "keep-old-files", no_argument, NULL, 'k' },
|
||||
{ "list", no_argument, NULL, 't' },
|
||||
{ "modification-time", no_argument, NULL, 'm' },
|
||||
{ "nodump", no_argument, NULL, OPTION_NODUMP },
|
||||
{ "norecurse", no_argument, NULL, 'n' },
|
||||
{ "preserve-permissions", no_argument, NULL, 'p' },
|
||||
{ "same-permissions", no_argument, NULL, 'p' },
|
||||
{ "to-stdout", no_argument, NULL, 'O' },
|
||||
{ "update", no_argument, NULL, 'u' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct bsdtar *bsdtar, bsdtar_storage;
|
||||
struct passwd *pwent;
|
||||
int opt, mode;
|
||||
|
||||
if (setlocale(LC_ALL, "") == NULL)
|
||||
bsdtar_warnc(0, "Failed to set default locale");
|
||||
|
||||
/*
|
||||
* Use a pointer for consistency, but stack-allocated storage
|
||||
* for ease of cleanup.
|
||||
*/
|
||||
bsdtar = &bsdtar_storage;
|
||||
memset(bsdtar, 0, sizeof(*bsdtar));
|
||||
bsdtar->fd = -1; /* Mark as "unused" */
|
||||
|
||||
/* Look up uid/uname of current user for future reference */
|
||||
bsdtar->user_uid = geteuid();
|
||||
bsdtar->user_uname = NULL;
|
||||
if ((pwent = getpwuid(bsdtar->user_uid))) {
|
||||
bsdtar->user_uname = (char *)malloc(strlen(pwent->pw_name)+1);
|
||||
if (bsdtar->user_uname)
|
||||
strcpy(bsdtar->user_uname, pwent->pw_name);
|
||||
}
|
||||
|
||||
/* Default: open tape drive. */
|
||||
bsdtar->filename = getenv("TAPE");
|
||||
if (bsdtar->filename == NULL)
|
||||
bsdtar->filename = _PATH_DEFTAPE;
|
||||
|
||||
bsdtar->bytes_per_block = 10240;
|
||||
|
||||
/* Default: preserve mod time on extract */
|
||||
bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;
|
||||
|
||||
if (bsdtar->user_uid == 0)
|
||||
bsdtar->extract_flags = ARCHIVE_EXTRACT_OWNER;
|
||||
|
||||
progname = *argv;
|
||||
|
||||
/* Rewrite traditional-style tar arguments, if used. */
|
||||
argv = rewrite_argv(&argc, argv, tar_opts);
|
||||
|
||||
bsdtar->argv = argv;
|
||||
bsdtar->argc = argc;
|
||||
|
||||
/* First option must be mode selector */
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
mode = getopt_long(bsdtar->argc, bsdtar->argv, tar_opts, tar_longopts,
|
||||
NULL);
|
||||
#else
|
||||
mode = getopt(bsdtar->argc, bsdtar->argv, tar_opts);
|
||||
#endif
|
||||
|
||||
switch (mode) {
|
||||
case -1:
|
||||
usage();
|
||||
break;
|
||||
case 'h':
|
||||
long_help();
|
||||
break;
|
||||
case 't':
|
||||
bsdtar->verbose = 1;
|
||||
break;
|
||||
case 'c': case 'r': case 'u': case 'x':
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"First option must be one of: -c, -r, -t, -u, -x\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Process all remaining arguments now. */
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
while ((opt = getopt_long(bsdtar->argc, bsdtar->argv,
|
||||
tar_opts, tar_longopts, NULL)) != -1) {
|
||||
#else
|
||||
while ((opt = getopt(bsdtar->argc, bsdtar->argv, tar_opts)) != -1) {
|
||||
#endif
|
||||
/* XXX TODO: Augment the compatibility notes below. */
|
||||
switch (opt) {
|
||||
case 'b': /* SUSv2 */
|
||||
bsdtar->bytes_per_block = 512 * atoi(optarg);
|
||||
break;
|
||||
case 'C': /* GNU tar */
|
||||
bsdtar->start_dir = optarg;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_EXCLUDE: /* GNU tar */
|
||||
only_mode(mode, opt, "xtcr");
|
||||
exclude(bsdtar, optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'F':
|
||||
only_mode(mode, opt, "c");
|
||||
bsdtar->create_format = optarg;
|
||||
break;
|
||||
case 'f': /* SUSv2 */
|
||||
bsdtar->filename = optarg;
|
||||
if (strcmp(bsdtar->filename, "-") == 0)
|
||||
bsdtar->filename = NULL;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_FAST_READ: /* GNU tar */
|
||||
only_mode(mode, opt, "tx");
|
||||
bsdtar->option_fast_read = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'H': /* BSD convention */
|
||||
only_mode(mode, opt, "cr");
|
||||
bsdtar->symlink_mode = 'H';
|
||||
break;
|
||||
case 'k': /* GNU tar */
|
||||
only_mode(mode, opt, "x");
|
||||
bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
|
||||
break;
|
||||
case 'L': /* BSD convention */
|
||||
only_mode(mode, opt, "cr");
|
||||
bsdtar->symlink_mode = 'L';
|
||||
break;
|
||||
case 'l': /* SUSv2 */
|
||||
only_mode(mode, opt, "cr");
|
||||
bsdtar->option_warn_links = 1;
|
||||
break;
|
||||
case 'm': /* SUSv2 */
|
||||
only_mode(mode, opt, "x");
|
||||
bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
|
||||
break;
|
||||
case 'n': /* GNU tar */
|
||||
only_mode(mode, opt, "cr");
|
||||
bsdtar->option_no_subdirs = 1;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_NODUMP: /* star */
|
||||
only_mode(mode, opt, "cr");
|
||||
bsdtar->option_honor_nodump = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'O': /* GNU tar */
|
||||
only_mode(mode, opt, "x");
|
||||
bsdtar->option_stdout = 1;
|
||||
break;
|
||||
case 'o': /* SUSv2 */
|
||||
only_mode(mode, opt, "x");
|
||||
bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
|
||||
break;
|
||||
#if 0
|
||||
/*
|
||||
* The common BSD -P option is not necessary, since
|
||||
* our default is to archive symlinks, not follow
|
||||
* them. This is convenient, as -P conflicts with GNU
|
||||
* tar anyway.
|
||||
*/
|
||||
case 'P': /* BSD convention */
|
||||
/* Default behavior, no option necessary. */
|
||||
break;
|
||||
#endif
|
||||
case 'P': /* GNU tar */
|
||||
only_mode(mode, opt, "xcru");
|
||||
bsdtar->option_absolute_paths = 1;
|
||||
break;
|
||||
case 'p': /* GNU tar, star */
|
||||
only_mode(mode, opt, "x");
|
||||
umask(0);
|
||||
bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
|
||||
break;
|
||||
case 'U': /* GNU tar */
|
||||
only_mode(mode, opt, "x");
|
||||
bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
|
||||
break;
|
||||
case 'v': /* SUSv2 */
|
||||
bsdtar->verbose++;
|
||||
break;
|
||||
case 'w': /* SUSv2 */
|
||||
bsdtar->option_interactive = 1;
|
||||
break;
|
||||
case 'X': /* -l in GNU tar */
|
||||
only_mode(mode, opt, "cr");
|
||||
bsdtar->option_dont_traverse_mounts = 1;
|
||||
break;
|
||||
case 'j': /* GNU tar */
|
||||
case 'y': /* FreeBSD version of GNU tar */
|
||||
case 'z': /* GNU tar, star */
|
||||
/*
|
||||
* Ignored in x/t modes, used in 'c' mode,
|
||||
* forbidden in r/u modes.
|
||||
*/
|
||||
only_mode(mode, opt, "cxt");
|
||||
bsdtar->create_compression = opt;
|
||||
break;
|
||||
case 'Z': /* GNU tar */
|
||||
bsdtar_warnc(0, ".Z compression not supported");
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
bsdtar->argc -= optind;
|
||||
bsdtar->argv += optind;
|
||||
|
||||
switch(mode) {
|
||||
case 'c':
|
||||
tar_mode_c(bsdtar);
|
||||
break;
|
||||
case 'r':
|
||||
tar_mode_r(bsdtar);
|
||||
break;
|
||||
case 't':
|
||||
tar_mode_t(bsdtar);
|
||||
break;
|
||||
case 'u':
|
||||
tar_mode_u(bsdtar);
|
||||
break;
|
||||
case 'x':
|
||||
tar_mode_x(bsdtar);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (bsdtar->user_uname != NULL)
|
||||
free(bsdtar->user_uname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the mode is correct.
|
||||
*/
|
||||
static void
|
||||
only_mode(char mode, char opt, const char *valid_modes)
|
||||
{
|
||||
if (strchr(valid_modes, mode) == NULL)
|
||||
bsdtar_errc(1, 0, "Option -%c is not permitted in mode -%c",
|
||||
opt, mode);
|
||||
}
|
||||
|
||||
|
||||
/*-
|
||||
* Convert traditional tar arguments into new-style.
|
||||
* For example,
|
||||
* tar tvfb file.tar 32 --exclude FOO
|
||||
* must be converted to
|
||||
* tar -t -v -f file.tar -b 32 --exclude FOO
|
||||
*
|
||||
* This requires building a new argv array. The initial bundled word
|
||||
* gets expanded into a new string that looks like "-t\0-v\0-f\0-b\0".
|
||||
* The new argv array has pointers into this string intermingled with
|
||||
* pointers to the existing arguments. Arguments are moved to
|
||||
* immediately follow their options.
|
||||
*
|
||||
* The optstring argument here is the same one passed to getopt(3).
|
||||
* It is used to determine which option letters have trailing arguments.
|
||||
*/
|
||||
char **
|
||||
rewrite_argv(int *argc, char ** src_argv, const char *optstring)
|
||||
{
|
||||
char **new_argv, **dest_argv;
|
||||
const char *p;
|
||||
char *src, *dest;
|
||||
|
||||
if (src_argv[1] == NULL || src_argv[1][0] == '-')
|
||||
return (src_argv);
|
||||
|
||||
*argc += strlen(src_argv[1]) - 1;
|
||||
new_argv = malloc((*argc + 1) * sizeof(new_argv[0]));
|
||||
if (new_argv == NULL)
|
||||
bsdtar_errc(1, errno, "No Memory");
|
||||
|
||||
dest_argv = new_argv;
|
||||
*dest_argv++ = *src_argv++;
|
||||
|
||||
dest = malloc(strlen(*src_argv) * 3);
|
||||
if (dest == NULL)
|
||||
bsdtar_errc(1, errno, "No memory");
|
||||
for (src = *src_argv++; *src != '\0'; src++) {
|
||||
*dest_argv++ = dest;
|
||||
*dest++ = '-';
|
||||
*dest++ = *src;
|
||||
*dest++ = '\0';
|
||||
/* If option takes an argument, insert that into the list. */
|
||||
for (p = optstring; p != NULL && *p != '\0'; p++) {
|
||||
if (*p != *src)
|
||||
continue;
|
||||
if (p[1] != ':') /* No arg required, done. */
|
||||
break;
|
||||
if (*src_argv == NULL) /* No arg available? Error. */
|
||||
bsdtar_errc(1, 0,
|
||||
"Option %c requires an argument",
|
||||
*src);
|
||||
*dest_argv++ = *src_argv++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy remaining arguments, including trailing NULL. */
|
||||
while ((*dest_argv++ = *src_argv++) != NULL)
|
||||
;
|
||||
|
||||
return (new_argv);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
p = strrchr(progname, '/');
|
||||
if (p != NULL)
|
||||
p++;
|
||||
else
|
||||
p = progname;
|
||||
|
||||
printf("Basic Usage:\n");
|
||||
printf(" List: %s -tf [archive-filename]\n", p);
|
||||
printf(" Extract: %s -xf [archive-filename]\n", p);
|
||||
printf(" Create: %s -cf [archive-filename] [filenames...]\n", p);
|
||||
printf(" Help: %s -h\n", p);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static const char *long_help_msg[] = {
|
||||
"First option must be a mode specifier:\n",
|
||||
" -c Create -r Add/Replace -t List -u Update -x Extract\n",
|
||||
"Common Options:\n",
|
||||
" -b # Use # 512-byte records per I/O block\n",
|
||||
" -f <filename> Location of archive (default " _PATH_DEFTAPE ")\n",
|
||||
" -v Verbose\n",
|
||||
" -w Interactive\n",
|
||||
"Create: %p -c [options] [<file> | <dir> | @<archive> | C=<dir> ]\n",
|
||||
" <file>, <dir> add these items to archive\n",
|
||||
" -z, -j Compress archive with gzip/bzip2\n",
|
||||
" -F {ustar|pax|cpio|shar} Select archive format\n",
|
||||
" --exclude <pattern> Skip files that match pattern\n",
|
||||
" C=<dir> Change to <dir> before processing remaining files\n",
|
||||
" @<archive> Add entries from <archive> to output\n",
|
||||
"List: %p -t [options] [<patterns>]\n",
|
||||
" <patterns> If specified, list only entries that match\n",
|
||||
"Extract: %p -x [options] [<patterns>]\n",
|
||||
" <patterns> If specified, extract only entries that match\n",
|
||||
" -k Keep (don't overwrite) existing files\n",
|
||||
" -m Don't restore modification times\n",
|
||||
" -O Write entries to stdout, don't restore to disk\n",
|
||||
" -p Restore permissions (including ACLs, owner, file flags)\n",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
long_help(void)
|
||||
{
|
||||
const char *prog;
|
||||
const char *p;
|
||||
const char **msg;
|
||||
|
||||
prog = strrchr(progname, '/');
|
||||
if (prog != NULL)
|
||||
prog++;
|
||||
else
|
||||
prog = progname;
|
||||
|
||||
printf("%s: manipulate archive files\n", prog);
|
||||
|
||||
for (msg = long_help_msg; *msg != NULL; msg++) {
|
||||
for (p = *msg; p != NULL; p++) {
|
||||
if (*p == '\0')
|
||||
break;
|
||||
else if (*p == '%') {
|
||||
if (p[1] == 'p') {
|
||||
fputs(prog, stdout);
|
||||
p++;
|
||||
} else
|
||||
putchar('%');
|
||||
} else
|
||||
putchar(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
bsdtar_progname(void)
|
||||
{
|
||||
return (progname);
|
||||
}
|
101
usr.bin/tar/bsdtar.h
Normal file
101
usr.bin/tar/bsdtar.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name(s) of the author(s) may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <archive.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Data for exclusion/inclusion handling: defined in matching.c */
|
||||
struct matching;
|
||||
struct links_entry;
|
||||
struct archive_dir_entry;
|
||||
|
||||
/*
|
||||
* The internal state for the "bsdtar" program. This is registered
|
||||
* with the 'archive' structure so that this information will be
|
||||
* available to the read/write callbacks.
|
||||
*/
|
||||
struct bsdtar {
|
||||
/* Options */
|
||||
const char *filename; /* -f filename */
|
||||
const char *create_format; /* -F format */
|
||||
const char *start_dir; /* -C dir */
|
||||
int bytes_per_block; /* -b block_size */
|
||||
int records_per_block;
|
||||
int verbose; /* -v */
|
||||
int extract_flags; /* Flags for extract operation */
|
||||
char symlink_mode; /* H or L, per BSD conventions */
|
||||
char create_compression; /* j, y, or z */
|
||||
char option_absolute_paths; /* -P */
|
||||
char option_dont_traverse_mounts; /* -X */
|
||||
char option_fast_read; /* --fast-read */
|
||||
char option_honor_nodump; /* --nodump */
|
||||
char option_interactive; /* -w */
|
||||
char option_no_subdirs; /* -d */
|
||||
char option_stdout; /* -p */
|
||||
char option_warn_links; /* -l */
|
||||
|
||||
/* If >= 0, then close this when done. */
|
||||
int fd;
|
||||
|
||||
/* Miscellaneous state information */
|
||||
size_t u_width; /* for 'list_item' */
|
||||
size_t gs_width; /* For 'list_item' */
|
||||
char *user_uname; /* User running this program */
|
||||
uid_t user_uid; /* UID running this program */
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
struct matching *matching;
|
||||
|
||||
struct links_entry *links_head;
|
||||
struct archive_dir_entry *archive_dir_head, *archive_dir_tail;
|
||||
};
|
||||
|
||||
const char *bsdtar_progname(void);
|
||||
void bsdtar_errc(int _eval, int _code, const char *fmt, ...);
|
||||
void bsdtar_warnc(int _code, const char *fmt, ...);
|
||||
void cleanup_exclusions(struct bsdtar *);
|
||||
void exclude(struct bsdtar *, const char *pattern);
|
||||
int excluded(struct bsdtar *, const char *pathname);
|
||||
void include(struct bsdtar *, const char *pattern);
|
||||
|
||||
void safe_fprintf(FILE *, const char *fmt, ...);
|
||||
|
||||
void tar_mode_c(struct bsdtar *bsdtar);
|
||||
void tar_mode_r(struct bsdtar *bsdtar);
|
||||
void tar_mode_t(struct bsdtar *bsdtar);
|
||||
void tar_mode_u(struct bsdtar *bsdtar);
|
||||
void tar_mode_x(struct bsdtar *bsdtar);
|
||||
|
||||
int unmatched_inclusions(struct bsdtar *bsdtar);
|
||||
void usage(void);
|
||||
int yes(const char *fmt, ...);
|
||||
|
94
usr.bin/tar/bsdtar_platform.h
Normal file
94
usr.bin/tar/bsdtar_platform.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header is the first thing included in any of the bsdtar
|
||||
* source files. As far as possible, platform-specific issues should
|
||||
* be dealt with here and not within individual source files.
|
||||
*/
|
||||
|
||||
#ifndef BSDTAR_PLATFORM_H_INCLUDED
|
||||
#define BSDTAR_PLATFORM_H_INCLUDED
|
||||
|
||||
/* FreeBSD-specific definitions. */
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/cdefs.h> /* For __FBSDID */
|
||||
#include <paths.h> /* For _PATH_DEFTAPE */
|
||||
|
||||
#define HAVE_CHFLAGS 1
|
||||
|
||||
#if __FreeBSD__ > 4
|
||||
#define HAVE_GETOPT_LONG 1
|
||||
#define HAVE_POSIX_ACL 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We need to be able to display a filesize using printf(). The type
|
||||
* and format string here must be compatible with one another and
|
||||
* large enough for any file.
|
||||
*/
|
||||
#include <inttypes.h> /* for uintmax_t, if it exists */
|
||||
#ifdef UINTMAX_MAX
|
||||
#define BSDTAR_FILESIZE_TYPE uintmax_t
|
||||
#define BSDTAR_FILESIZE_PRINTF "%ju"
|
||||
#else
|
||||
#define BSDTAR_FILESIZE_TYPE unsigned long long
|
||||
#define BSDTAR_FILESIZE_PRINTF "%llu"
|
||||
#endif
|
||||
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
|
||||
#ifndef __FreeBSD__
|
||||
#define __FBSDID(a) /* null */
|
||||
#endif
|
||||
|
||||
/* Linux */
|
||||
#ifdef LINUX
|
||||
#include <stdint.h> /* for uintmax_t */
|
||||
#define BSDTAR_FILESIZE_TYPE uintmax_t
|
||||
#define BSDTAR_FILESIZE_PRINTF "%ju"
|
||||
/* XXX get fnmatch GNU extensions (FNM_LEADING_DIR)
|
||||
* (should probably use AC_FUNC_FNMATCH_GNU once using autoconf...) */
|
||||
#define _GNU_SOURCE
|
||||
#define _PATH_DEFTAPE "/dev/st0"
|
||||
#define HAVE_GETOPT_LONG 1
|
||||
#define st_atimespec st_atim
|
||||
#define st_mtimespec st_mtim
|
||||
#define st_ctimespec st_ctim
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX TODO: Use autoconf to handle non-FreeBSD platforms.
|
||||
*
|
||||
* #if !defined(__FreeBSD__)
|
||||
* #include "config.h"
|
||||
* #endif
|
||||
*/
|
||||
|
||||
#endif /* !ARCHIVE_H_INCLUDED */
|
243
usr.bin/tar/matching.c
Normal file
243
usr.bin/tar/matching.c
Normal file
@ -0,0 +1,243 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "bsdtar_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
struct match {
|
||||
struct match *next;
|
||||
int matches;
|
||||
char pattern[1];
|
||||
};
|
||||
|
||||
struct matching {
|
||||
struct match *exclusions;
|
||||
int exclusions_count;
|
||||
struct match *inclusions;
|
||||
int inclusions_count;
|
||||
int inclusions_unmatched_count;
|
||||
};
|
||||
|
||||
|
||||
static void add_pattern(struct match **list, const char *pattern);
|
||||
static void initialize_matching(struct bsdtar *);
|
||||
static int match_exclusion(struct match *, const char *pathname);
|
||||
static int match_inclusion(struct match *, const char *pathname);
|
||||
|
||||
/*
|
||||
* The matching logic here needs to be re-thought. I started
|
||||
* out to try to mimic gtar's matching logic, but found it wasn't
|
||||
* really consistent. In particular 'tar -t' and 'tar -x' interpret
|
||||
* patterns on the command line as anchored, but --exclude doesn't.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Utility functions to manage exclusion/inclusion patterns
|
||||
*/
|
||||
|
||||
void
|
||||
exclude(struct bsdtar *bsdtar, const char *pattern)
|
||||
{
|
||||
struct matching *matching;
|
||||
|
||||
if (bsdtar->matching == NULL)
|
||||
initialize_matching(bsdtar);
|
||||
matching = bsdtar->matching;
|
||||
add_pattern(&(matching->exclusions), pattern);
|
||||
matching->exclusions_count++;
|
||||
}
|
||||
|
||||
void
|
||||
include(struct bsdtar *bsdtar, const char *pattern)
|
||||
{
|
||||
struct matching *matching;
|
||||
|
||||
if (bsdtar->matching == NULL)
|
||||
initialize_matching(bsdtar);
|
||||
matching = bsdtar->matching;
|
||||
add_pattern(&(matching->inclusions), pattern);
|
||||
matching->inclusions_count++;
|
||||
matching->inclusions_unmatched_count++;
|
||||
}
|
||||
|
||||
static void
|
||||
add_pattern(struct match **list, const char *pattern)
|
||||
{
|
||||
struct match *match;
|
||||
|
||||
match = malloc(sizeof(*match) + strlen(pattern) + 1);
|
||||
if (match == NULL)
|
||||
bsdtar_errc(1, errno, "Out of memory");
|
||||
if (pattern[0] == '/')
|
||||
pattern++;
|
||||
strcpy(match->pattern, pattern);
|
||||
match->next = *list;
|
||||
*list = match;
|
||||
match->matches = 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
excluded(struct bsdtar *bsdtar, const char *pathname)
|
||||
{
|
||||
struct matching *matching;
|
||||
struct match *match;
|
||||
struct match *matched;
|
||||
|
||||
matching = bsdtar->matching;
|
||||
if (matching == NULL)
|
||||
return (0);
|
||||
|
||||
/* Exclusions take priority */
|
||||
for (match = matching->exclusions; match != NULL; match = match->next){
|
||||
if (match_exclusion(match, pathname))
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Then check for inclusions */
|
||||
matched = NULL;
|
||||
for (match = matching->inclusions; match != NULL; match = match->next){
|
||||
if (match_inclusion(match, pathname)) {
|
||||
/*
|
||||
* If this pattern has never been matched,
|
||||
* then we're done.
|
||||
*/
|
||||
if (match->matches == 0) {
|
||||
match->matches++;
|
||||
matching->inclusions_unmatched_count++;
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* Otherwise, remember the match but keep checking
|
||||
* in case we can tick off an unmatched pattern.
|
||||
*/
|
||||
matched = match;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We didn't find a pattern that had never been matched, but
|
||||
* we did find a match, so count it and exit.
|
||||
*/
|
||||
if (matched != NULL) {
|
||||
matched->matches++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* If there were inclusions, default is to exclude. */
|
||||
if (matching->inclusions != NULL)
|
||||
return (1);
|
||||
|
||||
/* No explicit inclusions, default is to match. */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a little odd, but it matches the default behavior of
|
||||
* gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'
|
||||
*
|
||||
* XXX TODO: fnmatch isn't the most portable thing around, and even
|
||||
* worse, FNM_LEADING_DIR is a non-POSIX extension. <sigh> Thus, the
|
||||
* following two functions need to eventually be replaced with code
|
||||
* that does not rely on fnmatch().
|
||||
*/
|
||||
int
|
||||
match_exclusion(struct match *match, const char *pathname)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
if (*match->pattern == '*' || *match->pattern == '/')
|
||||
return (fnmatch(match->pattern, pathname, FNM_LEADING_DIR) == 0);
|
||||
|
||||
for (p = pathname; p != NULL; p = strchr(p, '/')) {
|
||||
if (*p == '/')
|
||||
p++;
|
||||
if (fnmatch(match->pattern, p, FNM_LEADING_DIR) == 0)
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Again, mimic gtar: inclusions are always anchored (have to match
|
||||
* the beginning of the path) even though exclusions are not anchored.
|
||||
*/
|
||||
int
|
||||
match_inclusion(struct match *match, const char *pathname)
|
||||
{
|
||||
return (fnmatch(match->pattern, pathname, FNM_LEADING_DIR) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
cleanup_exclusions(struct bsdtar *bsdtar)
|
||||
{
|
||||
struct match *p, *q;
|
||||
|
||||
if (bsdtar->matching) {
|
||||
p = bsdtar->matching->inclusions;
|
||||
while (p != NULL) {
|
||||
q = p;
|
||||
p = p->next;
|
||||
free(q);
|
||||
}
|
||||
p = bsdtar->matching->exclusions;
|
||||
while (p != NULL) {
|
||||
q = p;
|
||||
p = p->next;
|
||||
free(q);
|
||||
}
|
||||
free(bsdtar->matching);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
initialize_matching(struct bsdtar *bsdtar)
|
||||
{
|
||||
bsdtar->matching = malloc(sizeof(*bsdtar->matching));
|
||||
if (bsdtar->matching == NULL)
|
||||
bsdtar_errc(1, errno, "No memory");
|
||||
memset(bsdtar->matching, 0, sizeof(*bsdtar->matching));
|
||||
}
|
||||
|
||||
int
|
||||
unmatched_inclusions(struct bsdtar *bsdtar)
|
||||
{
|
||||
struct matching *matching;
|
||||
|
||||
matching = bsdtar->matching;
|
||||
if (matching == NULL)
|
||||
return (0);
|
||||
return (matching->inclusions_unmatched_count);
|
||||
}
|
292
usr.bin/tar/read.c
Normal file
292
usr.bin/tar/read.c
Normal file
@ -0,0 +1,292 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "bsdtar_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#ifdef DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
static void list_item_verbose(struct bsdtar *, struct archive_entry *);
|
||||
static void read_archive(struct bsdtar *bsdtar, char mode);
|
||||
|
||||
void
|
||||
tar_mode_t(struct bsdtar *bsdtar)
|
||||
{
|
||||
read_archive(bsdtar, 't');
|
||||
}
|
||||
|
||||
void
|
||||
tar_mode_x(struct bsdtar *bsdtar)
|
||||
{
|
||||
read_archive(bsdtar, 'x');
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle 'x' and 't' modes.
|
||||
*/
|
||||
void
|
||||
read_archive(struct bsdtar *bsdtar, char mode)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
int format;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
while (*bsdtar->argv) {
|
||||
include(bsdtar, *bsdtar->argv);
|
||||
bsdtar->argv++;
|
||||
}
|
||||
|
||||
format = -1;
|
||||
|
||||
a = archive_read_new();
|
||||
archive_read_support_compression_all(a);
|
||||
archive_read_support_format_all(a);
|
||||
if (archive_read_open_file(a, bsdtar->filename, bsdtar->bytes_per_block))
|
||||
bsdtar_errc(1, 0, "Error opening archive: %s",
|
||||
archive_error_string(a));
|
||||
|
||||
if (bsdtar->verbose > 2)
|
||||
fprintf(stdout, "Compression: %s\n",
|
||||
archive_compression_name(a));
|
||||
|
||||
if (bsdtar->start_dir != NULL && chdir(bsdtar->start_dir))
|
||||
bsdtar_errc(1, errno, "chdir(%s) failed", bsdtar->start_dir);
|
||||
|
||||
for (;;) {
|
||||
/* Support --fast-read option */
|
||||
if (bsdtar->option_fast_read &&
|
||||
unmatched_inclusions(bsdtar) == 0)
|
||||
break;
|
||||
|
||||
r = archive_read_next_header(a, &entry);
|
||||
if (r == ARCHIVE_EOF)
|
||||
break;
|
||||
if (r == ARCHIVE_WARN)
|
||||
bsdtar_warnc(0, "%s", archive_error_string(a));
|
||||
if (r == ARCHIVE_FATAL) {
|
||||
bsdtar_warnc(0, "%s", archive_error_string(a));
|
||||
break;
|
||||
}
|
||||
if (r == ARCHIVE_RETRY) {
|
||||
/* Retryable error: try again */
|
||||
bsdtar_warnc(0, "%s", archive_error_string(a));
|
||||
bsdtar_warnc(0, "Retrying...");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bsdtar->verbose > 2 && format != archive_format(a)) {
|
||||
format = archive_format(a);
|
||||
fprintf(stdout, "Archive Format: %s\n",
|
||||
archive_format_name(a));
|
||||
}
|
||||
|
||||
if (excluded(bsdtar, archive_entry_pathname(entry)))
|
||||
continue;
|
||||
|
||||
name = archive_entry_pathname(entry);
|
||||
if (name[0] == '/' && !bsdtar->option_absolute_paths) {
|
||||
name++;
|
||||
archive_entry_set_pathname(entry, name);
|
||||
}
|
||||
|
||||
if (mode == 't') {
|
||||
if (bsdtar->verbose < 2)
|
||||
safe_fprintf(stdout, "%s",
|
||||
archive_entry_pathname(entry));
|
||||
else
|
||||
list_item_verbose(bsdtar, entry);
|
||||
fflush(stdout);
|
||||
switch (archive_read_data_skip(a)) {
|
||||
case ARCHIVE_OK:
|
||||
break;
|
||||
case ARCHIVE_WARN:
|
||||
case ARCHIVE_RETRY:
|
||||
fprintf(stdout, "\n");
|
||||
bsdtar_warnc(0, "%s", archive_error_string(a));
|
||||
break;
|
||||
case ARCHIVE_FATAL:
|
||||
fprintf(stdout, "\n");
|
||||
bsdtar_errc(1, 0, "%s",
|
||||
archive_error_string(a));
|
||||
break;
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
} else {
|
||||
if (bsdtar->option_interactive &&
|
||||
!yes("extract '%s'", archive_entry_pathname(entry)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Format here is from SUSv2, including the
|
||||
* deferred '\n'.
|
||||
*/
|
||||
if (bsdtar->verbose) {
|
||||
safe_fprintf(stderr, "x %s",
|
||||
archive_entry_pathname(entry));
|
||||
fflush(stderr);
|
||||
}
|
||||
if (bsdtar->option_stdout) {
|
||||
/* TODO: Catch/recover any errors here. */
|
||||
archive_read_data_into_fd(a, 1);
|
||||
} else if (archive_read_extract(a, entry,
|
||||
bsdtar->extract_flags)) {
|
||||
if (!bsdtar->verbose)
|
||||
safe_fprintf(stderr, "%s",
|
||||
archive_entry_pathname(entry));
|
||||
safe_fprintf(stderr, ": %s",
|
||||
archive_error_string(a));
|
||||
if (!bsdtar->verbose)
|
||||
fprintf(stderr, "\n");
|
||||
/*
|
||||
* TODO: Decide how to handle
|
||||
* extraction error... <sigh>
|
||||
*/
|
||||
}
|
||||
if (bsdtar->verbose)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
archive_read_finish(a);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display information about the current file.
|
||||
*
|
||||
* The format here roughly duplicates the output of 'ls -l'.
|
||||
* This is based on SUSv2, where 'tar tv' is documented as
|
||||
* listing additional information in an "unspecified format,"
|
||||
* and 'pax -l' is documented as using the same format as 'ls -l'.
|
||||
*/
|
||||
static void
|
||||
list_item_verbose(struct bsdtar *bsdtar, struct archive_entry *entry)
|
||||
{
|
||||
FILE *out = stdout;
|
||||
const struct stat *st;
|
||||
char tmp[100];
|
||||
size_t w;
|
||||
const char *p;
|
||||
time_t tim;
|
||||
static time_t now;
|
||||
|
||||
st = archive_entry_stat(entry);
|
||||
|
||||
/*
|
||||
* We avoid collecting the entire list in memory at once by
|
||||
* listing things as we see them. However, that also means we can't
|
||||
* just pre-compute the field widths. Instead, we start with guesses
|
||||
* and just widen them as necessary. These numbers are completely
|
||||
* arbitrary.
|
||||
*/
|
||||
if (!bsdtar->u_width) {
|
||||
bsdtar->u_width = 6;
|
||||
bsdtar->gs_width = 13;
|
||||
}
|
||||
if (!now)
|
||||
time(&now);
|
||||
strmode(st->st_mode, tmp);
|
||||
fprintf(out, "%s %d ", tmp, st->st_nlink);
|
||||
|
||||
/* Use uname if it's present, else uid. */
|
||||
w = 0;
|
||||
p = archive_entry_uname(entry);
|
||||
if (p && *p) {
|
||||
sprintf(tmp, "%s ", p);
|
||||
} else {
|
||||
sprintf(tmp, "%d ", st->st_uid);
|
||||
}
|
||||
w = strlen(tmp);
|
||||
if (w > bsdtar->u_width)
|
||||
bsdtar->u_width = w;
|
||||
fprintf(out, "%-*s", (int)bsdtar->u_width, tmp);
|
||||
|
||||
/* Use gname if it's present, else gid. */
|
||||
w = 0;
|
||||
p = archive_entry_gname(entry);
|
||||
if (p && *p) {
|
||||
fprintf(out, "%s", p);
|
||||
w += strlen(p);
|
||||
} else {
|
||||
sprintf(tmp, "%d", st->st_gid);
|
||||
w += strlen(tmp);
|
||||
fprintf(out, "%s", tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print device number or file size, right-aligned so as to make
|
||||
* total width of group and devnum/filesize fields be gs_width.
|
||||
* If gs_width is too small, grow it.
|
||||
*/
|
||||
if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
|
||||
sprintf(tmp, "%u,%u", major(st->st_rdev), minor(st->st_rdev));
|
||||
} else {
|
||||
/*
|
||||
* Note the use of platform-dependent macros to format
|
||||
* the filesize here. We need the format string and the
|
||||
* corresponding type for the cast.
|
||||
*/
|
||||
sprintf(tmp, BSDTAR_FILESIZE_PRINTF,
|
||||
(BSDTAR_FILESIZE_TYPE)st->st_size);
|
||||
}
|
||||
if (w + strlen(tmp) >= bsdtar->gs_width)
|
||||
bsdtar->gs_width = w+strlen(tmp)+1;
|
||||
fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);
|
||||
|
||||
/* Format the time using 'ls -l' conventions. */
|
||||
tim = (time_t)st->st_mtime;
|
||||
if (tim < now - 6*30*24*60*60 || tim > now + 6*30*24*60*60)
|
||||
strftime(tmp, sizeof(tmp), "%b %e %Y", localtime(&tim));
|
||||
else
|
||||
strftime(tmp, sizeof(tmp), "%b %e %R", localtime(&tim));
|
||||
safe_fprintf(out, " %s %s", tmp, archive_entry_pathname(entry));
|
||||
|
||||
/* Extra information for links. */
|
||||
if (archive_entry_hardlink(entry)) /* Hard link */
|
||||
safe_fprintf(out, " link to %s",
|
||||
archive_entry_hardlink(entry));
|
||||
else if (S_ISLNK(st->st_mode)) /* Symbolic link */
|
||||
safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
|
||||
}
|
160
usr.bin/tar/util.c
Normal file
160
usr.bin/tar/util.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "bsdtar_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
static void bsdtar_vwarnc(int code, const char *fmt, va_list ap);
|
||||
|
||||
/*
|
||||
* Print a string, taking care with any non-printable characters.
|
||||
*/
|
||||
|
||||
void
|
||||
safe_fprintf(FILE *f, const char *fmt, ...)
|
||||
{
|
||||
char *buff;
|
||||
int bufflength;
|
||||
int length;
|
||||
va_list ap;
|
||||
char *p;
|
||||
|
||||
bufflength = 512;
|
||||
buff = malloc(bufflength);
|
||||
|
||||
va_start(ap, fmt);
|
||||
length = vsnprintf(buff, bufflength, fmt, ap);
|
||||
if (length >= bufflength) {
|
||||
bufflength = length+1;
|
||||
buff = realloc(buff, bufflength);
|
||||
length = vsnprintf(buff, bufflength, fmt, ap);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
for (p=buff; *p != '\0'; p++) {
|
||||
unsigned char c = *p;
|
||||
if (isprint(c) && c != '\\')
|
||||
putc(c, f);
|
||||
else
|
||||
switch (c) {
|
||||
case '\a': putc('\\', f); putc('a', f); break;
|
||||
case '\b': putc('\\', f); putc('b', f); break;
|
||||
case '\f': putc('\\', f); putc('f', f); break;
|
||||
case '\n': putc('\\', f); putc('n', f); break;
|
||||
#if '\r' != '\n'
|
||||
/* On some platforms, \n and \r are the same. */
|
||||
case '\r': putc('\\', f); putc('r', f); break;
|
||||
#endif
|
||||
case '\t': putc('\\', f); putc('t', f); break;
|
||||
case '\v': putc('\\', f); putc('v', f); break;
|
||||
case '\\': putc('\\', f); putc('\\', f); break;
|
||||
default:
|
||||
fprintf(f, "\\%03o", c);
|
||||
}
|
||||
}
|
||||
free(buff);
|
||||
}
|
||||
|
||||
static void
|
||||
bsdtar_vwarnc(int code, const char *fmt, va_list ap)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
p = strrchr(bsdtar_progname(), '/');
|
||||
if (p != NULL)
|
||||
p++;
|
||||
else
|
||||
p = bsdtar_progname();
|
||||
fprintf(stderr, "%s: ", p);
|
||||
|
||||
vfprintf(stderr, fmt, ap);
|
||||
if (code != 0)
|
||||
fprintf(stderr, ": %s", strerror(code));
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
bsdtar_warnc(int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
bsdtar_vwarnc(code, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
bsdtar_errc(int eval, int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
bsdtar_vwarnc(code, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(eval);
|
||||
}
|
||||
|
||||
int
|
||||
yes(const char *fmt, ...)
|
||||
{
|
||||
char buff[32];
|
||||
char *p;
|
||||
ssize_t l;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, " (y/N)? ");
|
||||
fflush(stderr);
|
||||
|
||||
l = read(2, buff, sizeof(buff));
|
||||
buff[l] = 0;
|
||||
|
||||
for (p = buff; *p != '\0'; p++) {
|
||||
if (isspace(*p))
|
||||
continue;
|
||||
switch(*p) {
|
||||
case 'y': case 'Y':
|
||||
return (1);
|
||||
case 'n': case 'N':
|
||||
return (0);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
988
usr.bin/tar/write.c
Normal file
988
usr.bin/tar/write.c
Normal file
@ -0,0 +1,988 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "bsdtar_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_POSIX_ACL
|
||||
#include <sys/acl.h>
|
||||
#endif
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#ifdef DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <fts.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
struct links_entry {
|
||||
struct links_entry *next;
|
||||
struct links_entry *previous;
|
||||
int links;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct archive_dir_entry {
|
||||
struct archive_dir_entry *next;
|
||||
time_t mtime_sec;
|
||||
int mtime_nsec;
|
||||
char *name;
|
||||
};
|
||||
|
||||
|
||||
static void add_dir_list(struct bsdtar *bsdtar, const char *path,
|
||||
time_t mtime_sec, int mtime_nsec);
|
||||
static void create_cleanup(struct bsdtar *);
|
||||
static int append_archive(struct bsdtar *, struct archive *,
|
||||
const char *fname);
|
||||
static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid);
|
||||
static const char * lookup_uname(struct bsdtar *bsdtar, uid_t uid);
|
||||
static int new_enough(struct bsdtar *, const char *path,
|
||||
time_t mtime_sec, int mtime_nsec);
|
||||
static void record_hardlink(struct bsdtar *,
|
||||
struct archive_entry *entry, const struct stat *);
|
||||
void setup_acls(struct bsdtar *, struct archive_entry *,
|
||||
const char *path);
|
||||
void test_for_append(struct bsdtar *);
|
||||
static void write_archive(struct archive *, struct bsdtar *);
|
||||
static void write_entry(struct bsdtar *, struct archive *,
|
||||
struct stat *, const char *pathname,
|
||||
unsigned pathlen, const char *accpath);
|
||||
static int write_file_data(struct archive *, int fd);
|
||||
static void write_heirarchy(struct bsdtar *, struct archive *,
|
||||
const char *);
|
||||
|
||||
void
|
||||
tar_mode_c(struct bsdtar *bsdtar)
|
||||
{
|
||||
struct archive *a;
|
||||
int r;
|
||||
|
||||
if (*bsdtar->argv == NULL)
|
||||
bsdtar_errc(1, 0, "no files or directories specified");
|
||||
|
||||
a = archive_write_new();
|
||||
|
||||
/* Support any format that the library supports. */
|
||||
if (bsdtar->create_format == NULL)
|
||||
archive_write_set_format_pax_restricted(a);
|
||||
else {
|
||||
r = archive_write_set_format_by_name(a, bsdtar->create_format);
|
||||
if (r != ARCHIVE_OK) {
|
||||
fprintf(stderr, "Can't use format %s: %s\n",
|
||||
bsdtar->create_format,
|
||||
archive_error_string(a));
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block);
|
||||
|
||||
switch (bsdtar->create_compression) {
|
||||
case 'j': case 'y':
|
||||
archive_write_set_compression_bzip2(a);
|
||||
break;
|
||||
case 'z':
|
||||
archive_write_set_compression_gzip(a);
|
||||
break;
|
||||
}
|
||||
|
||||
r = archive_write_open_file(a, bsdtar->filename);
|
||||
if (r != ARCHIVE_OK)
|
||||
bsdtar_errc(1, archive_errno(a),
|
||||
archive_error_string(a));
|
||||
|
||||
write_archive(a, bsdtar);
|
||||
|
||||
archive_write_finish(a);
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as 'c', except we only support tar formats in uncompressed
|
||||
* files on disk.
|
||||
*/
|
||||
void
|
||||
tar_mode_r(struct bsdtar *bsdtar)
|
||||
{
|
||||
off_t end_offset;
|
||||
int format;
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
|
||||
/* Sanity-test some arguments and the file. */
|
||||
test_for_append(bsdtar);
|
||||
|
||||
format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
|
||||
|
||||
bsdtar->fd = open(bsdtar->filename, O_RDWR);
|
||||
if (bsdtar->fd < 0)
|
||||
bsdtar_errc(1, errno, "Cannot open %s", bsdtar->filename);
|
||||
|
||||
a = archive_read_new();
|
||||
archive_read_support_compression_all(a);
|
||||
archive_read_support_format_tar(a);
|
||||
archive_read_support_format_gnutar(a);
|
||||
archive_read_open_fd(a, bsdtar->fd, 10240);
|
||||
while (0 == archive_read_next_header(a, &entry)) {
|
||||
if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) {
|
||||
archive_read_finish(a);
|
||||
close(bsdtar->fd);
|
||||
bsdtar_errc(1, 0,
|
||||
"Cannot append to compressed archive.");
|
||||
}
|
||||
/* Keep going until we hit end-of-archive */
|
||||
format = archive_format(a);
|
||||
}
|
||||
|
||||
end_offset = archive_read_header_position(a);
|
||||
archive_read_finish(a);
|
||||
|
||||
/* Re-open archive for writing */
|
||||
a = archive_write_new();
|
||||
archive_write_set_compression_none(a);
|
||||
/*
|
||||
* Set format to same one auto-detected above, except use
|
||||
* ustar for appending to GNU tar, since the library doesn't
|
||||
* write GNU tar format.
|
||||
*/
|
||||
if (format == ARCHIVE_FORMAT_TAR_GNUTAR)
|
||||
format = ARCHIVE_FORMAT_TAR_USTAR;
|
||||
archive_write_set_format(a, format);
|
||||
lseek(bsdtar->fd, end_offset, SEEK_SET);
|
||||
archive_write_open_fd(a, bsdtar->fd);
|
||||
|
||||
write_archive(a, bsdtar);
|
||||
|
||||
archive_write_finish(a);
|
||||
close(bsdtar->fd);
|
||||
bsdtar->fd = -1;
|
||||
}
|
||||
|
||||
void
|
||||
tar_mode_u(struct bsdtar *bsdtar)
|
||||
{
|
||||
off_t end_offset;
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
const char *filename;
|
||||
int format;
|
||||
struct archive_dir_entry *p;
|
||||
|
||||
filename = NULL;
|
||||
format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
|
||||
|
||||
/* Sanity-test some arguments and the file. */
|
||||
test_for_append(bsdtar);
|
||||
|
||||
bsdtar->fd = open(bsdtar->filename, O_RDWR);
|
||||
if (bsdtar->fd < 0)
|
||||
bsdtar_errc(1, errno, "Cannot open %s", bsdtar->filename);
|
||||
|
||||
a = archive_read_new();
|
||||
archive_read_support_compression_all(a);
|
||||
archive_read_support_format_tar(a);
|
||||
archive_read_support_format_gnutar(a);
|
||||
archive_read_open_fd(a, bsdtar->fd, bsdtar->bytes_per_block);
|
||||
|
||||
/* Build a list of all entries and their recorded mod times. */
|
||||
while (0 == archive_read_next_header(a, &entry)) {
|
||||
if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) {
|
||||
archive_read_finish(a);
|
||||
close(bsdtar->fd);
|
||||
bsdtar_errc(1, 0,
|
||||
"Cannot append to compressed archive.");
|
||||
}
|
||||
add_dir_list(bsdtar, archive_entry_pathname(entry),
|
||||
archive_entry_mtime(entry),
|
||||
archive_entry_mtime_nsec(entry));
|
||||
/* Record the last format determination we see */
|
||||
format = archive_format(a);
|
||||
/* Keep going until we hit end-of-archive */
|
||||
}
|
||||
|
||||
end_offset = archive_read_header_position(a);
|
||||
archive_read_finish(a);
|
||||
|
||||
/* Re-open archive for writing. */
|
||||
a = archive_write_new();
|
||||
archive_write_set_compression_none(a);
|
||||
/*
|
||||
* Set format to same one auto-detected above, except that
|
||||
* we don't write GNU tar format, so use ustar instead.
|
||||
*/
|
||||
if (format == ARCHIVE_FORMAT_TAR_GNUTAR)
|
||||
format = ARCHIVE_FORMAT_TAR_USTAR;
|
||||
archive_write_set_format(a, format);
|
||||
archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block);
|
||||
lseek(bsdtar->fd, end_offset, SEEK_SET);
|
||||
ftruncate(bsdtar->fd, end_offset);
|
||||
archive_write_open_fd(a, bsdtar->fd);
|
||||
|
||||
write_archive(a, bsdtar);
|
||||
|
||||
archive_write_finish(a);
|
||||
close(bsdtar->fd);
|
||||
bsdtar->fd = -1;
|
||||
|
||||
while (bsdtar->archive_dir_head != NULL) {
|
||||
p = bsdtar->archive_dir_head->next;
|
||||
free(bsdtar->archive_dir_head->name);
|
||||
free(bsdtar->archive_dir_head);
|
||||
bsdtar->archive_dir_head = p;
|
||||
}
|
||||
bsdtar->archive_dir_tail = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write files/dirs given on command line to opened archive.
|
||||
*/
|
||||
static void
|
||||
write_archive(struct archive *a, struct bsdtar *bsdtar)
|
||||
{
|
||||
const char *arg;
|
||||
char *pending_dir;
|
||||
|
||||
pending_dir = NULL;
|
||||
|
||||
if (bsdtar->start_dir != NULL && chdir(bsdtar->start_dir))
|
||||
bsdtar_errc(1, errno, "chdir(%s) failed", bsdtar->start_dir);
|
||||
|
||||
while (*bsdtar->argv) {
|
||||
arg = *bsdtar->argv;
|
||||
if (arg[0] == 'C' && arg[1] == '=') {
|
||||
arg += 2;
|
||||
|
||||
/*-
|
||||
* The logic here for C=<dir> attempts to avoid
|
||||
* chdir() as long as possible. For example:
|
||||
* "C=/foo C=/bar file"
|
||||
* needs chdir("/bar") but not chdir("/foo")
|
||||
* "C=/foo C=bar file"
|
||||
* needs chdir("/foo/bar")
|
||||
* "C=/foo C=bar /file1"
|
||||
* does not need chdir()
|
||||
* "C=/foo C=bar /file1 file2"
|
||||
* needs chdir("/foo/bar") before file2
|
||||
*
|
||||
* The only correct way to handle this is to
|
||||
* record a "pending" chdir request and only
|
||||
* execute the real chdir when a non-absolute
|
||||
* filename is seen on the command line.
|
||||
*
|
||||
* I went to all this work so that programs
|
||||
* that build tar command lines don't have to
|
||||
* worry about C= with non-existent
|
||||
* directories; such requests will only fail
|
||||
* if the directory must be accessed.
|
||||
*/
|
||||
if (pending_dir && *arg == '/') {
|
||||
/* The C=/foo C=/bar case; dump first one. */
|
||||
free(pending_dir);
|
||||
pending_dir = NULL;
|
||||
}
|
||||
if (pending_dir) {
|
||||
/* The C=/foo C=bar case; concatenate */
|
||||
char *old_pending = pending_dir;
|
||||
int old_len = strlen(old_pending);
|
||||
|
||||
pending_dir =
|
||||
malloc(old_len + 1 + strlen(arg));
|
||||
strcpy(pending_dir, old_pending);
|
||||
if (pending_dir[old_len - 1] != '/') {
|
||||
pending_dir[old_len] = '/';
|
||||
old_len ++;
|
||||
}
|
||||
strcpy(pending_dir + old_len, arg);
|
||||
} else {
|
||||
/* Easy case: no previously-saved dir. */
|
||||
pending_dir = strdup(arg);
|
||||
}
|
||||
} else {
|
||||
if (pending_dir &&
|
||||
(*arg != '/' || (*arg == '@' && arg[1] != '/'))) {
|
||||
/* Handle a deferred -C request, see
|
||||
* comments above. */
|
||||
if (chdir(pending_dir))
|
||||
bsdtar_errc(1, 0,
|
||||
"could not chdir to '%s'\n",
|
||||
pending_dir);
|
||||
free(pending_dir);
|
||||
pending_dir = NULL;
|
||||
}
|
||||
|
||||
if (*arg == '@')
|
||||
append_archive(bsdtar, a, arg+1);
|
||||
else
|
||||
write_heirarchy(bsdtar, a, arg);
|
||||
}
|
||||
bsdtar->argv++;
|
||||
}
|
||||
|
||||
create_cleanup(bsdtar);
|
||||
}
|
||||
|
||||
|
||||
/* Copy from specified archive to current archive. */
|
||||
static int
|
||||
append_archive(struct bsdtar *bsdtar, struct archive *a, const char *filename)
|
||||
{
|
||||
struct archive *ina;
|
||||
struct archive_entry *in_entry;
|
||||
int bytes_read, bytes_written;
|
||||
char buff[8192];
|
||||
|
||||
ina = archive_read_new();
|
||||
archive_read_support_format_all(ina);
|
||||
archive_read_support_compression_all(ina);
|
||||
archive_read_open_file(ina, filename, 10240);
|
||||
while (0 == archive_read_next_header(ina, &in_entry)) {
|
||||
if (!new_enough(bsdtar, archive_entry_pathname(in_entry),
|
||||
archive_entry_mtime(in_entry),
|
||||
archive_entry_mtime_nsec(in_entry)))
|
||||
continue;
|
||||
if (excluded(bsdtar, archive_entry_pathname(in_entry)))
|
||||
continue;
|
||||
if (bsdtar->option_interactive &&
|
||||
!yes("copy '%s'", archive_entry_pathname(in_entry)))
|
||||
continue;
|
||||
if (bsdtar->verbose)
|
||||
safe_fprintf(stderr, "a %s",
|
||||
archive_entry_pathname(in_entry));
|
||||
/* XXX handle/report errors XXX */
|
||||
archive_write_header(a, in_entry);
|
||||
bytes_read = archive_read_data(ina, buff, sizeof(buff));
|
||||
while (bytes_read > 0) {
|
||||
bytes_written =
|
||||
archive_write_data(a, buff, bytes_read);
|
||||
if (bytes_written < bytes_read) {
|
||||
bsdtar_warnc( archive_errno(a), "%s",
|
||||
archive_error_string(a));
|
||||
exit(1);
|
||||
}
|
||||
bytes_read =
|
||||
archive_read_data(ina, buff, sizeof(buff));
|
||||
}
|
||||
if (bsdtar->verbose)
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
}
|
||||
if (archive_errno(ina))
|
||||
bsdtar_warnc(0, "Error reading archive %s: %s", filename,
|
||||
archive_error_string(ina));
|
||||
|
||||
return (0); /* TODO: Return non-zero on error */
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the file or dir heirarchy named by 'path' to the archive
|
||||
*/
|
||||
static void
|
||||
write_heirarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
|
||||
{
|
||||
FTS *fts;
|
||||
FTSENT *ftsent;
|
||||
int ftsoptions;
|
||||
char *fts_argv[2];
|
||||
|
||||
/*
|
||||
* Sigh: fts_open modifies it's first parameter, so we have to
|
||||
* copy 'path' to mutable storage.
|
||||
*/
|
||||
fts_argv[0] = strdup(path);
|
||||
fts_argv[1] = NULL;
|
||||
ftsoptions = FTS_PHYSICAL;
|
||||
switch (bsdtar->symlink_mode) {
|
||||
case 'H':
|
||||
ftsoptions |= FTS_COMFOLLOW;
|
||||
break;
|
||||
case 'L':
|
||||
ftsoptions = FTS_COMFOLLOW | FTS_LOGICAL;
|
||||
break;
|
||||
}
|
||||
if (bsdtar->option_dont_traverse_mounts)
|
||||
ftsoptions |= FTS_XDEV;
|
||||
|
||||
fts = fts_open(fts_argv, ftsoptions, NULL);
|
||||
|
||||
|
||||
if (!fts) {
|
||||
bsdtar_warnc(errno, "%s: Cannot open", path);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((ftsent = fts_read(fts))) {
|
||||
switch (ftsent->fts_info) {
|
||||
case FTS_NS:
|
||||
bsdtar_warnc(ftsent->fts_errno, "%s: Could not stat",
|
||||
ftsent->fts_path);
|
||||
break;
|
||||
case FTS_ERR:
|
||||
bsdtar_warnc(ftsent->fts_errno, "%s", ftsent->fts_path);
|
||||
break;
|
||||
case FTS_DNR:
|
||||
bsdtar_warnc(ftsent->fts_errno,
|
||||
"%s: Cannot read directory contents",
|
||||
ftsent->fts_path);
|
||||
break;
|
||||
case FTS_W: /* Skip Whiteout entries */
|
||||
break;
|
||||
case FTS_DC: /* Directory that causes cycle */
|
||||
/* XXX Does this need special handling ? */
|
||||
break;
|
||||
case FTS_D:
|
||||
/*
|
||||
* If this dir is flagged "nodump" and we're
|
||||
* honoring such flags, tell FTS to skip the
|
||||
* entire tree and don't write the entry for the
|
||||
* directory itself.
|
||||
*/
|
||||
#ifdef HAVE_CHFLAGS
|
||||
if (bsdtar->option_honor_nodump &&
|
||||
(ftsent->fts_statp->st_flags & UF_NODUMP)) {
|
||||
fts_set(fts, ftsent, FTS_SKIP);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* In -u mode, we need to check whether this
|
||||
* is newer than what's already in the archive.
|
||||
*/
|
||||
if (!new_enough(bsdtar, ftsent->fts_path,
|
||||
ftsent->fts_statp->st_mtime,
|
||||
ftsent->fts_statp->st_mtimespec.tv_nsec))
|
||||
break;
|
||||
/*
|
||||
* If this dir is excluded by a filename
|
||||
* pattern, tell FTS to skip the entire tree
|
||||
* and don't write the entry for the directory
|
||||
* itself.
|
||||
*/
|
||||
if (excluded(bsdtar, ftsent->fts_path)) {
|
||||
fts_set(fts, ftsent, FTS_SKIP);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user vetoes the directory, skip
|
||||
* the whole thing.
|
||||
*/
|
||||
if (bsdtar->option_interactive &&
|
||||
!yes("add '%s'", ftsent->fts_path)) {
|
||||
fts_set(fts, ftsent, FTS_SKIP);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're not recursing, tell FTS to skip the
|
||||
* tree but do fall through and write the entry
|
||||
* for the dir itself.
|
||||
*/
|
||||
if (bsdtar->option_no_subdirs)
|
||||
fts_set(fts, ftsent, FTS_SKIP);
|
||||
write_entry(bsdtar, a, ftsent->fts_statp,
|
||||
ftsent->fts_path, ftsent->fts_pathlen,
|
||||
ftsent->fts_accpath);
|
||||
break;
|
||||
case FTS_F:
|
||||
case FTS_SL:
|
||||
case FTS_SLNONE:
|
||||
case FTS_DEFAULT:
|
||||
/*
|
||||
* Skip this file if it's flagged "nodump" and we're
|
||||
* honoring that flag.
|
||||
*/
|
||||
#ifdef HAVE_CHFLAGS
|
||||
if (bsdtar->option_honor_nodump &&
|
||||
(ftsent->fts_statp->st_flags & UF_NODUMP))
|
||||
break;
|
||||
#endif
|
||||
/*
|
||||
* Skip this file if it's excluded by a
|
||||
* filename pattern.
|
||||
*/
|
||||
if (excluded(bsdtar, ftsent->fts_path))
|
||||
break;
|
||||
|
||||
/*
|
||||
* In -u mode, we need to check whether this
|
||||
* is newer than what's already in the archive.
|
||||
*/
|
||||
if (!new_enough(bsdtar, ftsent->fts_path,
|
||||
ftsent->fts_statp->st_mtime,
|
||||
ftsent->fts_statp->st_mtimespec.tv_nsec))
|
||||
break;
|
||||
|
||||
if (bsdtar->option_interactive &&
|
||||
!yes("add '%s'", ftsent->fts_path)) {
|
||||
break;
|
||||
}
|
||||
|
||||
write_entry(bsdtar, a, ftsent->fts_statp,
|
||||
ftsent->fts_path, ftsent->fts_pathlen,
|
||||
ftsent->fts_accpath);
|
||||
break;
|
||||
case FTS_DP:
|
||||
break;
|
||||
default:
|
||||
bsdtar_warnc(0, "%s: Heirarchy traversal error %d\n",
|
||||
ftsent->fts_path,
|
||||
ftsent->fts_info);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (errno)
|
||||
bsdtar_warnc(errno, "%s", path);
|
||||
if (fts_close(fts))
|
||||
bsdtar_warnc(errno, "fts_close failed");
|
||||
free(fts_argv[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a single filesystem object to the archive.
|
||||
*/
|
||||
static void
|
||||
write_entry(struct bsdtar *bsdtar, struct archive *a, struct stat *st,
|
||||
const char *pathname, unsigned pathlen, const char *accpath)
|
||||
{
|
||||
struct archive_entry *entry;
|
||||
int e;
|
||||
int fd;
|
||||
char *fflags = NULL;
|
||||
static char linkbuffer[PATH_MAX+1];
|
||||
|
||||
(void)pathlen; /* UNUSED */
|
||||
|
||||
fd = -1;
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, pathname);
|
||||
|
||||
/* If there are hard links, record it for later use */
|
||||
if (!S_ISDIR(st->st_mode) && (st->st_nlink > 1))
|
||||
record_hardlink(bsdtar, entry, st);
|
||||
|
||||
/* Non-regular files get archived with zero size. */
|
||||
if (!S_ISREG(st->st_mode))
|
||||
st->st_size = 0;
|
||||
|
||||
/* Strip redundant "./" from start of filename. */
|
||||
if (pathname && pathname[0] == '.' && pathname[1] == '/') {
|
||||
pathname += 2;
|
||||
if (*pathname == 0) /* This is the "./" directory. */
|
||||
goto cleanup; /* Don't archive it ever. */
|
||||
}
|
||||
|
||||
/* Strip leading '/' unless user has asked us not to. */
|
||||
if (pathname && pathname[0] == '/' && !bsdtar->option_absolute_paths)
|
||||
pathname++;
|
||||
|
||||
/* Display entry as we process it. This format is required by SUSv2. */
|
||||
if (bsdtar->verbose)
|
||||
safe_fprintf(stderr, "a %s", pathname);
|
||||
|
||||
/* Read symbolic link information. */
|
||||
if ((st->st_mode & S_IFMT) == S_IFLNK) {
|
||||
int lnklen;
|
||||
|
||||
lnklen = readlink(accpath, linkbuffer, PATH_MAX);
|
||||
if (lnklen < 0) {
|
||||
if (!bsdtar->verbose)
|
||||
bsdtar_warnc(errno,
|
||||
"%s: Couldn't read symbolic link",
|
||||
pathname);
|
||||
else
|
||||
safe_fprintf(stderr,
|
||||
": Couldn't read symbolic link: %s",
|
||||
strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
linkbuffer[lnklen] = 0;
|
||||
archive_entry_set_symlink(entry, linkbuffer);
|
||||
}
|
||||
|
||||
/* Look up username and group name. */
|
||||
archive_entry_set_uname(entry, lookup_uname(bsdtar, st->st_uid));
|
||||
archive_entry_set_gname(entry, lookup_gname(bsdtar, st->st_gid));
|
||||
|
||||
#ifdef HAVE_CHFLAGS
|
||||
if (st->st_flags != 0) {
|
||||
fflags = fflagstostr(st->st_flags);
|
||||
archive_entry_set_fflags(entry, fflags);
|
||||
}
|
||||
#endif
|
||||
|
||||
setup_acls(bsdtar, entry, accpath);
|
||||
|
||||
/*
|
||||
* If it's a regular file (and non-zero in size) make sure we
|
||||
* can open it before we start to write. In particular, note
|
||||
* that we can always archive a zero-length file, even if we
|
||||
* can't read it.
|
||||
*/
|
||||
if (S_ISREG(st->st_mode) && st->st_size > 0) {
|
||||
fd = open(accpath, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
if (!bsdtar->verbose)
|
||||
bsdtar_warnc(errno, "%s", pathname);
|
||||
else
|
||||
fprintf(stderr, ": %s", strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
archive_entry_copy_stat(entry, st);
|
||||
archive_entry_set_pathname(entry, pathname);
|
||||
|
||||
e = archive_write_header(a, entry);
|
||||
if (e != ARCHIVE_OK) {
|
||||
if (!bsdtar->verbose)
|
||||
bsdtar_warnc(0, "%s: %s", pathname,
|
||||
archive_error_string(a));
|
||||
else
|
||||
fprintf(stderr, ": %s", archive_error_string(a));
|
||||
}
|
||||
|
||||
if (e == ARCHIVE_FATAL)
|
||||
exit(1);
|
||||
|
||||
/*
|
||||
* If we opened a file earlier, write it out now. Note that
|
||||
* the format handler might have reset the size field to zero
|
||||
* to inform us that the archive body won't get stored. In
|
||||
* that case, just skip the write.
|
||||
*/
|
||||
if (fd >= 0 && archive_entry_size(entry) > 0)
|
||||
write_file_data(a, fd);
|
||||
|
||||
cleanup:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
if (entry != NULL)
|
||||
archive_entry_free(entry);
|
||||
|
||||
if (bsdtar->verbose)
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (fflags != NULL) free(fflags);
|
||||
}
|
||||
|
||||
|
||||
/* Helper function to copy file to archive, with stack-allocated buffer. */
|
||||
static int
|
||||
write_file_data(struct archive *a, int fd)
|
||||
{
|
||||
char buff[8192];
|
||||
ssize_t bytes_read;
|
||||
ssize_t bytes_written;
|
||||
|
||||
bytes_read = read(fd, buff, sizeof(buff));
|
||||
while (bytes_read > 0) {
|
||||
bytes_written = archive_write_data(a, buff, bytes_read);
|
||||
|
||||
if (bytes_written == 0 && errno) {
|
||||
return -1; /* Write failed; this is bad */
|
||||
}
|
||||
bytes_read = read(fd, buff, sizeof(buff));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
create_cleanup(struct bsdtar * bsdtar)
|
||||
{
|
||||
/* Free inode->name map */
|
||||
while (bsdtar->links_head != NULL) {
|
||||
struct links_entry *lp = bsdtar->links_head->next;
|
||||
|
||||
if (bsdtar->option_warn_links)
|
||||
bsdtar_warnc(0, "Missing links to %s",
|
||||
bsdtar->links_head->name);
|
||||
|
||||
if (bsdtar->links_head->name != NULL)
|
||||
free(bsdtar->links_head->name);
|
||||
free(bsdtar->links_head);
|
||||
bsdtar->links_head = lp;
|
||||
}
|
||||
cleanup_exclusions(bsdtar);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
record_hardlink(struct bsdtar *bsdtar, struct archive_entry *entry,
|
||||
const struct stat *st)
|
||||
{
|
||||
struct links_entry *le;
|
||||
|
||||
/*
|
||||
* First look in the list of multiply-linked files. If we've
|
||||
* already dumped it, convert this entry to a hard link entry.
|
||||
*/
|
||||
for (le = bsdtar->links_head; le != NULL; le = le->next) {
|
||||
if (le->dev == st->st_dev && le->ino == st->st_ino) {
|
||||
archive_entry_set_hardlink(entry, le->name);
|
||||
|
||||
/*
|
||||
* Decrement link count each time and release
|
||||
* the entry if it hits zero. This saves
|
||||
* memory and is necessary for proper -l
|
||||
* implementation.
|
||||
*/
|
||||
if (--le->links <= 0) {
|
||||
if (le->previous != NULL)
|
||||
le->previous->next = le->next;
|
||||
if (le->next != NULL)
|
||||
le->next->previous = le->previous;
|
||||
if (bsdtar->links_head == le)
|
||||
bsdtar->links_head = le->next;
|
||||
free(le);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
le = malloc(sizeof(struct links_entry));
|
||||
if (bsdtar->links_head != NULL)
|
||||
bsdtar->links_head->previous = le;
|
||||
le->next = bsdtar->links_head;
|
||||
le->previous = NULL;
|
||||
bsdtar->links_head = le;
|
||||
le->dev = st->st_dev;
|
||||
le->ino = st->st_ino;
|
||||
le->links = st->st_nlink - 1;
|
||||
le->name = strdup(archive_entry_pathname(entry));
|
||||
}
|
||||
|
||||
#ifdef HAVE_POSIX_ACL
|
||||
void
|
||||
setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry,
|
||||
const char *accpath)
|
||||
{
|
||||
acl_t acl;
|
||||
acl_tag_t acl_tag;
|
||||
acl_entry_t acl_entry;
|
||||
acl_permset_t acl_permset;
|
||||
int s, ae_id, ae_tag, ae_perm;
|
||||
const char *ae_name;
|
||||
|
||||
archive_entry_acl_clear(entry);
|
||||
|
||||
/* Retrieve access ACL from file. */
|
||||
acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
|
||||
if (acl != NULL) {
|
||||
s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
|
||||
while (s == 1) {
|
||||
ae_id = -1;
|
||||
ae_name = NULL;
|
||||
|
||||
acl_get_tag_type(acl_entry, &acl_tag);
|
||||
if (acl_tag == ACL_USER) {
|
||||
ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
|
||||
ae_name = lookup_uname(bsdtar, ae_id);
|
||||
ae_tag = ARCHIVE_ENTRY_ACL_USER;
|
||||
} else if (acl_tag == ACL_GROUP) {
|
||||
ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
|
||||
ae_name = lookup_gname(bsdtar, ae_id);
|
||||
ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
|
||||
} else if (acl_tag == ACL_MASK) {
|
||||
ae_tag = ARCHIVE_ENTRY_ACL_MASK;
|
||||
} else if (acl_tag == ACL_USER_OBJ) {
|
||||
ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
|
||||
} else if (acl_tag == ACL_GROUP_OBJ) {
|
||||
ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
|
||||
} else if (acl_tag == ACL_OTHER) {
|
||||
ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
|
||||
} else {
|
||||
/* Skip types that libarchive can't support. */
|
||||
continue;
|
||||
}
|
||||
|
||||
acl_get_permset(acl_entry, &acl_permset);
|
||||
ae_perm = 0;
|
||||
if (acl_get_perm_np(acl_permset, ACL_EXECUTE))
|
||||
ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
|
||||
if (acl_get_perm_np(acl_permset, ACL_READ))
|
||||
ae_perm |= ARCHIVE_ENTRY_ACL_READ;
|
||||
if (acl_get_perm_np(acl_permset, ACL_WRITE))
|
||||
ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
|
||||
|
||||
archive_entry_acl_add_entry(entry,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ae_perm, ae_tag,
|
||||
ae_id, ae_name);
|
||||
|
||||
s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
|
||||
}
|
||||
acl_free(acl);
|
||||
}
|
||||
|
||||
/* XXX TODO: Default acl ?? XXX */
|
||||
}
|
||||
#else
|
||||
void
|
||||
setup_acls(struct archive_entry *entry, const char *accpath)
|
||||
{
|
||||
(void)entry;
|
||||
(void)accpath;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Lookup gid from gname and uid from uname.
|
||||
*
|
||||
* TODO: Cache gname/uname lookups to improve performance on
|
||||
* large extracts.
|
||||
*/
|
||||
const char *
|
||||
lookup_uname(struct bsdtar *bsdtar, uid_t uid)
|
||||
{
|
||||
struct passwd *pwent;
|
||||
|
||||
(void)bsdtar; /* UNUSED */
|
||||
|
||||
pwent = getpwuid(uid);
|
||||
if (pwent)
|
||||
return (pwent->pw_name);
|
||||
if (errno)
|
||||
bsdtar_warnc(errno, "getpwuid(%d) failed", uid);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
lookup_gname(struct bsdtar *bsdtar, gid_t gid)
|
||||
{
|
||||
struct group *grent;
|
||||
|
||||
(void)bsdtar; /* UNUSED */
|
||||
grent = getgrgid(gid);
|
||||
if (grent)
|
||||
return (grent->gr_name);
|
||||
if (errno)
|
||||
bsdtar_warnc(errno, "getgrgid(%d) failed", gid);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if the specified file is newer than what's already
|
||||
* in the archive.
|
||||
*/
|
||||
int
|
||||
new_enough(struct bsdtar *bsdtar, const char *path,
|
||||
time_t mtime_sec, int mtime_nsec)
|
||||
{
|
||||
struct archive_dir_entry *p;
|
||||
|
||||
if (path[0] == '.' && path[1] == '/' && path[2] != '\0')
|
||||
path += 2;
|
||||
|
||||
if (bsdtar->archive_dir_head == NULL)
|
||||
return (1);
|
||||
|
||||
for (p = bsdtar->archive_dir_head; p != NULL; p = p->next) {
|
||||
if (strcmp(path, p->name)==0)
|
||||
return (p->mtime_sec < mtime_sec ||
|
||||
(p->mtime_sec == mtime_sec &&
|
||||
p->mtime_nsec < mtime_nsec));
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry to the dir list for 'u' mode.
|
||||
*
|
||||
* XXX TODO: Make this fast.
|
||||
*/
|
||||
static void
|
||||
add_dir_list(struct bsdtar *bsdtar, const char *path,
|
||||
time_t mtime_sec, int mtime_nsec)
|
||||
{
|
||||
struct archive_dir_entry *p;
|
||||
|
||||
if (path[0] == '.' && path[1] == '/' && path[2] != '\0')
|
||||
path += 2;
|
||||
|
||||
p = bsdtar->archive_dir_head;
|
||||
while (p != NULL) {
|
||||
if (strcmp(path, p->name)==0) {
|
||||
p->mtime_sec = mtime_sec;
|
||||
p->mtime_nsec = mtime_nsec;
|
||||
return;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
p = malloc(sizeof(*p));
|
||||
p->name = strdup(path);
|
||||
p->mtime_sec = mtime_sec;
|
||||
p->mtime_nsec = mtime_nsec;
|
||||
p->next = NULL;
|
||||
if (bsdtar->archive_dir_tail == NULL) {
|
||||
bsdtar->archive_dir_head = bsdtar->archive_dir_tail = p;
|
||||
} else {
|
||||
bsdtar->archive_dir_tail->next = p;
|
||||
bsdtar->archive_dir_tail = p;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_for_append(struct bsdtar *bsdtar)
|
||||
{
|
||||
struct stat s;
|
||||
|
||||
if (*bsdtar->argv == NULL)
|
||||
bsdtar_errc(1, 0, "no files or directories specified");
|
||||
if (bsdtar->filename == NULL)
|
||||
bsdtar_errc(1, 0, "Cannot append to stdout.");
|
||||
|
||||
if (bsdtar->create_compression != 0)
|
||||
bsdtar_errc(1, 0, "Cannot append to %s with compression",
|
||||
bsdtar->filename);
|
||||
|
||||
if (stat(bsdtar->filename, &s) != 0)
|
||||
bsdtar_errc(1, errno, "Cannot stat %s", bsdtar->filename);
|
||||
|
||||
if (!S_ISREG(s.st_mode))
|
||||
bsdtar_errc(1, 0, "Cannot append to %s: not a regular file.",
|
||||
bsdtar->filename);
|
||||
}
|
Loading…
Reference in New Issue
Block a user