Update libarchive to 2.4.10. This includes a number of improvements
that I've been working on but put off committing until after the RELENG_7 branch, including: * New manpages: cpio.5 mtree.5 * New archive_entry_strmode() * New archive_entry_link_resolver() * New read support: mtree format * Internal API change: read format auction only runs once * Running the auction only once allowed simplifying a lot of bid logic. * Cpio robustness: search for next header after a sync error * Support device nodes on ISO9660 images * Eliminate a lot of unnecessary copies for uncompressed archives * Corrected handling of new GNU --sparse --posix formats * Correctly handle a zero-byte write to a compressed archive * Fixed memory leaks Many of these improvements were motivated by the upcoming bsdcpio front-end. There have also been extensive improvements to the libarchive_test test harness, which I'll commit separately.
This commit is contained in:
parent
79386ec7db
commit
9dd49f960f
@ -9,22 +9,16 @@ LDADD= -lbz2 -lz
|
||||
# Major: Bumped ONLY when API/ABI breakage happens (see SHLIB_MAJOR)
|
||||
# Minor: Bumped when significant new features are added
|
||||
# Revision: Bumped on any notable change
|
||||
VERSION= 2.2.4
|
||||
|
||||
ARCHIVE_API_MAJOR!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/\..*//'
|
||||
ARCHIVE_API_MINOR!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/[0-9]*\.//' -e 's/\..*//'
|
||||
ARCHIVE_API_REV!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/.*\.//'
|
||||
|
||||
# Can't use /usr/bin/printf to format the version stamp here, because
|
||||
# that's not available during installworld. Fortunately, awk is.
|
||||
ARCHIVE_VERSION_STAMP!= echo ${ARCHIVE_API_MAJOR} ${ARCHIVE_API_MINOR} ${ARCHIVE_API_REV} | awk '{printf("%d%03d%03d",$$1,$$2,$$3)}'
|
||||
# The useful version number (one integer, easy to compare)
|
||||
LIBARCHIVE_VERSION= 2004010
|
||||
# The pretty version string
|
||||
LIBARCHIVE_VERSION_STRING!= echo $$((${LIBARCHIVE_VERSION} / 1000000)).$$((${LIBARCHIVE_VERSION} / 1000 % 1000)).$$((${LIBARCHIVE_VERSION} % 1000))
|
||||
|
||||
# FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system.
|
||||
# It has no real relation to the version number above.
|
||||
SHLIB_MAJOR= 4
|
||||
|
||||
CFLAGS+= -DPACKAGE_NAME=\"lib${LIB}\"
|
||||
CFLAGS+= -DPACKAGE_VERSION=\"${VERSION}\"
|
||||
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
|
||||
CFLAGS+= -I${.OBJDIR}
|
||||
|
||||
@ -37,11 +31,9 @@ INCS= archive.h archive_entry.h
|
||||
# Note: FreeBSD has inttypes.h, so enable that include in archive.h.in
|
||||
archive.h: archive.h.in Makefile
|
||||
cat ${.CURDIR}/archive.h.in | sed \
|
||||
-e 's/@ARCHIVE_VERSION@/${VERSION}/g' \
|
||||
-e 's/@LIBARCHIVE_VERSION@/${LIBARCHIVE_VERSION}/g' \
|
||||
-e 's/@LIBARCHIVE_VERSION_STRING@/${LIBARCHIVE_VERSION_STRING}/g' \
|
||||
-e 's/@SHLIB_MAJOR@/${SHLIB_MAJOR}/g' \
|
||||
-e 's/@ARCHIVE_API_MAJOR@/${ARCHIVE_API_MAJOR}/g' \
|
||||
-e 's/@ARCHIVE_API_MINOR@/${ARCHIVE_API_MINOR}/g' \
|
||||
-e 's/@ARCHIVE_VERSION_STAMP@/${ARCHIVE_VERSION_STAMP}/g' \
|
||||
-e 's|@ARCHIVE_H_INCLUDE_INTTYPES_H@|#include <inttypes.h> /* For int64_t */|g' \
|
||||
> archive.h
|
||||
|
||||
@ -54,6 +46,8 @@ SRCS= archive.h \
|
||||
archive_entry.c \
|
||||
archive_entry_copy_stat.c \
|
||||
archive_entry_stat.c \
|
||||
archive_entry_strmode.c \
|
||||
archive_entry_link_resolver.c \
|
||||
archive_read.c \
|
||||
archive_read_data_into_fd.c \
|
||||
archive_read_extract.c \
|
||||
@ -72,6 +66,7 @@ SRCS= archive.h \
|
||||
archive_read_support_format_cpio.c \
|
||||
archive_read_support_format_empty.c \
|
||||
archive_read_support_format_iso9660.c \
|
||||
archive_read_support_format_mtree.c \
|
||||
archive_read_support_format_tar.c \
|
||||
archive_read_support_format_zip.c \
|
||||
archive_string.c \
|
||||
@ -105,8 +100,10 @@ MAN= archive_entry.3 \
|
||||
archive_util.3 \
|
||||
archive_write.3 \
|
||||
archive_write_disk.3 \
|
||||
cpio.5 \
|
||||
libarchive.3 \
|
||||
libarchive-formats.5 \
|
||||
mtree.5 \
|
||||
tar.5
|
||||
|
||||
# Symlink the man pages under each function name.
|
||||
@ -246,7 +243,7 @@ MLINKS+= archive_write_disk.3 archive_write_disk_set_standard_lookup.3
|
||||
MLINKS+= archive_write_disk.3 archive_write_disk_set_user_lookup.3
|
||||
MLINKS+= libarchive.3 archive.3
|
||||
|
||||
test:
|
||||
check:
|
||||
cd ${.CURDIR}/test && make test
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -60,39 +60,9 @@ extern "C" {
|
||||
/*
|
||||
* Textual name/version of the library, useful for version displays.
|
||||
*/
|
||||
#define ARCHIVE_LIBRARY_VERSION "libarchive @ARCHIVE_VERSION@"
|
||||
#define ARCHIVE_LIBRARY_VERSION "libarchive @LIBARCHIVE_VERSION_STRING@"
|
||||
const char * archive_version(void);
|
||||
|
||||
/*
|
||||
* Major version number: If ARCHIVE_API_VERSION !=
|
||||
* archive_api_version(), then the library you were linked with is
|
||||
* using an incompatible API to the one you were compiled with. This
|
||||
* is almost certainly a fatal problem.
|
||||
*/
|
||||
#define ARCHIVE_API_VERSION @ARCHIVE_API_MAJOR@
|
||||
int archive_api_version(void);
|
||||
|
||||
/*
|
||||
* Minor version number: ARCHIVE_API_FEATURE is incremented with each
|
||||
* significant feature addition, so you can test (at compile or run
|
||||
* time) if a particular feature is implemented. It's no big deal if
|
||||
* ARCHIVE_API_FEATURE != archive_api_feature(), as long as both are
|
||||
* high enough to include the features you're relying on. Specific
|
||||
* values of FEATURE are documented here:
|
||||
*
|
||||
* 1 - Version tests are available.
|
||||
* 2 - archive_{read,write}_close available separately from _finish.
|
||||
* 3 - open_memory, open_memory2, open_FILE, open_fd available
|
||||
* 5 - archive_write_disk interface available
|
||||
*
|
||||
* Unfortunately, this count resets whenever ARCHIVE_API_VERSION changes,
|
||||
* making it awkward to use in practice. For that reason, it is deprecated
|
||||
* in favor of the more-accurate version stamp below. It will eventually
|
||||
* be removed.
|
||||
*/
|
||||
#define ARCHIVE_API_FEATURE @ARCHIVE_API_MINOR@
|
||||
int archive_api_feature(void);
|
||||
|
||||
/*
|
||||
* The "version stamp" is a single integer that makes it easy to check
|
||||
* the exact version: for version a.b.c, the version stamp is
|
||||
@ -102,16 +72,34 @@ int archive_api_feature(void);
|
||||
* This was introduced with libarchive 1.9.0 in the libarchive 1.x family
|
||||
* and libarchive 2.2.4 in the libarchive 2.x family. The following
|
||||
* may be useful if you really want to do feature detection for earlier
|
||||
* libarchive versions:
|
||||
* libarchive versions (which defined API_VERSION and API_FEATURE):
|
||||
*
|
||||
* #ifndef ARCHIVE_VERSION_STAMP
|
||||
* #define ARCHIVE_VERSION_STAMP \
|
||||
* (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
|
||||
* #endif
|
||||
*/
|
||||
#define ARCHIVE_VERSION_STAMP @ARCHIVE_VERSION_STAMP@
|
||||
#define ARCHIVE_VERSION_STAMP @LIBARCHIVE_VERSION@
|
||||
int archive_version_stamp(void);
|
||||
|
||||
/*
|
||||
* Major version number: If ARCHIVE_API_VERSION !=
|
||||
* archive_api_version(), then the library you were linked with is
|
||||
* using an incompatible API to the one you were compiled with. This
|
||||
* is almost certainly a fatal problem.
|
||||
* This is deprecated and will be removed; use ARCHIVE_VERSION_STAMP
|
||||
* instead.
|
||||
*/
|
||||
#define ARCHIVE_API_VERSION (ARCHIVE_VERSION_STAMP / 1000000)
|
||||
int archive_api_version(void);
|
||||
|
||||
/*
|
||||
* Minor version number. This is deprecated and will be removed.
|
||||
* Use ARCHIVE_VERSION_STAMP to adapt to libarchive API variations.
|
||||
*/
|
||||
#define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_STAMP / 1000) % 1000)
|
||||
int archive_api_feature(void);
|
||||
|
||||
|
||||
#define ARCHIVE_BYTES_PER_RECORD 512
|
||||
#define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240
|
||||
@ -218,6 +206,9 @@ typedef int archive_close_callback(struct archive *, void *_client_data);
|
||||
#define ARCHIVE_FORMAT_AR 0x70000
|
||||
#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1)
|
||||
#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
|
||||
#define ARCHIVE_FORMAT_MTREE 0x80000
|
||||
#define ARCHIVE_FORMAT_MTREE_V1 (ARCHIVE_FORMAT_MTREE | 1)
|
||||
#define ARCHIVE_FORMAT_MTREE_V2 (ARCHIVE_FORMAT_MTREE | 2)
|
||||
|
||||
/*-
|
||||
* Basic outline for reading an archive:
|
||||
@ -254,6 +245,7 @@ int archive_read_support_format_cpio(struct archive *);
|
||||
int archive_read_support_format_empty(struct archive *);
|
||||
int archive_read_support_format_gnutar(struct archive *);
|
||||
int archive_read_support_format_iso9660(struct archive *);
|
||||
int archive_read_support_format_mtree(struct archive *);
|
||||
int archive_read_support_format_tar(struct archive *);
|
||||
int archive_read_support_format_zip(struct archive *);
|
||||
|
||||
|
@ -34,9 +34,6 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
#include <sys/mkdev.h>
|
||||
# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
|
||||
# define makedev mkdev
|
||||
# endif
|
||||
#else
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
#include <sys/sysmacros.h>
|
||||
@ -74,6 +71,22 @@ __FBSDID("$FreeBSD$");
|
||||
#undef max
|
||||
#define max(a, b) ((a)>(b)?(a):(b))
|
||||
|
||||
/* Play games to come up with a suitable makedev() definition. */
|
||||
#ifdef __QNXNTO__
|
||||
/* QNX. <sigh> */
|
||||
#include <sys/netmgr.h>
|
||||
#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
|
||||
#elif defined makedev
|
||||
/* There's a "makedev" macro. */
|
||||
#define ae_makedev(maj, min) makedev((maj), (min))
|
||||
#elif defined mkdev || defined _WIN32 || defined __WIN32__
|
||||
/* Windows. <sigh> */
|
||||
#define ae_makedev(maj, min) mkdev((maj), (min))
|
||||
#else
|
||||
/* There's a "makedev" function. */
|
||||
#define ae_makedev(maj, min) makedev((maj), (min))
|
||||
#endif
|
||||
|
||||
static void aes_clean(struct aes *);
|
||||
static void aes_copy(struct aes *dest, struct aes *src);
|
||||
static const char * aes_get_mbs(struct aes *);
|
||||
@ -402,7 +415,7 @@ dev_t
|
||||
archive_entry_dev(struct archive_entry *entry)
|
||||
{
|
||||
if (entry->ae_stat.aest_dev_is_broken_down)
|
||||
return makedev(entry->ae_stat.aest_devmajor,
|
||||
return ae_makedev(entry->ae_stat.aest_devmajor,
|
||||
entry->ae_stat.aest_devminor);
|
||||
else
|
||||
return (entry->ae_stat.aest_dev);
|
||||
@ -548,7 +561,7 @@ dev_t
|
||||
archive_entry_rdev(struct archive_entry *entry)
|
||||
{
|
||||
if (entry->ae_stat.aest_rdev_is_broken_down)
|
||||
return makedev(entry->ae_stat.aest_rdevmajor,
|
||||
return ae_makedev(entry->ae_stat.aest_rdevmajor,
|
||||
entry->ae_stat.aest_rdevminor);
|
||||
else
|
||||
return (entry->ae_stat.aest_rdev);
|
||||
@ -779,6 +792,14 @@ archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
|
||||
aes_copy_wcs(&entry->ae_pathname, name);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_perm(struct archive_entry *entry, mode_t p)
|
||||
{
|
||||
entry->stat_valid = 0;
|
||||
entry->ae_stat.aest_mode &= AE_IFMT;
|
||||
entry->ae_stat.aest_mode |= ~AE_IFMT & p;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
|
||||
{
|
||||
|
@ -62,6 +62,7 @@ struct archive_entry;
|
||||
#define AE_IFMT 0170000
|
||||
#define AE_IFREG 0100000
|
||||
#define AE_IFLNK 0120000
|
||||
#define AE_IFSOCK 0140000
|
||||
#define AE_IFCHR 0020000
|
||||
#define AE_IFBLK 0060000
|
||||
#define AE_IFDIR 0040000
|
||||
@ -108,6 +109,7 @@ dev_t archive_entry_rdev(struct archive_entry *);
|
||||
dev_t archive_entry_rdevmajor(struct archive_entry *);
|
||||
dev_t archive_entry_rdevminor(struct archive_entry *);
|
||||
int64_t archive_entry_size(struct archive_entry *);
|
||||
const char *archive_entry_strmode(struct archive_entry *);
|
||||
const char *archive_entry_symlink(struct archive_entry *);
|
||||
const wchar_t *archive_entry_symlink_w(struct archive_entry *);
|
||||
uid_t archive_entry_uid(struct archive_entry *);
|
||||
@ -148,6 +150,7 @@ void archive_entry_set_nlink(struct archive_entry *, unsigned int);
|
||||
void archive_entry_set_pathname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_pathname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_perm(struct archive_entry *, mode_t);
|
||||
void archive_entry_set_rdev(struct archive_entry *, dev_t);
|
||||
void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
|
||||
void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
|
||||
@ -260,11 +263,6 @@ int archive_entry_acl_count(struct archive_entry *, int want_type);
|
||||
int __archive_entry_acl_parse_w(struct archive_entry *,
|
||||
const wchar_t *, int type);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* extended attributes
|
||||
*/
|
||||
@ -283,5 +281,32 @@ int archive_entry_xattr_reset(struct archive_entry *);
|
||||
int archive_entry_xattr_next(struct archive_entry *,
|
||||
const char **name, const void **value, size_t *);
|
||||
|
||||
/*
|
||||
* Utility to detect hardlinks.
|
||||
*
|
||||
* The 'struct archive_hardlink_lookup' is a cache of entry
|
||||
* names and dev/ino numbers. Here's how to use it:
|
||||
* 1. Create a lookup object with archive_hardlink_lookup_new()
|
||||
* 2. Hand each archive_entry to archive_hardlink_lookup().
|
||||
* That function will return NULL (this is not a hardlink to
|
||||
* a previous entry) or the pathname of the first entry
|
||||
* that matched this.
|
||||
* 3. Use archive_hardlink_lookup_free() to release the cache.
|
||||
*
|
||||
* To make things more efficient, be sure that each entry has a valid
|
||||
* nlinks value. The hardlink cache uses this to track when all links
|
||||
* have been found. If the nlinks value is zero, it will keep every
|
||||
* name in the cache indefinitely, which can use a lot of memory.
|
||||
*/
|
||||
struct archive_entry_linkresolver;
|
||||
|
||||
struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
|
||||
void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
|
||||
const char *archive_entry_linkresolve(struct archive_entry_linkresolver *,
|
||||
struct archive_entry *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
|
||||
|
222
lib/libarchive/archive_entry_link_resolver.c
Normal file
222
lib/libarchive/archive_entry_link_resolver.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 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(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 "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive_entry.h"
|
||||
|
||||
/* Initial size of link cache. */
|
||||
#define links_cache_initial_size 1024
|
||||
|
||||
struct archive_entry_linkresolver {
|
||||
char *last_name;
|
||||
unsigned long number_entries;
|
||||
size_t number_buckets;
|
||||
struct links_entry **buckets;
|
||||
};
|
||||
|
||||
struct links_entry {
|
||||
struct links_entry *next;
|
||||
struct links_entry *previous;
|
||||
int links;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct archive_entry_linkresolver *
|
||||
archive_entry_linkresolver_new(void)
|
||||
{
|
||||
struct archive_entry_linkresolver *links_cache;
|
||||
size_t i;
|
||||
|
||||
links_cache = malloc(sizeof(struct archive_entry_linkresolver));
|
||||
if (links_cache == NULL)
|
||||
return (NULL);
|
||||
memset(links_cache, 0, sizeof(struct archive_entry_linkresolver));
|
||||
links_cache->number_buckets = links_cache_initial_size;
|
||||
links_cache->buckets = malloc(links_cache->number_buckets *
|
||||
sizeof(links_cache->buckets[0]));
|
||||
if (links_cache->buckets == NULL) {
|
||||
free(links_cache);
|
||||
return (NULL);
|
||||
}
|
||||
for (i = 0; i < links_cache->number_buckets; i++)
|
||||
links_cache->buckets[i] = NULL;
|
||||
return (links_cache);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_linkresolver_free(struct archive_entry_linkresolver *links_cache)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (links_cache->buckets == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < links_cache->number_buckets; i++) {
|
||||
while (links_cache->buckets[i] != NULL) {
|
||||
struct links_entry *lp = links_cache->buckets[i]->next;
|
||||
if (links_cache->buckets[i]->name != NULL)
|
||||
free(links_cache->buckets[i]->name);
|
||||
free(links_cache->buckets[i]);
|
||||
links_cache->buckets[i] = lp;
|
||||
}
|
||||
}
|
||||
free(links_cache->buckets);
|
||||
links_cache->buckets = NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
struct links_entry *le, **new_buckets;
|
||||
int hash;
|
||||
size_t i, new_size;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
int nlinks;
|
||||
|
||||
|
||||
/* Free a held name. */
|
||||
free(links_cache->last_name);
|
||||
links_cache->last_name = NULL;
|
||||
|
||||
/* If the links cache overflowed and got flushed, don't bother. */
|
||||
if (links_cache->buckets == NULL)
|
||||
return (NULL);
|
||||
|
||||
dev = archive_entry_dev(entry);
|
||||
ino = archive_entry_ino(entry);
|
||||
nlinks = archive_entry_nlink(entry);
|
||||
|
||||
/* An entry with one link can't be a hard link. */
|
||||
if (nlinks == 1)
|
||||
return (NULL);
|
||||
|
||||
/* If the links cache is getting too full, enlarge the hash table. */
|
||||
if (links_cache->number_entries > links_cache->number_buckets * 2)
|
||||
{
|
||||
/* Try to enlarge the bucket list. */
|
||||
new_size = links_cache->number_buckets * 2;
|
||||
new_buckets = malloc(new_size * sizeof(struct links_entry *));
|
||||
|
||||
if (new_buckets != NULL) {
|
||||
memset(new_buckets, 0,
|
||||
new_size * sizeof(struct links_entry *));
|
||||
for (i = 0; i < links_cache->number_buckets; i++) {
|
||||
while (links_cache->buckets[i] != NULL) {
|
||||
/* Remove entry from old bucket. */
|
||||
le = links_cache->buckets[i];
|
||||
links_cache->buckets[i] = le->next;
|
||||
|
||||
/* Add entry to new bucket. */
|
||||
hash = (le->dev ^ le->ino) % new_size;
|
||||
|
||||
if (new_buckets[hash] != NULL)
|
||||
new_buckets[hash]->previous =
|
||||
le;
|
||||
le->next = new_buckets[hash];
|
||||
le->previous = NULL;
|
||||
new_buckets[hash] = le;
|
||||
}
|
||||
}
|
||||
free(links_cache->buckets);
|
||||
links_cache->buckets = new_buckets;
|
||||
links_cache->number_buckets = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to locate this entry in the links cache. */
|
||||
hash = ( dev ^ ino ) % links_cache->number_buckets;
|
||||
for (le = links_cache->buckets[hash]; le != NULL; le = le->next) {
|
||||
if (le->dev == dev && le->ino == ino) {
|
||||
/*
|
||||
* Decrement link count each time and release
|
||||
* the entry if it hits zero. This saves
|
||||
* memory and is necessary for detecting
|
||||
* missed links.
|
||||
*/
|
||||
--le->links;
|
||||
if (le->links > 0)
|
||||
return (le->name);
|
||||
/*
|
||||
* When we release the entry, save the name
|
||||
* until the next call.
|
||||
*/
|
||||
links_cache->last_name = le->name;
|
||||
/*
|
||||
* Release the entry.
|
||||
*/
|
||||
if (le->previous != NULL)
|
||||
le->previous->next = le->next;
|
||||
if (le->next != NULL)
|
||||
le->next->previous = le->previous;
|
||||
if (links_cache->buckets[hash] == le)
|
||||
links_cache->buckets[hash] = le->next;
|
||||
links_cache->number_entries--;
|
||||
free(le);
|
||||
return (links_cache->last_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add this entry to the links cache. */
|
||||
le = malloc(sizeof(struct links_entry));
|
||||
if (le == NULL)
|
||||
return (NULL);
|
||||
le->name = strdup(archive_entry_pathname(entry));
|
||||
if (le->name == NULL) {
|
||||
free(le);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* If we could allocate the entry, record it. */
|
||||
if (links_cache->buckets[hash] != NULL)
|
||||
links_cache->buckets[hash]->previous = le;
|
||||
links_cache->number_entries++;
|
||||
le->next = links_cache->buckets[hash];
|
||||
le->previous = NULL;
|
||||
links_cache->buckets[hash] = le;
|
||||
le->dev = dev;
|
||||
le->ino = ino;
|
||||
le->links = nlinks - 1;
|
||||
return (NULL);
|
||||
}
|
@ -149,6 +149,8 @@ struct archive_entry {
|
||||
|
||||
struct ae_xattr *xattr_head;
|
||||
struct ae_xattr *xattr_p;
|
||||
|
||||
char strmode[11];
|
||||
};
|
||||
|
||||
|
||||
|
83
lib/libarchive/archive_entry_strmode.c
Normal file
83
lib/libarchive/archive_entry_strmode.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 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(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 "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_private.h"
|
||||
|
||||
const char *
|
||||
archive_entry_strmode(struct archive_entry *entry)
|
||||
{
|
||||
static const char *perms = "?rwxrwxrwx ";
|
||||
static const mode_t permbits[] =
|
||||
{ 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
|
||||
char *bp = entry->strmode;
|
||||
mode_t mode;
|
||||
int i;
|
||||
|
||||
/* Fill in a default string, then selectively override. */
|
||||
strcpy(bp, perms);
|
||||
|
||||
mode = archive_entry_mode(entry);
|
||||
switch (archive_entry_filetype(entry)) {
|
||||
case AE_IFREG: bp[0] = '-'; break;
|
||||
case AE_IFBLK: bp[0] = 'b'; break;
|
||||
case AE_IFCHR: bp[0] = 'c'; break;
|
||||
case AE_IFDIR: bp[0] = 'd'; break;
|
||||
case AE_IFLNK: bp[0] = 'l'; break;
|
||||
case AE_IFSOCK: bp[0] = 's'; break;
|
||||
case AE_IFIFO: bp[0] = 'p'; break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
if (!(mode & permbits[i]))
|
||||
bp[i+1] = '-';
|
||||
|
||||
if (mode & S_ISUID) {
|
||||
if (mode & S_IXUSR) bp[3] = 's';
|
||||
else bp[3] = 'S';
|
||||
}
|
||||
if (mode & S_ISGID) {
|
||||
if (mode & S_IXGRP) bp[6] = 's';
|
||||
else bp[6] = 'S';
|
||||
}
|
||||
if (mode & S_ISVTX) {
|
||||
if (mode & S_IXOTH) bp[9] = 't';
|
||||
else bp[9] = 'T';
|
||||
}
|
||||
if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
|
||||
bp[10] = '+';
|
||||
|
||||
return (bp);
|
||||
}
|
@ -57,7 +57,8 @@
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/cdefs.h> /* For __FBSDID */
|
||||
#else
|
||||
#define __FBSDID(a) /* null */
|
||||
/* Just leaving this macro replacement empty leads to a dangling semicolon. */
|
||||
#define __FBSDID(a) struct _undefined_hack
|
||||
#endif
|
||||
|
||||
/* Try to get standard C99-style integer type definitions. */
|
||||
@ -122,4 +123,4 @@
|
||||
#define ARCHIVE_ERRNO_MISC (-1)
|
||||
#endif
|
||||
|
||||
#endif /* !ARCHIVE_H_INCLUDED */
|
||||
#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
|
||||
|
@ -306,6 +306,18 @@ archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
|
||||
archive_entry_clear(entry);
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
/*
|
||||
* If no format has yet been chosen, choose one.
|
||||
*/
|
||||
if (a->format == NULL) {
|
||||
slot = choose_format(a);
|
||||
if (slot < 0) {
|
||||
a->archive.state = ARCHIVE_STATE_FATAL;
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->format = &(a->formats[slot]);
|
||||
}
|
||||
|
||||
/*
|
||||
* If client didn't consume entire data, skip any remainder
|
||||
* (This is especially important for GNU incremental directories.)
|
||||
@ -324,12 +336,6 @@ archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
|
||||
/* Record start-of-header. */
|
||||
a->header_position = a->archive.file_position;
|
||||
|
||||
slot = choose_format(a);
|
||||
if (slot < 0) {
|
||||
a->archive.state = ARCHIVE_STATE_FATAL;
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->format = &(a->formats[slot]);
|
||||
ret = (a->format->read_header)(a, entry);
|
||||
|
||||
/*
|
||||
|
@ -170,56 +170,45 @@ archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff,
|
||||
min = state->buffer_size;
|
||||
|
||||
/*
|
||||
* Try to satisfy the request directly from the client
|
||||
* buffer. We can do this if all of the data in the copy
|
||||
* buffer was copied from the current client buffer. This
|
||||
* also covers the case where the copy buffer is empty and
|
||||
* the client buffer has all the data we need.
|
||||
* Keep pulling more data until we can satisfy the request.
|
||||
*/
|
||||
if (state->client_total >= state->client_avail + state->avail
|
||||
&& state->client_avail + state->avail >= min) {
|
||||
state->client_avail += state->avail;
|
||||
state->client_next -= state->avail;
|
||||
state->avail = 0;
|
||||
state->next = state->buffer;
|
||||
*buff = state->client_next;
|
||||
return (state->client_avail);
|
||||
}
|
||||
for (;;) {
|
||||
|
||||
/*
|
||||
* If we can't use client buffer, we'll have to use copy buffer.
|
||||
*/
|
||||
/*
|
||||
* If we can satisfy from the copy buffer, we're done.
|
||||
*/
|
||||
if (state->avail >= min) {
|
||||
*buff = state->next;
|
||||
return (state->avail);
|
||||
}
|
||||
|
||||
/* Move data forward in copy buffer if necessary. */
|
||||
if (state->next > state->buffer &&
|
||||
state->next + min > state->buffer + state->buffer_size) {
|
||||
if (state->avail > 0)
|
||||
memmove(state->buffer, state->next, state->avail);
|
||||
state->next = state->buffer;
|
||||
}
|
||||
/*
|
||||
* We can satisfy directly from client buffer if everything
|
||||
* currently in the copy buffer is still in the client buffer.
|
||||
*/
|
||||
if (state->client_total >= state->client_avail + state->avail
|
||||
&& state->client_avail + state->avail >= min) {
|
||||
/* "Roll back" to client buffer. */
|
||||
state->client_avail += state->avail;
|
||||
state->client_next -= state->avail;
|
||||
/* Copy buffer is now empty. */
|
||||
state->avail = 0;
|
||||
state->next = state->buffer;
|
||||
/* Return data from client buffer. */
|
||||
*buff = state->client_next;
|
||||
return (state->client_avail);
|
||||
}
|
||||
|
||||
/* Collect data in copy buffer to fulfill request. */
|
||||
while (state->avail < min) {
|
||||
/* Copy data from client buffer to our copy buffer. */
|
||||
if (state->client_avail > 0) {
|
||||
/* First estimate: copy to fill rest of buffer. */
|
||||
size_t tocopy = (state->buffer + state->buffer_size)
|
||||
- (state->next + state->avail);
|
||||
/* Don't copy more than is available. */
|
||||
if (tocopy > state->client_avail)
|
||||
tocopy = state->client_avail;
|
||||
memcpy(state->next + state->avail, state->client_next,
|
||||
tocopy);
|
||||
state->client_next += tocopy;
|
||||
state->client_avail -= tocopy;
|
||||
state->avail += tocopy;
|
||||
} else {
|
||||
/* There is no more client data: fetch more. */
|
||||
/*
|
||||
* It seems to me that const void ** and const
|
||||
* char ** should be compatible, but they
|
||||
* aren't, hence the cast.
|
||||
*/
|
||||
/* Move data forward in copy buffer if necessary. */
|
||||
if (state->next > state->buffer &&
|
||||
state->next + min > state->buffer + state->buffer_size) {
|
||||
if (state->avail > 0)
|
||||
memmove(state->buffer, state->next, state->avail);
|
||||
state->next = state->buffer;
|
||||
}
|
||||
|
||||
/* If we've used up the client data, get more. */
|
||||
if (state->client_avail <= 0) {
|
||||
bytes_read = (a->client_reader)(&a->archive,
|
||||
a->client_data, &state->client_buff);
|
||||
if (bytes_read < 0) { /* Read error. */
|
||||
@ -232,17 +221,33 @@ archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff,
|
||||
state->client_total = state->client_avail = 0;
|
||||
state->client_next = state->client_buff = NULL;
|
||||
state->end_of_file = 1;
|
||||
break;
|
||||
/* Return whatever we do have. */
|
||||
*buff = state->next;
|
||||
return (state->avail);
|
||||
}
|
||||
a->archive.raw_position += bytes_read;
|
||||
state->client_total = bytes_read;
|
||||
state->client_avail = state->client_total;
|
||||
state->client_next = state->client_buff;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We can add client data to copy buffer. */
|
||||
/* First estimate: copy to fill rest of buffer. */
|
||||
size_t tocopy = (state->buffer + state->buffer_size)
|
||||
- (state->next + state->avail);
|
||||
/* Don't copy more than is available. */
|
||||
if (tocopy > state->client_avail)
|
||||
tocopy = state->client_avail;
|
||||
memcpy(state->next + state->avail, state->client_next,
|
||||
tocopy);
|
||||
/* Remove this data from client buffer. */
|
||||
state->client_next += tocopy;
|
||||
state->client_avail -= tocopy;
|
||||
/* add it to copy buffer. */
|
||||
state->avail += tocopy;
|
||||
}
|
||||
}
|
||||
|
||||
*buff = state->next;
|
||||
return (state->avail);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -35,6 +35,7 @@ archive_read_support_format_all(struct archive *a)
|
||||
archive_read_support_format_cpio(a);
|
||||
archive_read_support_format_empty(a);
|
||||
archive_read_support_format_iso9660(a);
|
||||
archive_read_support_format_mtree(a);
|
||||
archive_read_support_format_tar(a);
|
||||
archive_read_support_format_zip(a);
|
||||
return (ARCHIVE_OK);
|
||||
|
@ -47,7 +47,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include "archive_read_private.h"
|
||||
|
||||
struct ar {
|
||||
int bid;
|
||||
off_t entry_bytes_remaining;
|
||||
off_t entry_offset;
|
||||
off_t entry_padding;
|
||||
@ -103,7 +102,6 @@ archive_read_support_format_ar(struct archive *_a)
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
memset(ar, 0, sizeof(*ar));
|
||||
ar->bid = -1;
|
||||
ar->strtab = NULL;
|
||||
|
||||
r = __archive_read_register_format(a,
|
||||
@ -148,9 +146,6 @@ archive_read_format_ar_bid(struct archive_read *a)
|
||||
|
||||
ar = (struct ar *)(a->format->data);
|
||||
|
||||
if (ar->bid > 0)
|
||||
return (ar->bid);
|
||||
|
||||
/*
|
||||
* Verify the 8-byte file signature.
|
||||
* TODO: Do we need to check more than this?
|
||||
@ -159,8 +154,7 @@ archive_read_format_ar_bid(struct archive_read *a)
|
||||
if (bytes_read < 8)
|
||||
return (-1);
|
||||
if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
|
||||
ar->bid = 64;
|
||||
return (ar->bid);
|
||||
return (64);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ static int archive_read_format_cpio_read_data(struct archive_read *,
|
||||
static int archive_read_format_cpio_read_header(struct archive_read *,
|
||||
struct archive_entry *);
|
||||
static int be4(const unsigned char *);
|
||||
static int find_odc_header(struct archive_read *);
|
||||
static int find_newc_header(struct archive_read *);
|
||||
static int header_bin_be(struct archive_read *, struct cpio *,
|
||||
struct archive_entry *, size_t *, size_t *);
|
||||
static int header_bin_le(struct archive_read *, struct cpio *,
|
||||
@ -126,6 +128,8 @@ static int header_newc(struct archive_read *, struct cpio *,
|
||||
struct archive_entry *, size_t *, size_t *);
|
||||
static int header_odc(struct archive_read *, struct cpio *,
|
||||
struct archive_entry *, size_t *, size_t *);
|
||||
static int is_octal(const char *, size_t);
|
||||
static int is_hex(const char *, size_t);
|
||||
static int le4(const unsigned char *);
|
||||
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry);
|
||||
|
||||
@ -161,13 +165,14 @@ archive_read_support_format_cpio(struct archive *_a)
|
||||
static int
|
||||
archive_read_format_cpio_bid(struct archive_read *a)
|
||||
{
|
||||
int bid, bytes_read;
|
||||
int bytes_read;
|
||||
const void *h;
|
||||
const unsigned char *p;
|
||||
struct cpio *cpio;
|
||||
int bid;
|
||||
|
||||
cpio = (struct cpio *)(a->format->data);
|
||||
bid = 0;
|
||||
|
||||
bytes_read = (a->decompressor->read_ahead)(a, &h, 6);
|
||||
/* Convert error code into error return. */
|
||||
if (bytes_read < 0)
|
||||
@ -176,6 +181,7 @@ archive_read_format_cpio_bid(struct archive_read *a)
|
||||
return (-1);
|
||||
|
||||
p = (const unsigned char *)h;
|
||||
bid = 0;
|
||||
if (memcmp(p, "070707", 6) == 0) {
|
||||
/* ASCII cpio archive (odc, POSIX.1) */
|
||||
cpio->read_header = header_odc;
|
||||
@ -231,7 +237,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
|
||||
cpio = (struct cpio *)(a->format->data);
|
||||
r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
|
||||
|
||||
if (r != ARCHIVE_OK)
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
|
||||
/* Read name from buffer. */
|
||||
@ -266,7 +272,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
|
||||
/* Detect and record hardlinks to previously-extracted entries. */
|
||||
record_hardlink(cpio, entry);
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -306,6 +312,82 @@ archive_read_format_cpio_read_data(struct archive_read *a,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip forward to the next cpio newc header by searching for the
|
||||
* 07070[12] string. This should be generalized and merged with
|
||||
* find_odc_header below.
|
||||
*/
|
||||
static int
|
||||
is_hex(const char *p, size_t len)
|
||||
{
|
||||
while (len-- > 0) {
|
||||
if (*p < '0' || (*p > '9' && *p < 'a') || *p > 'f') {
|
||||
return (0);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
find_newc_header(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip, bytes, skipped = 0;
|
||||
|
||||
for (;;) {
|
||||
bytes = (a->decompressor->read_ahead)(a, &h, 2048);
|
||||
if (bytes < sizeof(struct cpio_newc_header))
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
/* Try the typical case first, then go into the slow search.*/
|
||||
if (memcmp("07070", p, 5) == 0
|
||||
&& (p[5] == '1' || p[5] == '2')
|
||||
&& is_hex(p, sizeof(struct cpio_newc_header)))
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/*
|
||||
* Scan ahead until we find something that looks
|
||||
* like an odc header.
|
||||
*/
|
||||
while (p + sizeof(struct cpio_newc_header) < q) {
|
||||
switch (p[5]) {
|
||||
case '1':
|
||||
case '2':
|
||||
if (memcmp("07070", p, 5) == 0
|
||||
&& is_hex(p, sizeof(struct cpio_newc_header))) {
|
||||
skip = p - (const char *)h;
|
||||
(a->decompressor->consume)(a, skip);
|
||||
skipped += skip;
|
||||
if (skipped > 0) {
|
||||
archive_set_error(&a->archive,
|
||||
0,
|
||||
"Skipped %d bytes before "
|
||||
"finding valid header",
|
||||
(int)skipped);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
p += 2;
|
||||
break;
|
||||
case '0':
|
||||
p++;
|
||||
break;
|
||||
default:
|
||||
p += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
(a->decompressor->consume)(a, skip);
|
||||
skipped += skip;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
header_newc(struct archive_read *a, struct cpio *cpio,
|
||||
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
|
||||
@ -313,6 +395,11 @@ header_newc(struct archive_read *a, struct cpio *cpio,
|
||||
const void *h;
|
||||
const struct cpio_newc_header *header;
|
||||
size_t bytes;
|
||||
int r;
|
||||
|
||||
r = find_newc_header(a);
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
|
||||
/* Read fixed-size portion of header. */
|
||||
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header));
|
||||
@ -357,7 +444,81 @@ header_newc(struct archive_read *a, struct cpio *cpio,
|
||||
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
|
||||
/* Pad file contents to a multiple of 4. */
|
||||
cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
|
||||
return (ARCHIVE_OK);
|
||||
return (r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip forward to the next cpio odc header by searching for the
|
||||
* 070707 string. This is a hand-optimized search that could
|
||||
* probably be easily generalized to handle all character-based
|
||||
* cpio variants.
|
||||
*/
|
||||
static int
|
||||
is_octal(const char *p, size_t len)
|
||||
{
|
||||
while (len-- > 0) {
|
||||
if (*p < '0' || *p > '7')
|
||||
return (0);
|
||||
++p;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
find_odc_header(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip, bytes, skipped = 0;
|
||||
|
||||
for (;;) {
|
||||
bytes = (a->decompressor->read_ahead)(a, &h, 512);
|
||||
if (bytes < sizeof(struct cpio_odc_header))
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
/* Try the typical case first, then go into the slow search.*/
|
||||
if (memcmp("070707", p, 6) == 0
|
||||
&& is_octal(p, sizeof(struct cpio_odc_header)))
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
/*
|
||||
* Scan ahead until we find something that looks
|
||||
* like an odc header.
|
||||
*/
|
||||
while (p + sizeof(struct cpio_odc_header) < q) {
|
||||
switch (p[5]) {
|
||||
case '7':
|
||||
if (memcmp("070707", p, 6) == 0
|
||||
&& is_octal(p, sizeof(struct cpio_odc_header))) {
|
||||
skip = p - (const char *)h;
|
||||
(a->decompressor->consume)(a, skip);
|
||||
skipped += skip;
|
||||
if (skipped > 0) {
|
||||
archive_set_error(&a->archive,
|
||||
0,
|
||||
"Skipped %d bytes before "
|
||||
"finding valid header",
|
||||
(int)skipped);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
p += 2;
|
||||
break;
|
||||
case '0':
|
||||
p++;
|
||||
break;
|
||||
default:
|
||||
p += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
(a->decompressor->consume)(a, skip);
|
||||
skipped += skip;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@ -365,12 +526,18 @@ header_odc(struct archive_read *a, struct cpio *cpio,
|
||||
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
|
||||
{
|
||||
const void *h;
|
||||
int r;
|
||||
const struct cpio_odc_header *header;
|
||||
size_t bytes;
|
||||
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
|
||||
a->archive.archive_format_name = "POSIX octet-oriented cpio";
|
||||
|
||||
/* Find the start of the next header. */
|
||||
r = find_odc_header(a);
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
|
||||
/* Read fixed-size portion of header. */
|
||||
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header));
|
||||
if (bytes < sizeof(struct cpio_odc_header))
|
||||
@ -400,7 +567,7 @@ header_odc(struct archive_read *a, struct cpio *cpio,
|
||||
atol8(header->c_filesize, sizeof(header->c_filesize));
|
||||
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
|
||||
cpio->entry_padding = 0;
|
||||
return (ARCHIVE_OK);
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -574,7 +741,7 @@ record_hardlink(struct cpio *cpio, struct archive_entry *entry)
|
||||
*/
|
||||
for (le = cpio->links_head; le; le = le->next) {
|
||||
if (le->dev == dev && le->ino == ino) {
|
||||
archive_entry_set_hardlink(entry, le->name);
|
||||
archive_entry_copy_hardlink(entry, le->name);
|
||||
|
||||
if (--le->links <= 0) {
|
||||
if (le->previous != NULL)
|
||||
@ -583,6 +750,7 @@ record_hardlink(struct cpio *cpio, struct archive_entry *entry)
|
||||
le->next->previous = le->previous;
|
||||
if (cpio->links_head == le)
|
||||
cpio->links_head = le->next;
|
||||
free(le->name);
|
||||
free(le);
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,7 @@ struct file_info {
|
||||
time_t mtime; /* File last modified time. */
|
||||
time_t atime; /* File last accessed time. */
|
||||
time_t ctime; /* File creation time. */
|
||||
uint64_t rdev; /* Device number */
|
||||
mode_t mode;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
@ -194,7 +195,6 @@ struct file_info {
|
||||
struct iso9660 {
|
||||
int magic;
|
||||
#define ISO9660_MAGIC 0x96609660
|
||||
int bid; /* If non-zero, return this as our bid. */
|
||||
struct archive_string pathname;
|
||||
char seenRockridge; /* Set true if RR extensions are used. */
|
||||
unsigned char suspOffset;
|
||||
@ -255,7 +255,6 @@ archive_read_support_format_iso9660(struct archive *_a)
|
||||
}
|
||||
memset(iso9660, 0, sizeof(*iso9660));
|
||||
iso9660->magic = ISO9660_MAGIC;
|
||||
iso9660->bid = -1; /* We haven't yet bid. */
|
||||
|
||||
r = __archive_read_register_format(a,
|
||||
iso9660,
|
||||
@ -280,12 +279,10 @@ archive_read_format_iso9660_bid(struct archive_read *a)
|
||||
ssize_t bytes_read;
|
||||
const void *h;
|
||||
const unsigned char *p;
|
||||
int bid;
|
||||
|
||||
iso9660 = (struct iso9660 *)(a->format->data);
|
||||
|
||||
if (iso9660->bid >= 0)
|
||||
return (iso9660->bid);
|
||||
|
||||
/*
|
||||
* Skip the first 32k (reserved area) and get the first
|
||||
* 8 sectors of the volume descriptor table. Of course,
|
||||
@ -293,7 +290,7 @@ archive_read_format_iso9660_bid(struct archive_read *a)
|
||||
*/
|
||||
bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048);
|
||||
if (bytes_read < 32768 + 8*2048)
|
||||
return (iso9660->bid = -1);
|
||||
return (-1);
|
||||
p = (const unsigned char *)h;
|
||||
|
||||
/* Skip the reserved area. */
|
||||
@ -302,16 +299,15 @@ archive_read_format_iso9660_bid(struct archive_read *a)
|
||||
|
||||
/* Check each volume descriptor to locate the PVD. */
|
||||
for (; bytes_read > 2048; bytes_read -= 2048, p += 2048) {
|
||||
iso9660->bid = isPVD(iso9660, p);
|
||||
if (iso9660->bid > 0)
|
||||
return (iso9660->bid);
|
||||
bid = isPVD(iso9660, p);
|
||||
if (bid > 0)
|
||||
return (bid);
|
||||
if (*p == '\177') /* End-of-volume-descriptor marker. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* We didn't find a valid PVD; return a bid of zero. */
|
||||
iso9660->bid = 0;
|
||||
return (iso9660->bid);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -365,6 +361,8 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
|
||||
archive_entry_set_mtime(entry, file->mtime, 0);
|
||||
archive_entry_set_ctime(entry, file->ctime, 0);
|
||||
archive_entry_set_atime(entry, file->atime, 0);
|
||||
/* N.B.: Rock Ridge supports 64-bit device numbers. */
|
||||
archive_entry_set_rdev(entry, (dev_t)file->rdev);
|
||||
archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
|
||||
archive_string_empty(&iso9660->pathname);
|
||||
archive_entry_set_pathname(entry,
|
||||
@ -680,6 +678,14 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file,
|
||||
*/
|
||||
break;
|
||||
}
|
||||
if (p[0] == 'P' && p[1] == 'N' && version == 1) {
|
||||
if (data_length == 16) {
|
||||
file->rdev = toi(data,4);
|
||||
file->rdev <<= 32;
|
||||
file->rdev |= toi(data + 8, 4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (p[0] == 'P' && p[1] == 'X' && version == 1) {
|
||||
/*
|
||||
* PX extension comprises:
|
||||
|
705
lib/libarchive/archive_read_support_format_mtree.c
Normal file
705
lib/libarchive/archive_read_support_format_mtree.c
Normal file
@ -0,0 +1,705 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 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(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 "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
/* #include <stdint.h> */ /* See archive_platform.h */
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_read_private.h"
|
||||
#include "archive_string.h"
|
||||
|
||||
struct mtree_entry {
|
||||
struct mtree_entry *next;
|
||||
char *name;
|
||||
char *option_start;
|
||||
char *option_end;
|
||||
char full;
|
||||
char used;
|
||||
};
|
||||
|
||||
struct mtree {
|
||||
struct archive_string line;
|
||||
size_t buffsize;
|
||||
char *buff;
|
||||
off_t offset;
|
||||
int fd;
|
||||
int filetype;
|
||||
int archive_format;
|
||||
const char *archive_format_name;
|
||||
struct mtree_entry *entries;
|
||||
struct mtree_entry *this_entry;
|
||||
struct archive_string current_dir;
|
||||
struct archive_string contents_name;
|
||||
};
|
||||
|
||||
static int cleanup(struct archive_read *);
|
||||
static int mtree_bid(struct archive_read *);
|
||||
static void parse_escapes(char *, struct mtree_entry *);
|
||||
static int parse_setting(struct archive_read *, struct mtree *,
|
||||
struct archive_entry *, char *, char *);
|
||||
static int read_data(struct archive_read *a,
|
||||
const void **buff, size_t *size, off_t *offset);
|
||||
static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t);
|
||||
static int skip(struct archive_read *a);
|
||||
static int read_header(struct archive_read *,
|
||||
struct archive_entry *);
|
||||
static int64_t mtree_atol10(char **);
|
||||
static int64_t mtree_atol8(char **);
|
||||
|
||||
int
|
||||
archive_read_support_format_mtree(struct archive *_a)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct mtree *mtree;
|
||||
int r;
|
||||
|
||||
mtree = (struct mtree *)malloc(sizeof(*mtree));
|
||||
if (mtree == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate mtree data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
memset(mtree, 0, sizeof(*mtree));
|
||||
mtree->fd = -1;
|
||||
|
||||
r = __archive_read_register_format(a, mtree,
|
||||
mtree_bid, read_header, read_data, skip, cleanup);
|
||||
|
||||
if (r != ARCHIVE_OK)
|
||||
free(mtree);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup(struct archive_read *a)
|
||||
{
|
||||
struct mtree *mtree;
|
||||
struct mtree_entry *p, *q;
|
||||
|
||||
mtree = (struct mtree *)(a->format->data);
|
||||
p = mtree->entries;
|
||||
while (p != NULL) {
|
||||
q = p->next;
|
||||
free(p->name);
|
||||
/*
|
||||
* Note: option_start, option_end are pointers into
|
||||
* the block that p->name points to. So we should
|
||||
* not try to free them!
|
||||
*/
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
archive_string_free(&mtree->line);
|
||||
archive_string_free(&mtree->current_dir);
|
||||
archive_string_free(&mtree->contents_name);
|
||||
free(mtree->buff);
|
||||
free(mtree);
|
||||
(a->format->data) = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mtree_bid(struct archive_read *a)
|
||||
{
|
||||
struct mtree *mtree;
|
||||
ssize_t bytes_read;
|
||||
const void *h;
|
||||
const char *signature = "#mtree";
|
||||
const char *p;
|
||||
int bid;
|
||||
|
||||
mtree = (struct mtree *)(a->format->data);
|
||||
|
||||
/* Now let's look at the actual header and see if it matches. */
|
||||
bytes_read = (a->decompressor->read_ahead)(a, &h, strlen(signature));
|
||||
|
||||
if (bytes_read <= 0)
|
||||
return (bytes_read);
|
||||
|
||||
p = h;
|
||||
bid = 0;
|
||||
while (bytes_read > 0 && *signature != '\0') {
|
||||
if (*p != *signature)
|
||||
return (bid = 0);
|
||||
bid += 8;
|
||||
p++;
|
||||
signature++;
|
||||
bytes_read--;
|
||||
}
|
||||
return (bid);
|
||||
}
|
||||
|
||||
/*
|
||||
* The extended mtree format permits multiple lines specifying
|
||||
* attributes for each file. Practically speaking, that means we have
|
||||
* to read the entire mtree file into memory up front.
|
||||
*/
|
||||
static int
|
||||
read_mtree(struct archive_read *a, struct mtree *mtree)
|
||||
{
|
||||
ssize_t len;
|
||||
char *p;
|
||||
struct mtree_entry *mentry;
|
||||
struct mtree_entry *last_mentry = NULL;
|
||||
|
||||
mtree->archive_format = ARCHIVE_FORMAT_MTREE_V1;
|
||||
mtree->archive_format_name = "mtree";
|
||||
|
||||
for (;;) {
|
||||
len = readline(a, mtree, &p, 256);
|
||||
if (len == 0) {
|
||||
mtree->this_entry = mtree->entries;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
if (len < 0)
|
||||
return (len);
|
||||
/* Leading whitespace is never significant, ignore it. */
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
++p;
|
||||
--len;
|
||||
}
|
||||
/* Skip content lines and blank lines. */
|
||||
if (*p == '#')
|
||||
continue;
|
||||
if (*p == '\r' || *p == '\n' || *p == '\0')
|
||||
continue;
|
||||
mentry = malloc(sizeof(*mentry));
|
||||
if (mentry == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
memset(mentry, 0, sizeof(*mentry));
|
||||
/* Add this entry to list. */
|
||||
if (last_mentry == NULL) {
|
||||
last_mentry = mtree->entries = mentry;
|
||||
} else {
|
||||
last_mentry->next = mentry;
|
||||
}
|
||||
last_mentry = mentry;
|
||||
|
||||
/* Copy line over onto heap. */
|
||||
mentry->name = malloc(len + 1);
|
||||
if (mentry->name == NULL) {
|
||||
free(mentry);
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(mentry->name, p);
|
||||
mentry->option_end = mentry->name + len;
|
||||
/* Find end of name. */
|
||||
p = mentry->name;
|
||||
while (*p != ' ' && *p != '\n' && *p != '\0')
|
||||
++p;
|
||||
*p++ = '\0';
|
||||
parse_escapes(mentry->name, mentry);
|
||||
/* Find start of options and record it. */
|
||||
while (p < mentry->option_end && (*p == ' ' || *p == '\t'))
|
||||
++p;
|
||||
mentry->option_start = p;
|
||||
/* Null terminate each separate option. */
|
||||
while (++p < mentry->option_end)
|
||||
if (*p == ' ' || *p == '\t' || *p == '\n')
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
read_header(struct archive_read *a, struct archive_entry *entry)
|
||||
{
|
||||
struct stat st;
|
||||
struct mtree *mtree;
|
||||
struct mtree_entry *mentry, *mentry2;
|
||||
char *p, *q;
|
||||
int r = ARCHIVE_OK, r1;
|
||||
|
||||
mtree = (struct mtree *)(a->format->data);
|
||||
|
||||
if (mtree->fd >= 0) {
|
||||
close(mtree->fd);
|
||||
mtree->fd = -1;
|
||||
}
|
||||
|
||||
if (mtree->entries == NULL) {
|
||||
r = read_mtree(a, mtree);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
}
|
||||
|
||||
a->archive.archive_format = mtree->archive_format;
|
||||
a->archive.archive_format_name = mtree->archive_format_name;
|
||||
|
||||
for (;;) {
|
||||
mentry = mtree->this_entry;
|
||||
if (mentry == NULL) {
|
||||
mtree->this_entry = NULL;
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
mtree->this_entry = mentry->next;
|
||||
if (mentry->used)
|
||||
continue;
|
||||
mentry->used = 1;
|
||||
if (strcmp(mentry->name, "..") == 0) {
|
||||
if (archive_strlen(&mtree->current_dir) > 0) {
|
||||
/* Roll back current path. */
|
||||
p = mtree->current_dir.s
|
||||
+ mtree->current_dir.length - 1;
|
||||
while (p >= mtree->current_dir.s && *p != '/')
|
||||
--p;
|
||||
if (p >= mtree->current_dir.s)
|
||||
--p;
|
||||
mtree->current_dir.length
|
||||
= p - mtree->current_dir.s + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
mtree->filetype = AE_IFREG;
|
||||
|
||||
/* Parse options. */
|
||||
p = mentry->option_start;
|
||||
while (p < mentry->option_end) {
|
||||
q = p + strlen(p);
|
||||
r1 = parse_setting(a, mtree, entry, p, q);
|
||||
if (r1 != ARCHIVE_OK)
|
||||
r = r1;
|
||||
p = q + 1;
|
||||
}
|
||||
|
||||
if (mentry->full) {
|
||||
archive_entry_copy_pathname(entry, mentry->name);
|
||||
/*
|
||||
* "Full" entries are allowed to have multiple
|
||||
* lines and those lines aren't required to be
|
||||
* adjacent. We don't support multiple lines
|
||||
* for "relative" entries nor do we make any
|
||||
* attempt to merge data from separate
|
||||
* "relative" and "full" entries. (Merging
|
||||
* "relative" and "full" entries would require
|
||||
* dealing with pathname canonicalization,
|
||||
* which is a very tricky subject.)
|
||||
*/
|
||||
mentry2 = mentry->next;
|
||||
while (mentry2 != NULL) {
|
||||
if (mentry2->full
|
||||
&& !mentry2->used
|
||||
&& strcmp(mentry->name, mentry2->name) == 0) {
|
||||
/*
|
||||
* Add those options as well;
|
||||
* later lines override
|
||||
* earlier ones.
|
||||
*/
|
||||
p = mentry2->option_start;
|
||||
while (p < mentry2->option_end) {
|
||||
q = p + strlen(p);
|
||||
r1 = parse_setting(a, mtree, entry, p, q);
|
||||
if (r1 != ARCHIVE_OK)
|
||||
r = r1;
|
||||
p = q + 1;
|
||||
}
|
||||
mentry2->used = 1;
|
||||
}
|
||||
mentry2 = mentry2->next;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Relative entries require us to construct
|
||||
* the full path and possibly update the
|
||||
* current directory.
|
||||
*/
|
||||
size_t n = archive_strlen(&mtree->current_dir);
|
||||
if (n > 0)
|
||||
archive_strcat(&mtree->current_dir, "/");
|
||||
archive_strcat(&mtree->current_dir, mentry->name);
|
||||
archive_entry_copy_pathname(entry, mtree->current_dir.s);
|
||||
if (archive_entry_filetype(entry) != AE_IFDIR)
|
||||
mtree->current_dir.length = n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to open and stat the file to get the real size.
|
||||
* It would be nice to avoid this here so that getting
|
||||
* a listing of an mtree wouldn't require opening
|
||||
* every referenced contents file. But then we
|
||||
* wouldn't know the actual contents size, so I don't
|
||||
* see a really viable way around this. (Also, we may
|
||||
* want to someday pull other unspecified info from
|
||||
* the contents file on disk.)
|
||||
*/
|
||||
if (archive_strlen(&mtree->contents_name) > 0) {
|
||||
mtree->fd = open(mtree->contents_name.s, O_RDONLY);
|
||||
if (mtree->fd < 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't open content=\"%s\"",
|
||||
mtree->contents_name.s);
|
||||
r = ARCHIVE_WARN;
|
||||
}
|
||||
} else {
|
||||
/* If the specified path opens, use it. */
|
||||
mtree->fd = open(mtree->current_dir.s, O_RDONLY);
|
||||
/* But don't fail if it's not there. */
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a contents file on disk, use that size;
|
||||
* otherwise leave it as-is (it might have been set from
|
||||
* the mtree size= keyword).
|
||||
*/
|
||||
if (mtree->fd >= 0) {
|
||||
fstat(mtree->fd, &st);
|
||||
archive_entry_set_size(entry, st.st_size);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, char *key, char *end)
|
||||
{
|
||||
char *val;
|
||||
|
||||
|
||||
if (end == key)
|
||||
return (ARCHIVE_OK);
|
||||
if (*key == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
val = strchr(key, '=');
|
||||
if (val == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Malformed attribute \"%s\" (%d)", key, key[0]);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
*val = '\0';
|
||||
++val;
|
||||
|
||||
switch (key[0]) {
|
||||
case 'c':
|
||||
if (strcmp(key, "content") == 0) {
|
||||
parse_escapes(val, NULL);
|
||||
archive_strcpy(&mtree->contents_name, val);
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
if (strcmp(key, "gid") == 0) {
|
||||
archive_entry_set_gid(entry, mtree_atol10(&val));
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "gname") == 0) {
|
||||
archive_entry_copy_gname(entry, val);
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
if (strcmp(key, "mode") == 0) {
|
||||
if (val[0] == '0') {
|
||||
archive_entry_set_perm(entry,
|
||||
mtree_atol8(&val));
|
||||
} else
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Symbolic mode \"%s\" unsupported", val);
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
if (strcmp(key, "type") == 0) {
|
||||
switch (val[0]) {
|
||||
case 'b':
|
||||
if (strcmp(val, "block") == 0) {
|
||||
mtree->filetype = AE_IFBLK;
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
if (strcmp(val, "char") == 0) {
|
||||
mtree->filetype = AE_IFCHR;
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
if (strcmp(val, "dir") == 0) {
|
||||
mtree->filetype = AE_IFDIR;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
if (strcmp(val, "fifo") == 0) {
|
||||
mtree->filetype = AE_IFIFO;
|
||||
break;
|
||||
}
|
||||
if (strcmp(val, "file") == 0) {
|
||||
mtree->filetype = AE_IFREG;
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
if (strcmp(val, "link") == 0) {
|
||||
mtree->filetype = AE_IFLNK;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Unrecognized file type \"%s\"", val);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
archive_entry_set_filetype(entry, mtree->filetype);
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "time") == 0) {
|
||||
archive_entry_set_mtime(entry, mtree_atol10(&val), 0);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
if (strcmp(key, "uid") == 0) {
|
||||
archive_entry_set_uid(entry, mtree_atol10(&val));
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "uname") == 0) {
|
||||
archive_entry_copy_uname(entry, val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Unrecognized key %s=%s", key, val);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset)
|
||||
{
|
||||
ssize_t bytes_read;
|
||||
struct mtree *mtree;
|
||||
|
||||
mtree = (struct mtree *)(a->format->data);
|
||||
if (mtree->fd < 0) {
|
||||
*buff = NULL;
|
||||
*offset = 0;
|
||||
*size = 0;
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
if (mtree->buff == NULL) {
|
||||
mtree->buffsize = 64 * 1024;
|
||||
mtree->buff = malloc(mtree->buffsize);
|
||||
if (mtree->buff == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory");
|
||||
}
|
||||
}
|
||||
|
||||
*buff = mtree->buff;
|
||||
*offset = mtree->offset;
|
||||
bytes_read = read(mtree->fd, mtree->buff, mtree->buffsize);
|
||||
if (bytes_read < 0) {
|
||||
archive_set_error(&a->archive, errno, "Can't read");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
*size = 0;
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
mtree->offset += bytes_read;
|
||||
*size = (size_t)bytes_read;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/* Skip does nothing except possibly close the contents file. */
|
||||
static int
|
||||
skip(struct archive_read *a)
|
||||
{
|
||||
struct mtree *mtree;
|
||||
|
||||
mtree = (struct mtree *)(a->format->data);
|
||||
if (mtree->fd >= 0) {
|
||||
close(mtree->fd);
|
||||
mtree->fd = -1;
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since parsing octal escapes always makes strings shorter,
|
||||
* we can always do this conversion in-place.
|
||||
*/
|
||||
static void
|
||||
parse_escapes(char *src, struct mtree_entry *mentry)
|
||||
{
|
||||
char *dest = src;
|
||||
char c;
|
||||
|
||||
while (*src != '\0') {
|
||||
c = *src++;
|
||||
if (c == '/' && mentry != NULL)
|
||||
mentry->full = 1;
|
||||
if (c == '\\') {
|
||||
if (src[0] >= '0' && src[0] <= '3'
|
||||
&& src[1] >= '0' && src[1] <= '7'
|
||||
&& src[2] >= '0' && src[2] <= '7') {
|
||||
c = (src[0] - '0') << 6;
|
||||
c |= (src[1] - '0') << 3;
|
||||
c |= (src[2] - '0');
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
*dest++ = c;
|
||||
}
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that this implementation does not (and should not!) obey
|
||||
* locale settings; you cannot simply substitute strtol here, since
|
||||
* it does obey locale.
|
||||
*/
|
||||
static int64_t
|
||||
mtree_atol8(char **p)
|
||||
{
|
||||
int64_t l, limit, last_digit_limit;
|
||||
int digit, base;
|
||||
|
||||
base = 8;
|
||||
limit = INT64_MAX / base;
|
||||
last_digit_limit = INT64_MAX % base;
|
||||
|
||||
l = 0;
|
||||
digit = **p - '0';
|
||||
while (digit >= 0 && digit < base) {
|
||||
if (l>limit || (l == limit && digit > last_digit_limit)) {
|
||||
l = INT64_MAX; /* Truncate on overflow. */
|
||||
break;
|
||||
}
|
||||
l = (l * base) + digit;
|
||||
digit = *++(*p) - '0';
|
||||
}
|
||||
return (l);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that this implementation does not (and should not!) obey
|
||||
* locale settings; you cannot simply substitute strtol here, since
|
||||
* it does obey locale.
|
||||
*/
|
||||
static int64_t
|
||||
mtree_atol10(char **p)
|
||||
{
|
||||
int64_t l, limit, last_digit_limit;
|
||||
int base, digit, sign;
|
||||
|
||||
base = 10;
|
||||
limit = INT64_MAX / base;
|
||||
last_digit_limit = INT64_MAX % base;
|
||||
|
||||
if (**p == '-') {
|
||||
sign = -1;
|
||||
++(*p);
|
||||
} else
|
||||
sign = 1;
|
||||
|
||||
l = 0;
|
||||
digit = **p - '0';
|
||||
while (digit >= 0 && digit < base) {
|
||||
if (l > limit || (l == limit && digit > last_digit_limit)) {
|
||||
l = UINT64_MAX; /* Truncate on overflow. */
|
||||
break;
|
||||
}
|
||||
l = (l * base) + digit;
|
||||
digit = *++(*p) - '0';
|
||||
}
|
||||
return (sign < 0) ? -l : l;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns length of line (including trailing newline)
|
||||
* or negative on error. 'start' argument is updated to
|
||||
* point to first character of line.
|
||||
*/
|
||||
static ssize_t
|
||||
readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit)
|
||||
{
|
||||
ssize_t bytes_read;
|
||||
ssize_t total_size = 0;
|
||||
const void *t;
|
||||
const char *s;
|
||||
void *p;
|
||||
|
||||
/* Accumulate line in a line buffer. */
|
||||
for (;;) {
|
||||
/* Read some more. */
|
||||
bytes_read = (a->decompressor->read_ahead)(a, &t, 1);
|
||||
if (bytes_read == 0)
|
||||
return (0);
|
||||
if (bytes_read < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
s = t; /* Start of line? */
|
||||
p = memchr(t, '\n', bytes_read);
|
||||
/* If we found '\n', trim the read. */
|
||||
if (p != NULL) {
|
||||
bytes_read = 1 + ((const char *)p) - s;
|
||||
}
|
||||
if (total_size + bytes_read + 1 > limit) {
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Line too long");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (archive_string_ensure(&mtree->line,
|
||||
total_size + bytes_read + 1) == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate working buffer");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
memcpy(mtree->line.s + total_size, t, bytes_read);
|
||||
(a->decompressor->consume)(a, bytes_read);
|
||||
total_size += bytes_read;
|
||||
/* Null terminate. */
|
||||
mtree->line.s[total_size] = '\0';
|
||||
/* If we found '\n', clean up and return. */
|
||||
if (p != NULL) {
|
||||
*start = mtree->line.s;
|
||||
return (total_size);
|
||||
}
|
||||
}
|
||||
}
|
@ -128,8 +128,8 @@ struct archive_entry_header_gnutar {
|
||||
char isextended[1];
|
||||
char realsize[12];
|
||||
/*
|
||||
* GNU doesn't use POSIX 'prefix' field; they use the 'L' (longname)
|
||||
* entry instead.
|
||||
* Old GNU format doesn't use POSIX 'prefix' field; they use
|
||||
* the 'L' (longname) entry instead.
|
||||
*/
|
||||
};
|
||||
|
||||
@ -164,7 +164,6 @@ struct tar {
|
||||
struct sparse_block *sparse_last;
|
||||
int64_t sparse_offset;
|
||||
int64_t sparse_numbytes;
|
||||
int64_t sparse_realsize;
|
||||
int sparse_gnu_major;
|
||||
int sparse_gnu_minor;
|
||||
char sparse_gnu_pending;
|
||||
@ -279,6 +278,8 @@ archive_read_format_tar_cleanup(struct archive_read *a)
|
||||
archive_string_free(&tar->line);
|
||||
archive_string_free(&tar->pax_global);
|
||||
archive_string_free(&tar->pax_header);
|
||||
archive_string_free(&tar->longname);
|
||||
archive_string_free(&tar->longlink);
|
||||
free(tar->pax_entry);
|
||||
free(tar);
|
||||
(a->format->data) = NULL;
|
||||
@ -319,23 +320,8 @@ archive_read_format_tar_bid(struct archive_read *a)
|
||||
bytes_read = 0; /* Empty file. */
|
||||
if (bytes_read < 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (bytes_read == 0 && bid > 0) {
|
||||
/* An archive without a proper end-of-archive marker. */
|
||||
/* Hold our nose and bid 1 anyway. */
|
||||
return (1);
|
||||
}
|
||||
if (bytes_read < 512) {
|
||||
/* If it's a new archive, then just return a zero bid. */
|
||||
if (bid == 0)
|
||||
return (0);
|
||||
/*
|
||||
* If we already know this is a tar archive,
|
||||
* then we have a problem.
|
||||
*/
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated tar archive");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (bytes_read < 512)
|
||||
return (0);
|
||||
|
||||
/* If it's an end-of-archive mark, we can handle it. */
|
||||
if ((*(const char *)h) == 0 && archive_block_is_null((const unsigned char *)h)) {
|
||||
@ -441,7 +427,7 @@ archive_read_format_tar_read_header(struct archive_read *a,
|
||||
free(sp);
|
||||
}
|
||||
tar->sparse_last = NULL;
|
||||
tar->sparse_realsize = -1; /* Mark this as "unset" */
|
||||
tar->realsize = -1; /* Mark this as "unset" */
|
||||
|
||||
r = tar_read_header(a, tar, entry);
|
||||
|
||||
@ -452,8 +438,6 @@ archive_read_format_tar_read_header(struct archive_read *a,
|
||||
if (tar->sparse_list == NULL)
|
||||
gnu_add_sparse_entry(tar, 0, tar->entry_bytes_remaining);
|
||||
|
||||
tar->realsize = archive_entry_size(entry);
|
||||
|
||||
if (r == ARCHIVE_OK) {
|
||||
/*
|
||||
* "Regular" entry with trailing '/' is really
|
||||
@ -581,16 +565,24 @@ tar_read_header(struct archive_read *a, struct tar *tar,
|
||||
|
||||
/* Read 512-byte header record */
|
||||
bytes = (a->decompressor->read_ahead)(a, &h, 512);
|
||||
if (bytes < 512) {
|
||||
if (bytes < 0)
|
||||
return (bytes);
|
||||
if (bytes == 0) {
|
||||
/*
|
||||
* If we're here, it's becase the _bid function accepted
|
||||
* this file. So just call a short read end-of-archive
|
||||
* and be done with it.
|
||||
* An archive that just ends without a proper
|
||||
* end-of-archive marker. Yes, there are tar programs
|
||||
* that do this; hold our nose and accept it.
|
||||
*/
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
if (bytes < 512) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated tar archive");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
(a->decompressor->consume)(a, 512);
|
||||
|
||||
|
||||
/* Check for end-of-archive mark. */
|
||||
if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) {
|
||||
/* Try to consume a second all-null record, as well. */
|
||||
@ -936,6 +928,7 @@ header_common(struct archive_read *a, struct tar *tar,
|
||||
archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
|
||||
archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
|
||||
tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
|
||||
tar->realsize = tar->entry_bytes_remaining;
|
||||
archive_entry_set_size(entry, tar->entry_bytes_remaining);
|
||||
archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
|
||||
|
||||
@ -1367,9 +1360,10 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
|
||||
tar->sparse_numbytes = -1;
|
||||
}
|
||||
}
|
||||
if (wcscmp(key, L"GNU.sparse.size") == 0)
|
||||
archive_entry_set_size(entry,
|
||||
tar_atol10(value, wcslen(value)));
|
||||
if (wcscmp(key, L"GNU.sparse.size") == 0) {
|
||||
tar->realsize = tar_atol10(value, wcslen(value));
|
||||
archive_entry_set_size(entry, tar->realsize);
|
||||
}
|
||||
|
||||
/* GNU "0.1" sparse pax format. */
|
||||
if (wcscmp(key, L"GNU.sparse.map") == 0) {
|
||||
@ -1391,8 +1385,8 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
|
||||
if (wcscmp(key, L"GNU.sparse.name") == 0)
|
||||
archive_entry_copy_pathname_w(entry, value);
|
||||
if (wcscmp(key, L"GNU.sparse.realsize") == 0) {
|
||||
tar->sparse_realsize = tar_atol10(value, wcslen(value));
|
||||
archive_entry_set_size(entry, tar->sparse_realsize);
|
||||
tar->realsize = tar_atol10(value, wcslen(value));
|
||||
archive_entry_set_size(entry, tar->realsize);
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
@ -1425,6 +1419,10 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
|
||||
archive_entry_set_ino(entry, tar_atol10(value, wcslen(value)));
|
||||
else if (wcscmp(key, L"SCHILY.nlink")==0)
|
||||
archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value)));
|
||||
else if (wcscmp(key, L"SCHILY.realsize")==0) {
|
||||
tar->realsize = tar_atol10(value, wcslen(value));
|
||||
archive_entry_set_size(entry, tar->realsize);
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
if (wcscmp(key, L"atime")==0) {
|
||||
@ -1481,12 +1479,14 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
|
||||
* But, "size" is not necessarily the size of
|
||||
* the file on disk; if this is a sparse file,
|
||||
* the disk size may have already been set from
|
||||
* GNU.sparse.realsize.
|
||||
* GNU.sparse.realsize or GNU.sparse.size or
|
||||
* an old GNU header field or SCHILY.realsize
|
||||
* or ....
|
||||
*/
|
||||
if (tar->sparse_realsize < 0) {
|
||||
if (tar->realsize < 0) {
|
||||
archive_entry_set_size(entry,
|
||||
tar->entry_bytes_remaining);
|
||||
tar->sparse_realsize
|
||||
tar->realsize
|
||||
= tar->entry_bytes_remaining;
|
||||
}
|
||||
}
|
||||
@ -1608,8 +1608,9 @@ header_gnutar(struct archive_read *a, struct tar *tar,
|
||||
archive_entry_set_ctime(entry,
|
||||
tar_atol(header->ctime, sizeof(header->ctime)), 0);
|
||||
if (header->realsize[0] != 0) {
|
||||
archive_entry_set_size(entry,
|
||||
tar_atol(header->realsize, sizeof(header->realsize)));
|
||||
tar->realsize
|
||||
= tar_atol(header->realsize, sizeof(header->realsize));
|
||||
archive_entry_set_size(entry, tar->realsize);
|
||||
}
|
||||
|
||||
if (header->sparse[0].offset[0] != 0) {
|
||||
|
@ -179,18 +179,19 @@ archive_read_format_zip_bid(struct archive_read *a)
|
||||
return (-1);
|
||||
p = (const char *)h;
|
||||
|
||||
/*
|
||||
* Bid of 30 here is: 16 bits for "PK",
|
||||
* next 16-bit field has four options (-2 bits).
|
||||
* 16 + 16-2 = 30.
|
||||
*/
|
||||
if (p[0] == 'P' && p[1] == 'K') {
|
||||
bid += 16;
|
||||
if (p[2] == '\001' && p[3] == '\002')
|
||||
bid += 16;
|
||||
else if (p[2] == '\003' && p[3] == '\004')
|
||||
bid += 16;
|
||||
else if (p[2] == '\005' && p[3] == '\006')
|
||||
bid += 16;
|
||||
else if (p[2] == '\007' && p[3] == '\010')
|
||||
bid += 16;
|
||||
if ((p[2] == '\001' && p[3] == '\002')
|
||||
|| (p[2] == '\003' && p[3] == '\004')
|
||||
|| (p[2] == '\005' && p[3] == '\006')
|
||||
|| (p[2] == '\007' && p[3] == '\010'))
|
||||
return (30);
|
||||
}
|
||||
return (bid);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -765,8 +766,8 @@ process_extra(const void* extra, struct zip* zip)
|
||||
if (flags & 0x01)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "mtime: %d -> %d\n",
|
||||
zip->mtime, i4(p + offset));
|
||||
fprintf(stderr, "mtime: %lld -> %d\n",
|
||||
(long long)zip->mtime, i4(p + offset));
|
||||
#endif
|
||||
if (datasize < 4)
|
||||
break;
|
||||
|
@ -60,7 +60,7 @@ archive_version_stamp(void)
|
||||
const char *
|
||||
archive_version(void)
|
||||
{
|
||||
return (PACKAGE_NAME " " PACKAGE_VERSION);
|
||||
return (ARCHIVE_LIBRARY_VERSION);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -421,6 +421,12 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
|
||||
/* We've created the object and are ready to pour data into it. */
|
||||
if (ret == ARCHIVE_OK)
|
||||
a->archive.state = ARCHIVE_STATE_DATA;
|
||||
/*
|
||||
* If it's not open, tell our client not to try writing.
|
||||
* In particular, dirs, links, etc, don't get written to.
|
||||
*/
|
||||
if (a->fd < 0)
|
||||
archive_entry_set_size(entry, 0);
|
||||
done:
|
||||
/* Restore the user's umask before returning. */
|
||||
umask(a->user_umask);
|
||||
|
@ -320,6 +320,10 @@ drive_compressor(struct archive_write *a, struct private_data *state, int finish
|
||||
state->stream.avail_out = bytes_written;
|
||||
}
|
||||
|
||||
/* If there's nothing to do, we're done. */
|
||||
if (!finishing && state->stream.avail_in == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
ret = BZ2_bzCompress(&(state->stream),
|
||||
finishing ? BZ_FINISH : BZ_RUN);
|
||||
|
||||
@ -339,7 +343,9 @@ drive_compressor(struct archive_write *a, struct private_data *state, int finish
|
||||
/* Any other return value indicates an error */
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_PROGRAMMER,
|
||||
"Bzip2 compression failed");
|
||||
"Bzip2 compression failed;"
|
||||
" BZ2_bzCompress() returned %d",
|
||||
ret);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
|
@ -378,6 +378,10 @@ drive_compressor(struct archive_write *a, struct private_data *state, int finish
|
||||
state->stream.avail_out = bytes_written;
|
||||
}
|
||||
|
||||
/* If there's nothing to do, we're done. */
|
||||
if (!finishing && state->stream.avail_in == 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
ret = deflate(&(state->stream),
|
||||
finishing ? Z_FINISH : Z_NO_FLUSH );
|
||||
|
||||
@ -396,7 +400,9 @@ drive_compressor(struct archive_write *a, struct private_data *state, int finish
|
||||
default:
|
||||
/* Any other return value indicates an error. */
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"GZip compression failed");
|
||||
"GZip compression failed:"
|
||||
" deflate() call returned status %d",
|
||||
ret);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
|
@ -151,11 +151,18 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff,
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
while ((remaining > 0) || (state->avail == 0)) {
|
||||
/*
|
||||
* If we have a full output block, write it and reset the
|
||||
* output buffer.
|
||||
*/
|
||||
/* If the copy buffer isn't empty, try to fill it. */
|
||||
if (state->avail < state->buffer_size) {
|
||||
/* If buffer is not empty... */
|
||||
/* ... copy data into buffer ... */
|
||||
to_copy = (remaining > state->avail) ?
|
||||
state->avail : remaining;
|
||||
memcpy(state->next, buff, to_copy);
|
||||
state->next += to_copy;
|
||||
state->avail -= to_copy;
|
||||
buff += to_copy;
|
||||
remaining -= to_copy;
|
||||
/* ... if it's full, write it out. */
|
||||
if (state->avail == 0) {
|
||||
bytes_written = (a->client_writer)(&a->archive,
|
||||
a->client_data, state->buffer, state->buffer_size);
|
||||
@ -166,16 +173,26 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff,
|
||||
state->next = state->buffer;
|
||||
state->avail = state->buffer_size;
|
||||
}
|
||||
|
||||
/* Now we have space in the buffer; copy new data into it. */
|
||||
to_copy = (remaining > state->avail) ?
|
||||
state->avail : remaining;
|
||||
memcpy(state->next, buff, to_copy);
|
||||
state->next += to_copy;
|
||||
state->avail -= to_copy;
|
||||
buff += to_copy;
|
||||
remaining -= to_copy;
|
||||
}
|
||||
|
||||
while (remaining > state->buffer_size) {
|
||||
/* Write out full blocks directly to client. */
|
||||
bytes_written = (a->client_writer)(&a->archive,
|
||||
a->client_data, buff, state->buffer_size);
|
||||
if (bytes_written <= 0)
|
||||
return (ARCHIVE_FATAL);
|
||||
a->archive.raw_position += bytes_written;
|
||||
buff += bytes_written;
|
||||
remaining -= bytes_written;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
/* Copy last bit into copy buffer. */
|
||||
memcpy(state->next, buff, remaining);
|
||||
state->next += remaining;
|
||||
state->avail -= remaining;
|
||||
}
|
||||
|
||||
a->archive.file_position += length;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ static ssize_t archive_write_ar_data(struct archive_write *,
|
||||
const void *buff, size_t s);
|
||||
static int archive_write_ar_destroy(struct archive_write *);
|
||||
static int archive_write_ar_finish_entry(struct archive_write *);
|
||||
static const char *basename(const char *path);
|
||||
static const char *ar_basename(const char *path);
|
||||
static int format_octal(int64_t v, char *p, int s);
|
||||
static int format_decimal(int64_t v, char *p, int s);
|
||||
|
||||
@ -192,11 +192,11 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
goto size;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Otherwise, entry is a normal archive member.
|
||||
* Strip leading paths from filenames, if any.
|
||||
*/
|
||||
if ((filename = basename(pathname)) == NULL) {
|
||||
if ((filename = ar_basename(pathname)) == NULL) {
|
||||
/* Reject filenames with trailing "/" */
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"Invalid filename");
|
||||
@ -507,7 +507,7 @@ format_decimal(int64_t v, char *p, int s)
|
||||
}
|
||||
|
||||
static const char *
|
||||
basename(const char *path)
|
||||
ar_basename(const char *path)
|
||||
{
|
||||
const char *endp, *startp;
|
||||
|
||||
|
@ -228,6 +228,7 @@ archive_write_cpio_finish(struct archive_write *a)
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
trailer = archive_entry_new();
|
||||
/* nlink = 1 here for GNU cpio compat. */
|
||||
archive_entry_set_nlink(trailer, 1);
|
||||
archive_entry_set_pathname(trailer, "TRAILER!!!");
|
||||
er = archive_write_cpio_header(a, trailer);
|
||||
|
@ -418,12 +418,15 @@ archive_write_pax_header(struct archive_write *a,
|
||||
p = archive_entry_pathname(entry_original);
|
||||
if (p[strlen(p) - 1] != '/') {
|
||||
t = (char *)malloc(strlen(p) + 2);
|
||||
if (t != NULL) {
|
||||
strcpy(t, p);
|
||||
strcat(t, "/");
|
||||
archive_entry_copy_pathname(entry_original, t);
|
||||
free(t);
|
||||
if (t == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate pax data");
|
||||
return(ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(t, p);
|
||||
strcat(t, "/");
|
||||
archive_entry_copy_pathname(entry_original, t);
|
||||
free(t);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -216,12 +216,15 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
p = archive_entry_pathname(entry);
|
||||
if (p[strlen(p) - 1] != '/') {
|
||||
t = (char *)malloc(strlen(p) + 2);
|
||||
if (t != NULL) {
|
||||
strcpy(t, p);
|
||||
strcat(t, "/");
|
||||
archive_entry_copy_pathname(entry, t);
|
||||
free(t);
|
||||
if (t == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate ustar data");
|
||||
return(ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(t, p);
|
||||
strcat(t, "/");
|
||||
archive_entry_copy_pathname(entry, t);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
325
lib/libarchive/cpio.5
Normal file
325
lib/libarchive/cpio.5
Normal file
@ -0,0 +1,325 @@
|
||||
.\" Copyright (c) 2007 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 October 5, 2007
|
||||
.Dt CPIO 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm cpio
|
||||
.Nd format of cpio archive files
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
archive format collects any number of files, directories, and other
|
||||
file system objects (symbolic links, device nodes, etc.) into a single
|
||||
stream of bytes.
|
||||
.Ss General Format
|
||||
Each file system object in a
|
||||
.Nm
|
||||
archive comprises a header record with basic numeric metadata
|
||||
followed by the full pathname of the entry and the file data.
|
||||
The header record stores a series of integer values that generally
|
||||
follow the fields in
|
||||
.Va struct stat .
|
||||
(See
|
||||
.Xr stat 2
|
||||
for details.)
|
||||
The variants differ primarily in how they store those integers
|
||||
(binary, octal, or hexadecimal).
|
||||
The header is followed by the pathname of the
|
||||
entry (the length of the pathname is stored in the header)
|
||||
and any file data.
|
||||
The end of the archive is indicated by a special record with
|
||||
the pathname
|
||||
.Dq TRAILER!!! .
|
||||
.Ss PWB format
|
||||
XXX Any documentation of the original PWB/UNIX 1.0 format? XXX
|
||||
.Ss Old Binary Format
|
||||
The old binary
|
||||
.Nm
|
||||
format stores numbers as 2-byte and 4-byte binary values.
|
||||
Each entry begins with a header in the following format:
|
||||
.Bd -literal -offset indent
|
||||
struct header_old_cpio {
|
||||
unsigned short c_magic;
|
||||
unsigned short c_dev;
|
||||
unsigned short c_ino;
|
||||
unsigned short c_mode;
|
||||
unsigned short c_uid;
|
||||
unsigned short c_gid;
|
||||
unsigned short c_nlink;
|
||||
unsigned short c_rdev;
|
||||
unsigned short c_mtime[2];
|
||||
unsigned short c_namesize;
|
||||
unsigned short c_filesize[2];
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Va unsigned short
|
||||
fields here are 16-bit integer values; the
|
||||
.Va unsigned int
|
||||
fields are 32-bit integer values.
|
||||
The fields are as follows
|
||||
.Bl -tag -width indent
|
||||
.It Va magic
|
||||
The integer value octal 070707.
|
||||
This value can be used to determine whether this archive is
|
||||
written with little-endian or big-endian integers.
|
||||
.It Va dev , Va ino
|
||||
The device and inode numbers from the disk.
|
||||
These are used by programs that read
|
||||
.Nm
|
||||
archives to determine when two entries refer to the same file.
|
||||
Programs that synthesize
|
||||
.Nm
|
||||
archives should be careful to set these to distinct values for each entry.
|
||||
.It Va mode
|
||||
The mode specifies both the regular permissions and the file type.
|
||||
It consists of several bit fields as follows:
|
||||
.Bl -tag -width "MMMMMMM" -compact
|
||||
.It 0170000
|
||||
This masks the file type bits.
|
||||
.It 0140000
|
||||
File type value for sockets.
|
||||
.It 0120000
|
||||
File type value for symbolic links.
|
||||
For symbolic links, the link body is stored as file data.
|
||||
.It 0100000
|
||||
File type value for regular files.
|
||||
.It 0060000
|
||||
File type value for block special devices.
|
||||
.It 0040000
|
||||
File type value for directories.
|
||||
.It 0020000
|
||||
File type value for character special devices.
|
||||
.It 0010000
|
||||
File type value for named pipes or FIFOs.
|
||||
.It 0004000
|
||||
SUID bit.
|
||||
.It 0002000
|
||||
SGID bit.
|
||||
.It 0001000
|
||||
Sticky bit.
|
||||
On some systems, this modifies the behavior of executables and/or directories.
|
||||
.It 0000777
|
||||
The lower 9 bits specify read/write/execute permissions
|
||||
for world, group, and user following standard POSIX conventions.
|
||||
.El
|
||||
.It Va uid , Va gid
|
||||
The numeric user id and group id of the owner.
|
||||
.It Va nlink
|
||||
The number of links to this file.
|
||||
Directories always have a value of at least two here.
|
||||
Note that hardlinked files include file data with every copy in the archive.
|
||||
.It Va rdev
|
||||
For block special and character special entries,
|
||||
this field contains the associated device number.
|
||||
For all other entry types, it should be set to zero by writers
|
||||
and ignored by readers.
|
||||
.It Va mtime
|
||||
Modification time of the file, indicated as the number
|
||||
of seconds since the start of the epoch,
|
||||
00:00:00 UTC January 1, 1970.
|
||||
The four-byte integer is stored with the most-significant 16 bits first
|
||||
followed by the least-significant 16 bits.
|
||||
Each of the two 16 bit values are stored in machine-native byte order.
|
||||
.It Va namesize
|
||||
The number of bytes in the pathname that follows the header.
|
||||
This count includes the trailing NULL byte.
|
||||
.It Va filesize
|
||||
The size of the file.
|
||||
Note that this archive format is limited to
|
||||
four gigabyte file sizes.
|
||||
See
|
||||
.Va mtime
|
||||
above for a description of the storage of four-byte integers.
|
||||
.El
|
||||
.Pp
|
||||
The pathname immediately follows the fixed header.
|
||||
If the
|
||||
.Cm namesize
|
||||
is odd, an additional NULL byte is added after the pathname.
|
||||
The file data is then appended, padded with NULL
|
||||
bytes to an even length.
|
||||
.Pp
|
||||
Hardlinked files are not given special treatment;
|
||||
the full file contents are included with each copy of the
|
||||
file.
|
||||
.Ss Portable ASCII Format
|
||||
.St -susv2
|
||||
standardized an ASCII variant that is portable across all
|
||||
platforms.
|
||||
It is commonly known as the
|
||||
.Dq old character
|
||||
format or as the
|
||||
.Dq odc
|
||||
format.
|
||||
It stores the same numeric fields as the old binary format, but
|
||||
represents them as 6-character or 11-character octal values.
|
||||
.Bd -literal -offset indent
|
||||
struct cpio_odc_header {
|
||||
char c_magic[6];
|
||||
char c_dev[6];
|
||||
char c_ino[6];
|
||||
char c_mode[6];
|
||||
char c_uid[6];
|
||||
char c_gid[6];
|
||||
char c_nlink[6];
|
||||
char c_rdev[6];
|
||||
char c_mtime[11];
|
||||
char c_namesize[6];
|
||||
char c_filesize[11];
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The fields are identical to those in the old binary format.
|
||||
The name and file body follow the fixed header.
|
||||
Unlike the old binary format, there is no additional padding
|
||||
after the pathname or file contents.
|
||||
If the files being archived are themselves entirely ASCII, then
|
||||
the resulting archive will be entirely ASCII, except for the
|
||||
NULL byte that terminates the name field.
|
||||
.Ss New ASCII Format
|
||||
The "new" ASCII format uses 8-byte hexadecimal fields for
|
||||
all numbers and separates device numbers into separate fields
|
||||
for major and minor numbers.
|
||||
.Bd -literal -offset indent
|
||||
struct cpio_newc_header {
|
||||
char c_magic[6];
|
||||
char c_ino[8];
|
||||
char c_mode[8];
|
||||
char c_uid[8];
|
||||
char c_gid[8];
|
||||
char c_nlink[8];
|
||||
char c_mtime[8];
|
||||
char c_filesize[8];
|
||||
char c_devmajor[8];
|
||||
char c_devminor[8];
|
||||
char c_rdevmajor[8];
|
||||
char c_rdevminor[8];
|
||||
char c_namesize[8];
|
||||
char c_check[8];
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
Except as specified below, the fields here match those specified
|
||||
for the old binary format above.
|
||||
.Bl -tag -width indent
|
||||
.It Va magic
|
||||
The string
|
||||
.Dq 070701 .
|
||||
.It Va check
|
||||
This field is always set to zero by writers and ignored by readers.
|
||||
See the next section for more details.
|
||||
.El
|
||||
.Pp
|
||||
The pathname is followed by NULL bytes so that the total size
|
||||
of the fixed header plus pathname is a multiple of four.
|
||||
Likewise, the file data is padded to a multiple of four bytes.
|
||||
Note that this format supports only 4 gigabyte files (unlike the
|
||||
older ASCII format, which supports 8 gigabyte files).
|
||||
.Pp
|
||||
In this format, hardlinked files are handled by setting the
|
||||
filesize to zero for each entry except the last one that
|
||||
appears in the archive.
|
||||
.Ss New CRC Format
|
||||
The CRC format is identical to the new ASCII format described
|
||||
in the previous section except that the magic field is set
|
||||
to
|
||||
.Dq 070702
|
||||
and the
|
||||
.Va check
|
||||
field is set to the sum of all bytes in the file data.
|
||||
This sum is computed treating all bytes as unsigned values
|
||||
and using unsigned arithmetic.
|
||||
Only the least-significant 32 bits of the sum are stored.
|
||||
.Ss HP variants
|
||||
The
|
||||
.Nm cpio
|
||||
implementation distributed with HPUX used XXXX but stored
|
||||
device numbers differently XXX.
|
||||
.Ss Other Extensions and Variants
|
||||
Sun Solaris uses additional file types to store extended file
|
||||
data, including ACLs and extended attributes, as special
|
||||
entries in cpio archives.
|
||||
.Pp
|
||||
XXX Others? XXX
|
||||
.Sh BUGS
|
||||
The
|
||||
.Dq CRC
|
||||
format is mis-named, as it uses a simple checksum and
|
||||
not a cyclic redundancy check.
|
||||
.Pp
|
||||
The old binary format is limited to 16 bits for user id,
|
||||
group id, device, and inode numbers.
|
||||
It is limited to 4 gigabyte file sizes.
|
||||
.Pp
|
||||
The old ASCII format is limited to 18 bits for
|
||||
the user id, group id, device, and inode numbers.
|
||||
It is limited to 8 gigabyte file sizes.
|
||||
.Pp
|
||||
The new ASCII format is limited to 4 gigabyte file sizes.
|
||||
.Pp
|
||||
None of the cpio formats store user or group names,
|
||||
which are essential when moving files between systems with
|
||||
dissimilar user or group numbering.
|
||||
.Pp
|
||||
Especially when writing older cpio variants, it may be necessary
|
||||
to map actual device/inode values to synthesized values that
|
||||
fit the available fields.
|
||||
With very large filesystems, this may be necessary even for
|
||||
the newer formats.
|
||||
.Sh SEE ALSO
|
||||
.Xr cpio 1 ,
|
||||
.Xr tar 5
|
||||
.Sh STANDARDS
|
||||
The
|
||||
.Nm cpio
|
||||
utility is no longer a part of POSIX or the Single Unix Standard.
|
||||
It last appeared in
|
||||
.St -susv2 .
|
||||
It has been supplanted in subsequent standards by
|
||||
.Xr pax 1 .
|
||||
The portable ASCII format is currently part of the specification for the
|
||||
.Xr pax 1
|
||||
utility.
|
||||
.Sh HISTORY
|
||||
The original cpio utility was written by Dick Haight
|
||||
while working in AT&T's Unix Support Group.
|
||||
It appeared in 1977 as part of PWB/UNIX 1.0, the
|
||||
.Dq Programmer's Work Bench
|
||||
derived from
|
||||
.At v6
|
||||
that was used internally at AT&T.
|
||||
Both the old binary and old character formats were in use
|
||||
by 1980, according to the System III source released
|
||||
by SCO under their
|
||||
.Dq Ancient Unix
|
||||
license.
|
||||
The character format was adopted as part of
|
||||
.St -p1003.1-88 .
|
||||
XXX when did "newc" appear? Who invented it? When did HP come out with their variant? When did Sun introduce ACLs and extended attributes? XXX
|
@ -30,6 +30,8 @@ __FBSDID("$FreeBSD$");
|
||||
#if defined(HAVE_POLL)
|
||||
# if defined(HAVE_POLL_H)
|
||||
# include <poll.h>
|
||||
# elif defined(HAVE_SYS_POLL_H)
|
||||
# include <sys/poll.h>
|
||||
# endif
|
||||
#elif defined(HAVE_SELECT)
|
||||
# if defined(HAVE_SYS_SELECT_H)
|
||||
|
@ -74,6 +74,8 @@ It currently supports the most popular GNU extensions, including
|
||||
modern long filename and linkname support, as well as atime and ctime data.
|
||||
The libarchive library does not support multi-volume
|
||||
archives, nor the old GNU long filename format.
|
||||
It can read GNU sparse file entries, including the new POSIX-based
|
||||
formats, but cannot write GNU sparse file entries.
|
||||
.It Cm pax
|
||||
The
|
||||
.Xr libarchive 3
|
||||
@ -93,7 +95,7 @@ defined by Joerg Schilling's
|
||||
.Dq star
|
||||
archiver.
|
||||
The libarchive library can read most of the SCHILY keys.
|
||||
It ignores any keywords that it does not understand.
|
||||
It silently ignores any keywords that it does not understand.
|
||||
.It Cm restricted pax
|
||||
The libarchive library can also write pax archives in which it
|
||||
attempts to suppress the extended attributes entry whenever
|
||||
@ -139,19 +141,18 @@ In particular, it supports base-256 values in certain numeric fields.
|
||||
This essentially removes the limitations on file size, modification time,
|
||||
and device numbers.
|
||||
.Pp
|
||||
The first tar program appeared in Sixth Edition Unix (circa 1976).
|
||||
This makes the tar format one of the oldest and most widely-supported
|
||||
archive formats.
|
||||
The first tar program appeared in Seventh Edition Unix in 1979.
|
||||
The first official standard for the tar file format was the
|
||||
.Dq ustar
|
||||
(Unix Standard Tar) format defined by POSIX in 1988.
|
||||
POSIX.1-2001 extended the ustar format to create the
|
||||
.Dq pax interchange
|
||||
format.
|
||||
There have also been many custom variations.
|
||||
.Ss Cpio Formats
|
||||
The libarchive library can read a number of common cpio variants and can write
|
||||
.Dq odc
|
||||
and
|
||||
.Dq newc
|
||||
format archives.
|
||||
A cpio archive stores each entry as a fixed-size header followed
|
||||
by a variable-length filename and variable-length data.
|
||||
@ -184,15 +185,26 @@ The SVR4 format can optionally include a CRC of the file
|
||||
contents, although libarchive does not currently verify this CRC.
|
||||
.El
|
||||
.Pp
|
||||
Cpio is an old format that was widely used because of its simplicity
|
||||
and its support for very long filenames.
|
||||
Unfortunately, it has many limitations that make it unsuitable
|
||||
Cpio first appeared in PWB/UNIX 1.0, which was released within
|
||||
AT&T in 1977.
|
||||
PWB/UNIX 1.0 formed the basis of System III Unix, released outside
|
||||
of AT&T in 1981.
|
||||
This makes cpio older than tar, although cpio was not included
|
||||
in Version 7 AT&T Unix.
|
||||
As a result, the tar command became much better known in universities
|
||||
and research groups that used Version 7.
|
||||
The combination of the
|
||||
.Nm find
|
||||
and
|
||||
.Nm cpio
|
||||
utilities provided very precise control over file selection.
|
||||
Unfortunately, the format has many limitations that make it unsuitable
|
||||
for widespread use.
|
||||
Only the POSIX format permits files over 4GB, and its 18-bit
|
||||
limit for most other fields makes it unsuitable for modern systems.
|
||||
In addition, cpio formats only store numeric UID/GID values (not
|
||||
usernames and group names), which can make it very difficult to correctly
|
||||
transfer archives across systems.
|
||||
transfer archives across systems with dissimilar user numbering.
|
||||
.Ss Shar Formats
|
||||
A
|
||||
.Dq shell archive
|
||||
@ -255,4 +267,6 @@ Libarchive provides read and write support for both variants.
|
||||
.Xr tar 1 ,
|
||||
.Xr zip 1 ,
|
||||
.Xr zlib 3 ,
|
||||
.Xr cpio 5 ,
|
||||
.Xr mtree 5 ,
|
||||
.Xr tar 5
|
||||
|
@ -96,12 +96,9 @@ When opening the archive, it reads an initial block of data
|
||||
and offers it to each registered compression handler.
|
||||
The one with the highest bid is initialized with the first block.
|
||||
Similarly, the format handlers are polled to see which handler
|
||||
is the best for each header request.
|
||||
(Note that a single file can have entries handled by different
|
||||
format handlers;
|
||||
this allows a simple handler for a generic version of a format
|
||||
with more complex handlers implemented independently for
|
||||
extended sub-formats.)
|
||||
is the best for each archive.
|
||||
(Prior to 2.4.0, the format bidders were invoked for each
|
||||
entry, but this design hindered error recovery.)
|
||||
.Ss I/O Layer and Client Callbacks
|
||||
The read API goes to some lengths to be nice to clients.
|
||||
As a result, there are few restrictions on the behavior of
|
||||
@ -116,6 +113,8 @@ In particular, blocks may be of different sizes.
|
||||
The client skip callback returns the number of bytes actually
|
||||
skipped, which may be much smaller than the skip requested.
|
||||
The only requirement is that the skip not be larger.
|
||||
In particular, clients are allowed to return zero for any
|
||||
skip that they don't want to handle.
|
||||
The skip callback must never be invoked with a negative value.
|
||||
.Pp
|
||||
Keep in mind that not all clients are reading from disk:
|
||||
@ -242,21 +241,12 @@ decompression method but not calling the
|
||||
method.
|
||||
This allows each bidder to look ahead in the input stream.
|
||||
Bidders should not look further ahead than necessary, as long
|
||||
look aheads put pressure on the compression layer to buffer
|
||||
look aheads put pressure on the decompression layer to buffer
|
||||
lots of data.
|
||||
Most formats only require a few hundred bytes of look ahead;
|
||||
look aheads of a few kilobytes are reasonable.
|
||||
(The ISO9660 reader sometimes looks ahead by 48k, which
|
||||
should be considered an upper limit.)
|
||||
Note that the bidder is invoked for every entry.
|
||||
For many formats, this is inappropriate; if you can only bid at
|
||||
the beginning of the file, store your bid value and check that
|
||||
each time your bid function is called.
|
||||
For example, the ISO9660 reader initializes a
|
||||
.Va bid
|
||||
value to -1 at registration time;
|
||||
each time the bid function is called, the bid value is returned
|
||||
immediately if it is zero or larger.
|
||||
.It Read header
|
||||
The header read is usually the most complex part of any format.
|
||||
There are a few strategies worth mentioning:
|
||||
|
270
lib/libarchive/mtree.5
Normal file
270
lib/libarchive/mtree.5
Normal file
@ -0,0 +1,270 @@
|
||||
.\" Copyright (c) 1989, 1990, 1993
|
||||
.\" The Regents of the University of California. 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.
|
||||
.\" 4. Neither the name of the University nor the names of its contributors
|
||||
.\" may be used to endorse or promote products derived from this software
|
||||
.\" without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
.\"
|
||||
.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 20, 2007
|
||||
.Dt MTREE 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mtree
|
||||
.Nd format of mtree dir heirarchy files
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
format is a textual format that describes a collection of filesystem objects.
|
||||
Such files are typically used to create or verify directory heirarchies.
|
||||
.Ss General Format
|
||||
An
|
||||
.Nm
|
||||
file consists of a series of lines, each providing information
|
||||
about a single filesystem object.
|
||||
Leading whitespace is always ignored.
|
||||
.Pp
|
||||
When encoding file or pathnames, any backslash character or
|
||||
character outside of the 95 printable ASCII characters must be
|
||||
encoded as a a backslash followed by three
|
||||
octal digits.
|
||||
When reading mtree files, any appearance of a backslash
|
||||
followed by three octal digits should be converted into the
|
||||
corresponding character.
|
||||
.Pp
|
||||
Each line is interpreted independently as one of the following types:
|
||||
.Bl -tag -width Cm
|
||||
.It Signature
|
||||
The first line of any mtree file must begin with
|
||||
.Dq #mtree .
|
||||
If a file contains any full path entries, the first line should
|
||||
begin with
|
||||
.Dq #mtree v2.0 ,
|
||||
otherwise, the first line should begin with
|
||||
.Dq #mtree v1.0 .
|
||||
.It Blank
|
||||
Blank lines are ignored.
|
||||
.It Comment
|
||||
Lines beginning with
|
||||
.Cm #
|
||||
are ignored.
|
||||
.It Special
|
||||
Lines beginning with
|
||||
.Cm /
|
||||
are special commands that influence
|
||||
the interpretation of later lines.
|
||||
.It Relative
|
||||
If the first whitespace-delimited word has no
|
||||
.Cm /
|
||||
characters,
|
||||
it is the name of a file in the current directory.
|
||||
Any relative entry that describes a directory changes the
|
||||
current directory.
|
||||
.It dot-dot
|
||||
As a special case, a relative entry with the filename
|
||||
.Pa ..
|
||||
changes the current directory to the parent directory.
|
||||
Options on dot-dot entries are always ignored.
|
||||
.It Full
|
||||
If the first whitespace-delimited word has a
|
||||
.Cm /
|
||||
character after
|
||||
the first character, it is the pathname of a file relative to the
|
||||
starting directory.
|
||||
There can be multiple full entries describing the same file.
|
||||
.El
|
||||
.Pp
|
||||
Some tools that process
|
||||
.Nm
|
||||
files may require that multiple lines describing the same file
|
||||
occur consecutively.
|
||||
It is not permitted for the same file to be mentioned using
|
||||
both a relative and a full file specification.
|
||||
.Ss Special commands
|
||||
Two special commands are currently defined:
|
||||
.Bl -tag -width Cm
|
||||
.It Cm /set
|
||||
This command defines default values for one or more keywords.
|
||||
It is followed on the same line by one or more whitespace-separated
|
||||
keyword definitions.
|
||||
These definitions apply to all following files that do not specify
|
||||
a value for that keyword.
|
||||
.It Cm /unset
|
||||
This command removes any default value set by a previous
|
||||
.Cm /set
|
||||
command.
|
||||
It is followed on the same line by one or more keywords
|
||||
separated by whitespace.
|
||||
.El
|
||||
.Ss Keywords
|
||||
After the filename, a full or relative entry consists of zero
|
||||
or more whitespace-separated keyword definitions.
|
||||
Each such definitions consists of a key from the following
|
||||
list immediately followed by an '=' sign
|
||||
and a value.
|
||||
Software programs reading mtree files should warn about
|
||||
unrecognized keywords.
|
||||
.Pp
|
||||
Currently supported keywords are as follows:
|
||||
.Bl -tag -width Cm
|
||||
.It Cm cksum
|
||||
The checksum of the file using the default algorithm specified by
|
||||
the
|
||||
.Xr cksum 1
|
||||
utility.
|
||||
.It Cm contents
|
||||
The full pathname of a file whose contents should be
|
||||
compared to the contents of this file.
|
||||
.It Cm flags
|
||||
The file flags as a symbolic name.
|
||||
See
|
||||
.Xr chflags 1
|
||||
for information on these names.
|
||||
If no flags are to be set the string
|
||||
.Dq none
|
||||
may be used to override the current default.
|
||||
.It Cm ignore
|
||||
Ignore any file hierarchy below this file.
|
||||
.It Cm gid
|
||||
The file group as a numeric value.
|
||||
.It Cm gname
|
||||
The file group as a symbolic name.
|
||||
.It Cm md5
|
||||
The MD5 message digest of the file.
|
||||
.It Cm md5digest
|
||||
A synonym for
|
||||
.Cm md5 .
|
||||
.It Cm sha1
|
||||
The
|
||||
.Tn FIPS
|
||||
160-1
|
||||
.Pq Dq Tn SHA-1
|
||||
message digest of the file.
|
||||
.It Cm sha1digest
|
||||
A synonym for
|
||||
.Cm sha1 .
|
||||
.It Cm sha256
|
||||
The
|
||||
.Tn FIPS
|
||||
180-2
|
||||
.Pq Dq Tn SHA-256
|
||||
message digest of the file.
|
||||
.It Cm sha256digest
|
||||
A synonym for
|
||||
.Cm sha256 .
|
||||
.It Cm ripemd160digest
|
||||
The
|
||||
.Tn RIPEMD160
|
||||
message digest of the file.
|
||||
.It Cm rmd160
|
||||
A synonym for
|
||||
.Cm ripemd160digest .
|
||||
.It Cm rmd160digest
|
||||
A synonym for
|
||||
.Cm ripemd160digest .
|
||||
.It Cm mode
|
||||
The current file's permissions as a numeric (octal) or symbolic
|
||||
value.
|
||||
.It Cm nlink
|
||||
The number of hard links the file is expected to have.
|
||||
.It Cm nochange
|
||||
Make sure this file or directory exists but otherwise ignore all attributes.
|
||||
.It Cm uid
|
||||
The file owner as a numeric value.
|
||||
.It Cm uname
|
||||
The file owner as a symbolic name.
|
||||
.It Cm size
|
||||
The size, in bytes, of the file.
|
||||
.It Cm link
|
||||
The file the symbolic link is expected to reference.
|
||||
.It Cm time
|
||||
The last modification time of the file.
|
||||
.It Cm type
|
||||
The type of the file; may be set to any one of the following:
|
||||
.Pp
|
||||
.Bl -tag -width Cm -compact
|
||||
.It Cm block
|
||||
block special device
|
||||
.It Cm char
|
||||
character special device
|
||||
.It Cm dir
|
||||
directory
|
||||
.It Cm fifo
|
||||
fifo
|
||||
.It Cm file
|
||||
regular file
|
||||
.It Cm link
|
||||
symbolic link
|
||||
.It Cm socket
|
||||
socket
|
||||
.El
|
||||
.El
|
||||
.Pp
|
||||
.Sh SEE ALSO
|
||||
.Xr cksum 1 ,
|
||||
.Xr find 1 ,
|
||||
.Xr mtree 8
|
||||
.Sh BUGS
|
||||
The
|
||||
.Fx
|
||||
implementation of mtree does not currently support
|
||||
the
|
||||
.Nm
|
||||
2.0
|
||||
format.
|
||||
The requirement for a
|
||||
.Dq #mtree
|
||||
signature line is new and not yet widely implemented.
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
utility appeared in
|
||||
.Bx 4.3 Reno .
|
||||
The
|
||||
.Tn MD5
|
||||
digest capability was added in
|
||||
.Fx 2.1 ,
|
||||
in response to the widespread use of programs which can spoof
|
||||
.Xr cksum 1 .
|
||||
The
|
||||
.Tn SHA-1
|
||||
and
|
||||
.Tn RIPEMD160
|
||||
digests were added in
|
||||
.Fx 4.0 ,
|
||||
as new attacks have demonstrated weaknesses in
|
||||
.Tn MD5 .
|
||||
The
|
||||
.Tn SHA-256
|
||||
digest was added in
|
||||
.Fx 6.0 .
|
||||
Support for file flags was added in
|
||||
.Fx 4.0 ,
|
||||
and mostly comes from
|
||||
.Nx .
|
||||
The
|
||||
.Dq full
|
||||
entry format was added by
|
||||
.Nx .
|
Loading…
Reference in New Issue
Block a user