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:
Tim Kientzle 2007-12-30 04:58:22 +00:00
parent 79386ec7db
commit 9dd49f960f
31 changed files with 2119 additions and 246 deletions

View File

@ -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>

View File

@ -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 *);

View File

@ -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)
{

View File

@ -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 */

View 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);
}

View File

@ -149,6 +149,8 @@ struct archive_entry {
struct ae_xattr *xattr_head;
struct ae_xattr *xattr_p;
char strmode[11];
};

View 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);
}

View File

@ -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 */

View File

@ -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);
/*

View File

@ -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);
}
/*

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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:

View 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);
}
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -60,7 +60,7 @@ archive_version_stamp(void)
const char *
archive_version(void)
{
return (PACKAGE_NAME " " PACKAGE_VERSION);
return (ARCHIVE_LIBRARY_VERSION);
}
int

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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
View 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

View File

@ -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)

View File

@ -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

View File

@ -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
View 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 .