MFp4: libarchive 2.5.4b. (Still 'b' until I get a bit more
feedback, but the 2.5 branch is shaping up nicely.) In addition to many small bug fixes and code improvements: * Another iteration of versioning; I think I've got it right now. * Portability: A lot of progress on Windows support (though I'm not committing all of the Windows support files to FreeBSD CVS) * Explicit tracking of MBS, WCS, and UTF-8 versions of strings in archive_entry; the archive_entry routines now correctly return NULL only when something is unset, setting NULL properly clears string values. Most charset conversions have been pushed down to archive_string. * Better handling of charset conversion failure when writing or reading UTF-8 headers in pax archives * archive_entry_linkify() provides multiple strategies for hardlink matching to suit different format expectations * More accurate bzip2 format detection * Joerg Sonnenberger's extensive improvements to mtree support * Rough support for self-extracting ZIP archives. Not an ideal approach, but it works for the archives I've tried. * New "sparsify" option in archive_write_disk converts blocks of nulls into seeks. * Better default behavior for the test harness; it now reports all failures by default instead of coredumping at the first one.
This commit is contained in:
parent
037dab5792
commit
fa07de5eeb
@ -4,19 +4,8 @@ LIB= archive
|
||||
DPADD= ${LIBBZ2} ${LIBZ}
|
||||
LDADD= -lbz2 -lz
|
||||
|
||||
# The libarchive version stamp.
|
||||
# Version is three numbers:
|
||||
# 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
|
||||
|
||||
# The useful version number (one integer, easy to compare)
|
||||
LIBARCHIVE_VERSION= 2004012
|
||||
# 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.
|
||||
# It has no real relation to the libarchive version number.
|
||||
SHLIB_MAJOR= 4
|
||||
|
||||
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
|
||||
@ -27,15 +16,9 @@ WARNS?= 6
|
||||
# Headers to be installed in /usr/include
|
||||
INCS= archive.h archive_entry.h
|
||||
|
||||
# Build archive.h from archive.h.in by substituting version information.
|
||||
# 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/@LIBARCHIVE_VERSION@/${LIBARCHIVE_VERSION}/g' \
|
||||
-e 's/@LIBARCHIVE_VERSION_STRING@/${LIBARCHIVE_VERSION_STRING}/g' \
|
||||
-e 's/@SHLIB_MAJOR@/${SHLIB_MAJOR}/g' \
|
||||
-e 's|@ARCHIVE_H_INCLUDE_INTTYPES_H@|#include <inttypes.h> /* For int64_t */|g' \
|
||||
> archive.h
|
||||
# For now, archive.h is the same as archive.h.in.
|
||||
archive.h: archive.h.in
|
||||
cat ${.CURDIR}/archive.h.in > archive.h
|
||||
|
||||
# archive.h needs to be cleaned
|
||||
CLEANFILES+= archive.h
|
||||
@ -117,6 +100,7 @@ MLINKS+= archive_entry.3 archive_entry_acl_reset.3
|
||||
MLINKS+= archive_entry.3 archive_entry_acl_text_w.3
|
||||
MLINKS+= archive_entry.3 archive_entry_clear.3
|
||||
MLINKS+= archive_entry.3 archive_entry_clone.3
|
||||
MLINKS+= archive_entry.3 archive_entry_copy_fflags_text.3
|
||||
MLINKS+= archive_entry.3 archive_entry_copy_fflags_text_w.3
|
||||
MLINKS+= archive_entry.3 archive_entry_copy_gname.3
|
||||
MLINKS+= archive_entry.3 archive_entry_copy_gname_w.3
|
||||
@ -232,6 +216,7 @@ MLINKS+= archive_write.3 archive_write_set_bytes_in_last_block.3
|
||||
MLINKS+= archive_write.3 archive_write_set_bytes_per_block.3
|
||||
MLINKS+= archive_write.3 archive_write_set_callbacks.3
|
||||
MLINKS+= archive_write.3 archive_write_set_compression_bzip2.3
|
||||
MLINKS+= archive_write.3 archive_write_set_compression_compress.3
|
||||
MLINKS+= archive_write.3 archive_write_set_compression_gzip.3
|
||||
MLINKS+= archive_write.3 archive_write_set_compression_none.3
|
||||
MLINKS+= archive_write.3 archive_write_set_compression_program.3
|
||||
@ -246,7 +231,8 @@ 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
|
||||
|
||||
check:
|
||||
.PHONY: check test
|
||||
check test:
|
||||
cd ${.CURDIR}/test && make test
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -28,17 +28,58 @@
|
||||
#ifndef ARCHIVE_H_INCLUDED
|
||||
#define ARCHIVE_H_INCLUDED
|
||||
|
||||
/*
|
||||
* Note: archive.h is for use outside of libarchive; the configuration
|
||||
* headers (config.h, archive_platform.h, etc.) are purely internal.
|
||||
* Do NOT use HAVE_XXX configuration macros to control the behavior of
|
||||
* this header! If you must conditionalize, use predefined compiler and/or
|
||||
* platform macros.
|
||||
*/
|
||||
|
||||
#include <sys/types.h> /* Linux requires this for off_t */
|
||||
@ARCHIVE_H_INCLUDE_INTTYPES_H@
|
||||
#include <stdio.h> /* For FILE * */
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h> /* For ssize_t and size_t */
|
||||
#else
|
||||
typedef long ssize_t;
|
||||
typedef unsigned int uid_t;
|
||||
typedef unsigned int gid_t;
|
||||
typedef unsigned short mode_t;
|
||||
#if !defined(__WATCOMC__) && !defined(_MSC_VER)
|
||||
/* Header unavailable on Watcom C or MS Visual C++. */
|
||||
#include <inttypes.h> /* int64_t, etc. */
|
||||
#endif
|
||||
#include <stdio.h> /* For FILE * */
|
||||
|
||||
/* Get appropriate definitions of standard POSIX-style types. */
|
||||
/* These should match the types used in 'struct stat' */
|
||||
#ifdef _WIN32
|
||||
#define __LA_SSIZE_T long
|
||||
#define __LA_UID_T unsigned int
|
||||
#define __LA_GID_T unsigned int
|
||||
#else
|
||||
#include <unistd.h> /* ssize_t, uid_t, and gid_t */
|
||||
#define __LA_SSIZE_T ssize_t
|
||||
#define __LA_UID_T uid_t
|
||||
#define __LA_GID_T gid_t
|
||||
#endif
|
||||
|
||||
/*
|
||||
* On Windows, define LIBARCHIVE_STATIC if you're building or using a
|
||||
* .lib. The default here assumes you're building a DLL. Only
|
||||
* libarchive source should ever define __LIBARCHIVE_BUILD.
|
||||
*/
|
||||
#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC)
|
||||
# ifdef __LIBARCHIVE_BUILD
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllexport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllexport)
|
||||
# endif
|
||||
# else
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllimport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
/* Static libraries or non-Windows needs no special declaration. */
|
||||
# define __LA_DECL
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -48,7 +89,9 @@ extern "C" {
|
||||
* The version number is provided as both a macro and a function.
|
||||
* The macro identifies the installed header; the function identifies
|
||||
* the library version (which may not be the same if you're using a
|
||||
* dynamically-linked version of the library).
|
||||
* dynamically-linked version of the library). Of course, if the
|
||||
* header and library are very different, you should expect some
|
||||
* strangeness. Don't do that.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -69,13 +112,14 @@ extern "C" {
|
||||
* (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
|
||||
* #endif
|
||||
*/
|
||||
#define ARCHIVE_VERSION_NUMBER @LIBARCHIVE_VERSION@
|
||||
int archive_version_number(void);
|
||||
#define ARCHIVE_VERSION_NUMBER 2005004
|
||||
__LA_DECL int archive_version_number(void);
|
||||
|
||||
/*
|
||||
* Textual name/version of the library, useful for version displays.
|
||||
*/
|
||||
const char * archive_version_string(void);
|
||||
#define ARCHIVE_VERSION_STRING "libarchive 2.5.4b"
|
||||
__LA_DECL const char * archive_version_string(void);
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
/*
|
||||
@ -83,13 +127,13 @@ const char * archive_version_string(void);
|
||||
* the simpler definitions above.
|
||||
*/
|
||||
#define ARCHIVE_VERSION_STAMP ARCHIVE_VERSION_NUMBER
|
||||
int archive_version_stamp(void);
|
||||
#define ARCHIVE_LIBRARY_VERSION "libarchive @LIBARCHIVE_VERSION_STRING@"
|
||||
const char * archive_version(void);
|
||||
__LA_DECL int archive_version_stamp(void);
|
||||
#define ARCHIVE_LIBRARY_VERSION ARCHIVE_VERSION_STRING
|
||||
__LA_DECL const char * archive_version(void);
|
||||
#define ARCHIVE_API_VERSION (ARCHIVE_VERSION_NUMBER / 1000000)
|
||||
int archive_api_version(void);
|
||||
__LA_DECL int archive_api_version(void);
|
||||
#define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_NUMBER / 1000) % 1000)
|
||||
int archive_api_feature(void);
|
||||
__LA_DECL int archive_api_feature(void);
|
||||
#endif
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
@ -139,18 +183,18 @@ struct archive_entry;
|
||||
*/
|
||||
|
||||
/* Returns pointer and size of next block of data from archive. */
|
||||
typedef ssize_t archive_read_callback(struct archive *, void *_client_data,
|
||||
typedef __LA_SSIZE_T archive_read_callback(struct archive *, void *_client_data,
|
||||
const void **_buffer);
|
||||
/* Skips at most request bytes from archive and returns the skipped amount */
|
||||
#if ARCHIVE_VERSION_NUMBER < 2000000
|
||||
typedef ssize_t archive_skip_callback(struct archive *, void *_client_data,
|
||||
typedef __LA_SSIZE_T archive_skip_callback(struct archive *, void *_client_data,
|
||||
size_t request);
|
||||
#else
|
||||
typedef off_t archive_skip_callback(struct archive *, void *_client_data,
|
||||
off_t request);
|
||||
#endif
|
||||
/* Returns size actually written, zero on EOF, -1 on error. */
|
||||
typedef ssize_t archive_write_callback(struct archive *, void *_client_data,
|
||||
typedef __LA_SSIZE_T archive_write_callback(struct archive *, void *_client_data,
|
||||
const void *_buffer, size_t _length);
|
||||
typedef int archive_open_callback(struct archive *, void *_client_data);
|
||||
typedef int archive_close_callback(struct archive *, void *_client_data);
|
||||
@ -218,7 +262,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data);
|
||||
* data for entries of interest.
|
||||
* 5) Call archive_read_finish to end processing.
|
||||
*/
|
||||
struct archive *archive_read_new(void);
|
||||
__LA_DECL struct archive *archive_read_new(void);
|
||||
|
||||
/*
|
||||
* The archive_read_support_XXX calls enable auto-detect for this
|
||||
@ -227,30 +271,30 @@ struct archive *archive_read_new(void);
|
||||
* support_compression_bzip2(). The "all" functions provide the
|
||||
* obvious shorthand.
|
||||
*/
|
||||
int archive_read_support_compression_all(struct archive *);
|
||||
int archive_read_support_compression_bzip2(struct archive *);
|
||||
int archive_read_support_compression_compress(struct archive *);
|
||||
int archive_read_support_compression_gzip(struct archive *);
|
||||
int archive_read_support_compression_none(struct archive *);
|
||||
int archive_read_support_compression_program(struct archive *,
|
||||
__LA_DECL int archive_read_support_compression_all(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_bzip2(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_compress(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_gzip(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_none(struct archive *);
|
||||
__LA_DECL int archive_read_support_compression_program(struct archive *,
|
||||
const char *command);
|
||||
|
||||
int archive_read_support_format_all(struct archive *);
|
||||
int archive_read_support_format_ar(struct archive *);
|
||||
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 *);
|
||||
__LA_DECL int archive_read_support_format_all(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_ar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_cpio(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_empty(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_gnutar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_iso9660(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_mtree(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_tar(struct archive *);
|
||||
__LA_DECL int archive_read_support_format_zip(struct archive *);
|
||||
|
||||
|
||||
/* Open the archive using callbacks for archive I/O. */
|
||||
int archive_read_open(struct archive *, void *_client_data,
|
||||
__LA_DECL int archive_read_open(struct archive *, void *_client_data,
|
||||
archive_open_callback *, archive_read_callback *,
|
||||
archive_close_callback *);
|
||||
int archive_read_open2(struct archive *, void *_client_data,
|
||||
__LA_DECL int archive_read_open2(struct archive *, void *_client_data,
|
||||
archive_open_callback *, archive_read_callback *,
|
||||
archive_skip_callback *, archive_close_callback *);
|
||||
|
||||
@ -260,43 +304,43 @@ int archive_read_open2(struct archive *, void *_client_data,
|
||||
* accept a block size handle tape blocking correctly.
|
||||
*/
|
||||
/* Use this if you know the filename. Note: NULL indicates stdin. */
|
||||
int archive_read_open_filename(struct archive *,
|
||||
__LA_DECL int archive_read_open_filename(struct archive *,
|
||||
const char *_filename, size_t _block_size);
|
||||
/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
|
||||
int archive_read_open_file(struct archive *,
|
||||
__LA_DECL int archive_read_open_file(struct archive *,
|
||||
const char *_filename, size_t _block_size);
|
||||
/* Read an archive that's stored in memory. */
|
||||
int archive_read_open_memory(struct archive *,
|
||||
__LA_DECL int archive_read_open_memory(struct archive *,
|
||||
void * buff, size_t size);
|
||||
/* A more involved version that is only used for internal testing. */
|
||||
int archive_read_open_memory2(struct archive *a, void *buff,
|
||||
__LA_DECL int archive_read_open_memory2(struct archive *a, void *buff,
|
||||
size_t size, size_t read_size);
|
||||
/* Read an archive that's already open, using the file descriptor. */
|
||||
int archive_read_open_fd(struct archive *, int _fd,
|
||||
__LA_DECL int archive_read_open_fd(struct archive *, int _fd,
|
||||
size_t _block_size);
|
||||
/* Read an archive that's already open, using a FILE *. */
|
||||
/* Note: DO NOT use this with tape drives. */
|
||||
int archive_read_open_FILE(struct archive *, FILE *_file);
|
||||
__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
|
||||
|
||||
/* Parses and returns next entry header. */
|
||||
int archive_read_next_header(struct archive *,
|
||||
__LA_DECL int archive_read_next_header(struct archive *,
|
||||
struct archive_entry **);
|
||||
|
||||
/*
|
||||
* Retrieve the byte offset in UNCOMPRESSED data where last-read
|
||||
* header started.
|
||||
*/
|
||||
int64_t archive_read_header_position(struct archive *);
|
||||
__LA_DECL int64_t archive_read_header_position(struct archive *);
|
||||
|
||||
/* Read data from the body of an entry. Similar to read(2). */
|
||||
ssize_t archive_read_data(struct archive *, void *, size_t);
|
||||
__LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, void *, size_t);
|
||||
/*
|
||||
* A zero-copy version of archive_read_data that also exposes the file offset
|
||||
* of each returned block. Note that the client has no way to specify
|
||||
* the desired size of the block. The API does guarantee that offsets will
|
||||
* be strictly increasing and that returned blocks will not overlap.
|
||||
*/
|
||||
int archive_read_data_block(struct archive *a,
|
||||
__LA_DECL int archive_read_data_block(struct archive *a,
|
||||
const void **buff, size_t *size, off_t *offset);
|
||||
|
||||
/*-
|
||||
@ -305,10 +349,10 @@ int archive_read_data_block(struct archive *a,
|
||||
* 'into_buffer': writes data into memory buffer that you provide
|
||||
* 'into_fd': writes data to specified filedes
|
||||
*/
|
||||
int archive_read_data_skip(struct archive *);
|
||||
int archive_read_data_into_buffer(struct archive *, void *buffer,
|
||||
ssize_t len);
|
||||
int archive_read_data_into_fd(struct archive *, int fd);
|
||||
__LA_DECL int archive_read_data_skip(struct archive *);
|
||||
__LA_DECL int archive_read_data_into_buffer(struct archive *, void *buffer,
|
||||
__LA_SSIZE_T len);
|
||||
__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
|
||||
|
||||
/*-
|
||||
* Convenience function to recreate the current entry (whose header
|
||||
@ -327,51 +371,55 @@ int archive_read_data_into_fd(struct archive *, int fd);
|
||||
/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
|
||||
|
||||
/* Default: Do not try to set owner/group. */
|
||||
#define ARCHIVE_EXTRACT_OWNER (1)
|
||||
#define ARCHIVE_EXTRACT_OWNER (0x0001)
|
||||
/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
|
||||
#define ARCHIVE_EXTRACT_PERM (2)
|
||||
#define ARCHIVE_EXTRACT_PERM (0x0002)
|
||||
/* Default: Do not restore mtime/atime. */
|
||||
#define ARCHIVE_EXTRACT_TIME (4)
|
||||
#define ARCHIVE_EXTRACT_TIME (0x0004)
|
||||
/* Default: Replace existing files. */
|
||||
#define ARCHIVE_EXTRACT_NO_OVERWRITE (8)
|
||||
#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008)
|
||||
/* Default: Try create first, unlink only if create fails with EEXIST. */
|
||||
#define ARCHIVE_EXTRACT_UNLINK (16)
|
||||
#define ARCHIVE_EXTRACT_UNLINK (0x0010)
|
||||
/* Default: Do not restore ACLs. */
|
||||
#define ARCHIVE_EXTRACT_ACL (32)
|
||||
#define ARCHIVE_EXTRACT_ACL (0x0020)
|
||||
/* Default: Do not restore fflags. */
|
||||
#define ARCHIVE_EXTRACT_FFLAGS (64)
|
||||
#define ARCHIVE_EXTRACT_FFLAGS (0x0040)
|
||||
/* Default: Do not restore xattrs. */
|
||||
#define ARCHIVE_EXTRACT_XATTR (128)
|
||||
#define ARCHIVE_EXTRACT_XATTR (0x0080)
|
||||
/* Default: Do not try to guard against extracts redirected by symlinks. */
|
||||
/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
|
||||
#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (256)
|
||||
#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100)
|
||||
/* Default: Do not reject entries with '..' as path elements. */
|
||||
#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (512)
|
||||
#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200)
|
||||
/* Default: Create parent directories as needed. */
|
||||
#define ARCHIVE_EXTRACT_NO_AUTODIR (1024)
|
||||
#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400)
|
||||
/* Default: Overwrite files, even if one on disk is newer. */
|
||||
#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (2048)
|
||||
#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800)
|
||||
/* Detect blocks of 0 and write holes instead. */
|
||||
#define ARCHIVE_EXTRACT_SPARSE (0x1000)
|
||||
|
||||
int archive_read_extract(struct archive *, struct archive_entry *,
|
||||
__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
|
||||
int flags);
|
||||
void archive_read_extract_set_progress_callback(struct archive *,
|
||||
__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,
|
||||
struct archive * /* dest */);
|
||||
__LA_DECL void archive_read_extract_set_progress_callback(struct archive *,
|
||||
void (*_progress_func)(void *), void *_user_data);
|
||||
|
||||
/* Record the dev/ino of a file that will not be written. This is
|
||||
* generally set to the dev/ino of the archive being read. */
|
||||
void archive_read_extract_set_skip_file(struct archive *,
|
||||
__LA_DECL void archive_read_extract_set_skip_file(struct archive *,
|
||||
dev_t, ino_t);
|
||||
|
||||
/* Close the file and release most resources. */
|
||||
int archive_read_close(struct archive *);
|
||||
__LA_DECL int archive_read_close(struct archive *);
|
||||
/* Release all resources and destroy the object. */
|
||||
/* Note that archive_read_finish will call archive_read_close for you. */
|
||||
#if ARCHIVE_VERSION_NUMBER >= 2000000
|
||||
int archive_read_finish(struct archive *);
|
||||
__LA_DECL int archive_read_finish(struct archive *);
|
||||
#else
|
||||
/* Temporarily allow library to compile with either 1.x or 2.0 API. */
|
||||
/* Erroneously declared to return void in libarchive 1.x */
|
||||
void archive_read_finish(struct archive *);
|
||||
__LA_DECL void archive_read_finish(struct archive *);
|
||||
#endif
|
||||
|
||||
/*-
|
||||
@ -389,75 +437,75 @@ void archive_read_finish(struct archive *);
|
||||
* 5) archive_write_close to close the output
|
||||
* 6) archive_write_finish to cleanup the writer and release resources
|
||||
*/
|
||||
struct archive *archive_write_new(void);
|
||||
int archive_write_set_bytes_per_block(struct archive *,
|
||||
__LA_DECL struct archive *archive_write_new(void);
|
||||
__LA_DECL int archive_write_set_bytes_per_block(struct archive *,
|
||||
int bytes_per_block);
|
||||
int archive_write_get_bytes_per_block(struct archive *);
|
||||
__LA_DECL int archive_write_get_bytes_per_block(struct archive *);
|
||||
/* XXX This is badly misnamed; suggestions appreciated. XXX */
|
||||
int archive_write_set_bytes_in_last_block(struct archive *,
|
||||
__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,
|
||||
int bytes_in_last_block);
|
||||
int archive_write_get_bytes_in_last_block(struct archive *);
|
||||
__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *);
|
||||
|
||||
/* The dev/ino of a file that won't be archived. This is used
|
||||
* to avoid recursively adding an archive to itself. */
|
||||
int archive_write_set_skip_file(struct archive *, dev_t, ino_t);
|
||||
__LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t);
|
||||
|
||||
int archive_write_set_compression_bzip2(struct archive *);
|
||||
int archive_write_set_compression_compress(struct archive *);
|
||||
int archive_write_set_compression_gzip(struct archive *);
|
||||
int archive_write_set_compression_none(struct archive *);
|
||||
int archive_write_set_compression_program(struct archive *,
|
||||
__LA_DECL int archive_write_set_compression_bzip2(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_compress(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_gzip(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_none(struct archive *);
|
||||
__LA_DECL int archive_write_set_compression_program(struct archive *,
|
||||
const char *cmd);
|
||||
/* A convenience function to set the format based on the code or name. */
|
||||
int archive_write_set_format(struct archive *, int format_code);
|
||||
int archive_write_set_format_by_name(struct archive *,
|
||||
__LA_DECL int archive_write_set_format(struct archive *, int format_code);
|
||||
__LA_DECL int archive_write_set_format_by_name(struct archive *,
|
||||
const char *name);
|
||||
/* To minimize link pollution, use one or more of the following. */
|
||||
int archive_write_set_format_ar_bsd(struct archive *);
|
||||
int archive_write_set_format_ar_svr4(struct archive *);
|
||||
int archive_write_set_format_cpio(struct archive *);
|
||||
int archive_write_set_format_cpio_newc(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
|
||||
/* TODO: int archive_write_set_format_old_tar(struct archive *); */
|
||||
int archive_write_set_format_pax(struct archive *);
|
||||
int archive_write_set_format_pax_restricted(struct archive *);
|
||||
int archive_write_set_format_shar(struct archive *);
|
||||
int archive_write_set_format_shar_dump(struct archive *);
|
||||
int archive_write_set_format_ustar(struct archive *);
|
||||
int archive_write_open(struct archive *, void *,
|
||||
__LA_DECL int archive_write_set_format_pax(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_shar(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ustar(struct archive *);
|
||||
__LA_DECL int archive_write_open(struct archive *, void *,
|
||||
archive_open_callback *, archive_write_callback *,
|
||||
archive_close_callback *);
|
||||
int archive_write_open_fd(struct archive *, int _fd);
|
||||
int archive_write_open_filename(struct archive *, const char *_file);
|
||||
__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
|
||||
__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
|
||||
/* A deprecated synonym for archive_write_open_filename() */
|
||||
int archive_write_open_file(struct archive *, const char *_file);
|
||||
int archive_write_open_FILE(struct archive *, FILE *);
|
||||
__LA_DECL int archive_write_open_file(struct archive *, const char *_file);
|
||||
__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);
|
||||
/* _buffSize is the size of the buffer, _used refers to a variable that
|
||||
* will be updated after each write into the buffer. */
|
||||
int archive_write_open_memory(struct archive *,
|
||||
__LA_DECL int archive_write_open_memory(struct archive *,
|
||||
void *_buffer, size_t _buffSize, size_t *_used);
|
||||
|
||||
/*
|
||||
* Note that the library will truncate writes beyond the size provided
|
||||
* to archive_write_header or pad if the provided data is short.
|
||||
*/
|
||||
int archive_write_header(struct archive *,
|
||||
__LA_DECL int archive_write_header(struct archive *,
|
||||
struct archive_entry *);
|
||||
#if ARCHIVE_VERSION_NUMBER >= 2000000
|
||||
ssize_t archive_write_data(struct archive *, const void *, size_t);
|
||||
__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, const void *, size_t);
|
||||
#else
|
||||
/* Temporarily allow library to compile with either 1.x or 2.0 API. */
|
||||
/* This was erroneously declared to return "int" in libarchive 1.x. */
|
||||
int archive_write_data(struct archive *, const void *, size_t);
|
||||
__LA_DECL int archive_write_data(struct archive *, const void *, size_t);
|
||||
#endif
|
||||
ssize_t archive_write_data_block(struct archive *, const void *, size_t, off_t);
|
||||
int archive_write_finish_entry(struct archive *);
|
||||
int archive_write_close(struct archive *);
|
||||
__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, off_t);
|
||||
__LA_DECL int archive_write_finish_entry(struct archive *);
|
||||
__LA_DECL int archive_write_close(struct archive *);
|
||||
#if ARCHIVE_VERSION_NUMBER >= 2000000
|
||||
int archive_write_finish(struct archive *);
|
||||
__LA_DECL int archive_write_finish(struct archive *);
|
||||
#else
|
||||
/* Temporarily allow library to compile with either 1.x or 2.0 API. */
|
||||
/* Return value was incorrect in libarchive 1.x. */
|
||||
void archive_write_finish(struct archive *);
|
||||
__LA_DECL void archive_write_finish(struct archive *);
|
||||
#endif
|
||||
|
||||
/*-
|
||||
@ -474,12 +522,12 @@ void archive_write_finish(struct archive *);
|
||||
* In particular, you can use this in conjunction with archive_read()
|
||||
* to pull entries out of an archive and create them on disk.
|
||||
*/
|
||||
struct archive *archive_write_disk_new(void);
|
||||
__LA_DECL struct archive *archive_write_disk_new(void);
|
||||
/* This file will not be overwritten. */
|
||||
int archive_write_disk_set_skip_file(struct archive *,
|
||||
__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
|
||||
dev_t, ino_t);
|
||||
/* Set flags to control how the next item gets created. */
|
||||
int archive_write_disk_set_options(struct archive *,
|
||||
__LA_DECL int archive_write_disk_set_options(struct archive *,
|
||||
int flags);
|
||||
/*
|
||||
* The lookup functions are given uname/uid (or gname/gid) pairs and
|
||||
@ -498,42 +546,47 @@ int archive_write_disk_set_options(struct archive *,
|
||||
* particular, these match the specifications of POSIX "pax" and old
|
||||
* POSIX "tar".
|
||||
*/
|
||||
int archive_write_disk_set_standard_lookup(struct archive *);
|
||||
__LA_DECL int archive_write_disk_set_standard_lookup(struct archive *);
|
||||
/*
|
||||
* If neither the default (naive) nor the standard (big) functions suit
|
||||
* your needs, you can write your own and register them. Be sure to
|
||||
* include a cleanup function if you have allocated private data.
|
||||
*/
|
||||
int archive_write_disk_set_group_lookup(struct archive *,
|
||||
void *private_data,
|
||||
gid_t (*loookup)(void *, const char *gname, gid_t gid),
|
||||
void (*cleanup)(void *));
|
||||
int archive_write_disk_set_user_lookup(struct archive *,
|
||||
void *private_data,
|
||||
uid_t (*)(void *, const char *uname, uid_t uid),
|
||||
void (*cleanup)(void *));
|
||||
__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
|
||||
void * /* private_data */,
|
||||
__LA_GID_T (*)(void *, const char *, __LA_GID_T),
|
||||
void (* /* cleanup */)(void *));
|
||||
__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
|
||||
void * /* private_data */,
|
||||
__LA_UID_T (*)(void *, const char *, __LA_UID_T),
|
||||
void (* /* cleanup */)(void *));
|
||||
|
||||
/*
|
||||
* Accessor functions to read/set various information in
|
||||
* the struct archive object:
|
||||
*/
|
||||
/* Bytes written after compression or read before decompression. */
|
||||
int64_t archive_position_compressed(struct archive *);
|
||||
__LA_DECL int64_t archive_position_compressed(struct archive *);
|
||||
/* Bytes written to compressor or read from decompressor. */
|
||||
int64_t archive_position_uncompressed(struct archive *);
|
||||
__LA_DECL int64_t archive_position_uncompressed(struct archive *);
|
||||
|
||||
const char *archive_compression_name(struct archive *);
|
||||
int archive_compression(struct archive *);
|
||||
int archive_errno(struct archive *);
|
||||
const char *archive_error_string(struct archive *);
|
||||
const char *archive_format_name(struct archive *);
|
||||
int archive_format(struct archive *);
|
||||
void archive_clear_error(struct archive *);
|
||||
void archive_set_error(struct archive *, int _err, const char *fmt, ...);
|
||||
void archive_copy_error(struct archive *dest, struct archive *src);
|
||||
__LA_DECL const char *archive_compression_name(struct archive *);
|
||||
__LA_DECL int archive_compression(struct archive *);
|
||||
__LA_DECL int archive_errno(struct archive *);
|
||||
__LA_DECL const char *archive_error_string(struct archive *);
|
||||
__LA_DECL const char *archive_format_name(struct archive *);
|
||||
__LA_DECL int archive_format(struct archive *);
|
||||
__LA_DECL void archive_clear_error(struct archive *);
|
||||
__LA_DECL void archive_set_error(struct archive *, int _err,
|
||||
const char *fmt, ...);
|
||||
__LA_DECL void archive_copy_error(struct archive *dest,
|
||||
struct archive *src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This is meaningless outside of this header. */
|
||||
#undef __LA_DECL
|
||||
|
||||
#endif /* !ARCHIVE_H_INCLUDED */
|
||||
|
@ -28,9 +28,23 @@
|
||||
* Borrowed from FreeBSD's <sys/endian.h>
|
||||
*/
|
||||
|
||||
/* Note: This is a purely internal header! */
|
||||
/* Do not use this outside of libarchive internal code! */
|
||||
|
||||
#ifndef ARCHIVE_ENDIAN_H_INCLUDED
|
||||
#define ARCHIVE_ENDIAN_H_INCLUDED
|
||||
|
||||
|
||||
/* Watcom C++ doesn't support 'inline' in C code. (For any version?) */
|
||||
#if defined( __WATCOMC__ )
|
||||
#define inline
|
||||
#endif
|
||||
|
||||
/* Visual C++ 6.0 doesn't support 'inline' in C code. (Does VC7? VC8?) */
|
||||
#if defined(_MSC_VER)
|
||||
#define inline
|
||||
#endif
|
||||
|
||||
/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
|
||||
|
||||
static inline uint16_t
|
||||
|
@ -24,7 +24,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd December 15, 2003
|
||||
.Dd May 12, 2008
|
||||
.Dt archive_entry 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -40,6 +40,7 @@
|
||||
.Nm archive_entry_atime_nsec ,
|
||||
.Nm archive_entry_clear ,
|
||||
.Nm archive_entry_clone ,
|
||||
.Nm archive_entry_copy_fflags_text ,
|
||||
.Nm archive_entry_copy_fflags_text_w ,
|
||||
.Nm archive_entry_copy_gname ,
|
||||
.Nm archive_entry_copy_gname_w ,
|
||||
@ -48,6 +49,7 @@
|
||||
.Nm archive_entry_copy_link ,
|
||||
.Nm archive_entry_copy_link_w ,
|
||||
.Nm archive_entry_copy_pathname_w ,
|
||||
.Nm archive_entry_copy_sourcepath ,
|
||||
.Nm archive_entry_copy_stat ,
|
||||
.Nm archive_entry_copy_symlink ,
|
||||
.Nm archive_entry_copy_symlink_w ,
|
||||
@ -95,6 +97,7 @@
|
||||
.Nm archive_entry_set_uid ,
|
||||
.Nm archive_entry_set_uname ,
|
||||
.Nm archive_entry_size ,
|
||||
.Nm archive_entry_sourcepath ,
|
||||
.Nm archive_entry_stat ,
|
||||
.Nm archive_entry_symlink ,
|
||||
.Nm archive_entry_uid ,
|
||||
@ -156,6 +159,8 @@
|
||||
.Fn archive_entry_clear "struct archive_entry *"
|
||||
.Ft struct archive_entry *
|
||||
.Fn archive_entry_clone "struct archive_entry *"
|
||||
.Ft const char * *
|
||||
.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const char *"
|
||||
.Ft const wchar_t *
|
||||
.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
|
||||
.Ft void
|
||||
@ -167,6 +172,8 @@
|
||||
.Ft void
|
||||
.Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *"
|
||||
.Ft void
|
||||
.Fn archive_entry_copy_sourcepath "struct archive_entry *" "const char *"
|
||||
.Ft void
|
||||
.Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *"
|
||||
.Ft void
|
||||
.Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
|
||||
@ -270,6 +277,8 @@
|
||||
.Fn archive_entry_set_uname "struct archive_entry *" "const char *"
|
||||
.Ft int64_t
|
||||
.Fn archive_entry_size "struct archive_entry *"
|
||||
.Ft const char *
|
||||
.Fn archive_entry_sourcepath "struct archive_entry *"
|
||||
.Ft const struct stat *
|
||||
.Fn archive_entry_stat "struct archive_entry *"
|
||||
.Ft const char *
|
||||
@ -376,8 +385,10 @@ will be ignored.
|
||||
.Pp
|
||||
The canonical text format is a comma-separated list of flag names.
|
||||
The
|
||||
.Fn archive_entry_copy_fflags_text
|
||||
and
|
||||
.Fn archive_entry_copy_fflags_text_w
|
||||
function parses the provided text and sets the internal bitmap values.
|
||||
functions parse the provided text and sets the internal bitmap values.
|
||||
This is a platform-specific operation; names that are not meaningful
|
||||
on the current platform will be ignored.
|
||||
The function returns a pointer to the start of the first name that was not
|
||||
|
@ -91,15 +91,17 @@ static void aes_clean(struct aes *);
|
||||
static void aes_copy(struct aes *dest, struct aes *src);
|
||||
static const char * aes_get_mbs(struct aes *);
|
||||
static const wchar_t * aes_get_wcs(struct aes *);
|
||||
static void aes_set_mbs(struct aes *, const char *mbs);
|
||||
static void aes_copy_mbs(struct aes *, const char *mbs);
|
||||
static int aes_set_mbs(struct aes *, const char *mbs);
|
||||
static int aes_copy_mbs(struct aes *, const char *mbs);
|
||||
/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */
|
||||
static void aes_copy_wcs(struct aes *, const wchar_t *wcs);
|
||||
static void aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
|
||||
static int aes_copy_wcs(struct aes *, const wchar_t *wcs);
|
||||
static int aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
|
||||
|
||||
static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
|
||||
static const wchar_t *ae_wcstofflags(const wchar_t *stringp,
|
||||
unsigned long *setp, unsigned long *clrp);
|
||||
static const char *ae_strtofflags(const char *stringp,
|
||||
unsigned long *setp, unsigned long *clrp);
|
||||
static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
|
||||
const wchar_t *wname, int perm, int id);
|
||||
static void append_id_w(wchar_t **wp, int id);
|
||||
@ -144,173 +146,216 @@ static size_t wcslen(const wchar_t *s)
|
||||
#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
aes_clean(struct aes *aes)
|
||||
{
|
||||
if (aes->aes_mbs_alloc) {
|
||||
free(aes->aes_mbs_alloc);
|
||||
aes->aes_mbs_alloc = NULL;
|
||||
if (aes->aes_wcs) {
|
||||
free((wchar_t *)(uintptr_t)aes->aes_wcs);
|
||||
aes->aes_wcs = NULL;
|
||||
}
|
||||
if (aes->aes_wcs_alloc) {
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs_alloc = NULL;
|
||||
}
|
||||
memset(aes, 0, sizeof(*aes));
|
||||
archive_string_free(&(aes->aes_mbs));
|
||||
archive_string_free(&(aes->aes_utf8));
|
||||
aes->aes_set = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
aes_copy(struct aes *dest, struct aes *src)
|
||||
{
|
||||
*dest = *src;
|
||||
if (src->aes_mbs != NULL) {
|
||||
dest->aes_mbs_alloc = strdup(src->aes_mbs);
|
||||
dest->aes_mbs = dest->aes_mbs_alloc;
|
||||
if (dest->aes_mbs == NULL)
|
||||
__archive_errx(1, "No memory for aes_copy()");
|
||||
}
|
||||
wchar_t *wp;
|
||||
|
||||
dest->aes_set = src->aes_set;
|
||||
archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
|
||||
archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
|
||||
|
||||
if (src->aes_wcs != NULL) {
|
||||
dest->aes_wcs_alloc = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
|
||||
wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
|
||||
* sizeof(wchar_t));
|
||||
dest->aes_wcs = dest->aes_wcs_alloc;
|
||||
if (dest->aes_wcs == NULL)
|
||||
if (wp == NULL)
|
||||
__archive_errx(1, "No memory for aes_copy()");
|
||||
wcscpy(dest->aes_wcs_alloc, src->aes_wcs);
|
||||
wcscpy(wp, src->aes_wcs);
|
||||
dest->aes_wcs = wp;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
aes_get_utf8(struct aes *aes)
|
||||
{
|
||||
if (aes->aes_set & AES_SET_UTF8)
|
||||
return (aes->aes_utf8.s);
|
||||
if ((aes->aes_set & AES_SET_WCS)
|
||||
&& archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) {
|
||||
aes->aes_set |= AES_SET_UTF8;
|
||||
return (aes->aes_utf8.s);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static const char *
|
||||
aes_get_mbs(struct aes *aes)
|
||||
{
|
||||
if (aes->aes_mbs == NULL && aes->aes_wcs == NULL)
|
||||
return NULL;
|
||||
if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) {
|
||||
/*
|
||||
* XXX Need to estimate the number of byte in the
|
||||
* multi-byte form. Assume that, on average, wcs
|
||||
* chars encode to no more than 3 bytes. There must
|
||||
* be a better way... XXX
|
||||
*/
|
||||
size_t mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
|
||||
|
||||
aes->aes_mbs_alloc = (char *)malloc(mbs_length);
|
||||
aes->aes_mbs = aes->aes_mbs_alloc;
|
||||
if (aes->aes_mbs == NULL)
|
||||
__archive_errx(1, "No memory for aes_get_mbs()");
|
||||
wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1);
|
||||
aes->aes_mbs_alloc[mbs_length - 1] = 0;
|
||||
/* If we already have an MBS form, return that immediately. */
|
||||
if (aes->aes_set & AES_SET_MBS)
|
||||
return (aes->aes_mbs.s);
|
||||
/* If there's a WCS form, try converting with the native locale. */
|
||||
if ((aes->aes_set & AES_SET_WCS)
|
||||
&& archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) {
|
||||
aes->aes_set |= AES_SET_MBS;
|
||||
return (aes->aes_mbs.s);
|
||||
}
|
||||
return (aes->aes_mbs);
|
||||
/* We'll use UTF-8 for MBS if all else fails. */
|
||||
return (aes_get_utf8(aes));
|
||||
}
|
||||
|
||||
static const wchar_t *
|
||||
aes_get_wcs(struct aes *aes)
|
||||
{
|
||||
wchar_t *w;
|
||||
int r;
|
||||
|
||||
if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
|
||||
return NULL;
|
||||
if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
|
||||
/* Return WCS form if we already have it. */
|
||||
if (aes->aes_set & AES_SET_WCS)
|
||||
return (aes->aes_wcs);
|
||||
|
||||
if (aes->aes_set & AES_SET_MBS) {
|
||||
/* Try converting MBS to WCS using native locale. */
|
||||
/*
|
||||
* No single byte will be more than one wide character,
|
||||
* so this length estimate will always be big enough.
|
||||
*/
|
||||
size_t wcs_length = strlen(aes->aes_mbs);
|
||||
size_t wcs_length = aes->aes_mbs.length;
|
||||
|
||||
aes->aes_wcs_alloc
|
||||
= (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
|
||||
aes->aes_wcs = aes->aes_wcs_alloc;
|
||||
if (aes->aes_wcs == NULL)
|
||||
w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
|
||||
if (w == NULL)
|
||||
__archive_errx(1, "No memory for aes_get_wcs()");
|
||||
r = mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
|
||||
aes->aes_wcs_alloc[wcs_length] = 0;
|
||||
if (r == -1) {
|
||||
/* Conversion failed, don't lie to our clients. */
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs = aes->aes_wcs_alloc = NULL;
|
||||
r = mbstowcs(w, aes->aes_mbs.s, wcs_length);
|
||||
w[wcs_length] = 0;
|
||||
if (r > 0) {
|
||||
aes->aes_set |= AES_SET_WCS;
|
||||
return (aes->aes_wcs = w);
|
||||
}
|
||||
free(w);
|
||||
}
|
||||
return (aes->aes_wcs);
|
||||
|
||||
if (aes->aes_set & AES_SET_UTF8) {
|
||||
/* Try converting UTF8 to WCS. */
|
||||
aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
|
||||
aes->aes_set |= AES_SET_WCS;
|
||||
return (aes->aes_wcs);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
aes_set_mbs(struct aes *aes, const char *mbs)
|
||||
{
|
||||
if (aes->aes_mbs_alloc) {
|
||||
free(aes->aes_mbs_alloc);
|
||||
aes->aes_mbs_alloc = NULL;
|
||||
}
|
||||
if (aes->aes_wcs_alloc) {
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs_alloc = NULL;
|
||||
}
|
||||
aes->aes_mbs = mbs;
|
||||
aes->aes_wcs = NULL;
|
||||
return (aes_copy_mbs(aes, mbs));
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
aes_copy_mbs(struct aes *aes, const char *mbs)
|
||||
{
|
||||
if (aes->aes_mbs_alloc) {
|
||||
free(aes->aes_mbs_alloc);
|
||||
aes->aes_mbs_alloc = NULL;
|
||||
if (mbs == NULL) {
|
||||
aes->aes_set = 0;
|
||||
return (0);
|
||||
}
|
||||
if (aes->aes_wcs_alloc) {
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs_alloc = NULL;
|
||||
aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
|
||||
archive_strcpy(&(aes->aes_mbs), mbs);
|
||||
archive_string_empty(&(aes->aes_utf8));
|
||||
if (aes->aes_wcs) {
|
||||
free((wchar_t *)(uintptr_t)aes->aes_wcs);
|
||||
aes->aes_wcs = NULL;
|
||||
}
|
||||
aes->aes_mbs_alloc = (char *)malloc((strlen(mbs) + 1) * sizeof(char));
|
||||
if (aes->aes_mbs_alloc == NULL)
|
||||
__archive_errx(1, "No memory for aes_copy_mbs()");
|
||||
strcpy(aes->aes_mbs_alloc, mbs);
|
||||
aes->aes_mbs = aes->aes_mbs_alloc;
|
||||
aes->aes_wcs = NULL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
aes_set_wcs(struct aes *aes, const wchar_t *wcs)
|
||||
/*
|
||||
* The 'update' form tries to proactively update all forms of
|
||||
* this string (WCS and MBS) and returns an error if any of
|
||||
* them fail. This is used by the 'pax' handler, for instance,
|
||||
* to detect and report character-conversion failures early while
|
||||
* still allowing clients to get potentially useful values from
|
||||
* the more tolerant lazy conversions. (get_mbs and get_wcs will
|
||||
* strive to give the user something useful, so you can get hopefully
|
||||
* usable values even if some of the character conversions are failing.)
|
||||
*/
|
||||
static int
|
||||
aes_update_utf8(struct aes *aes, const char *utf8)
|
||||
{
|
||||
if (aes->aes_mbs_alloc) {
|
||||
free(aes->aes_mbs_alloc);
|
||||
aes->aes_mbs_alloc = NULL;
|
||||
if (utf8 == NULL) {
|
||||
aes->aes_set = 0;
|
||||
return (1); /* Succeeded in clearing everything. */
|
||||
}
|
||||
if (aes->aes_wcs_alloc) {
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs_alloc = NULL;
|
||||
}
|
||||
aes->aes_mbs = NULL;
|
||||
aes->aes_wcs = wcs;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
/* Save the UTF8 string. */
|
||||
archive_strcpy(&(aes->aes_utf8), utf8);
|
||||
|
||||
/* Empty the mbs and wcs strings. */
|
||||
archive_string_empty(&(aes->aes_mbs));
|
||||
if (aes->aes_wcs) {
|
||||
free((wchar_t *)(uintptr_t)aes->aes_wcs);
|
||||
aes->aes_wcs = NULL;
|
||||
}
|
||||
|
||||
aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */
|
||||
|
||||
/* TODO: We should just do a direct UTF-8 to MBS conversion
|
||||
* here. That would be faster, use less space, and give the
|
||||
* same information. (If a UTF-8 to MBS conversion succeeds,
|
||||
* then UTF-8->WCS and Unicode->MBS conversions will both
|
||||
* succeed.) */
|
||||
|
||||
/* Try converting UTF8 to WCS, return false on failure. */
|
||||
aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
|
||||
if (aes->aes_wcs == NULL)
|
||||
return (0);
|
||||
aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */
|
||||
|
||||
/* Try converting WCS to MBS, return false on failure. */
|
||||
if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL)
|
||||
return (0);
|
||||
aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
|
||||
|
||||
/* All conversions succeeded. */
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
|
||||
{
|
||||
aes_copy_wcs_len(aes, wcs, wcslen(wcs));
|
||||
return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs));
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
|
||||
{
|
||||
if (aes->aes_mbs_alloc) {
|
||||
free(aes->aes_mbs_alloc);
|
||||
aes->aes_mbs_alloc = NULL;
|
||||
wchar_t *w;
|
||||
|
||||
if (wcs == NULL) {
|
||||
aes->aes_set = 0;
|
||||
return (0);
|
||||
}
|
||||
if (aes->aes_wcs_alloc) {
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs_alloc = NULL;
|
||||
aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
|
||||
archive_string_empty(&(aes->aes_mbs));
|
||||
archive_string_empty(&(aes->aes_utf8));
|
||||
if (aes->aes_wcs) {
|
||||
free((wchar_t *)(uintptr_t)aes->aes_wcs);
|
||||
aes->aes_wcs = NULL;
|
||||
}
|
||||
aes->aes_mbs = NULL;
|
||||
aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
|
||||
if (aes->aes_wcs_alloc == NULL)
|
||||
w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
|
||||
if (w == NULL)
|
||||
__archive_errx(1, "No memory for aes_copy_wcs()");
|
||||
wmemcpy(aes->aes_wcs_alloc, wcs, len);
|
||||
aes->aes_wcs_alloc[len] = L'\0';
|
||||
aes->aes_wcs = aes->aes_wcs_alloc;
|
||||
wmemcpy(w, wcs, len);
|
||||
w[len] = L'\0';
|
||||
aes->aes_wcs = w;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Public Interface
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
struct archive_entry *
|
||||
archive_entry_clear(struct archive_entry *entry)
|
||||
{
|
||||
@ -350,6 +395,8 @@ archive_entry_clone(struct archive_entry *entry)
|
||||
aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
|
||||
aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
|
||||
aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
|
||||
entry2->ae_hardlinkset = entry->ae_hardlinkset;
|
||||
entry2->ae_symlinkset = entry->ae_symlinkset;
|
||||
aes_copy(&entry2->ae_uname, &entry->ae_uname);
|
||||
|
||||
/* Copy ACL data over. */
|
||||
@ -515,12 +562,16 @@ archive_entry_gname_w(struct archive_entry *entry)
|
||||
const char *
|
||||
archive_entry_hardlink(struct archive_entry *entry)
|
||||
{
|
||||
if (!entry->ae_hardlinkset)
|
||||
return (NULL);
|
||||
return (aes_get_mbs(&entry->ae_hardlink));
|
||||
}
|
||||
|
||||
const wchar_t *
|
||||
archive_entry_hardlink_w(struct archive_entry *entry)
|
||||
{
|
||||
if (!entry->ae_hardlinkset)
|
||||
return (NULL);
|
||||
return (aes_get_wcs(&entry->ae_hardlink));
|
||||
}
|
||||
|
||||
@ -600,15 +651,25 @@ archive_entry_size(struct archive_entry *entry)
|
||||
return (entry->ae_stat.aest_size);
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_entry_sourcepath(struct archive_entry *entry)
|
||||
{
|
||||
return (aes_get_mbs(&entry->ae_sourcepath));
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_entry_symlink(struct archive_entry *entry)
|
||||
{
|
||||
if (!entry->ae_symlinkset)
|
||||
return (NULL);
|
||||
return (aes_get_mbs(&entry->ae_symlink));
|
||||
}
|
||||
|
||||
const wchar_t *
|
||||
archive_entry_symlink_w(struct archive_entry *entry)
|
||||
{
|
||||
if (!entry->ae_symlinkset)
|
||||
return (NULL);
|
||||
return (aes_get_wcs(&entry->ae_symlink));
|
||||
}
|
||||
|
||||
@ -651,6 +712,15 @@ archive_entry_set_fflags(struct archive_entry *entry,
|
||||
entry->ae_fflags_clear = clear;
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_entry_copy_fflags_text(struct archive_entry *entry,
|
||||
const char *flags)
|
||||
{
|
||||
aes_copy_mbs(&entry->ae_fflags_text, flags);
|
||||
return (ae_strtofflags(flags,
|
||||
&entry->ae_fflags_set, &entry->ae_fflags_clear));
|
||||
}
|
||||
|
||||
const wchar_t *
|
||||
archive_entry_copy_fflags_text_w(struct archive_entry *entry,
|
||||
const wchar_t *flags)
|
||||
@ -685,6 +755,12 @@ archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
|
||||
aes_copy_wcs(&entry->ae_gname, name);
|
||||
}
|
||||
|
||||
int
|
||||
archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
|
||||
{
|
||||
return (aes_update_utf8(&entry->ae_gname, name));
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
|
||||
{
|
||||
@ -696,18 +772,24 @@ void
|
||||
archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
|
||||
{
|
||||
aes_set_mbs(&entry->ae_hardlink, target);
|
||||
if (target != NULL)
|
||||
entry->ae_hardlinkset = 1;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
|
||||
{
|
||||
aes_copy_mbs(&entry->ae_hardlink, target);
|
||||
if (target != NULL)
|
||||
entry->ae_hardlinkset = 1;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
|
||||
{
|
||||
aes_copy_wcs(&entry->ae_hardlink, target);
|
||||
if (target != NULL)
|
||||
entry->ae_hardlinkset = 1;
|
||||
}
|
||||
|
||||
void
|
||||
@ -754,8 +836,7 @@ archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
|
||||
void
|
||||
archive_entry_set_link(struct archive_entry *entry, const char *target)
|
||||
{
|
||||
if (entry->ae_symlink.aes_mbs != NULL ||
|
||||
entry->ae_symlink.aes_wcs != NULL)
|
||||
if (entry->ae_symlinkset)
|
||||
aes_set_mbs(&entry->ae_symlink, target);
|
||||
else
|
||||
aes_set_mbs(&entry->ae_hardlink, target);
|
||||
@ -765,8 +846,7 @@ archive_entry_set_link(struct archive_entry *entry, const char *target)
|
||||
void
|
||||
archive_entry_copy_link(struct archive_entry *entry, const char *target)
|
||||
{
|
||||
if (entry->ae_symlink.aes_mbs != NULL ||
|
||||
entry->ae_symlink.aes_wcs != NULL)
|
||||
if (entry->ae_symlinkset)
|
||||
aes_copy_mbs(&entry->ae_symlink, target);
|
||||
else
|
||||
aes_copy_mbs(&entry->ae_hardlink, target);
|
||||
@ -776,13 +856,21 @@ archive_entry_copy_link(struct archive_entry *entry, const char *target)
|
||||
void
|
||||
archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
|
||||
{
|
||||
if (entry->ae_symlink.aes_mbs != NULL ||
|
||||
entry->ae_symlink.aes_wcs != NULL)
|
||||
if (entry->ae_symlinkset)
|
||||
aes_copy_wcs(&entry->ae_symlink, target);
|
||||
else
|
||||
aes_copy_wcs(&entry->ae_hardlink, target);
|
||||
}
|
||||
|
||||
int
|
||||
archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
|
||||
{
|
||||
if (entry->ae_symlinkset)
|
||||
return (aes_update_utf8(&entry->ae_symlink, target));
|
||||
else
|
||||
return (aes_update_utf8(&entry->ae_hardlink, target));
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_mode(struct archive_entry *entry, mode_t m)
|
||||
{
|
||||
@ -823,6 +911,12 @@ archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
|
||||
aes_copy_wcs(&entry->ae_pathname, name);
|
||||
}
|
||||
|
||||
int
|
||||
archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
|
||||
{
|
||||
return (aes_update_utf8(&entry->ae_pathname, name));
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_perm(struct archive_entry *entry, mode_t p)
|
||||
{
|
||||
@ -862,22 +956,34 @@ archive_entry_set_size(struct archive_entry *entry, int64_t s)
|
||||
entry->ae_stat.aest_size = s;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
|
||||
{
|
||||
aes_set_mbs(&entry->ae_sourcepath, path);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
|
||||
{
|
||||
aes_set_mbs(&entry->ae_symlink, linkname);
|
||||
if (linkname != NULL)
|
||||
entry->ae_symlinkset = 1;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
|
||||
{
|
||||
aes_copy_mbs(&entry->ae_symlink, linkname);
|
||||
if (linkname != NULL)
|
||||
entry->ae_symlinkset = 1;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
|
||||
{
|
||||
aes_copy_wcs(&entry->ae_symlink, linkname);
|
||||
if (linkname != NULL)
|
||||
entry->ae_symlinkset = 1;
|
||||
}
|
||||
|
||||
void
|
||||
@ -905,6 +1011,12 @@ archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
|
||||
aes_copy_wcs(&entry->ae_uname, name);
|
||||
}
|
||||
|
||||
int
|
||||
archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
|
||||
{
|
||||
return (aes_update_utf8(&entry->ae_uname, name));
|
||||
}
|
||||
|
||||
/*
|
||||
* ACL management. The following would, of course, be a lot simpler
|
||||
* if: 1) the last draft of POSIX.1e were a really thorough and
|
||||
@ -1744,7 +1856,7 @@ static struct flag {
|
||||
* Convert file flags to a comma-separated string. If no flags
|
||||
* are set, return the empty string.
|
||||
*/
|
||||
char *
|
||||
static char *
|
||||
ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
|
||||
{
|
||||
char *string, *dp;
|
||||
@ -1788,6 +1900,70 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
|
||||
return (string);
|
||||
}
|
||||
|
||||
/*
|
||||
* strtofflags --
|
||||
* Take string of arguments and return file flags. This
|
||||
* version works a little differently than strtofflags(3).
|
||||
* In particular, it always tests every token, skipping any
|
||||
* unrecognized tokens. It returns a pointer to the first
|
||||
* unrecognized token, or NULL if every token was recognized.
|
||||
* This version is also const-correct and does not modify the
|
||||
* provided string.
|
||||
*/
|
||||
static const char *
|
||||
ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
|
||||
{
|
||||
const char *start, *end;
|
||||
struct flag *flag;
|
||||
unsigned long set, clear;
|
||||
const char *failed;
|
||||
|
||||
set = clear = 0;
|
||||
start = s;
|
||||
failed = NULL;
|
||||
/* Find start of first token. */
|
||||
while (*start == '\t' || *start == ' ' || *start == ',')
|
||||
start++;
|
||||
while (*start != '\0') {
|
||||
/* Locate end of token. */
|
||||
end = start;
|
||||
while (*end != '\0' && *end != '\t' &&
|
||||
*end != ' ' && *end != ',')
|
||||
end++;
|
||||
for (flag = flags; flag->name != NULL; flag++) {
|
||||
if (memcmp(start, flag->name, end - start) == 0) {
|
||||
/* Matched "noXXXX", so reverse the sense. */
|
||||
clear |= flag->set;
|
||||
set |= flag->clear;
|
||||
break;
|
||||
} else if (memcmp(start, flag->name + 2, end - start)
|
||||
== 0) {
|
||||
/* Matched "XXXX", so don't reverse. */
|
||||
set |= flag->set;
|
||||
clear |= flag->clear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Ignore unknown flag names. */
|
||||
if (flag->name == NULL && failed == NULL)
|
||||
failed = start;
|
||||
|
||||
/* Find start of next token. */
|
||||
start = end;
|
||||
while (*start == '\t' || *start == ' ' || *start == ',')
|
||||
start++;
|
||||
|
||||
}
|
||||
|
||||
if (setp)
|
||||
*setp = set;
|
||||
if (clrp)
|
||||
*clrp = clear;
|
||||
|
||||
/* Return location of first failure. */
|
||||
return (failed);
|
||||
}
|
||||
|
||||
/*
|
||||
* wcstofflags --
|
||||
* Take string of arguments and return file flags. This
|
||||
@ -1798,7 +1974,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
|
||||
* This version is also const-correct and does not modify the
|
||||
* provided string.
|
||||
*/
|
||||
const wchar_t *
|
||||
static const wchar_t *
|
||||
ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
|
||||
{
|
||||
const wchar_t *start, *end;
|
||||
|
@ -28,20 +28,74 @@
|
||||
#ifndef ARCHIVE_ENTRY_H_INCLUDED
|
||||
#define ARCHIVE_ENTRY_H_INCLUDED
|
||||
|
||||
/*
|
||||
* Note: archive_entry.h is for use outside of libarchive; the
|
||||
* configuration headers (config.h, archive_platform.h, etc.) are
|
||||
* purely internal. Do NOT use HAVE_XXX configuration macros to
|
||||
* control the behavior of this header! If you must conditionalize,
|
||||
* use predefined compiler and/or platform macros.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h> /* for wchar_t */
|
||||
#include <time.h>
|
||||
|
||||
/* Get appropriate definitions of standard POSIX-style types. */
|
||||
/* These should match the types used in 'struct stat' */
|
||||
#ifdef _WIN32
|
||||
#define __LA_UID_T unsigned int
|
||||
#define __LA_GID_T unsigned int
|
||||
#define __LA_DEV_T unsigned int
|
||||
#define __LA_MODE_T unsigned short
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define __LA_UID_T uid_t
|
||||
#define __LA_GID_T gid_t
|
||||
#define __LA_DEV_T dev_t
|
||||
#define __LA_MODE_T mode_t
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX Is this defined for all Windows compilers? If so, in what
|
||||
* header? It would be nice to remove the __LA_INO_T indirection and
|
||||
* just use plain ino_t everywhere. Likewise for the other types just
|
||||
* above.
|
||||
*/
|
||||
#define __LA_INO_T ino_t
|
||||
|
||||
|
||||
/*
|
||||
* On Windows, define LIBARCHIVE_STATIC if you're building or using a
|
||||
* .lib. The default here assumes you're building a DLL. Only
|
||||
* libarchive source should ever define __LIBARCHIVE_BUILD.
|
||||
*/
|
||||
#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC)
|
||||
# ifdef __LIBARCHIVE_BUILD
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllexport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllexport)
|
||||
# endif
|
||||
# else
|
||||
# ifdef __GNUC__
|
||||
# define __LA_DECL __attribute__((dllimport)) extern
|
||||
# else
|
||||
# define __LA_DECL __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
/* Static libraries on all platforms and shared libraries on non-Windows. */
|
||||
# define __LA_DECL
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Description of an archive entry.
|
||||
*
|
||||
* Basically, a "struct stat" with a few text fields added in.
|
||||
* You can think of this as "struct stat" with some text fields added in.
|
||||
*
|
||||
* TODO: Add "comment", "charset", and possibly other entries that are
|
||||
* supported by "pax interchange" format. However, GNU, ustar, cpio,
|
||||
@ -90,50 +144,51 @@ struct archive_entry;
|
||||
* Basic object manipulation
|
||||
*/
|
||||
|
||||
struct archive_entry *archive_entry_clear(struct archive_entry *);
|
||||
__LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *);
|
||||
/* The 'clone' function does a deep copy; all of the strings are copied too. */
|
||||
struct archive_entry *archive_entry_clone(struct archive_entry *);
|
||||
void archive_entry_free(struct archive_entry *);
|
||||
struct archive_entry *archive_entry_new(void);
|
||||
__LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_free(struct archive_entry *);
|
||||
__LA_DECL struct archive_entry *archive_entry_new(void);
|
||||
|
||||
/*
|
||||
* Retrieve fields from an archive_entry.
|
||||
*/
|
||||
|
||||
time_t archive_entry_atime(struct archive_entry *);
|
||||
long archive_entry_atime_nsec(struct archive_entry *);
|
||||
time_t archive_entry_ctime(struct archive_entry *);
|
||||
long archive_entry_ctime_nsec(struct archive_entry *);
|
||||
dev_t archive_entry_dev(struct archive_entry *);
|
||||
dev_t archive_entry_devmajor(struct archive_entry *);
|
||||
dev_t archive_entry_devminor(struct archive_entry *);
|
||||
mode_t archive_entry_filetype(struct archive_entry *);
|
||||
void archive_entry_fflags(struct archive_entry *,
|
||||
__LA_DECL time_t archive_entry_atime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_atime_nsec(struct archive_entry *);
|
||||
__LA_DECL time_t archive_entry_ctime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_ctime_nsec(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_dev(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_devmajor(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_devminor(struct archive_entry *);
|
||||
__LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_fflags(struct archive_entry *,
|
||||
unsigned long * /* set */,
|
||||
unsigned long * /* clear */);
|
||||
const char *archive_entry_fflags_text(struct archive_entry *);
|
||||
gid_t archive_entry_gid(struct archive_entry *);
|
||||
const char *archive_entry_gname(struct archive_entry *);
|
||||
const wchar_t *archive_entry_gname_w(struct archive_entry *);
|
||||
const char *archive_entry_hardlink(struct archive_entry *);
|
||||
const wchar_t *archive_entry_hardlink_w(struct archive_entry *);
|
||||
ino_t archive_entry_ino(struct archive_entry *);
|
||||
mode_t archive_entry_mode(struct archive_entry *);
|
||||
time_t archive_entry_mtime(struct archive_entry *);
|
||||
long archive_entry_mtime_nsec(struct archive_entry *);
|
||||
unsigned int archive_entry_nlink(struct archive_entry *);
|
||||
const char *archive_entry_pathname(struct archive_entry *);
|
||||
const wchar_t *archive_entry_pathname_w(struct archive_entry *);
|
||||
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 *);
|
||||
const char *archive_entry_uname(struct archive_entry *);
|
||||
const wchar_t *archive_entry_uname_w(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_fflags_text(struct archive_entry *);
|
||||
__LA_DECL __LA_GID_T archive_entry_gid(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_gname(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_hardlink(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *);
|
||||
__LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *);
|
||||
__LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *);
|
||||
__LA_DECL time_t archive_entry_mtime(struct archive_entry *);
|
||||
__LA_DECL long archive_entry_mtime_nsec(struct archive_entry *);
|
||||
__LA_DECL unsigned int archive_entry_nlink(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_pathname(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_rdev(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *);
|
||||
__LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_sourcepath(struct archive_entry *);
|
||||
__LA_DECL int64_t archive_entry_size(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_strmode(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_symlink(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
|
||||
__LA_DECL __LA_UID_T archive_entry_uid(struct archive_entry *);
|
||||
__LA_DECL const char *archive_entry_uname(struct archive_entry *);
|
||||
__LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *);
|
||||
|
||||
/*
|
||||
* Set fields in an archive_entry.
|
||||
@ -142,48 +197,54 @@ const wchar_t *archive_entry_uname_w(struct archive_entry *);
|
||||
* In contrast, 'copy' functions do copy the object pointed to.
|
||||
*/
|
||||
|
||||
void archive_entry_set_atime(struct archive_entry *, time_t, long);
|
||||
void archive_entry_set_ctime(struct archive_entry *, time_t, long);
|
||||
void archive_entry_set_dev(struct archive_entry *, dev_t);
|
||||
void archive_entry_set_devmajor(struct archive_entry *, dev_t);
|
||||
void archive_entry_set_devminor(struct archive_entry *, dev_t);
|
||||
void archive_entry_set_filetype(struct archive_entry *, unsigned int);
|
||||
void archive_entry_set_fflags(struct archive_entry *,
|
||||
__LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int);
|
||||
__LA_DECL void archive_entry_set_fflags(struct archive_entry *,
|
||||
unsigned long /* set */, unsigned long /* clear */);
|
||||
/* Returns pointer to start of first invalid token, or NULL if none. */
|
||||
/* Note that all recognized tokens are processed, regardless. */
|
||||
const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
|
||||
__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
|
||||
const char *);
|
||||
__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
|
||||
const wchar_t *);
|
||||
void archive_entry_set_gid(struct archive_entry *, gid_t);
|
||||
void archive_entry_set_gname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_gname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_hardlink(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_hardlink(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_ino(struct archive_entry *, unsigned long);
|
||||
void archive_entry_set_link(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_link(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_mode(struct archive_entry *, mode_t);
|
||||
void archive_entry_set_mtime(struct archive_entry *, time_t, long);
|
||||
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);
|
||||
void archive_entry_set_size(struct archive_entry *, int64_t);
|
||||
void archive_entry_set_symlink(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_symlink(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_uid(struct archive_entry *, uid_t);
|
||||
void archive_entry_set_uname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_uname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
|
||||
|
||||
__LA_DECL void archive_entry_set_gid(struct archive_entry *, __LA_GID_T);
|
||||
__LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL void archive_entry_set_ino(struct archive_entry *, unsigned long);
|
||||
__LA_DECL void archive_entry_set_link(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
|
||||
__LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long);
|
||||
__LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int);
|
||||
__LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
|
||||
__LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
|
||||
__LA_DECL void archive_entry_set_size(struct archive_entry *, int64_t);
|
||||
__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_UID_T);
|
||||
__LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *);
|
||||
__LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
|
||||
__LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *);
|
||||
/*
|
||||
* Routines to bulk copy fields to/from a platform-native "struct
|
||||
* stat." Libarchive used to just store a struct stat inside of each
|
||||
@ -193,8 +254,8 @@ void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
|
||||
*
|
||||
* TODO: On Linux, provide both stat32 and stat64 versions of these functions.
|
||||
*/
|
||||
const struct stat *archive_entry_stat(struct archive_entry *);
|
||||
void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
|
||||
__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
|
||||
|
||||
/*
|
||||
* ACL routines. This used to simply store and return text-format ACL
|
||||
@ -242,11 +303,11 @@ void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
|
||||
* POSIX.1e) is useful for handling archive formats that combine
|
||||
* default and access information in a single ACL list.
|
||||
*/
|
||||
void archive_entry_acl_clear(struct archive_entry *);
|
||||
void archive_entry_acl_add_entry(struct archive_entry *,
|
||||
__LA_DECL void archive_entry_acl_clear(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_acl_add_entry(struct archive_entry *,
|
||||
int /* type */, int /* permset */, int /* tag */,
|
||||
int /* qual */, const char * /* name */);
|
||||
void archive_entry_acl_add_entry_w(struct archive_entry *,
|
||||
__LA_DECL void archive_entry_acl_add_entry_w(struct archive_entry *,
|
||||
int /* type */, int /* permset */, int /* tag */,
|
||||
int /* qual */, const wchar_t * /* name */);
|
||||
|
||||
@ -255,11 +316,11 @@ void archive_entry_acl_add_entry_w(struct archive_entry *,
|
||||
* "next" entry. The want_type parameter allows you to request only
|
||||
* access entries or only default entries.
|
||||
*/
|
||||
int archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
|
||||
int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
|
||||
__LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
|
||||
__LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
|
||||
int * /* type */, int * /* permset */, int * /* tag */,
|
||||
int * /* qual */, const char ** /* name */);
|
||||
int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
|
||||
__LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
|
||||
int * /* type */, int * /* permset */, int * /* tag */,
|
||||
int * /* qual */, const wchar_t ** /* name */);
|
||||
|
||||
@ -276,11 +337,11 @@ int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
|
||||
*/
|
||||
#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024
|
||||
#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048
|
||||
const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
|
||||
__LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
|
||||
int /* flags */);
|
||||
|
||||
/* Return a count of entries matching 'want_type' */
|
||||
int archive_entry_acl_count(struct archive_entry *, int /* want_type */);
|
||||
__LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */);
|
||||
|
||||
/*
|
||||
* Private ACL parser. This is private because it handles some
|
||||
@ -295,15 +356,15 @@ int archive_entry_acl_count(struct archive_entry *, int /* want_type */);
|
||||
* TODO: Move this declaration out of the public header and into
|
||||
* a private header. Warnings above are silly.
|
||||
*/
|
||||
int __archive_entry_acl_parse_w(struct archive_entry *,
|
||||
__LA_DECL int __archive_entry_acl_parse_w(struct archive_entry *,
|
||||
const wchar_t *, int /* type */);
|
||||
|
||||
/*
|
||||
* extended attributes
|
||||
*/
|
||||
|
||||
void archive_entry_xattr_clear(struct archive_entry *);
|
||||
void archive_entry_xattr_add_entry(struct archive_entry *,
|
||||
__LA_DECL void archive_entry_xattr_clear(struct archive_entry *);
|
||||
__LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *,
|
||||
const char * /* name */, const void * /* value */,
|
||||
size_t /* size */);
|
||||
|
||||
@ -312,37 +373,93 @@ void archive_entry_xattr_add_entry(struct archive_entry *,
|
||||
* "next" entry.
|
||||
*/
|
||||
|
||||
int archive_entry_xattr_count(struct archive_entry *);
|
||||
int archive_entry_xattr_reset(struct archive_entry *);
|
||||
int archive_entry_xattr_next(struct archive_entry *,
|
||||
__LA_DECL int archive_entry_xattr_count(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_xattr_reset(struct archive_entry *);
|
||||
__LA_DECL int archive_entry_xattr_next(struct archive_entry *,
|
||||
const char ** /* name */, const void ** /* value */, size_t *);
|
||||
|
||||
/*
|
||||
* Utility to detect hardlinks.
|
||||
* Utility to match up 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.
|
||||
* The 'struct archive_entry_linkresolver' is a cache of archive entries
|
||||
* for files with multiple links. Here's how to use it:
|
||||
* 1. Create a lookup object with archive_entry_linkresolver_new()
|
||||
* 2. Tell it the archive format you're using.
|
||||
* 3. Hand each archive_entry to archive_entry_linkify().
|
||||
* That function will return 0, 1, or 2 entries that should
|
||||
* be written.
|
||||
* 4. Call archive_entry_linkify(resolver, NULL) until
|
||||
* no more entries are returned.
|
||||
* 5. Call archive_entry_link_resolver_free(resolver) to free resources.
|
||||
*
|
||||
* The entries returned have their hardlink and size fields updated
|
||||
* appropriately. If an entry is passed in that does not refer to
|
||||
* a file with multiple links, it is returned unchanged. The intention
|
||||
* is that you should be able to simply filter all entries through
|
||||
* this machine.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Note that archive_entry_size() is reset to zero if the file
|
||||
* body should not be written to the archive. Pay attention!
|
||||
*/
|
||||
struct archive_entry_linkresolver;
|
||||
__LA_DECL 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 *);
|
||||
/*
|
||||
* There are three different strategies for marking hardlinks.
|
||||
* The descriptions below name them after the best-known
|
||||
* formats that rely on each strategy:
|
||||
*
|
||||
* "Old cpio" is the simplest, it always returns any entry unmodified.
|
||||
* As far as I know, only cpio formats use this. Old cpio archives
|
||||
* store every link with the full body; the onus is on the dearchiver
|
||||
* to detect and properly link the files as they are restored.
|
||||
* "tar" is also pretty simple; it caches a copy the first time it sees
|
||||
* any link. Subsequent appearances are modified to be hardlink
|
||||
* references to the first one without any body. Used by all tar
|
||||
* formats, although the newest tar formats permit the "old cpio" strategy
|
||||
* as well. This strategy is very simple for the dearchiver,
|
||||
* and reasonably straightforward for the archiver.
|
||||
* "new cpio" is trickier. It stores the body only with the last
|
||||
* occurrence. The complication is that we might not
|
||||
* see every link to a particular file in a single session, so
|
||||
* there's no easy way to know when we've seen the last occurrence.
|
||||
* The solution here is to queue one link until we see the next.
|
||||
* At the end of the session, you can enumerate any remaining
|
||||
* entries by calling archive_entry_linkify(NULL) and store those
|
||||
* bodies. If you have a file with three links l1, l2, and l3,
|
||||
* you'll get the following behavior if you see all three links:
|
||||
* linkify(l1) => NULL (the resolver stores l1 internally)
|
||||
* linkify(l2) => l1 (resolver stores l2, you write l1)
|
||||
* linkify(l3) => l2, l3 (all links seen, you can write both).
|
||||
* If you only see l1 and l2, you'll get this behavior:
|
||||
* linkify(l1) => NULL
|
||||
* linkify(l2) => l1
|
||||
* linkify(NULL) => l2 (at end, you retrieve remaining links)
|
||||
* As the name suggests, this strategy is used by newer cpio variants.
|
||||
* It's noticably more complex for the archiver, slightly more complex
|
||||
* for the dearchiver than the tar strategy, but makes it straightforward
|
||||
* to restore a file using any link by simply continuing to scan until
|
||||
* you see a link that is stored with a body. In contrast, the tar
|
||||
* strategy requires you to rescan the archive from the beginning to
|
||||
* correctly extract an arbitrary link.
|
||||
*/
|
||||
|
||||
__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
|
||||
__LA_DECL void archive_entry_linkresolver_set_strategy(
|
||||
struct archive_entry_linkresolver *, int /* format_code */);
|
||||
__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
|
||||
__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
|
||||
struct archive_entry **, struct archive_entry **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This is meaningless outside of this header. */
|
||||
#undef __LA_DECL
|
||||
|
||||
#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
|
||||
|
@ -40,135 +40,236 @@ __FBSDID("$FreeBSD$");
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
|
||||
/*
|
||||
* This is mostly a pretty straightforward hash table implementation.
|
||||
* The only interesting bit is the different strategies used to
|
||||
* match up links. These strategies match those used by various
|
||||
* archiving formats:
|
||||
* tar - content stored with first link, remainder refer back to it.
|
||||
* This requires us to match each subsequent link up with the
|
||||
* first appearance.
|
||||
* cpio - Old cpio just stored body with each link, match-ups were
|
||||
* implicit. This is trivial.
|
||||
* new cpio - New cpio only stores body with last link, match-ups
|
||||
* are implicit. This is actually quite tricky; see the notes
|
||||
* below.
|
||||
*/
|
||||
|
||||
/* Users pass us a format code, we translate that into a strategy here. */
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
|
||||
#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
|
||||
|
||||
/* 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;
|
||||
int links; /* # links not yet seen */
|
||||
int hash;
|
||||
struct archive_entry *canonical;
|
||||
struct archive_entry *entry;
|
||||
};
|
||||
|
||||
struct archive_entry_linkresolver {
|
||||
struct links_entry **buckets;
|
||||
struct links_entry *spare;
|
||||
unsigned long number_entries;
|
||||
size_t number_buckets;
|
||||
int strategy;
|
||||
};
|
||||
|
||||
static struct links_entry *find_entry(struct archive_entry_linkresolver *,
|
||||
struct archive_entry *);
|
||||
static void grow_hash(struct archive_entry_linkresolver *);
|
||||
static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
|
||||
struct archive_entry *);
|
||||
static struct links_entry *next_entry(struct archive_entry_linkresolver *);
|
||||
|
||||
struct archive_entry_linkresolver *
|
||||
archive_entry_linkresolver_new(void)
|
||||
{
|
||||
struct archive_entry_linkresolver *links_cache;
|
||||
struct archive_entry_linkresolver *res;
|
||||
size_t i;
|
||||
|
||||
links_cache = malloc(sizeof(struct archive_entry_linkresolver));
|
||||
if (links_cache == NULL)
|
||||
res = malloc(sizeof(struct archive_entry_linkresolver));
|
||||
if (res == 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);
|
||||
memset(res, 0, sizeof(struct archive_entry_linkresolver));
|
||||
res->number_buckets = links_cache_initial_size;
|
||||
res->buckets = malloc(res->number_buckets *
|
||||
sizeof(res->buckets[0]));
|
||||
if (res->buckets == NULL) {
|
||||
free(res);
|
||||
return (NULL);
|
||||
}
|
||||
for (i = 0; i < links_cache->number_buckets; i++)
|
||||
links_cache->buckets[i] = NULL;
|
||||
return (links_cache);
|
||||
for (i = 0; i < res->number_buckets; i++)
|
||||
res->buckets[i] = NULL;
|
||||
return (res);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_linkresolver_free(struct archive_entry_linkresolver *links_cache)
|
||||
archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
|
||||
int fmt)
|
||||
{
|
||||
size_t i;
|
||||
int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
|
||||
|
||||
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;
|
||||
switch (fmtbase) {
|
||||
case ARCHIVE_FORMAT_CPIO:
|
||||
switch (fmt) {
|
||||
case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
|
||||
case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
|
||||
break;
|
||||
default:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ARCHIVE_FORMAT_MTREE:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
|
||||
break;
|
||||
case ARCHIVE_FORMAT_TAR:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
|
||||
break;
|
||||
default:
|
||||
res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
|
||||
break;
|
||||
}
|
||||
free(links_cache->buckets);
|
||||
links_cache->buckets = NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache,
|
||||
void
|
||||
archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
|
||||
{
|
||||
struct links_entry *le;
|
||||
|
||||
if (res == NULL)
|
||||
return;
|
||||
|
||||
if (res->buckets != NULL) {
|
||||
while ((le = next_entry(res)) != NULL)
|
||||
archive_entry_free(le->entry);
|
||||
free(res->buckets);
|
||||
res->buckets = NULL;
|
||||
}
|
||||
free(res);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_linkify(struct archive_entry_linkresolver *res,
|
||||
struct archive_entry **e, struct archive_entry **f)
|
||||
{
|
||||
struct links_entry *le;
|
||||
struct archive_entry *t;
|
||||
|
||||
*f = NULL; /* Default: Don't return a second entry. */
|
||||
|
||||
if (*e == NULL) {
|
||||
le = next_entry(res);
|
||||
if (le != NULL) {
|
||||
*e = le->entry;
|
||||
le->entry = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If it has only one link, then we're done. */
|
||||
if (archive_entry_nlink(*e) == 1)
|
||||
return;
|
||||
|
||||
switch (res->strategy) {
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
|
||||
le = find_entry(res, *e);
|
||||
if (le != NULL) {
|
||||
archive_entry_set_size(*e, 0);
|
||||
archive_entry_copy_hardlink(*e,
|
||||
archive_entry_pathname(le->canonical));
|
||||
} else
|
||||
insert_entry(res, *e);
|
||||
return;
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
|
||||
le = find_entry(res, *e);
|
||||
if (le != NULL) {
|
||||
archive_entry_copy_hardlink(*e,
|
||||
archive_entry_pathname(le->canonical));
|
||||
} else
|
||||
insert_entry(res, *e);
|
||||
return;
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
|
||||
/* This one is trivial. */
|
||||
return;
|
||||
case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
|
||||
le = find_entry(res, *e);
|
||||
if (le != NULL) {
|
||||
/*
|
||||
* Put the new entry in le, return the
|
||||
* old entry from le.
|
||||
*/
|
||||
t = *e;
|
||||
*e = le->entry;
|
||||
le->entry = t;
|
||||
/* Make the old entry into a hardlink. */
|
||||
archive_entry_set_size(*e, 0);
|
||||
archive_entry_copy_hardlink(*e,
|
||||
archive_entry_pathname(le->canonical));
|
||||
/* If we ran out of links, return the
|
||||
* final entry as well. */
|
||||
if (le->links == 0) {
|
||||
*f = le->entry;
|
||||
le->entry = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If we haven't seen it, tuck it away
|
||||
* for future use.
|
||||
*/
|
||||
le = insert_entry(res, *e);
|
||||
le->entry = *e;
|
||||
*e = NULL;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static struct links_entry *
|
||||
find_entry(struct archive_entry_linkresolver *res,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
struct links_entry *le, **new_buckets;
|
||||
int hash;
|
||||
size_t i, new_size;
|
||||
struct links_entry *le;
|
||||
int hash, bucket;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
int nlinks;
|
||||
|
||||
|
||||
/* Free a held name. */
|
||||
free(links_cache->last_name);
|
||||
links_cache->last_name = NULL;
|
||||
/* Free a held entry. */
|
||||
if (res->spare != NULL) {
|
||||
archive_entry_free(res->spare->canonical);
|
||||
archive_entry_free(res->spare->entry);
|
||||
free(res->spare);
|
||||
res->spare = NULL;
|
||||
}
|
||||
|
||||
/* If the links cache overflowed and got flushed, don't bother. */
|
||||
if (links_cache->buckets == NULL)
|
||||
if (res->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;
|
||||
}
|
||||
}
|
||||
hash = dev ^ ino;
|
||||
|
||||
/* 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) {
|
||||
bucket = hash % res->number_buckets;
|
||||
for (le = res->buckets[bucket]; le != NULL; le = le->next) {
|
||||
if (le->hash == hash
|
||||
&& dev == archive_entry_dev(le->canonical)
|
||||
&& ino == archive_entry_ino(le->canonical)) {
|
||||
/*
|
||||
* Decrement link count each time and release
|
||||
* the entry if it hits zero. This saves
|
||||
@ -177,46 +278,123 @@ archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache,
|
||||
*/
|
||||
--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.
|
||||
*/
|
||||
return (le);
|
||||
/* Remove it from this hash bucket. */
|
||||
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);
|
||||
if (res->buckets[bucket] == le)
|
||||
res->buckets[bucket] = le->next;
|
||||
res->number_entries--;
|
||||
/* Defer freeing this entry. */
|
||||
res->spare = le;
|
||||
return (le);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct links_entry *
|
||||
next_entry(struct archive_entry_linkresolver *res)
|
||||
{
|
||||
struct links_entry *le;
|
||||
size_t bucket;
|
||||
|
||||
/* Free a held entry. */
|
||||
if (res->spare != NULL) {
|
||||
archive_entry_free(res->spare->canonical);
|
||||
free(res->spare);
|
||||
res->spare = NULL;
|
||||
}
|
||||
|
||||
/* If the links cache overflowed and got flushed, don't bother. */
|
||||
if (res->buckets == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Look for next non-empty bucket in the links cache. */
|
||||
for (bucket = 0; bucket < res->number_buckets; bucket++) {
|
||||
le = res->buckets[bucket];
|
||||
if (le != NULL) {
|
||||
/* Remove it from this hash bucket. */
|
||||
if (le->next != NULL)
|
||||
le->next->previous = le->previous;
|
||||
res->buckets[bucket] = le->next;
|
||||
res->number_entries--;
|
||||
/* Defer freeing this entry. */
|
||||
res->spare = le;
|
||||
return (le);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct links_entry *
|
||||
insert_entry(struct archive_entry_linkresolver *res,
|
||||
struct archive_entry *entry)
|
||||
{
|
||||
struct links_entry *le;
|
||||
int hash, bucket;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
memset(le, 0, sizeof(*le));
|
||||
le->canonical = archive_entry_clone(entry);
|
||||
|
||||
/* If the links cache is getting too full, enlarge the hash table. */
|
||||
if (res->number_entries > res->number_buckets * 2)
|
||||
grow_hash(res);
|
||||
|
||||
hash = archive_entry_dev(entry) ^ archive_entry_ino(entry);
|
||||
bucket = hash % res->number_buckets;
|
||||
|
||||
/* 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];
|
||||
if (res->buckets[bucket] != NULL)
|
||||
res->buckets[bucket]->previous = le;
|
||||
res->number_entries++;
|
||||
le->next = res->buckets[bucket];
|
||||
le->previous = NULL;
|
||||
links_cache->buckets[hash] = le;
|
||||
le->dev = dev;
|
||||
le->ino = ino;
|
||||
le->links = nlinks - 1;
|
||||
return (NULL);
|
||||
res->buckets[bucket] = le;
|
||||
le->hash = hash;
|
||||
le->links = archive_entry_nlink(entry) - 1;
|
||||
return (le);
|
||||
}
|
||||
|
||||
static void
|
||||
grow_hash(struct archive_entry_linkresolver *res)
|
||||
{
|
||||
struct links_entry *le, **new_buckets;
|
||||
size_t new_size;
|
||||
size_t i, bucket;
|
||||
|
||||
/* Try to enlarge the bucket list. */
|
||||
new_size = res->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 < res->number_buckets; i++) {
|
||||
while (res->buckets[i] != NULL) {
|
||||
/* Remove entry from old bucket. */
|
||||
le = res->buckets[i];
|
||||
res->buckets[i] = le->next;
|
||||
|
||||
/* Add entry to new bucket. */
|
||||
bucket = le->hash % new_size;
|
||||
|
||||
if (new_buckets[bucket] != NULL)
|
||||
new_buckets[bucket]->previous =
|
||||
le;
|
||||
le->next = new_buckets[bucket];
|
||||
le->previous = NULL;
|
||||
new_buckets[bucket] = le;
|
||||
}
|
||||
}
|
||||
free(res->buckets);
|
||||
res->buckets = new_buckets;
|
||||
res->number_buckets = new_size;
|
||||
}
|
||||
}
|
||||
|
@ -28,17 +28,25 @@
|
||||
#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
|
||||
|
||||
#include "archive_string.h"
|
||||
|
||||
/*
|
||||
* Handle wide character (i.e., Unicode) and non-wide character
|
||||
* strings transparently.
|
||||
*
|
||||
*/
|
||||
|
||||
struct aes {
|
||||
const char *aes_mbs;
|
||||
char *aes_mbs_alloc;
|
||||
struct archive_string aes_mbs;
|
||||
struct archive_string aes_utf8;
|
||||
const wchar_t *aes_wcs;
|
||||
wchar_t *aes_wcs_alloc;
|
||||
/* Bitmap of which of the above are valid. Because we're lazy
|
||||
* about malloc-ing and reusing the underlying storage, we
|
||||
* can't rely on NULL pointers to indicate whether a string
|
||||
* has been set. */
|
||||
int aes_set;
|
||||
#define AES_SET_MBS 1
|
||||
#define AES_SET_UTF8 2
|
||||
#define AES_SET_WCS 4
|
||||
};
|
||||
|
||||
struct ae_acl {
|
||||
@ -128,8 +136,6 @@ struct archive_entry {
|
||||
dev_t aest_rdevminor;
|
||||
} ae_stat;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Use aes here so that we get transparent mbs<->wcs conversions.
|
||||
*/
|
||||
@ -141,15 +147,23 @@ struct archive_entry {
|
||||
struct aes ae_pathname; /* Name of entry */
|
||||
struct aes ae_symlink; /* symlink contents */
|
||||
struct aes ae_uname; /* Name of owner */
|
||||
unsigned char ae_hardlinkset;
|
||||
unsigned char ae_symlinkset;
|
||||
|
||||
/* Not used within libarchive; useful for some clients. */
|
||||
struct aes ae_sourcepath; /* Path this entry is sourced from. */
|
||||
|
||||
/* ACL support. */
|
||||
struct ae_acl *acl_head;
|
||||
struct ae_acl *acl_p;
|
||||
int acl_state; /* See acl_next for details. */
|
||||
wchar_t *acl_text_w;
|
||||
|
||||
/* extattr support. */
|
||||
struct ae_xattr *xattr_head;
|
||||
struct ae_xattr *xattr_p;
|
||||
|
||||
/* Miscellaneous. */
|
||||
char strmode[12];
|
||||
};
|
||||
|
||||
|
@ -36,6 +36,9 @@
|
||||
#ifndef ARCHIVE_PLATFORM_H_INCLUDED
|
||||
#define ARCHIVE_PLATFORM_H_INCLUDED
|
||||
|
||||
/* archive.h and archive_entry.h require this. */
|
||||
#define __LIBARCHIVE_BUILD 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "config_windows.h"
|
||||
#include "archive_windows.h"
|
||||
|
@ -56,6 +56,7 @@
|
||||
.\" #endif
|
||||
.Nm archive_read_data_into_fd ,
|
||||
.Nm archive_read_extract ,
|
||||
.Nm archive_read_extract2 ,
|
||||
.Nm archive_read_extract_set_progress_callback ,
|
||||
.Nm archive_read_close ,
|
||||
.Nm archive_read_finish
|
||||
@ -145,6 +146,12 @@
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "int flags"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo archive_read_extract2
|
||||
.Fa "struct archive *src"
|
||||
.Fa "struct archive_entry *"
|
||||
.Fa "struct archive *dest"
|
||||
.Fc
|
||||
.Ft void
|
||||
.Fo archive_read_extract_set_progress_callback
|
||||
.Fa "struct archive *"
|
||||
@ -314,6 +321,22 @@ The
|
||||
.Va flags
|
||||
argument is passed unmodified to
|
||||
.Xr archive_write_disk_set_options 3 .
|
||||
.It Fn archive_read_extract2
|
||||
This is another version of
|
||||
.Fn archive_read_extract
|
||||
that allows you to provide your own restore object.
|
||||
In particular, this allows you to override the standard lookup functions
|
||||
using
|
||||
.Xr archive_write_disk_set_group_lookup 3 ,
|
||||
and
|
||||
.Xr archive_write_disk_set_user_lookup 3 .
|
||||
Note that
|
||||
.Fn archive_read_extract2
|
||||
does not accept a
|
||||
.Va flags
|
||||
argument; you should use
|
||||
.Fn archive_write_disk_set_options
|
||||
to set the restore options yourself.
|
||||
.It Fn archive_read_extract_set_progress_callback
|
||||
Sets a pointer to a user-defined callback that can be used
|
||||
for updating progress displays during extraction.
|
||||
|
@ -82,34 +82,40 @@ get_extract(struct archive_read *a)
|
||||
int
|
||||
archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
struct extract *extract;
|
||||
int r, r2;
|
||||
|
||||
extract = get_extract(a);
|
||||
extract = get_extract((struct archive_read *)_a);
|
||||
if (extract == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
archive_write_disk_set_options(extract->ad, flags);
|
||||
return (archive_read_extract2(_a, entry, extract->ad));
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_extract2(struct archive *_a, struct archive_entry *entry,
|
||||
struct archive *ad)
|
||||
{
|
||||
struct archive_read *a = (struct archive_read *)_a;
|
||||
int r, r2;
|
||||
|
||||
/* Set up for this particular entry. */
|
||||
extract = a->extract;
|
||||
archive_write_disk_set_options(a->extract->ad, flags);
|
||||
archive_write_disk_set_skip_file(a->extract->ad,
|
||||
archive_write_disk_set_skip_file(ad,
|
||||
a->skip_file_dev, a->skip_file_ino);
|
||||
r = archive_write_header(a->extract->ad, entry);
|
||||
r = archive_write_header(ad, entry);
|
||||
if (r < ARCHIVE_WARN)
|
||||
r = ARCHIVE_WARN;
|
||||
if (r != ARCHIVE_OK)
|
||||
/* If _write_header failed, copy the error. */
|
||||
archive_copy_error(&a->archive, extract->ad);
|
||||
archive_copy_error(&a->archive, ad);
|
||||
else
|
||||
/* Otherwise, pour data into the entry. */
|
||||
r = copy_data(_a, a->extract->ad);
|
||||
r2 = archive_write_finish_entry(a->extract->ad);
|
||||
r = copy_data(_a, ad);
|
||||
r2 = archive_write_finish_entry(ad);
|
||||
if (r2 < ARCHIVE_WARN)
|
||||
r2 = ARCHIVE_WARN;
|
||||
/* Use the first message. */
|
||||
if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
|
||||
archive_copy_error(&a->archive, extract->ad);
|
||||
archive_copy_error(&a->archive, ad);
|
||||
/* Use the worst error return. */
|
||||
if (r2 < r)
|
||||
r = r2;
|
||||
|
@ -116,17 +116,29 @@ bid(const void *buff, size_t len)
|
||||
if (buffer[3] < '1' || buffer[3] > '9')
|
||||
return (0);
|
||||
bits_checked += 5;
|
||||
if (len < 5)
|
||||
return (bits_checked);
|
||||
|
||||
/*
|
||||
* Research Question: Can we do any more to verify that this
|
||||
* really is BZip2 format?? For 99.9% of the time, the above
|
||||
* test is sufficient, but it would be nice to do a more
|
||||
* thorough check. It's especially troubling that the BZip2
|
||||
* signature begins with all ASCII characters; a tar archive
|
||||
* whose first filename begins with 'BZh3' would potentially
|
||||
* fool this logic. (It may also be possible to guard against
|
||||
* such anomalies in archive_read_support_compression_none.)
|
||||
*/
|
||||
/* After BZh[1-9], there must be either a data block
|
||||
* which begins with 0x314159265359 or an end-of-data
|
||||
* marker of 0x177245385090. */
|
||||
|
||||
if (buffer[4] == 0x31) {
|
||||
/* Verify the data block signature. */
|
||||
size_t s = len;
|
||||
if (s > 10) s = 10;
|
||||
if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", s - 4) != 0)
|
||||
return (0);
|
||||
bits_checked += 8 * (s - 4);
|
||||
} else if (buffer[4] == 0x17) {
|
||||
/* Verify the end-of-data marker. */
|
||||
size_t s = len;
|
||||
if (s > 10) s = 10;
|
||||
if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", s - 4) != 0)
|
||||
return (0);
|
||||
bits_checked += 8 * (s - 4);
|
||||
} else
|
||||
return (0);
|
||||
|
||||
return (bits_checked);
|
||||
}
|
||||
|
@ -26,6 +26,24 @@
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
|
||||
/* This capability is only available on POSIX systems. */
|
||||
#if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL)
|
||||
|
||||
/*
|
||||
* On non-Posix systems, allow the program to build, but choke if
|
||||
* this function is actually invoked.
|
||||
*/
|
||||
int
|
||||
archive_read_support_compression_program(struct archive *_a, const char *cmd)
|
||||
{
|
||||
archive_set_error(_a, -1,
|
||||
"External compression programs not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
@ -313,3 +331,5 @@ archive_decompressor_program_finish(struct archive_read *a)
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */
|
||||
|
@ -350,12 +350,16 @@ archive_read_format_ar_read_header(struct archive_read *a,
|
||||
|
||||
/* Parse the size of the name, adjust the file size. */
|
||||
number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
|
||||
if ((off_t)number > ar->entry_bytes_remaining) {
|
||||
bsd_name_length = (size_t)number;
|
||||
/* Guard against the filename + trailing NUL
|
||||
* overflowing a size_t and against the filename size
|
||||
* being larger than the entire entry. */
|
||||
if (number > (uint64_t)(bsd_name_length + 1)
|
||||
|| (off_t)bsd_name_length > ar->entry_bytes_remaining) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Bad input file size");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
bsd_name_length = (size_t)number;
|
||||
ar->entry_bytes_remaining -= bsd_name_length;
|
||||
/* Adjust file size reported to client. */
|
||||
archive_entry_set_size(entry, ar->entry_bytes_remaining);
|
||||
|
@ -595,8 +595,11 @@ add_entry(struct iso9660 *iso9660, struct file_info *file)
|
||||
struct file_info **new_pending_files;
|
||||
int new_size = iso9660->pending_files_allocated * 2;
|
||||
|
||||
if (new_size < 1024)
|
||||
if (iso9660->pending_files_allocated < 1024)
|
||||
new_size = 1024;
|
||||
/* Overflow might keep us from growing the list. */
|
||||
if (new_size <= iso9660->pending_files_allocated)
|
||||
__archive_errx(1, "Out of memory");
|
||||
new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0]));
|
||||
if (new_pending_files == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
@ -908,6 +911,11 @@ fprintf(stderr, " *** Discarding CE data.\n");
|
||||
file->ce_size = 0;
|
||||
}
|
||||
|
||||
/* Don't waste time seeking for zero-length bodies. */
|
||||
if (file->size == 0) {
|
||||
file->offset = iso9660->current_position;
|
||||
}
|
||||
|
||||
/* If CE exists, find and read it now. */
|
||||
if (file->ce_offset > 0)
|
||||
offset = file->ce_offset;
|
||||
@ -1041,51 +1049,22 @@ isodate17(const unsigned char *v)
|
||||
return (time_from_tm(&tm));
|
||||
}
|
||||
|
||||
/*
|
||||
* timegm() converts a struct tm to a time_t, except it isn't standard,
|
||||
* so I provide my own function here that (ideally) is just a wrapper
|
||||
* for timegm().
|
||||
*/
|
||||
static time_t
|
||||
time_from_tm(struct tm *t)
|
||||
{
|
||||
#if HAVE_TIMEGM
|
||||
/* Use platform timegm() if available. */
|
||||
return (timegm(t));
|
||||
#elif HAVE_STRUCT_TM_TM_GMTOFF
|
||||
/*
|
||||
* Unfortunately, timegm() isn't standard. The standard
|
||||
* mktime() function is a close match, except that it uses
|
||||
* local timezone instead of GMT. You can compensate for
|
||||
* this by adding the timezone and DST offsets back in, at
|
||||
* the cost of two calls to mktime().
|
||||
*/
|
||||
mktime(t); /* Normalize the time and get the TZ offset. */
|
||||
t->tm_sec += t->tm_gmtoff; /* Try to adjust for the timezone and DST.*/
|
||||
if (t->tm_isdst)
|
||||
t->tm_hour -= 1;
|
||||
return (mktime(t)); /* Re-convert. */
|
||||
#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && defined(HAVE_TZSET)
|
||||
/* No timegm() and no tm_gmtoff, let's try forcing mktime() to UTC. */
|
||||
time_t ret;
|
||||
char *tz;
|
||||
|
||||
/* Reset the timezone, remember the old one. */
|
||||
tz = getenv("TZ");
|
||||
setenv("TZ", "UTC 0", 1);
|
||||
tzset();
|
||||
|
||||
ret = mktime(t);
|
||||
|
||||
/* Restore the previous timezone. */
|
||||
if (tz)
|
||||
setenv("TZ", tz, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
tzset();
|
||||
return ret;
|
||||
#else
|
||||
/* <sigh> We have no choice but to use localtime instead of UTC. */
|
||||
return (mktime(t));
|
||||
/* Else use direct calculation using POSIX assumptions. */
|
||||
/* First, fix up tm_yday based on the year/month/day. */
|
||||
mktime(t);
|
||||
/* Then we can compute timegm() from first principles. */
|
||||
return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600
|
||||
+ t->tm_yday * 86400 + (t->tm_year - 70) * 31536000
|
||||
+ ((t->tm_year - 69) / 4) * 86400 -
|
||||
((t->tm_year - 1) / 100) * 86400
|
||||
+ ((t->tm_year + 299) / 400) * 86400);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2008 Joerg Sonnenberger
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -54,11 +55,29 @@ __FBSDID("$FreeBSD$");
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#define MTREE_HAS_DEVICE 0x0001
|
||||
#define MTREE_HAS_FFLAGS 0x0002
|
||||
#define MTREE_HAS_GID 0x0004
|
||||
#define MTREE_HAS_GNAME 0x0008
|
||||
#define MTREE_HAS_MTIME 0x0010
|
||||
#define MTREE_HAS_NLINK 0x0020
|
||||
#define MTREE_HAS_PERM 0x0040
|
||||
#define MTREE_HAS_SIZE 0x0080
|
||||
#define MTREE_HAS_TYPE 0x0100
|
||||
#define MTREE_HAS_UID 0x0200
|
||||
#define MTREE_HAS_UNAME 0x0400
|
||||
|
||||
#define MTREE_HAS_OPTIONAL 0x0800
|
||||
|
||||
struct mtree_option {
|
||||
struct mtree_option *next;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct mtree_entry {
|
||||
struct mtree_entry *next;
|
||||
struct mtree_option *options;
|
||||
char *name;
|
||||
char *option_start;
|
||||
char *option_end;
|
||||
char full;
|
||||
char used;
|
||||
};
|
||||
@ -77,18 +96,20 @@ struct mtree {
|
||||
struct archive_string current_dir;
|
||||
struct archive_string contents_name;
|
||||
|
||||
struct archive_entry_linkresolver *resolver;
|
||||
|
||||
off_t cur_size, cur_offset;
|
||||
};
|
||||
|
||||
static int cleanup(struct archive_read *);
|
||||
static int mtree_bid(struct archive_read *);
|
||||
static int parse_file(struct archive_read *, struct archive_entry *,
|
||||
struct mtree *, struct mtree_entry *);
|
||||
struct mtree *, struct mtree_entry *, int *);
|
||||
static void parse_escapes(char *, struct mtree_entry *);
|
||||
static int parse_line(struct archive_read *, struct archive_entry *,
|
||||
struct mtree *, struct mtree_entry *);
|
||||
struct mtree *, struct mtree_entry *, int *);
|
||||
static int parse_keyword(struct archive_read *, struct mtree *,
|
||||
struct archive_entry *, char *, char *);
|
||||
struct archive_entry *, struct mtree_option *, int *);
|
||||
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);
|
||||
@ -97,6 +118,19 @@ static int read_header(struct archive_read *,
|
||||
struct archive_entry *);
|
||||
static int64_t mtree_atol10(char **);
|
||||
static int64_t mtree_atol8(char **);
|
||||
static int64_t mtree_atol(char **);
|
||||
|
||||
static void
|
||||
free_options(struct mtree_option *head)
|
||||
{
|
||||
struct mtree_option *next;
|
||||
|
||||
for (; head != NULL; head = next) {
|
||||
next = head->next;
|
||||
free(head->value);
|
||||
free(head);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
archive_read_support_format_mtree(struct archive *_a)
|
||||
@ -129,21 +163,20 @@ cleanup(struct archive_read *a)
|
||||
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_options(p->options);
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
archive_string_free(&mtree->line);
|
||||
archive_string_free(&mtree->current_dir);
|
||||
archive_string_free(&mtree->contents_name);
|
||||
archive_entry_linkresolver_free(mtree->resolver);
|
||||
|
||||
free(mtree->buff);
|
||||
free(mtree);
|
||||
(a->format->data) = NULL;
|
||||
@ -184,21 +217,205 @@ mtree_bid(struct archive_read *a)
|
||||
|
||||
/*
|
||||
* The extended mtree format permits multiple lines specifying
|
||||
* attributes for each file. Practically speaking, that means we have
|
||||
* attributes for each file. For those entries, only the last line
|
||||
* is actually used. Practically speaking, that means we have
|
||||
* to read the entire mtree file into memory up front.
|
||||
*
|
||||
* The parsing is done in two steps. First, it is decided if a line
|
||||
* changes the global defaults and if it is, processed accordingly.
|
||||
* Otherwise, the options of the line are merged with the current
|
||||
* global options.
|
||||
*/
|
||||
static int
|
||||
add_option(struct archive_read *a, struct mtree_option **global,
|
||||
const char *value, size_t len)
|
||||
{
|
||||
struct mtree_option *option;
|
||||
|
||||
if ((option = malloc(sizeof(*option))) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Can't allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if ((option->value = malloc(len + 1)) == NULL) {
|
||||
free(option);
|
||||
archive_set_error(&a->archive, errno, "Can't allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
memcpy(option->value, value, len);
|
||||
option->value[len] = '\0';
|
||||
option->next = *global;
|
||||
*global = option;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_option(struct mtree_option **global, const char *value, size_t len)
|
||||
{
|
||||
struct mtree_option *iter, *last;
|
||||
|
||||
last = NULL;
|
||||
for (iter = *global; iter != NULL; last = iter, iter = iter->next) {
|
||||
if (strncmp(iter->value, value, len) == 0 &&
|
||||
(iter->value[len] == '\0' ||
|
||||
iter->value[len] == '='))
|
||||
break;
|
||||
}
|
||||
if (iter == NULL)
|
||||
return;
|
||||
if (last == NULL)
|
||||
*global = iter->next;
|
||||
else
|
||||
last->next = iter->next;
|
||||
|
||||
free(iter->value);
|
||||
free(iter);
|
||||
}
|
||||
|
||||
static int
|
||||
process_global_set(struct archive_read *a,
|
||||
struct mtree_option **global, const char *line)
|
||||
{
|
||||
const char *next, *eq;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
line += 4;
|
||||
for (;;) {
|
||||
next = line + strspn(line, " \t\r\n");
|
||||
if (*next == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
line = next;
|
||||
next = line + strcspn(line, " \t\r\n");
|
||||
eq = strchr(line, '=');
|
||||
if (eq > next)
|
||||
len = next - line;
|
||||
else
|
||||
len = eq - line;
|
||||
|
||||
remove_option(global, line, len);
|
||||
r = add_option(a, global, line, next - line);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
line = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
process_global_unset(struct archive_read *a,
|
||||
struct mtree_option **global, const char *line)
|
||||
{
|
||||
const char *next;
|
||||
size_t len;
|
||||
|
||||
line += 6;
|
||||
if ((next = strchr(line, '=')) != NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"/unset shall not contain `='");
|
||||
return ARCHIVE_FATAL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
next = line + strspn(line, " \t\r\n");
|
||||
if (*next == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
line = next;
|
||||
len = strcspn(line, " \t\r\n");
|
||||
|
||||
if (len == 3 && strncmp(line, "all", 3) == 0) {
|
||||
free_options(*global);
|
||||
*global = NULL;
|
||||
} else {
|
||||
remove_option(global, line, len);
|
||||
}
|
||||
|
||||
line += len;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
process_add_entry(struct archive_read *a, struct mtree *mtree,
|
||||
struct mtree_option **global, const char *line,
|
||||
struct mtree_entry **last_entry)
|
||||
{
|
||||
struct mtree_entry *entry;
|
||||
struct mtree_option *iter;
|
||||
const char *next, *eq;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
if ((entry = malloc(sizeof(*entry))) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Can't allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
entry->next = NULL;
|
||||
entry->options = NULL;
|
||||
entry->name = NULL;
|
||||
entry->used = 0;
|
||||
entry->full = 0;
|
||||
|
||||
/* Add this entry to list. */
|
||||
if (*last_entry == NULL)
|
||||
mtree->entries = entry;
|
||||
else
|
||||
(*last_entry)->next = entry;
|
||||
*last_entry = entry;
|
||||
|
||||
len = strcspn(line, " \t\r\n");
|
||||
if ((entry->name = malloc(len + 1)) == NULL) {
|
||||
archive_set_error(&a->archive, errno, "Can't allocate memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
memcpy(entry->name, line, len);
|
||||
entry->name[len] = '\0';
|
||||
parse_escapes(entry->name, entry);
|
||||
|
||||
line += len;
|
||||
for (iter = *global; iter != NULL; iter = iter->next) {
|
||||
r = add_option(a, &entry->options, iter->value,
|
||||
strlen(iter->value));
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
next = line + strspn(line, " \t\r\n");
|
||||
if (*next == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
line = next;
|
||||
next = line + strcspn(line, " \t\r\n");
|
||||
eq = strchr(line, '=');
|
||||
if (eq > next)
|
||||
len = next - line;
|
||||
else
|
||||
len = eq - line;
|
||||
|
||||
remove_option(&entry->options, line, len);
|
||||
r = add_option(a, &entry->options, line, next - line);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
line = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
read_mtree(struct archive_read *a, struct mtree *mtree)
|
||||
{
|
||||
ssize_t len;
|
||||
uintmax_t counter;
|
||||
char *p;
|
||||
struct mtree_entry *mentry;
|
||||
struct mtree_entry *last_mentry = NULL;
|
||||
struct mtree_option *global;
|
||||
struct mtree_entry *last_entry;
|
||||
int r;
|
||||
|
||||
mtree->archive_format = ARCHIVE_FORMAT_MTREE_V1;
|
||||
mtree->archive_format_name = "mtree";
|
||||
|
||||
for (;;) {
|
||||
global = NULL;
|
||||
last_entry = NULL;
|
||||
r = ARCHIVE_OK;
|
||||
|
||||
for (counter = 1; ; ++counter) {
|
||||
len = readline(a, mtree, &p, 256);
|
||||
if (len == 0) {
|
||||
mtree->this_entry = mtree->entries;
|
||||
@ -216,46 +433,27 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
|
||||
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;
|
||||
if (*p != '/') {
|
||||
r = process_add_entry(a, mtree, &global, p,
|
||||
&last_entry);
|
||||
} else if (strncmp(p, "/set", 4) == 0) {
|
||||
if (p[4] != ' ' && p[4] != '\t')
|
||||
break;
|
||||
r = process_global_set(a, &global, p);
|
||||
} else if (strncmp(p, "/unset", 6) == 0) {
|
||||
if (p[6] != ' ' && p[6] != '\t')
|
||||
break;
|
||||
r = process_global_unset(a, &global, p);
|
||||
} else
|
||||
break;
|
||||
|
||||
/* 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';
|
||||
if (r != ARCHIVE_OK)
|
||||
return r;
|
||||
}
|
||||
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't parse line %ju", counter);
|
||||
return ARCHIVE_FATAL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -267,7 +465,7 @@ read_header(struct archive_read *a, struct archive_entry *entry)
|
||||
{
|
||||
struct mtree *mtree;
|
||||
char *p;
|
||||
int r;
|
||||
int r, use_next;
|
||||
|
||||
mtree = (struct mtree *)(a->format->data);
|
||||
|
||||
@ -277,6 +475,11 @@ read_header(struct archive_read *a, struct archive_entry *entry)
|
||||
}
|
||||
|
||||
if (mtree->entries == NULL) {
|
||||
mtree->resolver = archive_entry_linkresolver_new();
|
||||
if (mtree->resolver == NULL)
|
||||
return ARCHIVE_FATAL;
|
||||
archive_entry_linkresolver_set_strategy(mtree->resolver,
|
||||
ARCHIVE_FORMAT_MTREE);
|
||||
r = read_mtree(a, mtree);
|
||||
if (r != ARCHIVE_OK)
|
||||
return (r);
|
||||
@ -303,8 +506,10 @@ read_header(struct archive_read *a, struct archive_entry *entry)
|
||||
}
|
||||
}
|
||||
if (!mtree->this_entry->used) {
|
||||
r = parse_file(a, entry, mtree, mtree->this_entry);
|
||||
return (r);
|
||||
use_next = 0;
|
||||
r = parse_file(a, entry, mtree, mtree->this_entry, &use_next);
|
||||
if (use_next == 0)
|
||||
return (r);
|
||||
}
|
||||
mtree->this_entry = mtree->this_entry->next;
|
||||
}
|
||||
@ -317,11 +522,13 @@ read_header(struct archive_read *a, struct archive_entry *entry)
|
||||
*/
|
||||
static int
|
||||
parse_file(struct archive_read *a, struct archive_entry *entry,
|
||||
struct mtree *mtree, struct mtree_entry *mentry)
|
||||
struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
|
||||
{
|
||||
struct stat st;
|
||||
const char *path;
|
||||
struct stat st_storage, *st;
|
||||
struct mtree_entry *mp;
|
||||
int r = ARCHIVE_OK, r1;
|
||||
struct archive_entry *sparse_entry;
|
||||
int r = ARCHIVE_OK, r1, parsed_kws, mismatched_type;
|
||||
|
||||
mentry->used = 1;
|
||||
|
||||
@ -330,7 +537,8 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
/* Parse options from this line. */
|
||||
r = parse_line(a, entry, mtree, mentry);
|
||||
parsed_kws = 0;
|
||||
r = parse_line(a, entry, mtree, mentry, &parsed_kws);
|
||||
|
||||
if (mentry->full) {
|
||||
archive_entry_copy_pathname(entry, mentry->name);
|
||||
@ -349,7 +557,8 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
|
||||
&& strcmp(mentry->name, mp->name) == 0) {
|
||||
/* Later lines override earlier ones. */
|
||||
mp->used = 1;
|
||||
r1 = parse_line(a, entry, mtree, mp);
|
||||
r1 = parse_line(a, entry, mtree, mp,
|
||||
&parsed_kws);
|
||||
if (r1 < r)
|
||||
r = r1;
|
||||
}
|
||||
@ -381,18 +590,37 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
|
||||
* disk.)
|
||||
*/
|
||||
mtree->fd = -1;
|
||||
if (archive_strlen(&mtree->contents_name) > 0) {
|
||||
mtree->fd = open(mtree->contents_name.s,
|
||||
if (archive_strlen(&mtree->contents_name) > 0)
|
||||
path = mtree->contents_name.s;
|
||||
else
|
||||
path = archive_entry_pathname(entry);
|
||||
|
||||
if (archive_entry_filetype(entry) == AE_IFREG ||
|
||||
archive_entry_filetype(entry) == AE_IFDIR) {
|
||||
mtree->fd = open(path,
|
||||
O_RDONLY | O_BINARY);
|
||||
if (mtree->fd < 0) {
|
||||
if (mtree->fd == -1 &&
|
||||
(errno != ENOENT ||
|
||||
archive_strlen(&mtree->contents_name) > 0)) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't open content=\"%s\"",
|
||||
mtree->contents_name.s);
|
||||
"Can't open %s", path);
|
||||
r = ARCHIVE_WARN;
|
||||
}
|
||||
} else if (archive_entry_filetype(entry) == AE_IFREG) {
|
||||
mtree->fd = open(archive_entry_pathname(entry),
|
||||
O_RDONLY | O_BINARY);
|
||||
}
|
||||
|
||||
st = &st_storage;
|
||||
if (mtree->fd >= 0) {
|
||||
if (fstat(mtree->fd, st) == -1) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Could not fstat %s", path);
|
||||
r = ARCHIVE_WARN;
|
||||
/* If we can't stat it, don't keep it open. */
|
||||
close(mtree->fd);
|
||||
mtree->fd = -1;
|
||||
st = NULL;
|
||||
}
|
||||
} else if (lstat(path, st) == -1) {
|
||||
st = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -400,30 +628,88 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
|
||||
* otherwise leave it as-is (it might have been set from
|
||||
* the mtree size= keyword).
|
||||
*/
|
||||
if (mtree->fd >= 0) {
|
||||
if (fstat(mtree->fd, &st) != 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"could not stat %s",
|
||||
archive_entry_pathname(entry));
|
||||
r = ARCHIVE_WARN;
|
||||
/* If we can't stat it, don't keep it open. */
|
||||
close(mtree->fd);
|
||||
mtree->fd = -1;
|
||||
} else if ((st.st_mode & S_IFMT) != S_IFREG) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"%s is not a regular file",
|
||||
archive_entry_pathname(entry));
|
||||
r = ARCHIVE_WARN;
|
||||
if (st != NULL) {
|
||||
mismatched_type = 0;
|
||||
if ((st->st_mode & S_IFMT) == S_IFREG &&
|
||||
archive_entry_filetype(entry) != AE_IFREG)
|
||||
mismatched_type = 1;
|
||||
if ((st->st_mode & S_IFMT) == S_IFLNK &&
|
||||
archive_entry_filetype(entry) != AE_IFLNK)
|
||||
mismatched_type = 1;
|
||||
if ((st->st_mode & S_IFSOCK) == S_IFSOCK &&
|
||||
archive_entry_filetype(entry) != AE_IFSOCK)
|
||||
mismatched_type = 1;
|
||||
if ((st->st_mode & S_IFMT) == S_IFCHR &&
|
||||
archive_entry_filetype(entry) != AE_IFCHR)
|
||||
mismatched_type = 1;
|
||||
if ((st->st_mode & S_IFMT) == S_IFBLK &&
|
||||
archive_entry_filetype(entry) != AE_IFBLK)
|
||||
mismatched_type = 1;
|
||||
if ((st->st_mode & S_IFMT) == S_IFDIR &&
|
||||
archive_entry_filetype(entry) != AE_IFDIR)
|
||||
mismatched_type = 1;
|
||||
if ((st->st_mode & S_IFMT) == S_IFIFO &&
|
||||
archive_entry_filetype(entry) != AE_IFIFO)
|
||||
mismatched_type = 1;
|
||||
|
||||
if (mismatched_type) {
|
||||
if ((parsed_kws & MTREE_HAS_OPTIONAL) == 0) {
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
"mtree specification has different type for %s",
|
||||
archive_entry_pathname(entry));
|
||||
r = ARCHIVE_WARN;
|
||||
} else {
|
||||
*use_next = 1;
|
||||
}
|
||||
/* Don't hold a non-regular file open. */
|
||||
close(mtree->fd);
|
||||
mtree->fd = -1;
|
||||
} else {
|
||||
archive_entry_set_size(entry, st.st_size);
|
||||
archive_entry_set_ino(entry, st.st_ino);
|
||||
archive_entry_set_dev(entry, st.st_dev);
|
||||
archive_entry_set_nlink(entry, st.st_nlink);
|
||||
st = NULL;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (st != NULL) {
|
||||
if ((parsed_kws & MTREE_HAS_DEVICE) == 0 &&
|
||||
(archive_entry_filetype(entry) == AE_IFCHR ||
|
||||
archive_entry_filetype(entry) == AE_IFBLK))
|
||||
archive_entry_set_rdev(entry, st->st_rdev);
|
||||
if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0)
|
||||
archive_entry_set_gid(entry, st->st_gid);
|
||||
if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0)
|
||||
archive_entry_set_uid(entry, st->st_uid);
|
||||
if ((parsed_kws & MTREE_HAS_MTIME) == 0) {
|
||||
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
|
||||
archive_entry_set_mtime(entry, st->st_mtime,
|
||||
st->st_mtimespec.tv_nsec);
|
||||
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
|
||||
archive_entry_set_mtime(entry, st->st_mtime,
|
||||
st->st_mtim.tv_nsec);
|
||||
#else
|
||||
archive_entry_set_mtime(entry, st->st_mtime, 0);
|
||||
#endif
|
||||
}
|
||||
if ((parsed_kws & MTREE_HAS_NLINK) == 0)
|
||||
archive_entry_set_nlink(entry, st->st_nlink);
|
||||
if ((parsed_kws & MTREE_HAS_PERM) == 0)
|
||||
archive_entry_set_perm(entry, st->st_mode);
|
||||
if ((parsed_kws & MTREE_HAS_SIZE) == 0)
|
||||
archive_entry_set_size(entry, st->st_size);
|
||||
archive_entry_set_ino(entry, st->st_ino);
|
||||
archive_entry_set_dev(entry, st->st_dev);
|
||||
|
||||
archive_entry_linkify(mtree->resolver, &entry, &sparse_entry);
|
||||
} else if (parsed_kws & MTREE_HAS_OPTIONAL) {
|
||||
/*
|
||||
* Couldn't open the entry, stat it or the on-disk type
|
||||
* didn't match. If this entry is optional, just ignore it
|
||||
* and read the next header entry.
|
||||
*/
|
||||
*use_next = 1;
|
||||
return ARCHIVE_OK;
|
||||
}
|
||||
|
||||
mtree->cur_size = archive_entry_size(entry);
|
||||
mtree->offset = 0;
|
||||
|
||||
@ -435,36 +721,82 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
|
||||
*/
|
||||
static int
|
||||
parse_line(struct archive_read *a, struct archive_entry *entry,
|
||||
struct mtree *mtree, struct mtree_entry *mp)
|
||||
struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)
|
||||
{
|
||||
char *p, *q;
|
||||
struct mtree_option *iter;
|
||||
int r = ARCHIVE_OK, r1;
|
||||
|
||||
p = mp->option_start;
|
||||
while (p < mp->option_end) {
|
||||
q = p + strlen(p);
|
||||
r1 = parse_keyword(a, mtree, entry, p, q);
|
||||
for (iter = mp->options; iter != NULL; iter = iter->next) {
|
||||
r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);
|
||||
if (r1 < r)
|
||||
r = r1;
|
||||
p = q + 1;
|
||||
}
|
||||
if ((*parsed_kws & MTREE_HAS_TYPE) == 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Missing type keyword in mtree specification");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
return (r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Device entries have one of the following forms:
|
||||
* raw dev_t
|
||||
* format,major,minor[,subdevice]
|
||||
*
|
||||
* Just use major and minor, no translation etc is done
|
||||
* between formats.
|
||||
*/
|
||||
static int
|
||||
parse_device(struct archive *a, struct archive_entry *entry, char *val)
|
||||
{
|
||||
char *comma1, *comma2;
|
||||
|
||||
comma1 = strchr(val, ',');
|
||||
if (comma1 == NULL) {
|
||||
archive_entry_set_dev(entry, mtree_atol10(&val));
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
++comma1;
|
||||
comma2 = strchr(comma1, ',');
|
||||
if (comma1 == NULL) {
|
||||
archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Malformed device attribute");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
++comma2;
|
||||
archive_entry_set_rdevmajor(entry, mtree_atol(&comma1));
|
||||
archive_entry_set_rdevminor(entry, mtree_atol(&comma2));
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a single keyword and its value.
|
||||
*/
|
||||
static int
|
||||
parse_keyword(struct archive_read *a, struct mtree *mtree,
|
||||
struct archive_entry *entry, char *key, char *end)
|
||||
struct archive_entry *entry, struct mtree_option *option, int *parsed_kws)
|
||||
{
|
||||
char *val;
|
||||
char *val, *key;
|
||||
|
||||
key = option->value;
|
||||
|
||||
if (end == key)
|
||||
return (ARCHIVE_OK);
|
||||
if (*key == '\0')
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
if (strcmp(key, "optional") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_OPTIONAL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
if (strcmp(key, "ignore") == 0) {
|
||||
/*
|
||||
* The mtree processing is not recursive, so
|
||||
* recursion will only happen for explicitly listed
|
||||
* entries.
|
||||
*/
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
val = strchr(key, '=');
|
||||
if (val == NULL) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
@ -483,38 +815,93 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
|
||||
archive_strcpy(&mtree->contents_name, val);
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "cksum") == 0)
|
||||
break;
|
||||
case 'd':
|
||||
if (strcmp(key, "device") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_DEVICE;
|
||||
return parse_device(&a->archive, entry, val);
|
||||
}
|
||||
case 'f':
|
||||
if (strcmp(key, "flags") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_FFLAGS;
|
||||
archive_entry_copy_fflags_text(entry, val);
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
if (strcmp(key, "gid") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_GID;
|
||||
archive_entry_set_gid(entry, mtree_atol10(&val));
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "gname") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_GNAME;
|
||||
archive_entry_copy_gname(entry, val);
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
if (strcmp(key, "link") == 0) {
|
||||
archive_entry_set_link(entry, val);
|
||||
archive_entry_copy_symlink(entry, val);
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)
|
||||
break;
|
||||
if (strcmp(key, "mode") == 0) {
|
||||
if (val[0] == '0') {
|
||||
if (val[0] >= '0' && val[0] <= '9') {
|
||||
*parsed_kws |= MTREE_HAS_PERM;
|
||||
archive_entry_set_perm(entry,
|
||||
mtree_atol8(&val));
|
||||
} else
|
||||
} else {
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Symbolic mode \"%s\" unsupported", val);
|
||||
return ARCHIVE_WARN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
if (strcmp(key, "nlink") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_NLINK;
|
||||
archive_entry_set_nlink(entry, mtree_atol10(&val));
|
||||
break;
|
||||
}
|
||||
case 'r':
|
||||
if (strcmp(key, "rmd160") == 0 ||
|
||||
strcmp(key, "rmd160digest") == 0)
|
||||
break;
|
||||
case 's':
|
||||
if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0)
|
||||
break;
|
||||
if (strcmp(key, "sha256") == 0 ||
|
||||
strcmp(key, "sha256digest") == 0)
|
||||
break;
|
||||
if (strcmp(key, "sha384") == 0 ||
|
||||
strcmp(key, "sha384digest") == 0)
|
||||
break;
|
||||
if (strcmp(key, "sha512") == 0 ||
|
||||
strcmp(key, "sha512digest") == 0)
|
||||
break;
|
||||
if (strcmp(key, "size") == 0) {
|
||||
archive_entry_set_size(entry, mtree_atol10(&val));
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
if (strcmp(key, "tags") == 0) {
|
||||
/*
|
||||
* Comma delimited list of tags.
|
||||
* Ignore the tags for now, but the interface
|
||||
* should be extended to allow inclusion/exclusion.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "time") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_MTIME;
|
||||
archive_entry_set_mtime(entry, mtree_atol10(&val), 0);
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "type") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_TYPE;
|
||||
switch (val[0]) {
|
||||
case 'b':
|
||||
if (strcmp(val, "block") == 0) {
|
||||
@ -554,16 +941,14 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
|
||||
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) {
|
||||
*parsed_kws |= MTREE_HAS_UID;
|
||||
archive_entry_set_uid(entry, mtree_atol10(&val));
|
||||
break;
|
||||
}
|
||||
if (strcmp(key, "uname") == 0) {
|
||||
*parsed_kws |= MTREE_HAS_UNAME;
|
||||
archive_entry_copy_uname(entry, val);
|
||||
break;
|
||||
}
|
||||
@ -643,6 +1028,13 @@ parse_escapes(char *src, struct mtree_entry *mentry)
|
||||
char *dest = src;
|
||||
char c;
|
||||
|
||||
/*
|
||||
* The current directory is somewhat special, it should be archived
|
||||
* only once as it will confuse extraction otherwise.
|
||||
*/
|
||||
if (strcmp(src, ".") == 0)
|
||||
mentry->full = 1;
|
||||
|
||||
while (*src != '\0') {
|
||||
c = *src++;
|
||||
if (c == '/' && mentry != NULL)
|
||||
@ -724,6 +1116,66 @@ mtree_atol10(char **p)
|
||||
return (sign < 0) ? -l : 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_atol16(char **p)
|
||||
{
|
||||
int64_t l, limit, last_digit_limit;
|
||||
int base, digit, sign;
|
||||
|
||||
base = 16;
|
||||
limit = INT64_MAX / base;
|
||||
last_digit_limit = INT64_MAX % base;
|
||||
|
||||
if (**p == '-') {
|
||||
sign = -1;
|
||||
++(*p);
|
||||
} else
|
||||
sign = 1;
|
||||
|
||||
l = 0;
|
||||
if (**p >= '0' && **p <= '9')
|
||||
digit = **p - '0';
|
||||
else if (**p >= 'a' && **p <= 'f')
|
||||
digit = **p - 'a' + 10;
|
||||
else if (**p >= 'A' && **p <= 'F')
|
||||
digit = **p - 'A' + 10;
|
||||
else
|
||||
digit = -1;
|
||||
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;
|
||||
if (**p >= '0' && **p <= '9')
|
||||
digit = **p - '0';
|
||||
else if (**p >= 'a' && **p <= 'f')
|
||||
digit = **p - 'a' + 10;
|
||||
else if (**p >= 'A' && **p <= 'F')
|
||||
digit = **p - 'A' + 10;
|
||||
else
|
||||
digit = -1;
|
||||
}
|
||||
return (sign < 0) ? -l : l;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
mtree_atol(char **p)
|
||||
{
|
||||
if (**p != '0')
|
||||
return mtree_atol10(p);
|
||||
if ((*p)[1] == 'x' || (*p)[1] == 'X') {
|
||||
*p += 2;
|
||||
return mtree_atol16(p);
|
||||
}
|
||||
return mtree_atol8(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns length of line (including trailing newline)
|
||||
* or negative on error. 'start' argument is updated to
|
||||
|
@ -145,6 +145,8 @@ struct sparse_block {
|
||||
struct tar {
|
||||
struct archive_string acl_text;
|
||||
struct archive_string entry_pathname;
|
||||
/* For "GNU.sparse.name" and other similar path extensions. */
|
||||
struct archive_string entry_pathname_override;
|
||||
struct archive_string entry_linkpath;
|
||||
struct archive_string entry_uname;
|
||||
struct archive_string entry_gname;
|
||||
@ -272,6 +274,7 @@ archive_read_format_tar_cleanup(struct archive_read *a)
|
||||
gnu_clear_sparse_list(tar);
|
||||
archive_string_free(&tar->acl_text);
|
||||
archive_string_free(&tar->entry_pathname);
|
||||
archive_string_free(&tar->entry_pathname_override);
|
||||
archive_string_free(&tar->entry_linkpath);
|
||||
archive_string_free(&tar->entry_uname);
|
||||
archive_string_free(&tar->entry_gname);
|
||||
@ -1174,7 +1177,6 @@ pax_header(struct archive_read *a, struct tar *tar,
|
||||
size_t attr_length, l, line_length;
|
||||
char *line, *p;
|
||||
char *key, *value;
|
||||
wchar_t *wp;
|
||||
int err, err2;
|
||||
|
||||
attr_length = strlen(attr);
|
||||
@ -1182,6 +1184,7 @@ pax_header(struct archive_read *a, struct tar *tar,
|
||||
archive_string_empty(&(tar->entry_gname));
|
||||
archive_string_empty(&(tar->entry_linkpath));
|
||||
archive_string_empty(&(tar->entry_pathname));
|
||||
archive_string_empty(&(tar->entry_pathname_override));
|
||||
archive_string_empty(&(tar->entry_uname));
|
||||
err = ARCHIVE_OK;
|
||||
while (attr_length > 0) {
|
||||
@ -1257,13 +1260,13 @@ pax_header(struct archive_read *a, struct tar *tar,
|
||||
if (tar->pax_hdrcharset_binary)
|
||||
archive_entry_copy_gname(entry, value);
|
||||
else {
|
||||
wp = utf8_decode(tar, value, strlen(value));
|
||||
if (wp == NULL) {
|
||||
archive_entry_copy_gname(entry, value);
|
||||
if (err > ARCHIVE_WARN)
|
||||
err = ARCHIVE_WARN;
|
||||
} else
|
||||
archive_entry_copy_gname_w(entry, wp);
|
||||
if (!archive_entry_update_gname_utf8(entry, value)) {
|
||||
err = ARCHIVE_WARN;
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Gname in pax header can't "
|
||||
"be converted to current locale.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (archive_strlen(&(tar->entry_linkpath)) > 0) {
|
||||
@ -1271,27 +1274,40 @@ pax_header(struct archive_read *a, struct tar *tar,
|
||||
if (tar->pax_hdrcharset_binary)
|
||||
archive_entry_copy_link(entry, value);
|
||||
else {
|
||||
wp = utf8_decode(tar, value, strlen(value));
|
||||
if (wp == NULL) {
|
||||
archive_entry_copy_link(entry, value);
|
||||
if (err > ARCHIVE_WARN)
|
||||
err = ARCHIVE_WARN;
|
||||
} else
|
||||
archive_entry_copy_link_w(entry, wp);
|
||||
if (!archive_entry_update_link_utf8(entry, value)) {
|
||||
err = ARCHIVE_WARN;
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Linkname in pax header can't "
|
||||
"be converted to current locale.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (archive_strlen(&(tar->entry_pathname)) > 0) {
|
||||
/*
|
||||
* Some extensions (such as the GNU sparse file extensions)
|
||||
* deliberately store a synthetic name under the regular 'path'
|
||||
* attribute and the real file name under a different attribute.
|
||||
* Since we're supposed to not care about the order, we
|
||||
* have no choice but to store all of the various filenames
|
||||
* we find and figure it all out afterwards. This is the
|
||||
* figuring out part.
|
||||
*/
|
||||
value = NULL;
|
||||
if (archive_strlen(&(tar->entry_pathname_override)) > 0)
|
||||
value = tar->entry_pathname_override.s;
|
||||
else if (archive_strlen(&(tar->entry_pathname)) > 0)
|
||||
value = tar->entry_pathname.s;
|
||||
if (value != NULL) {
|
||||
if (tar->pax_hdrcharset_binary)
|
||||
archive_entry_copy_pathname(entry, value);
|
||||
else {
|
||||
wp = utf8_decode(tar, value, strlen(value));
|
||||
if (wp == NULL) {
|
||||
archive_entry_copy_pathname(entry, value);
|
||||
if (err > ARCHIVE_WARN)
|
||||
err = ARCHIVE_WARN;
|
||||
} else
|
||||
archive_entry_copy_pathname_w(entry, wp);
|
||||
if (!archive_entry_update_pathname_utf8(entry, value)) {
|
||||
err = ARCHIVE_WARN;
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Pathname in pax header can't be "
|
||||
"converted to current locale.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (archive_strlen(&(tar->entry_uname)) > 0) {
|
||||
@ -1299,13 +1315,13 @@ pax_header(struct archive_read *a, struct tar *tar,
|
||||
if (tar->pax_hdrcharset_binary)
|
||||
archive_entry_copy_uname(entry, value);
|
||||
else {
|
||||
wp = utf8_decode(tar, value, strlen(value));
|
||||
if (wp == NULL) {
|
||||
archive_entry_copy_uname(entry, value);
|
||||
if (err > ARCHIVE_WARN)
|
||||
err = ARCHIVE_WARN;
|
||||
} else
|
||||
archive_entry_copy_uname_w(entry, wp);
|
||||
if (!archive_entry_update_uname_utf8(entry, value)) {
|
||||
err = ARCHIVE_WARN;
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Uname in pax header can't "
|
||||
"be converted to current locale.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return (err);
|
||||
@ -1415,11 +1431,13 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
|
||||
tar->sparse_gnu_pending = 1;
|
||||
}
|
||||
if (strcmp(key, "GNU.sparse.name") == 0) {
|
||||
wp = utf8_decode(tar, value, strlen(value));
|
||||
if (wp != NULL)
|
||||
archive_entry_copy_pathname_w(entry, wp);
|
||||
else
|
||||
archive_entry_copy_pathname(entry, value);
|
||||
/*
|
||||
* The real filename; when storing sparse
|
||||
* files, GNU tar puts a synthesized name into
|
||||
* the regular 'path' attribute in an attempt
|
||||
* to limit confusion. ;-)
|
||||
*/
|
||||
archive_strcpy(&(tar->entry_pathname_override), value);
|
||||
}
|
||||
if (strcmp(key, "GNU.sparse.realsize") == 0) {
|
||||
tar->realsize = tar_atol10(value, strlen(value));
|
||||
@ -1455,9 +1473,7 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
|
||||
archive_entry_set_rdevminor(entry,
|
||||
tar_atol10(value, strlen(value)));
|
||||
} else if (strcmp(key, "SCHILY.fflags")==0) {
|
||||
wp = utf8_decode(tar, value, strlen(value));
|
||||
/* TODO: if (wp == NULL) */
|
||||
archive_entry_copy_fflags_text_w(entry, wp);
|
||||
archive_entry_copy_fflags_text(entry, value);
|
||||
} else if (strcmp(key, "SCHILY.dev")==0) {
|
||||
archive_entry_set_dev(entry,
|
||||
tar_atol10(value, strlen(value)));
|
||||
@ -2287,7 +2303,7 @@ base64_decode(const char *s, size_t len, size_t *out_len)
|
||||
|
||||
/* Allocate enough space to hold the entire output. */
|
||||
/* Note that we may not use all of this... */
|
||||
out = (char *)malloc((len * 3 + 3) / 4);
|
||||
out = (char *)malloc(len - len / 4 + 1);
|
||||
if (out == NULL) {
|
||||
*out_len = 0;
|
||||
return (NULL);
|
||||
|
@ -162,11 +162,9 @@ archive_read_support_format_zip(struct archive *_a)
|
||||
static int
|
||||
archive_read_format_zip_bid(struct archive_read *a)
|
||||
{
|
||||
int bid = 0;
|
||||
const char *p;
|
||||
|
||||
if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP)
|
||||
bid += 1;
|
||||
const void *buff;
|
||||
size_t bytes_avail;
|
||||
|
||||
if ((p = __archive_read_ahead(a, 4)) == NULL)
|
||||
return (-1);
|
||||
@ -184,9 +182,104 @@ archive_read_format_zip_bid(struct archive_read *a)
|
||||
|| (p[2] == '0' && p[3] == '0'))
|
||||
return (30);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to handle self-extracting archives
|
||||
* by noting a PE header and searching forward
|
||||
* up to 64k for a 'PK\003\004' marker.
|
||||
*/
|
||||
if (p[0] == 'M' && p[1] == 'Z') {
|
||||
/*
|
||||
* TODO: Additional checks that this really is a PE
|
||||
* file before we invoke the 128k lookahead below.
|
||||
* No point in allocating a bigger lookahead buffer
|
||||
* if we don't need to.
|
||||
*/
|
||||
/*
|
||||
* TODO: Of course, the compression layer lookahead
|
||||
* buffers aren't dynamically sized yet; they should be.
|
||||
*/
|
||||
bytes_avail = (a->decompressor->read_ahead)(a, &buff, 128*1024);
|
||||
p = (const char *)buff;
|
||||
|
||||
/*
|
||||
* TODO: Optimize by jumping forward based on values
|
||||
* in the PE header. Note that we don't need to be
|
||||
* exact, but we mustn't skip too far. The search
|
||||
* below will compensate if we undershoot. Skipping
|
||||
* will also reduce the chance of false positives
|
||||
* (which is not really all that high to begin with,
|
||||
* so maybe skipping isn't really necessary).
|
||||
*/
|
||||
|
||||
while (p < bytes_avail + (const char *)buff) {
|
||||
if (p[0] == 'P' && p[1] == 'K' /* "PK" signature */
|
||||
&& p[2] == 3 && p[3] == 4 /* File entry */
|
||||
&& p[8] == 8 /* compression == deflate */
|
||||
&& p[9] == 0 /* High byte of compression */
|
||||
)
|
||||
{
|
||||
return (30);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search forward for a "PK\003\004" file header. This handles the
|
||||
* case of self-extracting archives, where there is an executable
|
||||
* prepended to the ZIP archive.
|
||||
*/
|
||||
static int
|
||||
skip_sfx(struct archive_read *a)
|
||||
{
|
||||
const void *h;
|
||||
const char *p, *q;
|
||||
size_t skip, bytes;
|
||||
|
||||
/*
|
||||
* TODO: We should be able to skip forward by a bunch
|
||||
* by lifting some values from the PE header. We don't
|
||||
* need to be exact (we're still going to search forward
|
||||
* to find the header), but it will speed things up and
|
||||
* reduce the chance of a false positive.
|
||||
*/
|
||||
for (;;) {
|
||||
bytes = (a->decompressor->read_ahead)(a, &h, 4096);
|
||||
if (bytes < 4)
|
||||
return (ARCHIVE_FATAL);
|
||||
p = h;
|
||||
q = p + bytes;
|
||||
|
||||
/*
|
||||
* Scan ahead until we find something that looks
|
||||
* like the zip header.
|
||||
*/
|
||||
while (p + 4 < q) {
|
||||
switch (p[3]) {
|
||||
case '\004':
|
||||
/* TODO: Additional verification here. */
|
||||
if (memcmp("PK\003\004", p, 4) == 0) {
|
||||
skip = p - (const char *)h;
|
||||
(a->decompressor->consume)(a, skip);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
p += 4;
|
||||
break;
|
||||
case '\003': p += 1; break;
|
||||
case 'K': p += 2; break;
|
||||
case 'P': p += 3; break;
|
||||
default: p += 4; break;
|
||||
}
|
||||
}
|
||||
skip = p - (const char *)h;
|
||||
(a->decompressor->consume)(a, skip);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
archive_read_format_zip_read_header(struct archive_read *a,
|
||||
struct archive_entry *entry)
|
||||
@ -194,6 +287,7 @@ archive_read_format_zip_read_header(struct archive_read *a,
|
||||
const void *h;
|
||||
const char *signature;
|
||||
struct zip *zip;
|
||||
int r = ARCHIVE_OK, r1;
|
||||
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
|
||||
if (a->archive.archive_format_name == NULL)
|
||||
@ -209,6 +303,16 @@ archive_read_format_zip_read_header(struct archive_read *a,
|
||||
return (ARCHIVE_FATAL);
|
||||
|
||||
signature = (const char *)h;
|
||||
if (signature[0] == 'M' && signature[1] == 'Z') {
|
||||
/* This is an executable? Must be self-extracting... */
|
||||
r = skip_sfx(a);
|
||||
if (r < ARCHIVE_WARN)
|
||||
return (r);
|
||||
if ((h = __archive_read_ahead(a, 4)) == NULL)
|
||||
return (ARCHIVE_FATAL);
|
||||
signature = (const char *)h;
|
||||
}
|
||||
|
||||
if (signature[0] != 'P' || signature[1] != 'K') {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Bad ZIP file");
|
||||
@ -239,7 +343,10 @@ archive_read_format_zip_read_header(struct archive_read *a,
|
||||
|
||||
if (signature[2] == '\003' && signature[3] == '\004') {
|
||||
/* Regular file entry. */
|
||||
return (zip_read_file_header(a, entry, zip));
|
||||
r1 = zip_read_file_header(a, entry, zip);
|
||||
if (r1 != ARCHIVE_OK)
|
||||
return (r1);
|
||||
return (r);
|
||||
}
|
||||
|
||||
if (signature[2] == '\005' && signature[3] == '\006') {
|
||||
|
@ -37,6 +37,17 @@ __FBSDID("$FreeBSD$");
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
|
||||
#ifdef __sgi
|
||||
/*
|
||||
* The following prototype is missing on IRXI,
|
||||
* even though the function is implemented in libc.
|
||||
*/
|
||||
size_t wcrtomb(char *, wchar_t, mbstate_t *);
|
||||
#endif
|
||||
|
||||
#include "archive_private.h"
|
||||
#include "archive_string.h"
|
||||
@ -55,11 +66,15 @@ __archive_string_append(struct archive_string *as, const char *p, size_t s)
|
||||
void
|
||||
__archive_string_copy(struct archive_string *dest, struct archive_string *src)
|
||||
{
|
||||
if (__archive_string_ensure(dest, src->length + 1) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
memcpy(dest->s, src->s, src->length);
|
||||
dest->length = src->length;
|
||||
dest->s[dest->length] = 0;
|
||||
if (src->length == 0)
|
||||
dest->length = 0;
|
||||
else {
|
||||
if (__archive_string_ensure(dest, src->length + 1) == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
memcpy(dest->s, src->s, src->length);
|
||||
dest->length = src->length;
|
||||
dest->s[dest->length] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -67,21 +82,52 @@ __archive_string_free(struct archive_string *as)
|
||||
{
|
||||
as->length = 0;
|
||||
as->buffer_length = 0;
|
||||
if (as->s != NULL)
|
||||
if (as->s != NULL) {
|
||||
free(as->s);
|
||||
as->s = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns NULL on any allocation failure. */
|
||||
struct archive_string *
|
||||
__archive_string_ensure(struct archive_string *as, size_t s)
|
||||
{
|
||||
/* If buffer is already big enough, don't reallocate. */
|
||||
if (as->s && (s <= as->buffer_length))
|
||||
return (as);
|
||||
|
||||
/*
|
||||
* Growing the buffer at least exponentially ensures that
|
||||
* append operations are always linear in the number of
|
||||
* characters appended. Using a smaller growth rate for
|
||||
* larger buffers reduces memory waste somewhat at the cost of
|
||||
* a larger constant factor.
|
||||
*/
|
||||
if (as->buffer_length < 32)
|
||||
/* Start with a minimum 32-character buffer. */
|
||||
as->buffer_length = 32;
|
||||
while (as->buffer_length < s)
|
||||
else if (as->buffer_length < 8192)
|
||||
/* Buffers under 8k are doubled for speed. */
|
||||
as->buffer_length *= 2;
|
||||
else {
|
||||
/* Buffers 8k and over grow by at least 25% each time. */
|
||||
size_t old_length = as->buffer_length;
|
||||
as->buffer_length = (as->buffer_length * 5) / 4;
|
||||
/* Be safe: If size wraps, release buffer and return NULL. */
|
||||
if (as->buffer_length < old_length) {
|
||||
free(as->s);
|
||||
as->s = NULL;
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The computation above is a lower limit to how much we'll
|
||||
* grow the buffer. In any case, we have to grow it enough to
|
||||
* hold the request.
|
||||
*/
|
||||
if (as->buffer_length < s)
|
||||
as->buffer_length = s;
|
||||
/* Now we can reallocate the buffer. */
|
||||
as->s = (char *)realloc(as->s, as->buffer_length);
|
||||
if (as->s == NULL)
|
||||
return (NULL);
|
||||
@ -124,3 +170,206 @@ __archive_strappend_int(struct archive_string *as, int d, int base)
|
||||
__archive_strappend_char(as, digits[d % base]);
|
||||
return (as);
|
||||
}
|
||||
|
||||
/*
|
||||
* Home-grown wcrtomb for UTF-8.
|
||||
*/
|
||||
static size_t
|
||||
my_wcrtomb_utf8(char *p, wchar_t wc, mbstate_t *s)
|
||||
{
|
||||
(void)s; /* UNUSED */
|
||||
|
||||
if (p == NULL)
|
||||
return (0);
|
||||
if (wc <= 0x7f) {
|
||||
p[0] = (char)wc;
|
||||
return (1);
|
||||
}
|
||||
if (wc <= 0x7ff) {
|
||||
p[0] = 0xc0 | ((wc >> 6) & 0x1f);
|
||||
p[1] = 0x80 | (wc & 0x3f);
|
||||
return (2);
|
||||
}
|
||||
if (wc <= 0xffff) {
|
||||
p[0] = 0xe0 | ((wc >> 12) & 0x0f);
|
||||
p[1] = 0x80 | ((wc >> 6) & 0x3f);
|
||||
p[2] = 0x80 | (wc & 0x3f);
|
||||
return (3);
|
||||
}
|
||||
if (wc <= 0x1fffff) {
|
||||
p[0] = 0xf0 | ((wc >> 18) & 0x07);
|
||||
p[1] = 0x80 | ((wc >> 12) & 0x3f);
|
||||
p[2] = 0x80 | ((wc >> 6) & 0x3f);
|
||||
p[3] = 0x80 | (wc & 0x3f);
|
||||
return (4);
|
||||
}
|
||||
/* Unicode has no codes larger than 0x1fffff. */
|
||||
/*
|
||||
* Awkward point: UTF-8 <-> wchar_t conversions
|
||||
* can actually fail.
|
||||
*/
|
||||
return ((size_t)-1);
|
||||
}
|
||||
|
||||
static int
|
||||
my_wcstombs(struct archive_string *as, const wchar_t *w,
|
||||
size_t (*func)(char *, wchar_t, mbstate_t *))
|
||||
{
|
||||
size_t n;
|
||||
char *p;
|
||||
mbstate_t shift_state;
|
||||
char buff[256];
|
||||
|
||||
/*
|
||||
* Convert one wide char at a time into 'buff', whenever that
|
||||
* fills, append it to the string.
|
||||
*/
|
||||
p = buff;
|
||||
wcrtomb(NULL, L'\0', &shift_state);
|
||||
while (*w != L'\0') {
|
||||
/* Flush the buffer when we have <=16 bytes free. */
|
||||
/* (No encoding has a single character >16 bytes.) */
|
||||
if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) {
|
||||
*p = '\0';
|
||||
archive_strcat(as, buff);
|
||||
p = buff;
|
||||
}
|
||||
n = (*func)(p, *w++, &shift_state);
|
||||
if (n == (size_t)-1)
|
||||
return (-1);
|
||||
p += n;
|
||||
}
|
||||
*p = '\0';
|
||||
archive_strcat(as, buff);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translates a wide character string into UTF-8 and appends
|
||||
* to the archive_string. Note: returns NULL if conversion fails.
|
||||
*/
|
||||
struct archive_string *
|
||||
__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w)
|
||||
{
|
||||
if (my_wcstombs(as, w, my_wcrtomb_utf8))
|
||||
return (NULL);
|
||||
return (as);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translates a wide character string into current locale character set
|
||||
* and appends to the archive_string. Note: returns NULL if conversion
|
||||
* fails.
|
||||
*
|
||||
* TODO: use my_wcrtomb_utf8 if !HAVE_WCRTOMB (add configure logic first!)
|
||||
*/
|
||||
struct archive_string *
|
||||
__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
|
||||
{
|
||||
if (my_wcstombs(as, w, wcrtomb))
|
||||
return (NULL);
|
||||
return (as);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Home-grown mbrtowc for UTF-8. Some systems lack UTF-8
|
||||
* (or even lack mbrtowc()) and we need UTF-8 support for pax
|
||||
* format. So please don't replace this with a call to the
|
||||
* standard mbrtowc() function!
|
||||
*/
|
||||
static size_t
|
||||
my_mbrtowc_utf8(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
|
||||
{
|
||||
int ch;
|
||||
|
||||
/*
|
||||
* This argument is here to make the prototype identical to the
|
||||
* standard mbrtowc(), so I can build generic string processors
|
||||
* that just accept a pointer to a suitable mbrtowc() function.
|
||||
*/
|
||||
(void)ps; /* UNUSED */
|
||||
|
||||
/* Standard behavior: a NULL value for 's' just resets shift state. */
|
||||
if (s == NULL)
|
||||
return (0);
|
||||
/* If length argument is zero, don't look at the first character. */
|
||||
if (n <= 0)
|
||||
return ((size_t)-2);
|
||||
|
||||
/*
|
||||
* Decode 1-4 bytes depending on the value of the first byte.
|
||||
*/
|
||||
ch = (unsigned char)*s;
|
||||
if (ch == 0) {
|
||||
return (0); /* Standard: return 0 for end-of-string. */
|
||||
}
|
||||
if ((ch & 0x80) == 0) {
|
||||
*pwc = ch & 0x7f;
|
||||
return (1);
|
||||
}
|
||||
if ((ch & 0xe0) == 0xc0) {
|
||||
if (n < 2)
|
||||
return ((size_t)-2);
|
||||
if ((s[1] & 0xc0) != 0x80) return (size_t)-1;
|
||||
*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
|
||||
return (2);
|
||||
}
|
||||
if ((ch & 0xf0) == 0xe0) {
|
||||
if (n < 3)
|
||||
return ((size_t)-2);
|
||||
if ((s[1] & 0xc0) != 0x80) return (size_t)-1;
|
||||
if ((s[2] & 0xc0) != 0x80) return (size_t)-1;
|
||||
*pwc = ((ch & 0x0f) << 12)
|
||||
| ((s[1] & 0x3f) << 6)
|
||||
| (s[2] & 0x3f);
|
||||
return (3);
|
||||
}
|
||||
if ((ch & 0xf8) == 0xf0) {
|
||||
if (n < 4)
|
||||
return ((size_t)-2);
|
||||
if ((s[1] & 0xc0) != 0x80) return (size_t)-1;
|
||||
if ((s[2] & 0xc0) != 0x80) return (size_t)-1;
|
||||
if ((s[3] & 0xc0) != 0x80) return (size_t)-1;
|
||||
*pwc = ((ch & 0x07) << 18)
|
||||
| ((s[1] & 0x3f) << 12)
|
||||
| ((s[2] & 0x3f) << 6)
|
||||
| (s[3] & 0x3f);
|
||||
return (4);
|
||||
}
|
||||
/* Invalid first byte. */
|
||||
return ((size_t)-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a wide-character string by converting this archive_string
|
||||
* from UTF-8.
|
||||
*/
|
||||
wchar_t *
|
||||
__archive_string_utf8_w(struct archive_string *as)
|
||||
{
|
||||
wchar_t *ws, *dest;
|
||||
const char *src;
|
||||
size_t n;
|
||||
int err;
|
||||
|
||||
ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t));
|
||||
if (ws == NULL)
|
||||
__archive_errx(1, "Out of memory");
|
||||
err = 0;
|
||||
dest = ws;
|
||||
src = as->s;
|
||||
while (*src != '\0') {
|
||||
n = my_mbrtowc_utf8(dest, src, 8, NULL);
|
||||
if (n == 0)
|
||||
break;
|
||||
if (n == (size_t)-1 || n == (size_t)-2) {
|
||||
free(ws);
|
||||
return (NULL);
|
||||
}
|
||||
dest++;
|
||||
src += n;
|
||||
}
|
||||
*dest++ = L'\0';
|
||||
return (ws);
|
||||
}
|
||||
|
@ -33,6 +33,9 @@
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Basic resizable/reusable string support a la Java's "StringBuffer."
|
||||
@ -60,16 +63,22 @@ struct archive_string *
|
||||
__archive_strappend_char(struct archive_string *, char);
|
||||
#define archive_strappend_char __archive_strappend_char
|
||||
|
||||
/* Append a char to an archive_string using UTF8. */
|
||||
struct archive_string *
|
||||
__archive_strappend_char_UTF8(struct archive_string *, int);
|
||||
#define archive_strappend_char_UTF8 __archive_strappend_char_UTF8
|
||||
|
||||
/* Append an integer in the specified base (2 <= base <= 16). */
|
||||
struct archive_string *
|
||||
__archive_strappend_int(struct archive_string *as, int d, int base);
|
||||
#define archive_strappend_int __archive_strappend_int
|
||||
|
||||
/* Convert a wide-char string to UTF-8 and append the result. */
|
||||
struct archive_string *
|
||||
__archive_strappend_w_utf8(struct archive_string *, const wchar_t *);
|
||||
#define archive_strappend_w_utf8 __archive_strappend_w_utf8
|
||||
|
||||
/* Convert a wide-char string to current locale and append the result. */
|
||||
/* Returns NULL if conversion fails. */
|
||||
struct archive_string *
|
||||
__archive_strappend_w_mbs(struct archive_string *, const wchar_t *);
|
||||
#define archive_strappend_w_mbs __archive_strappend_w_mbs
|
||||
|
||||
/* Basic append operation. */
|
||||
struct archive_string *
|
||||
__archive_string_append(struct archive_string *as, const char *p, size_t s);
|
||||
@ -95,7 +104,7 @@ __archive_strncat(struct archive_string *, const char *, size_t);
|
||||
|
||||
/* Copy a C string to an archive_string, resizing as necessary. */
|
||||
#define archive_strcpy(as,p) \
|
||||
((as)->length = 0, __archive_string_append((as), (p), strlen(p)))
|
||||
((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p)))
|
||||
|
||||
/* Copy a C string to an archive_string with limit, resizing as necessary. */
|
||||
#define archive_strncpy(as,p,l) \
|
||||
@ -119,4 +128,9 @@ void __archive_string_vsprintf(struct archive_string *, const char *,
|
||||
void __archive_string_sprintf(struct archive_string *, const char *, ...);
|
||||
#define archive_string_sprintf __archive_string_sprintf
|
||||
|
||||
/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.
|
||||
* Returns NULL if conversion failed in any way. */
|
||||
wchar_t *__archive_string_utf8_w(struct archive_string *as);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -77,32 +77,10 @@ archive_version_number(void)
|
||||
return (ARCHIVE_VERSION_NUMBER);
|
||||
}
|
||||
|
||||
/*
|
||||
* Format a version string of the form "libarchive x.y.z", where x, y,
|
||||
* z are the correct parts of the version ID from
|
||||
* archive_version_number().
|
||||
*
|
||||
* I used to do all of this at build time in shell scripts but that
|
||||
* proved to be a portability headache.
|
||||
*/
|
||||
|
||||
const char *
|
||||
archive_version_string(void)
|
||||
{
|
||||
static char buff[128];
|
||||
struct archive_string as;
|
||||
int n;
|
||||
|
||||
if (buff[0] == '\0') {
|
||||
n = archive_version_number();
|
||||
memset(&as, 0, sizeof(as));
|
||||
archive_string_sprintf(&as, "libarchive %d.%d.%d",
|
||||
n / 1000000, (n / 1000) % 1000, n % 1000);
|
||||
strncpy(buff, as.s, sizeof(buff));
|
||||
buff[sizeof(buff) - 1] = '\0';
|
||||
archive_string_free(&as);
|
||||
}
|
||||
return (buff);
|
||||
return (ARCHIVE_VERSION_STRING);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -24,7 +24,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 19, 2006
|
||||
.Dd May 11, 2008
|
||||
.Dt archive_write 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -39,6 +39,7 @@
|
||||
.Nm archive_write_set_bytes_per_block ,
|
||||
.Nm archive_write_set_bytes_in_last_block ,
|
||||
.Nm archive_write_set_compression_bzip2 ,
|
||||
.Nm archive_write_set_compression_compress ,
|
||||
.Nm archive_write_set_compression_gzip ,
|
||||
.Nm archive_write_set_compression_none ,
|
||||
.Nm archive_write_set_compression_program ,
|
||||
@ -66,6 +67,8 @@
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_bzip2 "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_compress "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_gzip "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_compression_none "struct archive *"
|
||||
@ -197,6 +200,7 @@ the pax extended header for most normal files.
|
||||
In most cases, this will result in ordinary ustar archives.
|
||||
.It Xo
|
||||
.Fn archive_write_set_compression_bzip2 ,
|
||||
.Fn archive_write_set_compression_compress ,
|
||||
.Fn archive_write_set_compression_gzip ,
|
||||
.Fn archive_write_set_compression_none
|
||||
.Xc
|
||||
|
@ -170,6 +170,10 @@ Note that paths ending in
|
||||
.Pa ..
|
||||
always cause an error, regardless of this flag.
|
||||
.El
|
||||
.It Cm ARCHIVE_EXTRACT_SPARSE
|
||||
Scan data for blocks of NUL bytes and try to recreate them with holes.
|
||||
This results in sparse files, independent of whether the archive format
|
||||
supports or uses them.
|
||||
.It Xo
|
||||
.Fn archive_write_disk_set_group_lookup ,
|
||||
.Fn archive_write_disk_set_user_lookup
|
||||
|
@ -187,6 +187,8 @@ struct archive_write_disk {
|
||||
/* UID/GID to use in restoring this entry. */
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
/* Last offset written to disk. */
|
||||
off_t last_offset;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -242,6 +244,31 @@ static int _archive_write_finish_entry(struct archive *);
|
||||
static ssize_t _archive_write_data(struct archive *, const void *, size_t);
|
||||
static ssize_t _archive_write_data_block(struct archive *, const void *, size_t, off_t);
|
||||
|
||||
static int
|
||||
_archive_write_disk_lazy_stat(struct archive_write_disk *a)
|
||||
{
|
||||
if (a->pst != NULL) {
|
||||
/* Already have stat() data available. */
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
#ifdef HAVE_FSTAT
|
||||
if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
|
||||
a->pst = &a->st;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* XXX At this point, symlinks should not be hit, otherwise
|
||||
* XXX a race occured. Do we want to check explicitly for that?
|
||||
*/
|
||||
if (lstat(a->name, &a->st) == 0) {
|
||||
a->pst = &a->st;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
archive_set_error(&a->archive, errno, "Couldn't stat file");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
static struct archive_vtable *
|
||||
archive_write_disk_vtable(void)
|
||||
{
|
||||
@ -294,7 +321,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
|
||||
archive_clear_error(&a->archive);
|
||||
if (a->archive.state & ARCHIVE_STATE_DATA) {
|
||||
r = _archive_write_finish_entry(&a->archive);
|
||||
if (r != ARCHIVE_OK)
|
||||
if (r == ARCHIVE_FATAL)
|
||||
return (r);
|
||||
}
|
||||
|
||||
@ -308,6 +335,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
|
||||
}
|
||||
a->entry = archive_entry_clone(entry);
|
||||
a->fd = -1;
|
||||
a->last_offset = 0;
|
||||
a->offset = 0;
|
||||
a->uid = a->user_uid;
|
||||
a->mode = archive_entry_mode(a->entry);
|
||||
@ -463,6 +491,7 @@ _archive_write_data_block(struct archive *_a,
|
||||
{
|
||||
struct archive_write_disk *a = (struct archive_write_disk *)_a;
|
||||
ssize_t bytes_written = 0;
|
||||
ssize_t block_size, bytes_to_write;
|
||||
int r = ARCHIVE_OK;
|
||||
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
|
||||
@ -473,31 +502,53 @@ _archive_write_data_block(struct archive *_a,
|
||||
}
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
/* Seek if necessary to the specified offset. */
|
||||
if (offset != a->offset) {
|
||||
if (lseek(a->fd, offset, SEEK_SET) < 0) {
|
||||
archive_set_error(&a->archive, errno, "Seek failed");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
a->offset = offset;
|
||||
if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
|
||||
if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
|
||||
return (r);
|
||||
block_size = a->pst->st_blksize;
|
||||
} else
|
||||
block_size = -1;
|
||||
|
||||
if ((off_t)(offset + size) > a->filesize) {
|
||||
size = (size_t)(a->filesize - a->offset);
|
||||
archive_set_error(&a->archive, 0,
|
||||
"Write request too large");
|
||||
r = ARCHIVE_WARN;
|
||||
}
|
||||
|
||||
/* Write the data. */
|
||||
while (size > 0 && a->offset < a->filesize) {
|
||||
if ((off_t)(a->offset + size) > a->filesize) {
|
||||
size = (size_t)(a->filesize - a->offset);
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Write request too large");
|
||||
r = ARCHIVE_WARN;
|
||||
}
|
||||
while (size > 0) {
|
||||
if (block_size != -1) {
|
||||
const char *buf;
|
||||
|
||||
for (buf = buff; size; ++buf, --size, ++offset) {
|
||||
if (*buf != '\0')
|
||||
break;
|
||||
}
|
||||
if (size == 0)
|
||||
break;
|
||||
bytes_to_write = block_size - offset % block_size;
|
||||
buff = buf;
|
||||
} else
|
||||
bytes_to_write = size;
|
||||
/* Seek if necessary to the specified offset. */
|
||||
if (offset != a->last_offset) {
|
||||
if (lseek(a->fd, offset, SEEK_SET) < 0) {
|
||||
archive_set_error(&a->archive, errno, "Seek failed");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
bytes_written = write(a->fd, buff, size);
|
||||
if (bytes_written < 0) {
|
||||
archive_set_error(&a->archive, errno, "Write failed");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
buff = (const char *)buff + bytes_written;
|
||||
size -= bytes_written;
|
||||
a->offset += bytes_written;
|
||||
offset += bytes_written;
|
||||
a->last_offset = a->offset = offset;
|
||||
}
|
||||
a->offset = offset;
|
||||
return (r);
|
||||
}
|
||||
|
||||
@ -505,7 +556,6 @@ static ssize_t
|
||||
_archive_write_data(struct archive *_a, const void *buff, size_t size)
|
||||
{
|
||||
struct archive_write_disk *a = (struct archive_write_disk *)_a;
|
||||
off_t offset;
|
||||
int r;
|
||||
|
||||
__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
|
||||
@ -513,11 +563,10 @@ _archive_write_data(struct archive *_a, const void *buff, size_t size)
|
||||
if (a->fd < 0)
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
offset = a->offset;
|
||||
r = _archive_write_data_block(_a, buff, size, a->offset);
|
||||
if (r < ARCHIVE_OK)
|
||||
return (r);
|
||||
return (a->offset - offset);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -533,6 +582,34 @@ _archive_write_finish_entry(struct archive *_a)
|
||||
return (ARCHIVE_OK);
|
||||
archive_clear_error(&a->archive);
|
||||
|
||||
if (a->last_offset != a->filesize && a->fd >= 0) {
|
||||
if (ftruncate(a->fd, a->filesize) == -1 &&
|
||||
a->filesize == 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"File size could not be restored");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
/*
|
||||
* Explicitly stat the file as some platforms might not
|
||||
* implement the XSI option to extend files via ftruncate.
|
||||
*/
|
||||
a->pst = NULL;
|
||||
if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
|
||||
return (ret);
|
||||
if (a->st.st_size != a->filesize) {
|
||||
const char nul = '\0';
|
||||
if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) {
|
||||
archive_set_error(&a->archive, errno, "Seek failed");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (write(a->fd, &nul, 1) < 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Write to restore size failed");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore metadata. */
|
||||
|
||||
/*
|
||||
@ -723,11 +800,13 @@ restore_entry(struct archive_write_disk *a)
|
||||
* object isn't a dir.
|
||||
*/
|
||||
if (unlink(a->name) == 0) {
|
||||
/* We removed it, we're done. */
|
||||
/* We removed it, reset cached stat. */
|
||||
a->pst = NULL;
|
||||
} else if (errno == ENOENT) {
|
||||
/* File didn't exist, that's just as good. */
|
||||
} else if (rmdir(a->name) == 0) {
|
||||
/* It was a dir, but now it's gone. */
|
||||
a->pst = NULL;
|
||||
} else {
|
||||
/* We tried, but couldn't get rid of it. */
|
||||
archive_set_error(&a->archive, errno,
|
||||
@ -768,6 +847,7 @@ restore_entry(struct archive_write_disk *a)
|
||||
"Can't remove already-existing dir");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
a->pst = NULL;
|
||||
/* Try again. */
|
||||
en = create_filesystem_object(a);
|
||||
} else if (en == EEXIST) {
|
||||
@ -807,6 +887,7 @@ restore_entry(struct archive_write_disk *a)
|
||||
"Can't unlink already-existing object");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
a->pst = NULL;
|
||||
/* Try again. */
|
||||
en = create_filesystem_object(a);
|
||||
} else if (!S_ISDIR(a->mode)) {
|
||||
@ -866,8 +947,18 @@ create_filesystem_object(struct archive_write_disk *a)
|
||||
* New cpio and pax formats allow hardlink entries
|
||||
* to carry data, so we may have to open the file
|
||||
* for hardlink entries.
|
||||
*
|
||||
* If the hardlink was successfully created and
|
||||
* the archive doesn't have carry data for it,
|
||||
* consider it to be non-authoritive for meta data.
|
||||
* This is consistent with GNU tar and BSD pax.
|
||||
* If the hardlink does carry data, let the last
|
||||
* archive entry decide ownership.
|
||||
*/
|
||||
if (r == 0 && a->filesize > 0) {
|
||||
if (r == 0 && a->filesize == 0) {
|
||||
a->todo = 0;
|
||||
a->deferred = 0;
|
||||
} if (r == 0 && a->filesize > 0) {
|
||||
a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY);
|
||||
if (a->fd < 0)
|
||||
r = errno;
|
||||
@ -1203,6 +1294,7 @@ check_symlinks(struct archive_write_disk *a)
|
||||
pn[0] = c;
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
a->pst = NULL;
|
||||
/*
|
||||
* Even if we did remove it, a warning
|
||||
* is in order. The warning is silly,
|
||||
@ -1226,6 +1318,7 @@ check_symlinks(struct archive_write_disk *a)
|
||||
pn[0] = c;
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
a->pst = NULL;
|
||||
} else {
|
||||
archive_set_error(&a->archive, 0,
|
||||
"Cannot extract through symlink %s",
|
||||
@ -1608,19 +1701,8 @@ set_mode(struct archive_write_disk *a, int mode)
|
||||
* process, since systems sometimes set GID from
|
||||
* the enclosing dir or based on ACLs.
|
||||
*/
|
||||
if (a->pst != NULL) {
|
||||
/* Already have stat() data available. */
|
||||
#ifdef HAVE_FSTAT
|
||||
} else if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
|
||||
a->pst = &a->st;
|
||||
#endif
|
||||
} else if (stat(a->name, &a->st) == 0) {
|
||||
a->pst = &a->st;
|
||||
} else {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't stat file");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
|
||||
return (r);
|
||||
if (a->pst->st_gid != a->gid) {
|
||||
mode &= ~ S_ISGID;
|
||||
if (a->flags & ARCHIVE_EXTRACT_OWNER) {
|
||||
@ -1783,6 +1865,8 @@ static int
|
||||
set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
|
||||
mode_t mode, unsigned long set, unsigned long clear)
|
||||
{
|
||||
int r;
|
||||
|
||||
(void)mode; /* UNUSED */
|
||||
if (set == 0 && clear == 0)
|
||||
return (ARCHIVE_OK);
|
||||
@ -1793,15 +1877,8 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
|
||||
* about the correct approach if we're overwriting an existing
|
||||
* file that already has flags on it. XXX
|
||||
*/
|
||||
if (fd >= 0 && fstat(fd, &a->st) == 0)
|
||||
a->pst = &a->st;
|
||||
else if (lstat(name, &a->st) == 0)
|
||||
a->pst = &a->st;
|
||||
else {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Couldn't stat file");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
|
||||
return (r);
|
||||
|
||||
a->st.st_flags &= ~clear;
|
||||
a->st.st_flags |= set;
|
||||
|
@ -27,6 +27,23 @@
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/* This capability is only available on POSIX systems. */
|
||||
#if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL)
|
||||
|
||||
/*
|
||||
* On non-Posix systems, allow the program to build, but choke if
|
||||
* this function is actually invoked.
|
||||
*/
|
||||
int
|
||||
archive_write_set_compression_program(struct archive *_a, const char *cmd)
|
||||
{
|
||||
archive_set_error(_a, -1,
|
||||
"External compression programs not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
@ -320,3 +337,5 @@ archive_compressor_program_finish(struct archive_write *a)
|
||||
free(state);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */
|
||||
|
@ -142,12 +142,15 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
struct ar_w *ar;
|
||||
const char *pathname;
|
||||
const char *filename;
|
||||
int64_t size;
|
||||
|
||||
ret = 0;
|
||||
append_fn = 0;
|
||||
ar = (struct ar_w *)a->format_data;
|
||||
ar->is_strtab = 0;
|
||||
filename = NULL;
|
||||
size = archive_entry_size(entry);
|
||||
|
||||
|
||||
/*
|
||||
* Reject files with empty name.
|
||||
@ -285,8 +288,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
append_fn = 1;
|
||||
archive_entry_set_size(entry,
|
||||
archive_entry_size(entry) + strlen(filename));
|
||||
size += strlen(filename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,8 +324,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
}
|
||||
|
||||
size:
|
||||
if (format_decimal(archive_entry_size(entry), buff + AR_size_offset,
|
||||
AR_size_size)) {
|
||||
if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"File size out of range");
|
||||
return (ARCHIVE_WARN);
|
||||
@ -333,7 +334,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
|
||||
ar->entry_bytes_remaining = archive_entry_size(entry);
|
||||
ar->entry_bytes_remaining = size;
|
||||
ar->entry_padding = ar->entry_bytes_remaining % 2;
|
||||
|
||||
if (append_fn > 0) {
|
||||
|
@ -386,7 +386,7 @@ archive_write_pax_header(struct archive_write *a,
|
||||
const char *p;
|
||||
char *t;
|
||||
const wchar_t *wp;
|
||||
const char *suffix_start;
|
||||
const char *suffix;
|
||||
int need_extension, r, ret;
|
||||
struct pax *pax;
|
||||
const char *hdrcharset = NULL;
|
||||
@ -496,34 +496,73 @@ archive_write_pax_header(struct archive_write *a,
|
||||
if (hdrcharset != NULL)
|
||||
add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset);
|
||||
|
||||
/*
|
||||
* Determining whether or not the name is too big is ugly
|
||||
* because of the rules for dividing names between 'name' and
|
||||
* 'prefix' fields. Here, I pick out the longest possible
|
||||
* suffix, then test whether the remaining prefix is too long.
|
||||
*/
|
||||
if (strlen(path) <= 100) /* Short enough for just 'name' field */
|
||||
suffix_start = path; /* Record a zero-length prefix */
|
||||
else
|
||||
/* Find the largest suffix that fits in 'name' field. */
|
||||
suffix_start = strchr(path + strlen(path) - 100 - 1, '/');
|
||||
|
||||
/*
|
||||
* If name is too long, or has non-ASCII characters, add
|
||||
* 'path' to pax extended attrs. (Note that an unconvertible
|
||||
* name must have non-ASCII characters.)
|
||||
*/
|
||||
if (suffix_start == NULL || suffix_start - path > 155
|
||||
|| path_w == NULL || has_non_ASCII(path_w)) {
|
||||
if (path_w == NULL || hdrcharset != NULL)
|
||||
if (path == NULL) {
|
||||
/* We don't have a narrow version, so we have to store
|
||||
* the wide version. */
|
||||
add_pax_attr_w(&(pax->pax_header), "path", path_w);
|
||||
archive_entry_set_pathname(entry_main, "@WidePath");
|
||||
need_extension = 1;
|
||||
} else if (has_non_ASCII(path_w)) {
|
||||
/* We have non-ASCII characters. */
|
||||
if (path_w == NULL || hdrcharset != NULL) {
|
||||
/* Can't do UTF-8, so store it raw. */
|
||||
add_pax_attr(&(pax->pax_header), "path", path);
|
||||
else
|
||||
add_pax_attr_w(&(pax->pax_header), "path", path_w);
|
||||
} else {
|
||||
/* Store UTF-8 */
|
||||
add_pax_attr_w(&(pax->pax_header),
|
||||
"path", path_w);
|
||||
}
|
||||
archive_entry_set_pathname(entry_main,
|
||||
build_ustar_entry_name(ustar_entry_name,
|
||||
path, strlen(path), NULL));
|
||||
need_extension = 1;
|
||||
} else {
|
||||
/* We have an all-ASCII path; we'd like to just store
|
||||
* it in the ustar header if it will fit. Yes, this
|
||||
* duplicates some of the logic in
|
||||
* write_set_format_ustar.c
|
||||
*/
|
||||
if (strlen(path) <= 100) {
|
||||
/* Fits in the old 100-char tar name field. */
|
||||
} else {
|
||||
/* Find largest suffix that will fit. */
|
||||
/* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */
|
||||
suffix = strchr(path + strlen(path) - 100 - 1, '/');
|
||||
/* Don't attempt an empty prefix. */
|
||||
if (suffix == path)
|
||||
suffix = strchr(suffix + 1, '/');
|
||||
/* We can put it in the ustar header if it's
|
||||
* all ASCII and it's either <= 100 characters
|
||||
* or can be split at a '/' into a prefix <=
|
||||
* 155 chars and a suffix <= 100 chars. (Note
|
||||
* the strchr() above will return NULL exactly
|
||||
* when the path can't be split.)
|
||||
*/
|
||||
if (suffix == NULL /* Suffix > 100 chars. */
|
||||
|| suffix[1] == '\0' /* empty suffix */
|
||||
|| suffix - path > 155) /* Prefix > 155 chars */
|
||||
{
|
||||
if (path_w == NULL || hdrcharset != NULL) {
|
||||
/* Can't do UTF-8, so store it raw. */
|
||||
add_pax_attr(&(pax->pax_header),
|
||||
"path", path);
|
||||
} else {
|
||||
/* Store UTF-8 */
|
||||
add_pax_attr_w(&(pax->pax_header),
|
||||
"path", path_w);
|
||||
}
|
||||
archive_entry_set_pathname(entry_main,
|
||||
build_ustar_entry_name(ustar_entry_name,
|
||||
path, strlen(path), NULL));
|
||||
need_extension = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (linkpath != NULL) {
|
||||
@ -1215,6 +1254,8 @@ archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
|
||||
static int
|
||||
has_non_ASCII(const wchar_t *wp)
|
||||
{
|
||||
if (wp == NULL)
|
||||
return (1);
|
||||
while (*wp != L'\0' && *wp < 128)
|
||||
wp++;
|
||||
return (*wp != L'\0');
|
||||
|
@ -195,7 +195,7 @@ static int
|
||||
archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
char buff[512];
|
||||
int ret;
|
||||
int ret, ret2;
|
||||
struct ustar *ustar;
|
||||
|
||||
ustar = (struct ustar *)a->format_data;
|
||||
@ -206,7 +206,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
!(archive_entry_filetype(entry) == AE_IFREG))
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
if (AE_IFDIR == archive_entry_mode(entry)) {
|
||||
if (AE_IFDIR == archive_entry_filetype(entry)) {
|
||||
const char *p;
|
||||
char *t;
|
||||
/*
|
||||
@ -229,15 +229,17 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
|
||||
}
|
||||
|
||||
ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
ret = (a->compressor.write)(a, buff, 512);
|
||||
if (ret != ARCHIVE_OK)
|
||||
if (ret < ARCHIVE_WARN)
|
||||
return (ret);
|
||||
ret2 = (a->compressor.write)(a, buff, 512);
|
||||
if (ret2 < ARCHIVE_WARN)
|
||||
return (ret2);
|
||||
if (ret2 < ret)
|
||||
ret = ret2;
|
||||
|
||||
ustar->entry_bytes_remaining = archive_entry_size(entry);
|
||||
ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
|
||||
return (ARCHIVE_OK);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -282,27 +284,33 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
/* Store in two pieces, splitting at a '/'. */
|
||||
p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/');
|
||||
/*
|
||||
* If the separator we found is the first '/', find
|
||||
* the next one. (This is a pathological case that
|
||||
* occurs for paths of exactly 101 bytes that start with
|
||||
* '/'; it occurs because the separating '/' is not
|
||||
* stored explicitly and the reconstruction assumes that
|
||||
* an empty prefix means there is no '/' separator.)
|
||||
* Look for the next '/' if we chose the first character
|
||||
* as the separator. (ustar format doesn't permit
|
||||
* an empty prefix.)
|
||||
*/
|
||||
if (p == pp)
|
||||
p = strchr(p + 1, '/');
|
||||
/*
|
||||
* If there is no path separator, or the prefix or
|
||||
* remaining name are too large, return an error.
|
||||
*/
|
||||
/* Fail if the name won't fit. */
|
||||
if (!p) {
|
||||
/* No separator. */
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Pathname too long");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
} else if (p[1] == '\0') {
|
||||
/*
|
||||
* The only feasible separator is a final '/';
|
||||
* this would result in a non-empty prefix and
|
||||
* an empty name, which POSIX doesn't
|
||||
* explicity forbid, but it just feels wrong.
|
||||
*/
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Pathname too long");
|
||||
ret = ARCHIVE_FAILED;
|
||||
} else if (p > pp + USTAR_prefix_size) {
|
||||
/* Prefix is too long. */
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Pathname too long");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
} else {
|
||||
/* Copy prefix and remainder to appropriate places */
|
||||
memcpy(h + USTAR_prefix_offset, pp, p - pp);
|
||||
@ -320,7 +328,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
if (copy_length > USTAR_linkname_size) {
|
||||
archive_set_error(&a->archive, ENAMETOOLONG,
|
||||
"Link contents too long");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
copy_length = USTAR_linkname_size;
|
||||
}
|
||||
memcpy(h + USTAR_linkname_offset, p, copy_length);
|
||||
@ -332,7 +340,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
if (copy_length > USTAR_uname_size) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Username too long");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
copy_length = USTAR_uname_size;
|
||||
}
|
||||
memcpy(h + USTAR_uname_offset, p, copy_length);
|
||||
@ -344,7 +352,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
if (strlen(p) > USTAR_gname_size) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Group name too long");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
copy_length = USTAR_gname_size;
|
||||
}
|
||||
memcpy(h + USTAR_gname_offset, p, copy_length);
|
||||
@ -352,28 +360,28 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
|
||||
if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE, "Numeric mode too large");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
|
||||
if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE, "Numeric user ID too large");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
|
||||
if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE, "Numeric group ID too large");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
|
||||
if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE, "File size out of range");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
|
||||
if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"File modification time too large");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
|
||||
if (archive_entry_filetype(entry) == AE_IFBLK
|
||||
@ -382,14 +390,14 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"Major device number too large");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
|
||||
if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset,
|
||||
USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"Minor device number too large");
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,7 +417,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"tar format cannot archive this (mode=0%lo)",
|
||||
(unsigned long)archive_entry_mode(entry));
|
||||
ret = ARCHIVE_WARN;
|
||||
ret = ARCHIVE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#define HAVE_FCHFLAGS 1
|
||||
#define HAVE_FCHMOD 1
|
||||
#define HAVE_FCHOWN 1
|
||||
#define HAVE_FCNTL 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_FSEEKO 1
|
||||
#define HAVE_FSTAT 1
|
||||
@ -70,6 +71,7 @@
|
||||
#define HAVE_MKDIR 1
|
||||
#define HAVE_MKFIFO 1
|
||||
#define HAVE_MKNOD 1
|
||||
#define HAVE_PIPE 1
|
||||
#define HAVE_POLL 1
|
||||
#define HAVE_POLL_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
@ -102,6 +104,7 @@
|
||||
#define HAVE_UTIME 1
|
||||
#define HAVE_UTIMES 1
|
||||
#define HAVE_UTIME_H 1
|
||||
#define HAVE_VFORK 1
|
||||
#define HAVE_WCHAR_H 1
|
||||
#define HAVE_WCSCPY 1
|
||||
#define HAVE_WCSLEN 1
|
||||
|
@ -149,7 +149,7 @@ 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.
|
||||
This count includes the trailing NUL byte.
|
||||
.It Va filesize
|
||||
The size of the file.
|
||||
Note that this archive format is limited to
|
||||
@ -162,8 +162,8 @@ above for a description of the storage of four-byte integers.
|
||||
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
|
||||
is odd, an additional NUL byte is added after the pathname.
|
||||
The file data is then appended, padded with NUL
|
||||
bytes to an even length.
|
||||
.Pp
|
||||
Hardlinked files are not given special treatment;
|
||||
@ -202,7 +202,7 @@ 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.
|
||||
NUL 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
|
||||
@ -237,7 +237,7 @@ 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
|
||||
The pathname is followed by NUL 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
|
||||
@ -322,4 +322,4 @@ by SCO under their
|
||||
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
|
||||
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
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
#include "archive_platform.h"
|
||||
|
||||
/* This capability is only available on POSIX systems. */
|
||||
#if defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL)
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#if defined(HAVE_POLL)
|
||||
@ -137,3 +140,5 @@ __archive_check_child(int in, int out)
|
||||
sleep(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
|
||||
|
@ -259,6 +259,14 @@ There are two common variants:
|
||||
the GNU format derived from SVR4,
|
||||
and the BSD format, which first appeared in 4.4BSD.
|
||||
Libarchive provides read and write support for both variants.
|
||||
.Ss mtree
|
||||
Libarchive can read files in
|
||||
.Xr mtree 5
|
||||
format. This format is not a true archive format, but rather a description
|
||||
of a file hierarchy. When requested, libarchive obtains the contents of
|
||||
the files described by the
|
||||
.Xr mtree 5
|
||||
format from files on disk instead.
|
||||
.Sh SEE ALSO
|
||||
.Xr ar 1 ,
|
||||
.Xr cpio 1 ,
|
||||
|
@ -221,7 +221,7 @@ field with several new type values:
|
||||
.Bl -tag -width indent -compact
|
||||
.It Dq 0
|
||||
Regular file.
|
||||
NULL should be treated as a synonym, for compatibility purposes.
|
||||
NUL should be treated as a synonym, for compatibility purposes.
|
||||
.It Dq 1
|
||||
Hard link.
|
||||
.It Dq 2
|
||||
@ -258,7 +258,7 @@ by readers.
|
||||
.It Va magic
|
||||
Contains the magic value
|
||||
.Dq ustar
|
||||
followed by a NULL byte to indicate that this is a POSIX standard archive.
|
||||
followed by a NUL byte to indicate that this is a POSIX standard archive.
|
||||
Full compliance requires the uname and gname fields be properly set.
|
||||
.It Va version
|
||||
Version.
|
||||
@ -285,7 +285,7 @@ character to the regular name field to obtain the full pathname.
|
||||
.El
|
||||
.Pp
|
||||
Note that all unused bytes must be set to
|
||||
.Dv NULL .
|
||||
.Dv NUL .
|
||||
.Pp
|
||||
Field termination is specified slightly differently by POSIX
|
||||
than by previous implementations.
|
||||
@ -295,14 +295,14 @@ The
|
||||
and
|
||||
.Va gname
|
||||
fields must have a trailing
|
||||
.Dv NULL .
|
||||
.Dv NUL .
|
||||
The
|
||||
.Va pathname ,
|
||||
.Va linkname ,
|
||||
and
|
||||
.Va prefix
|
||||
fields must have a trailing
|
||||
.Dv NULL
|
||||
.Dv NUL
|
||||
unless they fill the entire field.
|
||||
(In particular, it is possible to store a 256-character pathname if it
|
||||
happens to have a
|
||||
@ -310,7 +310,7 @@ happens to have a
|
||||
as the 156th character.)
|
||||
POSIX requires numeric fields to be zero-padded in the front, and allows
|
||||
them to be terminated with either space or
|
||||
.Dv NULL
|
||||
.Dv NUL
|
||||
characters.
|
||||
.Pp
|
||||
Currently, most tar implementations comply with the ustar
|
||||
|
@ -18,6 +18,7 @@ TESTS= \
|
||||
test_empty_write.c \
|
||||
test_entry.c \
|
||||
test_entry_strmode.c \
|
||||
test_link_resolver.c \
|
||||
test_pax_filename_encoding.c \
|
||||
test_read_compress_program.c \
|
||||
test_read_data_large.c \
|
||||
@ -38,6 +39,7 @@ TESTS= \
|
||||
test_read_format_mtree.c \
|
||||
test_read_format_pax_bz2.c \
|
||||
test_read_format_tar.c \
|
||||
test_read_format_tar_empty_filename.c \
|
||||
test_read_format_tbz.c \
|
||||
test_read_format_tgz.c \
|
||||
test_read_format_tz.c \
|
||||
@ -48,6 +50,7 @@ TESTS= \
|
||||
test_read_truncated.c \
|
||||
test_tar_filenames.c \
|
||||
test_tar_large.c \
|
||||
test_ustar_filenames.c \
|
||||
test_write_compress_program.c \
|
||||
test_write_compress.c \
|
||||
test_write_disk.c \
|
||||
@ -61,6 +64,7 @@ TESTS= \
|
||||
test_write_format_cpio_empty.c \
|
||||
test_write_format_shar_empty.c \
|
||||
test_write_format_tar.c \
|
||||
test_write_format_tar_ustar.c \
|
||||
test_write_format_tar_empty.c \
|
||||
test_write_open_memory.c
|
||||
|
||||
@ -68,12 +72,10 @@ TESTS= \
|
||||
# Build the test program using all libarchive sources + the test sources.
|
||||
SRCS= ${LA_SRCS} \
|
||||
${TESTS} \
|
||||
list.h \
|
||||
${.OBJDIR}/list.h \
|
||||
main.c \
|
||||
read_open_memory.c
|
||||
|
||||
CLEANFILES+= list.h archive.h
|
||||
|
||||
NO_MAN=yes
|
||||
|
||||
PROG=libarchive_test
|
||||
@ -82,12 +84,7 @@ DPADD=${LIBBZ2} ${LIBZ}
|
||||
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
|
||||
LDADD= -lz -lbz2
|
||||
CFLAGS+= -static -g
|
||||
CFLAGS+= -I${.OBJDIR}
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
CFLAGS+= -I${LA_SRCDIR}
|
||||
# Without this, libarchive source files find archive.h in LA_SRCDIR,
|
||||
# which may not be the same as archive.h in the test dir.
|
||||
CFLAGS+= -I-
|
||||
|
||||
# Uncomment to link against dmalloc
|
||||
LDADD+= -L/usr/local/lib -ldmalloc
|
||||
@ -96,20 +93,13 @@ WARNS=6
|
||||
|
||||
# Build libarchive_test and run it.
|
||||
check test: libarchive_test
|
||||
./libarchive_test -k -r ${.CURDIR}
|
||||
|
||||
INCS=archive.h list.h
|
||||
|
||||
# Build archive.h, but in our .OBJDIR, not libarchive's
|
||||
# This keeps libarchive_test and libarchive builds completely separate.
|
||||
archive.h: ${LA_SRCDIR}/archive.h.in ${LA_SRCDIR}/Makefile
|
||||
cd ${LA_SRCDIR} && unset MAKEOBJDIRPREFIX && MAKEOBJDIR=${.OBJDIR} make archive.h
|
||||
./libarchive_test -v -r ${.CURDIR}
|
||||
|
||||
# list.h is just a list of all tests, as indicated by DEFINE_TEST macro lines
|
||||
list.h: ${TESTS} Makefile
|
||||
${.OBJDIR}/list.h: ${TESTS} Makefile
|
||||
(cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h
|
||||
|
||||
CLEANFILES += *.out *.o *.core *~ list.h archive.h
|
||||
CLEANFILES += *.out *.o *.core *~ list.h
|
||||
|
||||
cleantest:
|
||||
-chmod -R +w /tmp/libarchive_test.*
|
||||
|
@ -63,10 +63,14 @@ extern char *optarg;
|
||||
extern int optind;
|
||||
#endif
|
||||
|
||||
/* Default is to crash and try to force a core dump on failure. */
|
||||
static int dump_on_failure = 1;
|
||||
/* Enable core dump on failure. */
|
||||
static int dump_on_failure = 0;
|
||||
/* Default is to remove temp dirs for successful tests. */
|
||||
static int keep_temp_files = 0;
|
||||
/* Default is to print some basic information about each test. */
|
||||
static int quiet_flag = 0;
|
||||
/* Default is to summarize repeated failures. */
|
||||
static int verbose = 0;
|
||||
/* Cumulative count of component failures. */
|
||||
static int failures = 0;
|
||||
/* Cumulative count of skipped component tests. */
|
||||
@ -242,7 +246,7 @@ test_assert(const char *file, int line, int value, const char *condition, void *
|
||||
return (value);
|
||||
}
|
||||
failures ++;
|
||||
if (previous_failures(file, line))
|
||||
if (!verbose && previous_failures(file, line))
|
||||
return (value);
|
||||
fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
|
||||
fprintf(stderr, " Condition: %s\n", condition);
|
||||
@ -261,7 +265,7 @@ test_assert_equal_int(const char *file, int line,
|
||||
return (1);
|
||||
}
|
||||
failures ++;
|
||||
if (previous_failures(file, line))
|
||||
if (!verbose && previous_failures(file, line))
|
||||
return (0);
|
||||
fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
|
||||
file, line);
|
||||
@ -271,6 +275,30 @@ test_assert_equal_int(const char *file, int line,
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void strdump(const char *p)
|
||||
{
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "(null)");
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "\"");
|
||||
while (*p != '\0') {
|
||||
unsigned int c = 0xff & *p++;
|
||||
switch (c) {
|
||||
case '\a': fprintf(stderr, "\a"); break;
|
||||
case '\b': fprintf(stderr, "\b"); break;
|
||||
case '\n': fprintf(stderr, "\n"); break;
|
||||
case '\r': fprintf(stderr, "\r"); break;
|
||||
default:
|
||||
if (c >= 32 && c < 127)
|
||||
fprintf(stderr, "%c", c);
|
||||
else
|
||||
fprintf(stderr, "\\x%02X", c);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\"");
|
||||
}
|
||||
|
||||
/* assertEqualString() displays the values of the two strings. */
|
||||
int
|
||||
test_assert_equal_string(const char *file, int line,
|
||||
@ -289,16 +317,41 @@ test_assert_equal_string(const char *file, int line,
|
||||
return (1);
|
||||
}
|
||||
failures ++;
|
||||
if (previous_failures(file, line))
|
||||
if (!verbose && previous_failures(file, line))
|
||||
return (0);
|
||||
fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
|
||||
file, line);
|
||||
fprintf(stderr, " %s = \"%s\"\n", e1, v1);
|
||||
fprintf(stderr, " %s = \"%s\"\n", e2, v2);
|
||||
fprintf(stderr, " %s = ", e1);
|
||||
strdump(v1);
|
||||
fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1));
|
||||
fprintf(stderr, " %s = ", e2);
|
||||
strdump(v2);
|
||||
fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2));
|
||||
report_failure(extra);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void wcsdump(const wchar_t *w)
|
||||
{
|
||||
if (w == NULL) {
|
||||
fprintf(stderr, "(null)");
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "\"");
|
||||
while (*w != L'\0') {
|
||||
unsigned int c = *w++;
|
||||
if (c >= 32 && c < 127)
|
||||
fprintf(stderr, "%c", c);
|
||||
else if (c < 256)
|
||||
fprintf(stderr, "\\x%02X", c);
|
||||
else if (c < 0x10000)
|
||||
fprintf(stderr, "\\u%04X", c);
|
||||
else
|
||||
fprintf(stderr, "\\U%08X", c);
|
||||
}
|
||||
fprintf(stderr, "\"");
|
||||
}
|
||||
|
||||
/* assertEqualWString() displays the values of the two strings. */
|
||||
int
|
||||
test_assert_equal_wstring(const char *file, int line,
|
||||
@ -307,17 +360,31 @@ test_assert_equal_wstring(const char *file, int line,
|
||||
void *extra)
|
||||
{
|
||||
++assertions;
|
||||
if (wcscmp(v1, v2) == 0) {
|
||||
if (v1 == NULL) {
|
||||
if (v2 == NULL) {
|
||||
msg[0] = '\0';
|
||||
return (1);
|
||||
}
|
||||
} else if (v2 == NULL) {
|
||||
if (v1 == NULL) {
|
||||
msg[0] = '\0';
|
||||
return (1);
|
||||
}
|
||||
} else if (wcscmp(v1, v2) == 0) {
|
||||
msg[0] = '\0';
|
||||
return (1);
|
||||
}
|
||||
failures ++;
|
||||
if (previous_failures(file, line))
|
||||
if (!verbose && previous_failures(file, line))
|
||||
return (0);
|
||||
fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
|
||||
file, line);
|
||||
fwprintf(stderr, L" %s = \"%ls\"\n", e1, v1);
|
||||
fwprintf(stderr, L" %s = \"%ls\"\n", e2, v2);
|
||||
fprintf(stderr, " %s = ", e1);
|
||||
wcsdump(v1);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " %s = ", e2);
|
||||
wcsdump(v2);
|
||||
fprintf(stderr, "\n");
|
||||
report_failure(extra);
|
||||
return (0);
|
||||
}
|
||||
@ -378,7 +445,7 @@ test_assert_equal_mem(const char *file, int line,
|
||||
return (1);
|
||||
}
|
||||
failures ++;
|
||||
if (previous_failures(file, line))
|
||||
if (!verbose && previous_failures(file, line))
|
||||
return (0);
|
||||
fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n",
|
||||
file, line);
|
||||
@ -410,12 +477,13 @@ test_assert_empty_file(const char *f1fmt, ...)
|
||||
if (stat(f1, &st) != 0) {
|
||||
fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1);
|
||||
report_failure(NULL);
|
||||
return (0);
|
||||
}
|
||||
if (st.st_size == 0)
|
||||
return (1);
|
||||
|
||||
failures ++;
|
||||
if (previous_failures(test_filename, test_line))
|
||||
if (!verbose && previous_failures(test_filename, test_line))
|
||||
return (0);
|
||||
|
||||
fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1);
|
||||
@ -462,7 +530,7 @@ test_assert_equal_file(const char *f1, const char *f2pattern, ...)
|
||||
break;
|
||||
}
|
||||
failures ++;
|
||||
if (previous_failures(test_filename, test_line))
|
||||
if (!verbose && previous_failures(test_filename, test_line))
|
||||
return (0);
|
||||
fprintf(stderr, "%s:%d: Files are not identical\n",
|
||||
test_filename, test_line);
|
||||
@ -633,6 +701,12 @@ static int test_run(int i, const char *tmpdir)
|
||||
(*tests[i].func)();
|
||||
/* Summarize the results of this test. */
|
||||
summarize();
|
||||
/* If there were no failures, we can remove the work dir. */
|
||||
if (failures == failures_before) {
|
||||
if (!keep_temp_files && chdir(tmpdir) == 0) {
|
||||
systemf("rm -rf %s", tests[i].name);
|
||||
}
|
||||
}
|
||||
/* Return appropriate status. */
|
||||
return (failures == failures_before ? 0 : 1);
|
||||
}
|
||||
@ -646,8 +720,9 @@ static void usage(const char *program)
|
||||
printf("Default is to run all tests.\n");
|
||||
printf("Otherwise, specify the numbers of the tests you wish to run.\n");
|
||||
printf("Options:\n");
|
||||
printf(" -k Keep running after failures.\n");
|
||||
printf(" Default: Core dump after any failure.\n");
|
||||
printf(" -d Dump core after any failure, for debugging.\n");
|
||||
printf(" -k Keep all temp files.\n");
|
||||
printf(" Default: temp files for successful tests deleted.\n");
|
||||
#ifdef PROGRAM
|
||||
printf(" -p <path> Path to executable to be tested.\n");
|
||||
printf(" Default: path taken from " ENVBASE " environment variable.\n");
|
||||
@ -655,6 +730,7 @@ static void usage(const char *program)
|
||||
printf(" -q Quiet.\n");
|
||||
printf(" -r <dir> Path to dir containing reference files.\n");
|
||||
printf(" Default: Current directory.\n");
|
||||
printf(" -v Verbose.\n");
|
||||
printf("Available tests:\n");
|
||||
for (i = 0; i < limit; i++)
|
||||
printf(" %d: %s\n", i, tests[i].name);
|
||||
@ -747,9 +823,9 @@ int main(int argc, char **argv)
|
||||
testprog = getenv(ENVBASE);
|
||||
#endif
|
||||
|
||||
/* Allow -k to be controlled through the environment. */
|
||||
if (getenv(ENVBASE "_KEEP_GOING") != NULL)
|
||||
dump_on_failure = 0;
|
||||
/* Allow -d to be controlled through the environment. */
|
||||
if (getenv(ENVBASE "_DEBUG") != NULL)
|
||||
dump_on_failure = 1;
|
||||
|
||||
/* Get the directory holding test files from environment. */
|
||||
refdir = getenv(ENVBASE "_TEST_FILES");
|
||||
@ -757,10 +833,13 @@ int main(int argc, char **argv)
|
||||
/*
|
||||
* Parse options.
|
||||
*/
|
||||
while ((opt = getopt(argc, argv, "kp:qr:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
dump_on_failure = 1;
|
||||
break;
|
||||
case 'k':
|
||||
dump_on_failure = 0;
|
||||
keep_temp_files = 1;
|
||||
break;
|
||||
case 'p':
|
||||
#ifdef PROGRAM
|
||||
@ -775,6 +854,9 @@ int main(int argc, char **argv)
|
||||
case 'r':
|
||||
refdir = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage(progname);
|
||||
@ -823,6 +905,7 @@ int main(int argc, char **argv)
|
||||
--p;
|
||||
*p = '\0';
|
||||
}
|
||||
systemf("rm %s/refdir", tmpdir);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -878,5 +961,9 @@ int main(int argc, char **argv)
|
||||
|
||||
free(refdir_alloc);
|
||||
|
||||
/* If the final tmpdir is empty, we can remove it. */
|
||||
/* This should be the usual case when all tests succeed. */
|
||||
rmdir(tmpdir);
|
||||
|
||||
return (tests_failed);
|
||||
}
|
||||
|
@ -332,14 +332,10 @@ acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const cha
|
||||
return (1);
|
||||
if (qual != acl->qual)
|
||||
return (0);
|
||||
if (name == NULL) {
|
||||
if (acl->name == NULL || acl->name[0] == '\0')
|
||||
return (1);
|
||||
}
|
||||
if (acl->name == NULL) {
|
||||
if (name[0] == '\0')
|
||||
return (1);
|
||||
}
|
||||
if (name == NULL)
|
||||
return (acl->name == NULL || acl->name[0] == '\0');
|
||||
if (acl->name == NULL)
|
||||
return (name == NULL || name[0] == '\0');
|
||||
return (0 == strcmp(name, acl->name));
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$");
|
||||
DEFINE_TEST(test_archive_api_feature)
|
||||
{
|
||||
char buff[128];
|
||||
const char *p;
|
||||
|
||||
/* This is the (hopefully) final versioning API. */
|
||||
assertEqualInt(ARCHIVE_VERSION_NUMBER, archive_version_number());
|
||||
@ -35,7 +36,17 @@ DEFINE_TEST(test_archive_api_feature)
|
||||
archive_version_number() / 1000000,
|
||||
(archive_version_number() / 1000) % 1000,
|
||||
archive_version_number() % 1000);
|
||||
assertEqualString(buff, archive_version_string());
|
||||
failure("Version string is: %s, computed is: %s",
|
||||
archive_version_string(), buff);
|
||||
assert(memcmp(buff, archive_version_string(), strlen(buff)) == 0);
|
||||
if (strlen(buff) < strlen(archive_version_string())) {
|
||||
p = archive_version_string() + strlen(buff);
|
||||
failure("Version string is: %s", archive_version_string());
|
||||
assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd');
|
||||
++p;
|
||||
failure("Version string is: %s", archive_version_string());
|
||||
assert(*p == '\0');
|
||||
}
|
||||
|
||||
/* This is all scheduled to disappear in libarchive 3.0 */
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
|
@ -52,6 +52,8 @@ DEFINE_TEST(test_entry)
|
||||
const void *xval; /* For xattr tests. */
|
||||
size_t xsize; /* For xattr tests. */
|
||||
int c;
|
||||
wchar_t wc;
|
||||
long l;
|
||||
|
||||
assert((e = archive_entry_new()) != NULL);
|
||||
|
||||
@ -146,7 +148,7 @@ DEFINE_TEST(test_entry)
|
||||
archive_entry_copy_link_w(e, L"link3");
|
||||
assertEqualString(archive_entry_hardlink(e), NULL);
|
||||
assertEqualString(archive_entry_symlink(e), "link3");
|
||||
/* Arbitrarily override hardlink if both hardlink and symlink set. */
|
||||
/* Arbitrarily override symlink if both hardlink and symlink set. */
|
||||
archive_entry_set_hardlink(e, "hardlink");
|
||||
archive_entry_set_symlink(e, "symlink");
|
||||
archive_entry_set_link(e, "link");
|
||||
@ -230,6 +232,11 @@ DEFINE_TEST(test_entry)
|
||||
assertEqualString(archive_entry_fflags_text(e),
|
||||
"uappnd,nouchg,nodump,noopaque,uunlnk");
|
||||
/* TODO: Test archive_entry_copy_fflags_text_w() */
|
||||
/* Test archive_entry_copy_fflags_text() */
|
||||
archive_entry_copy_fflags_text(e, "nouappnd, nouchg, dump,uunlnk");
|
||||
archive_entry_fflags(e, &set, &clear);
|
||||
assertEqualInt(16, set);
|
||||
assertEqualInt(7, clear);
|
||||
#endif
|
||||
|
||||
/* See test_acl_basic.c for tests of ACL set/get consistency. */
|
||||
@ -726,8 +733,10 @@ DEFINE_TEST(test_entry)
|
||||
/*
|
||||
* Exercise the character-conversion logic, if we can.
|
||||
*/
|
||||
failure("Can't exercise charset-conversion logic.");
|
||||
if (assert(NULL != setlocale(LC_ALL, "de_DE.UTF-8"))) {
|
||||
if (NULL == setlocale(LC_ALL, "de_DE.UTF-8")) {
|
||||
skipping("Can't exercise charset-conversion logic without"
|
||||
" a suitable locale.");
|
||||
} else {
|
||||
/* A filename that cannot be converted to wide characters. */
|
||||
archive_entry_copy_pathname(e, "abc\314\214mno\374xyz");
|
||||
failure("Converting invalid chars to Unicode should fail.");
|
||||
@ -756,6 +765,26 @@ DEFINE_TEST(test_entry)
|
||||
assert(NULL == archive_entry_symlink_w(e));
|
||||
}
|
||||
|
||||
l = 0x12345678L;
|
||||
wc = (wchar_t)l; /* Wide character too big for UTF-8. */
|
||||
if (NULL == setlocale(LC_ALL, "C") || (long)wc != l) {
|
||||
skipping("Testing charset conversion failure requires 32-bit wchar_t and support for \"C\" locale.");
|
||||
} else {
|
||||
/*
|
||||
* Build the string L"xxx\U12345678yyy\u5678zzz" without
|
||||
* using C99 \u#### syntax, which isn't uniformly
|
||||
* supported. (GCC 3.4.6, for instance, defaults to
|
||||
* "c89 plus GNU extensions.")
|
||||
*/
|
||||
wcscpy(wbuff, L"xxxAyyyBzzz");
|
||||
wbuff[3] = 0x12345678;
|
||||
wbuff[7] = 0x5678;
|
||||
/* A wide filename that cannot be converted to narrow. */
|
||||
archive_entry_copy_pathname_w(e, wbuff);
|
||||
failure("Converting wide characters from Unicode should fail.");
|
||||
assertEqualString(NULL, archive_entry_pathname(e));
|
||||
}
|
||||
|
||||
/* Release the experimental entry. */
|
||||
archive_entry_free(e);
|
||||
}
|
||||
|
187
lib/libarchive/test/test_link_resolver.c
Normal file
187
lib/libarchive/test/test_link_resolver.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*-
|
||||
* 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 "test.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
static void test_linkify_tar(void)
|
||||
{
|
||||
struct archive_entry *entry, *e2;
|
||||
struct archive_entry_linkresolver *resolver;
|
||||
|
||||
/* Initialize the resolver. */
|
||||
assert(NULL != (resolver = archive_entry_linkresolver_new()));
|
||||
archive_entry_linkresolver_set_strategy(resolver,
|
||||
ARCHIVE_FORMAT_TAR_USTAR);
|
||||
|
||||
/* Create an entry with only 1 link and try to linkify it. */
|
||||
assert(NULL != (entry = archive_entry_new()));
|
||||
archive_entry_set_pathname(entry, "test1");
|
||||
archive_entry_set_ino(entry, 1);
|
||||
archive_entry_set_dev(entry, 2);
|
||||
archive_entry_set_nlink(entry, 1);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
|
||||
/* Shouldn't have been changed. */
|
||||
assert(e2 == NULL);
|
||||
assertEqualInt(10, archive_entry_size(entry));
|
||||
assertEqualString("test1", archive_entry_pathname(entry));
|
||||
|
||||
/* Now, try again with an entry that has 2 links. */
|
||||
archive_entry_set_pathname(entry, "test2");
|
||||
archive_entry_set_nlink(entry, 2);
|
||||
archive_entry_set_ino(entry, 2);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
/* Shouldn't be altered, since it wasn't seen before. */
|
||||
assert(e2 == NULL);
|
||||
assertEqualString("test2", archive_entry_pathname(entry));
|
||||
assertEqualString(NULL, archive_entry_hardlink(entry));
|
||||
assertEqualInt(10, archive_entry_size(entry));
|
||||
|
||||
/* Match again and make sure it does get altered. */
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
assert(e2 == NULL);
|
||||
assertEqualString("test2", archive_entry_pathname(entry));
|
||||
assertEqualString("test2", archive_entry_hardlink(entry));
|
||||
assertEqualInt(0, archive_entry_size(entry));
|
||||
|
||||
|
||||
archive_entry_free(entry);
|
||||
archive_entry_linkresolver_free(resolver);
|
||||
}
|
||||
|
||||
static void test_linkify_old_cpio(void)
|
||||
{
|
||||
struct archive_entry *entry, *e2;
|
||||
struct archive_entry_linkresolver *resolver;
|
||||
|
||||
/* Initialize the resolver. */
|
||||
assert(NULL != (resolver = archive_entry_linkresolver_new()));
|
||||
archive_entry_linkresolver_set_strategy(resolver,
|
||||
ARCHIVE_FORMAT_CPIO_POSIX);
|
||||
|
||||
/* Create an entry with 2 link and try to linkify it. */
|
||||
assert(NULL != (entry = archive_entry_new()));
|
||||
archive_entry_set_pathname(entry, "test1");
|
||||
archive_entry_set_ino(entry, 1);
|
||||
archive_entry_set_dev(entry, 2);
|
||||
archive_entry_set_nlink(entry, 2);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
|
||||
/* Shouldn't have been changed. */
|
||||
assert(e2 == NULL);
|
||||
assertEqualInt(10, archive_entry_size(entry));
|
||||
assertEqualString("test1", archive_entry_pathname(entry));
|
||||
|
||||
/* Still shouldn't be matched. */
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
assert(e2 == NULL);
|
||||
assertEqualString("test1", archive_entry_pathname(entry));
|
||||
assertEqualString(NULL, archive_entry_hardlink(entry));
|
||||
assertEqualInt(10, archive_entry_size(entry));
|
||||
|
||||
archive_entry_free(entry);
|
||||
archive_entry_linkresolver_free(resolver);
|
||||
}
|
||||
|
||||
static void test_linkify_new_cpio(void)
|
||||
{
|
||||
struct archive_entry *entry, *e2;
|
||||
struct archive_entry_linkresolver *resolver;
|
||||
|
||||
/* Initialize the resolver. */
|
||||
assert(NULL != (resolver = archive_entry_linkresolver_new()));
|
||||
archive_entry_linkresolver_set_strategy(resolver,
|
||||
ARCHIVE_FORMAT_CPIO_SVR4_NOCRC);
|
||||
|
||||
/* Create an entry with only 1 link and try to linkify it. */
|
||||
assert(NULL != (entry = archive_entry_new()));
|
||||
archive_entry_set_pathname(entry, "test1");
|
||||
archive_entry_set_ino(entry, 1);
|
||||
archive_entry_set_dev(entry, 2);
|
||||
archive_entry_set_nlink(entry, 1);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
|
||||
/* Shouldn't have been changed. */
|
||||
assert(e2 == NULL);
|
||||
assertEqualInt(10, archive_entry_size(entry));
|
||||
assertEqualString("test1", archive_entry_pathname(entry));
|
||||
|
||||
/* Now, try again with an entry that has 3 links. */
|
||||
archive_entry_set_pathname(entry, "test2");
|
||||
archive_entry_set_nlink(entry, 3);
|
||||
archive_entry_set_ino(entry, 2);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
|
||||
/* First time, it just gets swallowed. */
|
||||
assert(entry == NULL);
|
||||
assert(e2 == NULL);
|
||||
|
||||
/* Match again. */
|
||||
assert(NULL != (entry = archive_entry_new()));
|
||||
archive_entry_set_pathname(entry, "test3");
|
||||
archive_entry_set_ino(entry, 2);
|
||||
archive_entry_set_dev(entry, 2);
|
||||
archive_entry_set_nlink(entry, 2);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
|
||||
/* Should get back "test2" and nothing else. */
|
||||
assertEqualString("test2", archive_entry_pathname(entry));
|
||||
assertEqualInt(0, archive_entry_size(entry));
|
||||
archive_entry_free(entry);
|
||||
assert(NULL == e2);
|
||||
archive_entry_free(e2); /* This should be a no-op. */
|
||||
|
||||
/* Match a third time. */
|
||||
assert(NULL != (entry = archive_entry_new()));
|
||||
archive_entry_set_pathname(entry, "test4");
|
||||
archive_entry_set_ino(entry, 2);
|
||||
archive_entry_set_dev(entry, 2);
|
||||
archive_entry_set_nlink(entry, 3);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_linkify(resolver, &entry, &e2);
|
||||
|
||||
/* Should get back "test3". */
|
||||
assertEqualString("test3", archive_entry_pathname(entry));
|
||||
assertEqualInt(0, archive_entry_size(entry));
|
||||
|
||||
/* Since "test4" was the last link, should get it back also. */
|
||||
assertEqualString("test4", archive_entry_pathname(e2));
|
||||
assertEqualInt(10, archive_entry_size(e2));
|
||||
|
||||
archive_entry_free(entry);
|
||||
archive_entry_free(e2);
|
||||
archive_entry_linkresolver_free(resolver);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_link_resolver)
|
||||
{
|
||||
test_linkify_tar();
|
||||
test_linkify_old_cpio();
|
||||
test_linkify_new_cpio();
|
||||
}
|
@ -34,24 +34,20 @@ __FBSDID("$FreeBSD$");
|
||||
* stored and restored correctly, regardless of the encodings.
|
||||
*/
|
||||
|
||||
DEFINE_TEST(test_pax_filename_encoding)
|
||||
/*
|
||||
* Read a manually-created archive that has filenames that are
|
||||
* stored in binary instead of UTF-8 and verify that we get
|
||||
* the right filename returned and that we get a warning only
|
||||
* if the header isn't marked as binary.
|
||||
*/
|
||||
DEFINE_TEST(test_pax_filename_encoding_1)
|
||||
{
|
||||
static const char testname[] = "test_pax_filename_encoding.tar.gz";
|
||||
char buff[65536];
|
||||
/*
|
||||
* \314\214 is a valid 2-byte UTF-8 sequence.
|
||||
* \374 is invalid in UTF-8.
|
||||
*/
|
||||
char filename[] = "abc\314\214mno\374xyz";
|
||||
char longname[] = "abc\314\214mno\374xyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
;
|
||||
size_t used;
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
|
||||
@ -69,8 +65,7 @@ DEFINE_TEST(test_pax_filename_encoding)
|
||||
* in it, but the header is not marked as hdrcharset=BINARY, so that
|
||||
* requires a warning.
|
||||
*/
|
||||
failure("An invalid UTF8 pathname in a pax archive should be read\n"
|
||||
" without conversion but with a warning");
|
||||
failure("Invalid UTF8 in a pax archive pathname should cause a warning");
|
||||
assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
|
||||
assertEqualString(filename, archive_entry_pathname(entry));
|
||||
/*
|
||||
@ -82,15 +77,39 @@ DEFINE_TEST(test_pax_filename_encoding)
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &entry));
|
||||
assertEqualString(filename, archive_entry_pathname(entry));
|
||||
archive_read_finish(a);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the locale and write a pathname containing invalid characters.
|
||||
* This should work; the underlying implementation should automatically
|
||||
* fall back to storing the pathname in binary.
|
||||
*/
|
||||
DEFINE_TEST(test_pax_filename_encoding_2)
|
||||
{
|
||||
char filename[] = "abc\314\214mno\374xyz";
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
char buff[65536];
|
||||
char longname[] = "abc\314\214mno\374xyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
"/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
|
||||
;
|
||||
size_t used;
|
||||
|
||||
/*
|
||||
* We need a starting locale which has invalid sequences.
|
||||
* de_DE.UTF-8 seems to be commonly supported.
|
||||
*/
|
||||
/* If it doesn't exist, just warn and return. */
|
||||
failure("We need a suitable locale for the encoding tests.");
|
||||
if (!assert(NULL != setlocale(LC_ALL, "de_DE.UTF-8")))
|
||||
if (NULL == setlocale(LC_ALL, "de_DE.UTF-8")) {
|
||||
skipping("invalid encoding tests require a suitable locale;"
|
||||
" de_DE.UTF-8 not available on this system");
|
||||
return;
|
||||
}
|
||||
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertEqualIntA(a, 0, archive_write_set_format_pax(a));
|
||||
@ -159,3 +178,120 @@ DEFINE_TEST(test_pax_filename_encoding)
|
||||
assertEqualInt(0, archive_read_finish(a));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an entry starting from a wide-character Unicode pathname,
|
||||
* read it back into "C" locale, which doesn't support the name.
|
||||
* TODO: Figure out the "right" behavior here.
|
||||
*/
|
||||
DEFINE_TEST(test_pax_filename_encoding_3)
|
||||
{
|
||||
wchar_t badname[] = L"xxxAyyyBzzz";
|
||||
const char badname_utf8[] = "xxx\xE1\x88\xB4yyy\xE5\x99\xB8zzz";
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
char buff[65536];
|
||||
size_t used;
|
||||
|
||||
badname[3] = 0x1234;
|
||||
badname[7] = 0x5678;
|
||||
|
||||
/* If it doesn't exist, just warn and return. */
|
||||
if (NULL == setlocale(LC_ALL, "C")) {
|
||||
skipping("Can't set \"C\" locale, so can't exercise "
|
||||
"certain character-conversion failures");
|
||||
return;
|
||||
}
|
||||
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertEqualIntA(a, 0, archive_write_set_format_pax(a));
|
||||
assertEqualIntA(a, 0, archive_write_set_compression_none(a));
|
||||
assertEqualIntA(a, 0, archive_write_set_bytes_per_block(a, 0));
|
||||
assertEqualInt(0,
|
||||
archive_write_open_memory(a, buff, sizeof(buff), &used));
|
||||
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
/* Set pathname to non-convertible wide value. */
|
||||
archive_entry_copy_pathname_w(entry, badname);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname_w(entry, L"abc");
|
||||
/* Set gname to non-convertible wide value. */
|
||||
archive_entry_copy_gname_w(entry, badname);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname_w(entry, L"abc");
|
||||
/* Set uname to non-convertible wide value. */
|
||||
archive_entry_copy_uname_w(entry, badname);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname_w(entry, L"abc");
|
||||
/* Set hardlink to non-convertible wide value. */
|
||||
archive_entry_copy_hardlink_w(entry, badname);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname_w(entry, L"abc");
|
||||
/* Set symlink to non-convertible wide value. */
|
||||
archive_entry_copy_symlink_w(entry, badname);
|
||||
archive_entry_set_filetype(entry, AE_IFLNK);
|
||||
assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
assertEqualInt(0, archive_write_close(a));
|
||||
assertEqualInt(0, archive_write_finish(a));
|
||||
|
||||
/*
|
||||
* Now read the entries back.
|
||||
*/
|
||||
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualInt(0, archive_read_support_format_tar(a));
|
||||
assertEqualInt(0, archive_read_open_memory(a, buff, used));
|
||||
|
||||
failure("A non-convertible pathname should cause a warning.");
|
||||
assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
|
||||
assertEqualWString(badname, archive_entry_pathname_w(entry));
|
||||
failure("If native locale can't convert, we should get UTF-8 back.");
|
||||
assertEqualString(badname_utf8, archive_entry_pathname(entry));
|
||||
|
||||
failure("A non-convertible gname should cause a warning.");
|
||||
assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
|
||||
assertEqualWString(badname, archive_entry_gname_w(entry));
|
||||
failure("If native locale can't convert, we should get UTF-8 back.");
|
||||
assertEqualString(badname_utf8, archive_entry_gname(entry));
|
||||
|
||||
failure("A non-convertible uname should cause a warning.");
|
||||
assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
|
||||
assertEqualWString(badname, archive_entry_uname_w(entry));
|
||||
failure("If native locale can't convert, we should get UTF-8 back.");
|
||||
assertEqualString(badname_utf8, archive_entry_uname(entry));
|
||||
|
||||
failure("A non-convertible hardlink should cause a warning.");
|
||||
assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
|
||||
assertEqualWString(badname, archive_entry_hardlink_w(entry));
|
||||
failure("If native locale can't convert, we should get UTF-8 back.");
|
||||
assertEqualString(badname_utf8, archive_entry_hardlink(entry));
|
||||
|
||||
failure("A non-convertible symlink should cause a warning.");
|
||||
assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
|
||||
assertEqualWString(badname, archive_entry_symlink_w(entry));
|
||||
assertEqualWString(NULL, archive_entry_hardlink_w(entry));
|
||||
failure("If native locale can't convert, we should get UTF-8 back.");
|
||||
assertEqualString(badname_utf8, archive_entry_symlink(entry));
|
||||
|
||||
assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &entry));
|
||||
|
||||
assertEqualInt(0, archive_read_close(a));
|
||||
assertEqualInt(0, archive_read_finish(a));
|
||||
}
|
||||
|
@ -32,18 +32,18 @@ static unsigned char archive[] = {
|
||||
"dir type=dir\n"
|
||||
" file\\040with\\040space type=file uid=18\n"
|
||||
" ..\n"
|
||||
"file\\04with\\040space\n"
|
||||
"file\\04with\\040space type=file\n"
|
||||
"dir2 type=dir\n"
|
||||
" dir3a type=dir\n"
|
||||
" indir3a\n"
|
||||
" indir3a type=file\n"
|
||||
"dir2/fullindir2 type=file mode=0777\n"
|
||||
" ..\n"
|
||||
" indir2\n"
|
||||
" indir2 type=file\n"
|
||||
" dir3b type=dir\n"
|
||||
" indir3b\n"
|
||||
" indir3b type=file\n"
|
||||
" ..\n"
|
||||
" ..\n"
|
||||
"notindir\n"
|
||||
"notindir type=file\n"
|
||||
"dir2/fullindir2 mode=0644\n"
|
||||
};
|
||||
|
||||
|
66
lib/libarchive/test/test_read_format_tar_empty_filename.c
Normal file
66
lib/libarchive/test/test_read_format_tar_empty_filename.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*-
|
||||
* 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 "test.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* Tar entries with empty filenames are unusual, but shouldn't crash us.
|
||||
*/
|
||||
DEFINE_TEST(test_read_format_tar_empty_filename)
|
||||
{
|
||||
char name[] = "test_read_format_tar_empty_filename.tar";
|
||||
struct archive_entry *ae;
|
||||
struct archive *a;
|
||||
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
extract_reference_file(name);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
|
||||
|
||||
/* Read first entry. */
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
|
||||
assertEqualString("", archive_entry_pathname(ae));
|
||||
assertEqualInt(1208628157, archive_entry_mtime(ae));
|
||||
assertEqualInt(1000, archive_entry_uid(ae));
|
||||
assertEqualString("tim", archive_entry_uname(ae));
|
||||
assertEqualInt(0, archive_entry_gid(ae));
|
||||
assertEqualString("wheel", archive_entry_gname(ae));
|
||||
assertEqualInt(040775, archive_entry_mode(ae));
|
||||
|
||||
/* Verify the end-of-archive. */
|
||||
assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
|
||||
|
||||
/* Verify that the format detection worked. */
|
||||
assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
|
||||
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
|
||||
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_close(a));
|
||||
#if ARCHIVE_API_VERSION > 1
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
|
||||
#else
|
||||
archive_read_finish(a);
|
||||
#endif
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
begin 644 test_compat_tar_1.tar
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M`````````````#`P,#<W-2``,#`Q-S4P(``P,#`P,#`@`#`P,#`P,#`P,#`P
|
||||
M(#$Q,#`R-#,Q-C<U(#`Q,3`P,0`@-0``````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M``````````````````````````````````````````!U<W1A<@`P,'1I;0``
|
||||
M````````````````````````````````````=VAE96P`````````````````
|
||||
M```````````````````P,#`P,#`@`#`P,#`P,"``````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
M````````````````````````````````````````````````````````````
|
||||
&````````
|
||||
`
|
||||
end
|
@ -40,19 +40,22 @@ test_filename(const char *prefix, int dlen, int flen)
|
||||
struct archive_entry *ae;
|
||||
struct archive *a;
|
||||
size_t used;
|
||||
size_t prefix_length = 0;
|
||||
unsigned i = 0;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
p = filename;
|
||||
if (prefix) {
|
||||
strcpy(filename, prefix);
|
||||
i = prefix_length = strlen(prefix);
|
||||
p += strlen(p);
|
||||
}
|
||||
for (; i < prefix_length + dlen; i++)
|
||||
filename[i] = 'a';
|
||||
filename[i++] = '/';
|
||||
for (; i < prefix_length + dlen + flen + 1; i++)
|
||||
filename[i] = 'b';
|
||||
filename[i++] = '\0';
|
||||
if (dlen > 0) {
|
||||
for (i = 0; i < dlen; i++)
|
||||
*p++ = 'a';
|
||||
*p++ = '/';
|
||||
}
|
||||
for (i = 0; i < flen; i++)
|
||||
*p++ = 'b';
|
||||
*p = '\0';
|
||||
|
||||
strcpy(dirname, filename);
|
||||
|
||||
@ -160,15 +163,22 @@ DEFINE_TEST(test_tar_filenames)
|
||||
int dlen, flen;
|
||||
|
||||
/* Repeat the following for a variety of dir/file lengths. */
|
||||
for (dlen = 40; dlen < 60; dlen++) {
|
||||
for (flen = 40; flen < 60; flen++) {
|
||||
for (dlen = 45; dlen < 55; dlen++) {
|
||||
for (flen = 45; flen < 55; flen++) {
|
||||
test_filename(NULL, dlen, flen);
|
||||
test_filename("/", dlen, flen);
|
||||
}
|
||||
}
|
||||
|
||||
for (dlen = 0; dlen < 140; dlen += 10) {
|
||||
for (flen = 98; flen < 102; flen++) {
|
||||
test_filename(NULL, dlen, flen);
|
||||
test_filename("/", dlen, flen);
|
||||
}
|
||||
}
|
||||
|
||||
for (dlen = 140; dlen < 160; dlen++) {
|
||||
for (flen = 90; flen < 110; flen++) {
|
||||
for (flen = 95; flen < 105; flen++) {
|
||||
test_filename(NULL, dlen, flen);
|
||||
test_filename("/", dlen, flen);
|
||||
}
|
||||
|
@ -242,6 +242,11 @@ DEFINE_TEST(test_tar_large)
|
||||
archive_entry_copy_pathname(ae, namebuff);
|
||||
archive_entry_set_mode(ae, S_IFREG | 0755);
|
||||
filesize = tests[i];
|
||||
|
||||
if (filesize < 0) {
|
||||
skipping("32-bit off_t doesn't permit testing of very large files.");
|
||||
return;
|
||||
}
|
||||
archive_entry_set_size(ae, filesize);
|
||||
|
||||
assertA(0 == archive_write_header(a, ae));
|
||||
|
183
lib/libarchive/test/test_ustar_filenames.c
Normal file
183
lib/libarchive/test/test_ustar_filenames.c
Normal file
@ -0,0 +1,183 @@
|
||||
/*-
|
||||
* 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 "test.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* Exercise various lengths of filenames in ustar archives.
|
||||
*/
|
||||
|
||||
static void
|
||||
test_filename(const char *prefix, int dlen, int flen)
|
||||
{
|
||||
char buff[8192];
|
||||
char filename[400];
|
||||
char dirname[400];
|
||||
struct archive_entry *ae;
|
||||
struct archive *a;
|
||||
size_t used;
|
||||
int separator = 0;
|
||||
int i = 0;
|
||||
|
||||
if (prefix != NULL) {
|
||||
strcpy(filename, prefix);
|
||||
i = strlen(prefix);
|
||||
}
|
||||
if (dlen > 0) {
|
||||
for (; i < dlen; i++)
|
||||
filename[i] = 'a';
|
||||
filename[i++] = '/';
|
||||
separator = 1;
|
||||
}
|
||||
for (; i < dlen + flen + separator; i++)
|
||||
filename[i] = 'b';
|
||||
filename[i++] = '\0';
|
||||
|
||||
strcpy(dirname, filename);
|
||||
|
||||
/* Create a new archive in memory. */
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertA(0 == archive_write_set_format_ustar(a));
|
||||
assertA(0 == archive_write_set_compression_none(a));
|
||||
assertA(0 == archive_write_set_bytes_per_block(a,0));
|
||||
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
|
||||
|
||||
/*
|
||||
* Write a file to it.
|
||||
*/
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, filename);
|
||||
archive_entry_set_mode(ae, S_IFREG | 0755);
|
||||
failure("dlen=%d, flen=%d", dlen, flen);
|
||||
if (flen > 100) {
|
||||
assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
|
||||
} else {
|
||||
assertEqualIntA(a, 0, archive_write_header(a, ae));
|
||||
}
|
||||
archive_entry_free(ae);
|
||||
|
||||
/*
|
||||
* Write a dir to it (without trailing '/').
|
||||
*/
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, dirname);
|
||||
archive_entry_set_mode(ae, S_IFDIR | 0755);
|
||||
failure("dlen=%d, flen=%d", dlen, flen);
|
||||
if (flen >= 100) {
|
||||
assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
|
||||
} else {
|
||||
assertEqualIntA(a, 0, archive_write_header(a, ae));
|
||||
}
|
||||
archive_entry_free(ae);
|
||||
|
||||
/* Tar adds a '/' to directory names. */
|
||||
strcat(dirname, "/");
|
||||
|
||||
/*
|
||||
* Write a dir to it (with trailing '/').
|
||||
*/
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, dirname);
|
||||
archive_entry_set_mode(ae, S_IFDIR | 0755);
|
||||
failure("dlen=%d, flen=%d", dlen, flen);
|
||||
if (flen >= 100) {
|
||||
assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
|
||||
} else {
|
||||
assertEqualIntA(a, 0, archive_write_header(a, ae));
|
||||
}
|
||||
archive_entry_free(ae);
|
||||
|
||||
/* Close out the archive. */
|
||||
assertA(0 == archive_write_close(a));
|
||||
assertA(0 == archive_write_finish(a));
|
||||
|
||||
/*
|
||||
* Now, read the data back.
|
||||
*/
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertA(0 == archive_read_support_format_all(a));
|
||||
assertA(0 == archive_read_support_compression_all(a));
|
||||
assertA(0 == archive_read_open_memory(a, buff, used));
|
||||
|
||||
if (flen <= 100) {
|
||||
/* Read the file and check the filename. */
|
||||
assertA(0 == archive_read_next_header(a, &ae));
|
||||
failure("dlen=%d, flen=%d", dlen, flen);
|
||||
assertEqualString(filename, archive_entry_pathname(ae));
|
||||
assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the two dirs and check the names.
|
||||
*
|
||||
* Both dirs should read back with the same name, since
|
||||
* tar should add a trailing '/' to any dir that doesn't
|
||||
* already have one.
|
||||
*/
|
||||
if (flen <= 99) {
|
||||
assertA(0 == archive_read_next_header(a, &ae));
|
||||
assert((S_IFDIR | 0755) == archive_entry_mode(ae));
|
||||
failure("dlen=%d, flen=%d", dlen, flen);
|
||||
assertEqualString(dirname, archive_entry_pathname(ae));
|
||||
}
|
||||
|
||||
if (flen <= 99) {
|
||||
assertA(0 == archive_read_next_header(a, &ae));
|
||||
assert((S_IFDIR | 0755) == archive_entry_mode(ae));
|
||||
assertEqualString(dirname, archive_entry_pathname(ae));
|
||||
}
|
||||
|
||||
/* Verify the end of the archive. */
|
||||
failure("This fails if entries were written that should not have been written. dlen=%d, flen=%d", dlen, flen);
|
||||
assertEqualInt(1, archive_read_next_header(a, &ae));
|
||||
assert(0 == archive_read_close(a));
|
||||
assert(0 == archive_read_finish(a));
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_ustar_filenames)
|
||||
{
|
||||
int dlen, flen;
|
||||
|
||||
/* Try a bunch of different file/dir lengths that add up
|
||||
* to just a little less or a little more than 100 bytes.
|
||||
* This exercises the code that splits paths between ustar
|
||||
* filename and prefix fields.
|
||||
*/
|
||||
for (dlen = 5; dlen < 70; dlen += 5) {
|
||||
for (flen = 100 - dlen - 5; flen < 100 - dlen + 5; flen++) {
|
||||
test_filename(NULL, dlen, flen);
|
||||
test_filename("/", dlen, flen);
|
||||
}
|
||||
}
|
||||
|
||||
/* Probe the 100-char limit for paths with no '/'. */
|
||||
for (flen = 90; flen < 110; flen++) {
|
||||
test_filename(NULL, 0, flen);
|
||||
test_filename("/", dlen, flen);
|
||||
}
|
||||
|
||||
/* XXXX TODO Probe the 100-char limit with a dir prefix. */
|
||||
/* XXXX TODO Probe the 255-char total limit. */
|
||||
}
|
@ -29,6 +29,10 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* Exercise hardlink recreation.
|
||||
*
|
||||
* File permissions are chosen so that the authoritive entry
|
||||
* has the correct permission and the non-authoritive versions
|
||||
* are just writeable files.
|
||||
*/
|
||||
DEFINE_TEST(test_write_disk_hardlink)
|
||||
{
|
||||
@ -64,7 +68,7 @@ DEFINE_TEST(test_write_disk_hardlink)
|
||||
/* Link. */
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link1b");
|
||||
archive_entry_set_mode(ae, S_IFREG | 0755);
|
||||
archive_entry_set_mode(ae, S_IFREG | 0600);
|
||||
archive_entry_set_size(ae, 0);
|
||||
archive_entry_copy_hardlink(ae, "link1a");
|
||||
assertEqualIntA(ad, 0, archive_write_header(ad, ae));
|
||||
@ -80,7 +84,7 @@ DEFINE_TEST(test_write_disk_hardlink)
|
||||
/* Regular file. */
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link2a");
|
||||
archive_entry_set_mode(ae, S_IFREG | 0755);
|
||||
archive_entry_set_mode(ae, S_IFREG | 0600);
|
||||
archive_entry_set_size(ae, sizeof(data));
|
||||
assertEqualIntA(ad, 0, archive_write_header(ad, ae));
|
||||
assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
|
||||
@ -106,10 +110,14 @@ DEFINE_TEST(test_write_disk_hardlink)
|
||||
/* Regular file. */
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link3a");
|
||||
archive_entry_set_mode(ae, S_IFREG | 0755);
|
||||
archive_entry_set_mode(ae, S_IFREG | 0600);
|
||||
archive_entry_set_size(ae, 0);
|
||||
assertEqualIntA(ad, 0, archive_write_header(ad, ae));
|
||||
assertEqualInt(0, archive_write_data(ad, data, sizeof(data)));
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1));
|
||||
#else
|
||||
assertEqualInt(-1, archive_write_data(ad, data, 1));
|
||||
#endif
|
||||
assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
|
||||
archive_entry_free(ae);
|
||||
|
||||
|
@ -30,7 +30,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
char buff[4096];
|
||||
char buff2[64];
|
||||
static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n";
|
||||
static char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n";
|
||||
|
||||
DEFINE_TEST(test_write_format_ar)
|
||||
{
|
||||
@ -162,6 +162,7 @@ DEFINE_TEST(test_write_format_ar)
|
||||
archive_entry_set_filetype(ae, AE_IFREG);
|
||||
archive_entry_set_size(ae, 5);
|
||||
assertA(0 == archive_write_header(a, ae));
|
||||
assertA(5 == archive_entry_size(ae));
|
||||
assertA(5 == archive_write_data(a, "12345", 7));
|
||||
archive_entry_free(ae);
|
||||
|
||||
|
342
lib/libarchive/test/test_write_format_tar_ustar.c
Normal file
342
lib/libarchive/test/test_write_format_tar_ustar.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*-
|
||||
* 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 "test.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
static int
|
||||
is_null(const char *p, size_t l)
|
||||
{
|
||||
while (l > 0) {
|
||||
if (*p != '\0')
|
||||
return (0);
|
||||
--l;
|
||||
++p;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Verify the contents, then erase them to NUL bytes. */
|
||||
/* Tar requires all "unused" bytes be set to NUL; this allows us
|
||||
* to easily verify that by invoking is_null() over the entire header
|
||||
* after verifying each field. */
|
||||
#define myAssertEqualMem(a,b,s) assertEqualMem(a, b, s); memset(a, 0, s)
|
||||
|
||||
/*
|
||||
* Detailed verification that 'ustar' archives are written with
|
||||
* the correct format.
|
||||
*/
|
||||
DEFINE_TEST(test_write_format_tar_ustar)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
char *buff, *e;
|
||||
size_t buffsize = 100000;
|
||||
size_t used;
|
||||
int i;
|
||||
char f99[100];
|
||||
char f100[101];
|
||||
char f256[257];
|
||||
|
||||
for (i = 0; i < 99; ++i)
|
||||
f99[i] = 'a' + i % 26;
|
||||
f99[99] = '\0';
|
||||
|
||||
for (i = 0; i < 100; ++i)
|
||||
f100[i] = 'A' + i % 26;
|
||||
f100[100] = '\0';
|
||||
|
||||
for (i = 0; i < 256; ++i)
|
||||
f256[i] = 'A' + i % 26;
|
||||
f256[155] = '/';
|
||||
f256[256] = '\0';
|
||||
|
||||
buff = malloc(buffsize);
|
||||
|
||||
/* Create a new archive in memory. */
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertEqualIntA(a, 0, archive_write_set_format_ustar(a));
|
||||
assertEqualIntA(a, 0, archive_write_set_compression_none(a));
|
||||
assertEqualIntA(a, 0, archive_write_open_memory(a, buff, buffsize, &used));
|
||||
|
||||
/*
|
||||
* Add various files to it.
|
||||
* TODO: Extend this to cover more filetypes.
|
||||
*/
|
||||
|
||||
/* "file" with 10 bytes of content */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 1, 10);
|
||||
archive_entry_set_pathname(entry, "file");
|
||||
archive_entry_set_mode(entry, S_IFREG | 0664);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_set_uid(entry, 80);
|
||||
archive_entry_set_gid(entry, 90);
|
||||
archive_entry_set_dev(entry, 12);
|
||||
archive_entry_set_ino(entry, 89);
|
||||
archive_entry_set_nlink(entry, 2);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
assertEqualIntA(a, 10, archive_write_data(a, "1234567890", 10));
|
||||
|
||||
/* Hardlink to "file" with 10 bytes of content */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 1, 10);
|
||||
archive_entry_set_pathname(entry, "linkfile");
|
||||
archive_entry_set_mode(entry, S_IFREG | 0664);
|
||||
/* TODO: Put this back and fix the bug. */
|
||||
/* archive_entry_set_size(entry, 10); */
|
||||
archive_entry_set_uid(entry, 80);
|
||||
archive_entry_set_gid(entry, 90);
|
||||
archive_entry_set_dev(entry, 12);
|
||||
archive_entry_set_ino(entry, 89);
|
||||
archive_entry_set_nlink(entry, 2);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
/* Write of data to dir should fail == zero bytes get written. */
|
||||
assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
|
||||
|
||||
/* "dir" */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 2, 20);
|
||||
archive_entry_set_pathname(entry, "dir");
|
||||
archive_entry_set_mode(entry, S_IFDIR | 0775);
|
||||
archive_entry_set_size(entry, 10);
|
||||
archive_entry_set_nlink(entry, 2);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
/* Write of data to dir should fail == zero bytes get written. */
|
||||
assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
|
||||
|
||||
/* "symlink" pointing to "file" */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 3, 30);
|
||||
archive_entry_set_pathname(entry, "symlink");
|
||||
archive_entry_set_mode(entry, S_IFLNK | 0664);
|
||||
archive_entry_set_symlink(entry,"file");
|
||||
archive_entry_set_size(entry, 0);
|
||||
archive_entry_set_uid(entry, 88);
|
||||
archive_entry_set_gid(entry, 98);
|
||||
archive_entry_set_dev(entry, 12);
|
||||
archive_entry_set_ino(entry, 90);
|
||||
archive_entry_set_nlink(entry, 1);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
/* Write of data to symlink should fail == zero bytes get written. */
|
||||
assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
|
||||
|
||||
/* file with 99-char filename. */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 1, 10);
|
||||
archive_entry_set_pathname(entry, f99);
|
||||
archive_entry_set_mode(entry, S_IFREG | 0664);
|
||||
archive_entry_set_size(entry, 0);
|
||||
archive_entry_set_uid(entry, 82);
|
||||
archive_entry_set_gid(entry, 93);
|
||||
archive_entry_set_dev(entry, 102);
|
||||
archive_entry_set_ino(entry, 7);
|
||||
archive_entry_set_nlink(entry, 1);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
/* file with 100-char filename. */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 1, 10);
|
||||
archive_entry_set_pathname(entry, f100);
|
||||
archive_entry_set_mode(entry, S_IFREG | 0664);
|
||||
archive_entry_set_size(entry, 0);
|
||||
archive_entry_set_uid(entry, 82);
|
||||
archive_entry_set_gid(entry, 93);
|
||||
archive_entry_set_dev(entry, 102);
|
||||
archive_entry_set_ino(entry, 7);
|
||||
archive_entry_set_nlink(entry, 1);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
/* file with 256-char filename. */
|
||||
assert((entry = archive_entry_new()) != NULL);
|
||||
archive_entry_set_mtime(entry, 1, 10);
|
||||
archive_entry_set_pathname(entry, f256);
|
||||
archive_entry_set_mode(entry, S_IFREG | 0664);
|
||||
archive_entry_set_size(entry, 0);
|
||||
archive_entry_set_uid(entry, 82);
|
||||
archive_entry_set_gid(entry, 93);
|
||||
archive_entry_set_dev(entry, 102);
|
||||
archive_entry_set_ino(entry, 7);
|
||||
archive_entry_set_nlink(entry, 1);
|
||||
assertEqualIntA(a, 0, archive_write_header(a, entry));
|
||||
archive_entry_free(entry);
|
||||
|
||||
assert(0 == archive_write_finish(a));
|
||||
|
||||
/*
|
||||
* Verify the archive format.
|
||||
*/
|
||||
e = buff;
|
||||
|
||||
/* "file" */
|
||||
myAssertEqualMem(e + 0, "file", 5); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000120 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000132 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000012 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "010034\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
|
||||
myAssertEqualMem(e + 157, "", 1); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, "", 1); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
myAssertEqualMem(e + 512, "1234567890", 10);
|
||||
assert(is_null(e + 512, 512));
|
||||
e += 1024;
|
||||
|
||||
/* hardlink to "file" */
|
||||
myAssertEqualMem(e + 0, "linkfile", 9); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000120 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000132 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "010707\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
|
||||
myAssertEqualMem(e + 157, "", 1); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, "", 1); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
e += 512;
|
||||
|
||||
/* "dir" */
|
||||
myAssertEqualMem(e + 0, "dir/", 4); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000775 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000000 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000000 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000002 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "007747\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "5", 1); /* typeflag */
|
||||
myAssertEqualMem(e + 157, "", 1); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, "", 1); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
e += 512;
|
||||
|
||||
/* "symlink" pointing to "file" */
|
||||
myAssertEqualMem(e + 0, "symlink", 8); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000130 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000142 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000003 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "011446\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "2", 1); /* linkflag */
|
||||
myAssertEqualMem(e + 157, "file", 5); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, "", 1); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
e += 512;
|
||||
|
||||
/* File with 99-char filename */
|
||||
myAssertEqualMem(e + 0, f99, 100); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000122 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000135 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "034242\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
|
||||
myAssertEqualMem(e + 157, "", 1); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, "", 1); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
e += 512;
|
||||
|
||||
/* File with 100-char filename */
|
||||
myAssertEqualMem(e + 0, f100, 100); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000122 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000135 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "026230\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
|
||||
myAssertEqualMem(e + 157, "", 1); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, "", 1); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
e += 512;
|
||||
|
||||
/* File with 256-char filename */
|
||||
myAssertEqualMem(e + 0, f256 + 156, 100); /* Filename */
|
||||
myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
|
||||
myAssertEqualMem(e + 108, "000122 ", 8); /* uid */
|
||||
myAssertEqualMem(e + 116, "000135 ", 8); /* gid */
|
||||
myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
|
||||
myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
|
||||
myAssertEqualMem(e + 148, "055570\0 ", 8); /* checksum */
|
||||
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
|
||||
myAssertEqualMem(e + 157, "", 1); /* linkname */
|
||||
myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
|
||||
myAssertEqualMem(e + 265, "", 1); /* uname */
|
||||
myAssertEqualMem(e + 297, "", 1); /* gname */
|
||||
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
|
||||
myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
|
||||
myAssertEqualMem(e + 345, f256, 155); /* prefix */
|
||||
assert(is_null(e + 0, 512));
|
||||
e += 512;
|
||||
|
||||
/* TODO: Verify other types of entries. */
|
||||
|
||||
/* Last entry is end-of-archive marker. */
|
||||
assert(is_null(e, 1024));
|
||||
e += 1024;
|
||||
|
||||
assertEqualInt(used, e - buff);
|
||||
|
||||
free(buff);
|
||||
}
|
Loading…
Reference in New Issue
Block a user