diff options
author | Andy Cedilnik <andy.cedilnik@kitware.com> | 2006-01-02 07:53:05 -0500 |
---|---|---|
committer | Andy Cedilnik <andy.cedilnik@kitware.com> | 2006-01-02 07:53:05 -0500 |
commit | c9f3ad1e23a9ed6d17ab4224e0fe6ab4e8629c30 (patch) | |
tree | 412004fda7dbb0f004d04e5af94807c239fef69c | |
parent | b7ba65ed684a663be0085ae96dca3768ebdd3325 (diff) | |
download | cmake-c9f3ad1e23a9ed6d17ab4224e0fe6ab4e8629c30.tar.gz |
ENH: Use libtar
-rw-r--r-- | Source/CPack/cmCPackPackageMakerGenerator.cxx | 2 | ||||
-rw-r--r-- | Source/CPack/cmCPackTGZGenerator.cxx | 4022 | ||||
-rw-r--r-- | Source/CPack/cmCPackTGZGenerator.h | 6 |
3 files changed, 70 insertions, 3960 deletions
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx index 65444edad2..4a28fe1d03 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.cxx +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -46,6 +46,8 @@ int cmCPackPackageMakerGenerator::ProcessGenerator() int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName, const char* toplevel, const std::vector<std::string>& files) { + (void) files; // TODO: Fix api to not need files. + (void) toplevel; // TODO: Use toplevel // Create directory structure std::string resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); resDir += "/Resources"; diff --git a/Source/CPack/cmCPackTGZGenerator.cxx b/Source/CPack/cmCPackTGZGenerator.cxx index 594e4149fe..c337e6fc7a 100644 --- a/Source/CPack/cmCPackTGZGenerator.cxx +++ b/Source/CPack/cmCPackTGZGenerator.cxx @@ -26,9 +26,10 @@ #include <cmsys/SystemTools.hxx> #include <cmzlib/zlib.h> - +#include <libtar/libtar.h> #include <memory> // auto_ptr -#include <fcntl.h> // O_WRONLY, O_CREAT +#include <fcntl.h> +#include <errno.h> //---------------------------------------------------------------------- cmCPackTGZGenerator::cmCPackTGZGenerator() @@ -53,3102 +54,6 @@ int cmCPackTGZGenerator::Initialize(const char* name) } //---------------------------------------------------------------------- -// The following code is modified version of bsdtar -/*- -* Copyright (c) 2003-2004 Tim Kientzle -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer -* in this position and unchanged. -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR -* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined(HAVE_IO_H) -# include <io.h> -#endif -#if defined(HAVE_UNISTD_H) -# include <unistd.h> // for geteuid -#endif -#if defined(HAVE_INTTYPES_H) -# include <inttypes.h> // for uintptr_t -#endif -#include <sys/types.h> -#include <errno.h> // for ENOMEM -#include <sys/stat.h> // for struct stat -#include <time.h> // for time - -#if defined(WIN32) && !defined(__CYGWIN__) -# ifndef S_ISREG -# define S_ISREG(x) ((x) & _S_IFREG) -# endif -# ifndef S_ISLNK -# define S_ISLNK(x) (0) -# endif -#endif - - //--- archive_plarform.h - // - /* Set up defaults for internal error codes. */ -#ifndef ARCHIVE_ERRNO_FILE_FORMAT -#if HAVE_EFTYPE -#define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE -#else -#if HAVE_EILSEQ -#define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ -#else -#define ARCHIVE_ERRNO_FILE_FORMAT EINVAL -#endif -#endif -#endif - -#ifndef ARCHIVE_ERRNO_PROGRAMMER -#define ARCHIVE_ERRNO_PROGRAMMER EINVAL -#endif - -#ifndef ARCHIVE_ERRNO_MISC -#define ARCHIVE_ERRNO_MISC (-1) -#endif - - /* Select the best way to set/get hi-res timestamps. */ -#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC - /* FreeBSD uses "timespec" members. */ -#define ARCHIVE_STAT_ATIME_NANOS(st) (st)->st_atimespec.tv_nsec -#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec -#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec -#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n) -#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n) -#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n) -#else -#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC - /* Linux uses "tim" members. */ -#define ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec -#define ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec -#define ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec -#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n) -#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n) -#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n) -#else - /* If we can't find a better way, just use stubs. */ -#define ARCHIVE_STAT_ATIME_NANOS(pstat) 0 -#define ARCHIVE_STAT_CTIME_NANOS(pstat) 0 -#define ARCHIVE_STAT_MTIME_NANOS(pstat) 0 -#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) -#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) -#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) -#endif -#endif - - //--- archive.h - -#define ARCHIVE_BYTES_PER_RECORD 512 -#define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240 - - /* Declare our basic types. */ - struct archive; - struct archive_entry; - - /* - * Error codes: Use archive_errno() and archive_error_string() - * to retrieve details. Unless specified otherwise, all functions - * that return 'int' use these codes. - */ -#define ARCHIVE_EOF 1 /* Found end of archive. */ -#define ARCHIVE_OK 0 /* Operation was successful. */ -#define ARCHIVE_RETRY (-10) /* Retry might succeed. */ -#define ARCHIVE_WARN (-20) /* Partial sucess. */ -#define ARCHIVE_FATAL (-30) /* No more operations are possible. */ - - /* - * As far as possible, archive_errno returns standard platform errno codes. - * Of course, the details vary by platform, so the actual definitions - * here are stored in "archive_platform.h". The symbols are listed here - * for reference; as a rule, clients should not need to know the exact - * platform-dependent error code. - */ - /* Unrecognized or invalid file format. */ - /* #define ARCHIVE_ERRNO_FILE_FORMAT */ - /* Illegal usage of the library. */ - /* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */ - /* Unknown or unclassified error. */ - /* #define ARCHIVE_ERRNO_MISC */ - - /* - * Callbacks are invoked to automatically read/write/open/close the archive. - * You can provide your own for complex tasks (like breaking archives - * across multiple tapes) or use standard ones built into the library. - */ - - /* Returns pointer and size of next block of data from archive. */ - typedef ssize_t archive_read_callback(struct archive *, void *_client_data, - const void **_buffer); -/* Returns size actually written, zero on EOF, -1 on error. */ -typedef ssize_t archive_write_callback(struct archive *, void *_client_data, - 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); - -/* - * Codes for archive_compression. - */ -#define ARCHIVE_COMPRESSION_NONE 0 -#define ARCHIVE_COMPRESSION_GZIP 1 -#define ARCHIVE_COMPRESSION_BZIP2 2 -#define ARCHIVE_COMPRESSION_COMPRESS 3 - -/* - * Codes returned by archive_format. - * - * Top 16 bits identifies the format family (e.g., "tar"); lower - * 16 bits indicate the variant. This is updated by read_next_header. - * Note that the lower 16 bits will often vary from entry to entry. - */ -#define ARCHIVE_FORMAT_BASE_MASK 0xff0000U -#define ARCHIVE_FORMAT_CPIO 0x10000 -#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) -#define ARCHIVE_FORMAT_SHAR 0x20000 -#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) -#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) -#define ARCHIVE_FORMAT_TAR 0x30000 -#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1) -#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2) -#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3) -#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4) -#define ARCHIVE_FORMAT_ISO9660 0x40000 -#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) -#define ARCHIVE_FORMAT_ZIP 0x50000 - -/*- - * Basic outline for reading an archive: - * 1) Ask archive_read_new for an archive reader object. - * 2) Update any global properties as appropriate. - * In particular, you'll certainly want to call appropriate - * archive_read_support_XXX functions. - * 3) Call archive_read_open_XXX to open the archive - * 4) Repeatedly call archive_read_next_header to get information about - * successive archive entries. Call archive_read_data to extract - * data for entries of interest. - * 5) Call archive_read_finish to end processing. - */ -struct archive *archive_read_new(void); - -/* - * The archive_read_support_XXX calls enable auto-detect for this - * archive handle. They also link in the necessary support code. - * For example, if you don't want bzlib linked in, don't invoke - * 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_format_all(struct archive *); -int archive_read_support_format_cpio(struct archive *); -int archive_read_support_format_gnutar(struct archive *); -int archive_read_support_format_iso9660(struct archive *); -int archive_read_support_format_tar(struct archive *); -int archive_read_support_format_tp(struct archive *a); -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, - archive_open_callback *, archive_read_callback *, - archive_close_callback *); - -/* - * The archive_read_open_file function is a convenience function built - * on archive_read_open that uses a canned callback suitable for - * common situations. Note that a NULL filename indicates stdin. - */ -int archive_read_open_file(struct archive *, const char *_file, - size_t _block_size); -int archive_read_open_fd(struct archive *, int _fd, - size_t _block_size); - -/* Parses and returns next entry header. */ -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 *); - -/* Read data from the body of an entry. Similar to read(2). */ -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 gaurantee that offsets will - * be strictly increasing and that returned blocks will not overlap. - */ -int archive_read_data_block(struct archive *a, - const void **buff, size_t *size, off_t *offset); - -/*- - * Some convenience functions that are built on archive_read_data: - * 'skip': skips entire entry - * '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); - -/*- - * Convenience function to recreate the current entry (whose header - * has just been read) on disk. - * - * This does quite a bit more than just copy data to disk. It also: - * - Creates intermediate directories as required. - * - Manages directory permissions: non-writable directories will - * be initially created with write permission enabled; when the - * archive is closed, dir permissions are edited to the values specified - * in the archive. - * - Checks hardlinks: hardlinks will not be extracted unless the - * linked-to file was also extracted within the same session. (TODO) - */ - -/* The "flags" argument selects optional behavior, 'OR' the flags you want. */ -/* TODO: The 'Default' comments here are not quite correct; clean this up. */ -#define ARCHIVE_EXTRACT_OWNER (1) /* Default: owner/group not restored */ -#define ARCHIVE_EXTRACT_PERM (2) /* Default: restore perm only for reg file*/ -#define ARCHIVE_EXTRACT_TIME (4) /* Default: mod time not restored */ -#define ARCHIVE_EXTRACT_NO_OVERWRITE (8) /* Default: Replace files on disk */ -#define ARCHIVE_EXTRACT_UNLINK (16) /* Default: don't unlink existing files */ -#define ARCHIVE_EXTRACT_ACL (32) /* Default: don't restore ACLs */ -#define ARCHIVE_EXTRACT_FFLAGS (64) /* Default: don't restore fflags */ - -int archive_read_extract(struct archive *, struct archive_entry *, - int flags); -void archive_read_extract_set_progress_callback(struct archive *, - void (*_progress_func)(void *), void *_user_data); - -/* Close the file and release most resources. */ -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. */ -void archive_read_finish(struct archive *); - -/*- - * To create an archive: - * 1) Ask archive_write_new for a archive writer object. - * 2) Set any global properties. In particular, you should set - * the compression and format to use. - * 3) Call archive_write_open to open the file (most people - * will use archive_write_open_file or archive_write_open_fd, - * which provide convenient canned I/O callbacks for you). - * 4) For each entry: - * - construct an appropriate struct archive_entry structure - * - archive_write_header to write the header - * - archive_write_data to write the entry data - * 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 *, - int bytes_per_block); -/* XXX This is badly misnamed; suggestions appreciated. XXX */ -int archive_write_set_bytes_in_last_block(struct archive *, - int bytes_in_last_block); - -int archive_write_set_compression_bzip2(struct archive *); -int archive_write_set_compression_gzip(struct archive *); -int archive_write_set_compression_none(struct archive *); -/* 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 *, - const char *name); -/* To minimize link pollution, use one or more of the following. */ -int archive_write_set_format_cpio(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 *, - archive_open_callback *, archive_write_callback *, - archive_close_callback *); -int archive_write_open_fd(struct archive *, int _fd); -int archive_write_open_file(struct archive *, const char *_file); - -/* - * 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 *, - struct archive_entry *); -/* TODO: should be ssize_t, but that might require .so version bump? */ -int archive_write_data(struct archive *, const void *, size_t); -int archive_write_close(struct archive *); -void archive_write_finish(struct archive *); - -/* - * 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 *); -/* Bytes written to compressor or read from decompressor. */ -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_set_error(struct archive *, int _err, const char *fmt, ...); - -//--- archive_string.h -/* - * Basic resizable/reusable string support a la Java's "StringBuffer." - * - * Unlike sbuf(9), the buffers here are fully reusable and track the - * length throughout. - * - * Note that all visible symbols here begin with "__archive" as they - * are internal symbols not intended for anyone outside of this library - * to see or use. - */ - -struct archive_string { - char *s; /* Pointer to the storage */ - size_t length; /* Length of 's' */ - size_t buffer_length; /* Length of malloc-ed storage */ -}; - -/* Initialize an archive_string object on the stack or elsewhere. */ -#define archive_string_init(a) \ - do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0) - -/* Append a C char to an archive_string, resizing as necessary. */ -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 - -/* Basic append operation. */ -struct archive_string * -__archive_string_append(struct archive_string *as, const char *p, size_t s); - -/* Ensure that the underlying buffer is at least as large as the request. */ -struct archive_string * -__archive_string_ensure(struct archive_string *, size_t); -#define archive_string_ensure __archive_string_ensure - -/* Append C string, which may lack trailing \0. */ -struct archive_string * -__archive_strncat(struct archive_string *, const char *, size_t); -#define archive_strncat __archive_strncat - -/* Append a C string to an archive_string, resizing as necessary. */ -#define archive_strcat(as,p) __archive_string_append((as),(p),strlen(p)) - -/* 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))) - -/* Copy a C string to an archive_string with limit, resizing as necessary. */ -#define archive_strncpy(as,p,l) \ - ((as)->length=0, archive_strncat((as), (p), (l))) - -/* Return length of string. */ -#define archive_strlen(a) ((a)->length) - -/* Set string length to zero. */ -#define archive_string_empty(a) ((a)->length = 0) - -/* Release any allocated storage resources. */ -void __archive_string_free(struct archive_string *); -#define archive_string_free __archive_string_free - -/* Like 'vsprintf', but resizes the underlying string as necessary. */ -void __archive_string_vsprintf(struct archive_string *, const char *, - va_list); -#define archive_string_vsprintf __archive_string_vsprintf -//--- archive_private.h - -#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU) -#define ARCHIVE_READ_MAGIC (0xdeb0c5U) - -struct archive { - /* - * The magic/state values are used to sanity-check the - * client's usage. If an API function is called at a - * rediculous time, or the client passes us an invalid - * pointer, these values allow me to catch that. - */ - unsigned magic; - unsigned state; - - struct archive_entry *entry; - uid_t user_uid; /* UID of current user. */ - - /* Dev/ino of the archive being read/written. */ - dev_t skip_file_dev; - ino_t skip_file_ino; - - /* Utility: Pointer to a block of nulls. */ - const unsigned char *nulls; - size_t null_length; - - /* - * Used by archive_read_data() to track blocks and copy - * data to client buffers, filling gaps with zero bytes. - */ - const char *read_data_block; - off_t read_data_offset; - off_t read_data_output_offset; - size_t read_data_remaining; - - /* Callbacks to open/read/write/close archive stream. */ - archive_open_callback *client_opener; - archive_read_callback *client_reader; - archive_write_callback *client_writer; - archive_close_callback *client_closer; - void *client_data; - - /* - * Blocking information. Note that bytes_in_last_block is - * misleadingly named; I should find a better name. These - * control the final output from all compressors, including - * compression_none. - */ - int bytes_per_block; - int bytes_in_last_block; - - /* - * These control whether data within a gzip/bzip2 compressed - * stream gets padded or not. If pad_uncompressed is set, - * the data will be padded to a full block before being - * compressed. The pad_uncompressed_byte determines the value - * that will be used for padding. Note that these have no - * effect on compression "none." - */ - int pad_uncompressed; - int pad_uncompressed_byte; /* TODO: Support this. */ - - /* Position in UNCOMPRESSED data stream. */ - off_t file_position; - /* Position in COMPRESSED data stream. */ - off_t raw_position; - /* File offset of beginning of most recently-read header. */ - off_t header_position; - - /* - * Detection functions for decompression: bid functions are - * given a block of data from the beginning of the stream and - * can bid on whether or not they support the data stream. - * General guideline: bid the number of bits that you actually - * test, e.g., 16 if you test a 2-byte magic value. The - * highest bidder will have their init function invoked, which - * can set up pointers to specific handlers. - * - * On write, the client just invokes an archive_write_set function - * which sets up the data here directly. - */ - int compression_code; /* Currently active compression. */ - const char *compression_name; - struct { - int (*bid)(const void *buff, size_t); - int (*init)(struct archive *, const void *buff, size_t); - } decompressors[4]; - /* Read/write data stream (with compression). */ - void *compression_data; /* Data for (de)compressor. */ - int (*compression_init)(struct archive *); /* Initialize. */ - int (*compression_finish)(struct archive *); - int (*compression_write)(struct archive *, const void *, size_t); - /* - * Read uses a peek/consume I/O model: the decompression code - * returns a pointer to the requested block and advances the - * file position only when requested by a consume call. This - * reduces copying and also simplifies look-ahead for format - * detection. - */ - ssize_t (*compression_read_ahead)(struct archive *, - const void **, size_t request); - ssize_t (*compression_read_consume)(struct archive *, size_t); - - /* - * Format detection is mostly the same as compression - * detection, with two significant differences: The bidders - * use the read_ahead calls above to examine the stream rather - * than having the supervisor hand them a block of data to - * examine, and the auction is repeated for every header. - * Winning bidders should set the archive_format and - * archive_format_name appropriately. Bid routines should - * check archive_format and decline to bid if the format of - * the last header was incompatible. - * - * Again, write support is considerably simpler because there's - * no need for an auction. - */ - int archive_format; - const char *archive_format_name; - - struct archive_format_descriptor { - int (*bid)(struct archive *); - int (*read_header)(struct archive *, struct archive_entry *); - int (*read_data)(struct archive *, const void **, size_t *, off_t *); - int (*read_data_skip)(struct archive *); - int (*cleanup)(struct archive *); - void *format_data; /* Format-specific data for readers. */ - } formats[8]; - struct archive_format_descriptor *format; /* Active format. */ - - /* - * Storage for format-specific data. Note that there can be - * multiple format readers active at one time, so we need to - * allow for multiple format readers to have their data - * available. The pformat_data slot here is the solution: on - * read, it is gauranteed to always point to a void* variable - * that the format can use. - */ - void **pformat_data; /* Pointer to current format_data. */ - void *format_data; /* Used by writers. */ - - /* - * Pointers to format-specific functions for writing. They're - * initialized by archive_write_set_format_XXX() calls. - */ - int (*format_init)(struct archive *); /* Only used on write. */ - int (*format_finish)(struct archive *); - int (*format_finish_entry)(struct archive *); - int (*format_write_header)(struct archive *, - struct archive_entry *); - int (*format_write_data)(struct archive *, - const void *buff, size_t); - - /* - * Various information needed by archive_extract. - */ - struct extract *extract; - void (*extract_progress)(void *); - void *extract_progress_user_data; - void (*cleanup_archive_extract)(struct archive *); - - int archive_error_number; - const char *error; - struct archive_string error_string; -}; - - -#define ARCHIVE_STATE_ANY 0xFFFFU -#define ARCHIVE_STATE_NEW 1U -#define ARCHIVE_STATE_HEADER 2U -#define ARCHIVE_STATE_DATA 4U -#define ARCHIVE_STATE_EOF 8U -#define ARCHIVE_STATE_CLOSED 0x10U -#define ARCHIVE_STATE_FATAL 0x8000U - -/* Check magic value and state; exit if it isn't valid. */ -void __archive_check_magic(struct archive *, unsigned magic, - unsigned state, const char *func); - - -int __archive_read_register_format(struct archive *a, - void *format_data, - int (*bid)(struct archive *), - int (*read_header)(struct archive *, struct archive_entry *), - int (*read_data)(struct archive *, const void **, size_t *, off_t *), - int (*read_data_skip)(struct archive *), - int (*cleanup)(struct archive *)); - -int __archive_read_register_compression(struct archive *a, - int (*bid)(const void *, size_t), - int (*init)(struct archive *, const void *, size_t)); - -void __archive_errx(int retvalue, const char *msg); - -#define err_combine(a,b) ((a) < (b) ? (a) : (b)) - - - /* - * Utility function to format a USTAR header into a buffer. If - * "strict" is set, this tries to create the absolutely most portable - * version of a ustar header. If "strict" is set to 0, then it will - * relax certain requirements. - * - * Generally, format-specific declarations don't belong in this - * header; this is a rare example of a function that is shared by - * two very similar formats (ustar and pax). - */ - int - __archive_write_format_header_ustar(struct archive *, char buff[512], - struct archive_entry *, int tartype, int strict); - -//--- archive_entry.h - -/* - * Description of an archive entry. - * - * Basically, a "struct stat" with a few text fields added in. - * - * TODO: Add "comment", "charset", and possibly other entries that are - * supported by "pax interchange" format. However, GNU, ustar, cpio, - * and other variants don't support these features, so they're not an - * excruciatingly high priority right now. - * - * TODO: "pax interchange" format allows essentially arbitrary - * key/value attributes to be attached to any entry. Supporting - * such extensions may make this library useful for special - * applications (e.g., a package manager could attach special - * package-management attributes to each entry). - */ -struct archive_entry; - -/* - * Basic object manipulation - */ - -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); - -/* - * 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 *); -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 *); -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 *); -int64_t archive_entry_size(struct archive_entry *); -const struct stat *archive_entry_stat(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 *); - -/* - * Set fields in an archive_entry. - * - * Note that string 'set' functions do not copy the string, only the pointer. - * In contrast, 'copy' functions do copy the object pointed to. - */ - -void archive_entry_copy_stat(struct archive_entry *, const struct stat *); -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_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 *, - 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_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_link(struct archive_entry *, const char *); -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_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_size(struct archive_entry *, int64_t); -void archive_entry_set_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_w(struct archive_entry *, const wchar_t *); - -/* - * ACL routines. This used to simply store and return text-format ACL - * strings, but that proved insufficient for a number of reasons: - * = clients need control over uname/uid and gname/gid mappings - * = there are many different ACL text formats - * = would like to be able to read/convert archives containing ACLs - * on platforms that lack ACL libraries - */ - -/* - * Permission bits mimic POSIX.1e. Note that I've not followed POSIX.1e's - * "permset"/"perm" abstract type nonsense. A permset is just a simple - * bitmap, following long-standing Unix tradition. - */ -#define ARCHIVE_ENTRY_ACL_EXECUTE 1 -#define ARCHIVE_ENTRY_ACL_WRITE 2 -#define ARCHIVE_ENTRY_ACL_READ 4 - -/* We need to be able to specify either or both of these. */ -#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256 -#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512 - -/* Tag values mimic POSIX.1e */ -#define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */ -#define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */ -#define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */ -#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */ -#define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access. */ -#define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public. */ - -/* - * Set the ACL by clearing it and adding entries one at a time. - * Unlike the POSIX.1e ACL routines, you must specify the type - * (access/default) for each entry. Internally, the ACL data is just - * a soup of entries. API calls here allow you to retrieve just the - * entries of interest. This design (which goes against the spirit of - * 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 *, - int type, int permset, int tag, int qual, const char *name); -void archive_entry_acl_add_entry_w(struct archive_entry *, - int type, int permset, int tag, int qual, const wchar_t *name); - -/* - * To retrieve the ACL, first "reset", then repeatedly ask for the - * "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, - int *type, int *permset, int *tag, int *qual, const char **name); -int archive_entry_acl_next_w(struct archive_entry *, int want_type, - int *type, int *permset, int *tag, int *qual, - const wchar_t **name); - -/* - * Construct a text-format ACL. The flags argument is a bitmask that - * can include any of the following: - * - * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries. - * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries. - * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in - * each ACL entry. (As used by 'star'.) - * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each - * default ACL entry. - */ -#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 *, int flags); - -/* Return a count of entries matching 'want_type' */ -int archive_entry_acl_count(struct archive_entry *, int want_type); - -/* - * Private ACL parser. This is private because it handles some - * very weird formats that clients should not be messing with. - * Clients should only deal with their platform-native formats. - * Because of the need to support many formats cleanly, new arguments - * are likely to get added on a regular basis. Clients who try to use - * this interface are likely to be surprised when it changes. - * - * You were warned! - */ -int __archive_entry_acl_parse_w(struct archive_entry *, - const wchar_t *, int type); -//--- archive_write.h - -/* - * Allocate, initialize and return an archive object. - */ - struct archive * -archive_write_new(void) -{ - struct archive *a; - unsigned char *nulls; - - a = (struct archive*)malloc(sizeof(*a)); - if (a == NULL) - return (NULL); - memset(a, 0, sizeof(*a)); - a->magic = ARCHIVE_WRITE_MAGIC; -#ifdef HAVE_GETEUID - a->user_uid = geteuid(); -#endif - a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK; - a->bytes_in_last_block = -1; /* Default */ - a->state = ARCHIVE_STATE_NEW; - a->pformat_data = &(a->format_data); - - /* Initialize a block of nulls for padding purposes. */ - a->null_length = 1024; - nulls = (unsigned char*)malloc(a->null_length); - if (nulls == NULL) { - free(a); - return (NULL); - } - memset(nulls, 0, a->null_length); - a->nulls = nulls; - /* - * Set default compression, but don't set a default format. - * Were we to set a default format here, we would force every - * client to link in support for that format, even if they didn't - * ever use it. - */ - archive_write_set_compression_none(a); - return (a); -} - - -/* - * Set the block size. Returns 0 if successful. - */ - int -archive_write_set_bytes_per_block(struct archive *a, int bytes_per_block) -{ - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block"); - a->bytes_per_block = bytes_per_block; - return (ARCHIVE_OK); -} - - -/* - * Set the size for the last block. - * Returns 0 if successful. - */ - int -archive_write_set_bytes_in_last_block(struct archive *a, int bytes) -{ - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block"); - a->bytes_in_last_block = bytes; - return (ARCHIVE_OK); -} - - -/* - * Open the archive using the current settings. - */ - int -archive_write_open(struct archive *a, void *client_data, - archive_open_callback *opener, archive_write_callback *writer, - archive_close_callback *closer) -{ - int ret; - - ret = ARCHIVE_OK; - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open"); - archive_string_empty(&a->error_string); - a->state = ARCHIVE_STATE_HEADER; - a->client_data = client_data; - a->client_writer = writer; - a->client_opener = opener; - a->client_closer = closer; - ret = (a->compression_init)(a); - if (a->format_init && ret == ARCHIVE_OK) - ret = (a->format_init)(a); - return (ret); -} - - -/* - * Close out the archive. - * - * Be careful: user might just call write_new and then write_finish. - * Don't assume we actually wrote anything or performed any non-trivial - * initialization. - */ - int -archive_write_close(struct archive *a) -{ - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_close"); - - /* Finish the last entry. */ - if (a->state & ARCHIVE_STATE_DATA) - ((a->format_finish_entry)(a)); - - /* Finish off the archive. */ - if (a->format_finish != NULL) - (a->format_finish)(a); - - /* Finish the compression and close the stream. */ - if (a->compression_finish != NULL) - (a->compression_finish)(a); - - a->state = ARCHIVE_STATE_CLOSED; - return (ARCHIVE_OK); -} - -/* - * Destroy the archive structure. - */ - void -archive_write_finish(struct archive *a) -{ - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_finish"); - if (a->state != ARCHIVE_STATE_CLOSED) - archive_write_close(a); - - /* Release various dynamic buffers. */ - free((void *)(uintptr_t)(const void *)a->nulls); - archive_string_free(&a->error_string); - a->magic = 0; - free(a); -} - - -/* - * Write the appropriate header. - */ - int -archive_write_header(struct archive *a, struct archive_entry *entry) -{ - int ret; - - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_header"); - archive_string_empty(&a->error_string); - - /* Finish last entry. */ - if (a->state & ARCHIVE_STATE_DATA) - ((a->format_finish_entry)(a)); - - if (archive_entry_dev(entry) == a->skip_file_dev && - archive_entry_ino(entry) == a->skip_file_ino) { - archive_set_error(a, 0, "Can't add archive to itself"); - return (ARCHIVE_WARN); - } - - /* Format and write header. */ - ret = ((a->format_write_header)(a, entry)); - - a->state = ARCHIVE_STATE_DATA; - return (ret); -} - -/* - * Note that the compressor is responsible for blocking. - */ -/* Should be "ssize_t", but that breaks the ABI. <sigh> */ - int -archive_write_data(struct archive *a, const void *buff, size_t s) -{ - int ret; - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); - archive_string_empty(&a->error_string); - ret = (a->format_write_data)(a, buff, s); - return (ret == ARCHIVE_OK ? (ssize_t)s : -1); -} - -//--- archive_write_set_compression_none.c - -static int archive_compressor_none_finish(struct archive *a); -static int archive_compressor_none_init(struct archive *); -static int archive_compressor_none_write(struct archive *, const void *, - size_t); - -struct archive_none { - char *buffer; - ssize_t buffer_size; - char *next; /* Current insert location */ - ssize_t avail; /* Free space left in buffer */ -}; - - int -archive_write_set_compression_none(struct archive *a) -{ - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_none"); - a->compression_init = &archive_compressor_none_init; - a->compression_code = ARCHIVE_COMPRESSION_NONE; - a->compression_name = "none"; - return (0); -} - -/* - * Setup callback. - */ - static int -archive_compressor_none_init(struct archive *a) -{ - int ret; - struct archive_none *state; - - a->compression_code = ARCHIVE_COMPRESSION_NONE; - a->compression_name = "none"; - - if (a->client_opener != NULL) { - ret = (a->client_opener)(a, a->client_data); - if (ret != 0) - return (ret); - } - - state = (struct archive_none *)malloc(sizeof(*state)); - if (state == NULL) { - archive_set_error(a, ENOMEM, - "Can't allocate data for output buffering"); - return (ARCHIVE_FATAL); - } - memset(state, 0, sizeof(*state)); - - state->buffer_size = a->bytes_per_block; - state->buffer = (char*)malloc(state->buffer_size); - - if (state->buffer == NULL) { - archive_set_error(a, ENOMEM, - "Can't allocate output buffer"); - free(state); - return (ARCHIVE_FATAL); - } - - state->next = state->buffer; - state->avail = state->buffer_size; - - a->compression_data = state; - a->compression_write = archive_compressor_none_write; - a->compression_finish = archive_compressor_none_finish; - return (ARCHIVE_OK); -} - -/* - * Write data to the stream. - */ - static int -archive_compressor_none_write(struct archive *a, const void *vbuff, - size_t length) -{ - const char *buff; - ssize_t remaining, to_copy; - ssize_t bytes_written; - struct archive_none *state; - - state = (struct archive_none*)a->compression_data; - buff = (const char*)vbuff; - if (a->client_writer == NULL) { - archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER, - "No write callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } - - remaining = length; - while (remaining > 0) { - /* - * If we have a full output block, write it and reset the - * output buffer. - */ - if (state->avail == 0) { - bytes_written = (a->client_writer)(a, a->client_data, - state->buffer, state->buffer_size); - if (bytes_written <= 0) - return (ARCHIVE_FATAL); - /* XXX TODO: if bytes_written < state->buffer_size */ - a->raw_position += bytes_written; - state->next = state->buffer; - state->avail = state->buffer_size; - } - - /* Now we have space in the buffer; copy new data into it. */ - to_copy = (remaining > state->avail) ? - state->avail : remaining; - memcpy(state->next, buff, to_copy); - state->next += to_copy; - state->avail -= to_copy; - buff += to_copy; - remaining -= to_copy; - } - a->file_position += length; - return (ARCHIVE_OK); -} - - -/* - * Finish the compression. - */ - static int -archive_compressor_none_finish(struct archive *a) -{ - ssize_t block_length; - ssize_t target_block_length; - ssize_t bytes_written; - int ret; - int ret2; - struct archive_none *state; - - state = (struct archive_none*)a->compression_data; - ret = ret2 = ARCHIVE_OK; - if (a->client_writer == NULL) { - archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER, - "No write callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } - - /* If there's pending data, pad and write the last block */ - if (state->next != state->buffer) { - block_length = state->buffer_size - state->avail; - - /* Tricky calculation to determine size of last block */ - target_block_length = block_length; - if (a->bytes_in_last_block <= 0) - /* Default or Zero: pad to full block */ - target_block_length = a->bytes_per_block; - else - /* Round to next multiple of bytes_in_last_block. */ - target_block_length = a->bytes_in_last_block * - ( (block_length + a->bytes_in_last_block - 1) / - a->bytes_in_last_block); - if (target_block_length > a->bytes_per_block) - target_block_length = a->bytes_per_block; - if (block_length < target_block_length) { - memset(state->next, 0, - target_block_length - block_length); - block_length = target_block_length; - } - bytes_written = (a->client_writer)(a, a->client_data, - state->buffer, block_length); - if (bytes_written <= 0) - ret = ARCHIVE_FATAL; - else { - a->raw_position += bytes_written; - ret = ARCHIVE_OK; - } - } - - /* Close the output */ - if (a->client_closer != NULL) - ret2 = (a->client_closer)(a, a->client_data); - - free(state->buffer); - free(state); - a->compression_data = NULL; - - return (ret != ARCHIVE_OK ? ret : ret2); -} - -//--- archive_check_magic.c - - static void -errmsg(const char *m) -{ - cmSystemTools::Error("CPack error: ", m); -} - - static void -diediedie(void) -{ - *(char *)0 = 1; /* Deliberately segfault and force a coredump. */ - _exit(1); /* If that didn't work, just exit with an error. */ -} - - static const char * -state_name(unsigned s) -{ - switch (s) { - case ARCHIVE_STATE_NEW: return ("new"); - case ARCHIVE_STATE_HEADER: return ("header"); - case ARCHIVE_STATE_DATA: return ("data"); - case ARCHIVE_STATE_EOF: return ("eof"); - case ARCHIVE_STATE_CLOSED: return ("closed"); - case ARCHIVE_STATE_FATAL: return ("fatal"); - default: return ("??"); - } -} - - - static void -write_all_states(int states) -{ - unsigned lowbit; - - /* A trick for computing the lowest set bit. */ - while ((lowbit = states & (-states)) != 0) { - states &= ~lowbit; /* Clear the low bit. */ - errmsg(state_name(lowbit)); - if (states != 0) - errmsg("/"); - } -} - -/* - * Check magic value and current state; bail if it isn't valid. - * - * This is designed to catch serious programming errors that violate - * the libarchive API. - */ - void -__archive_check_magic(struct archive *a, unsigned magic, unsigned state, - const char *function) -{ - if (a->magic != magic) { - errmsg("INTERNAL ERROR: Function "); - errmsg(function); - errmsg(" invoked with invalid struct archive structure.\n"); - diediedie(); - } - - if (state == ARCHIVE_STATE_ANY) - return; - - if ((a->state & state) == 0) { - errmsg("INTERNAL ERROR: Function '"); - errmsg(function); - errmsg("' invoked with archive structure in state '"); - write_all_states(a->state); - errmsg("', should be in state '"); - write_all_states(state); - errmsg("'\n"); - diediedie(); - } -} - -//--- archive_util.c - int -archive_errno(struct archive *a) -{ - return (a->archive_error_number); -} - - const char * -archive_error_string(struct archive *a) -{ - - if (a->error != NULL && *a->error != '\0') - return (a->error); - else - return ("(Empty error message)"); -} - - - int -archive_format(struct archive *a) -{ - return (a->archive_format); -} - - const char * -archive_format_name(struct archive *a) -{ - return (a->archive_format_name); -} - - - int -archive_compression(struct archive *a) -{ - return (a->compression_code); -} - - const char * -archive_compression_name(struct archive *a) -{ - return (a->compression_name); -} - - -/* - * Return a count of the number of compressed bytes processed. - */ - int64_t -archive_position_compressed(struct archive *a) -{ - return (a->raw_position); -} - -/* - * Return a count of the number of uncompressed bytes processed. - */ - int64_t -archive_position_uncompressed(struct archive *a) -{ - return (a->file_position); -} - - - void -archive_set_error(struct archive *a, int error_number, const char *fmt, ...) -{ - va_list ap; -#ifdef HAVE_STRERROR_R - char errbuff[512]; -#endif - char *errp; - - a->archive_error_number = error_number; - if (fmt == NULL) { - a->error = NULL; - return; - } - - va_start(ap, fmt); - archive_string_vsprintf(&(a->error_string), fmt, ap); - if (error_number > 0) { - archive_strcat(&(a->error_string), ": "); -#ifdef HAVE_STRERROR_R -#ifdef STRERROR_R_CHAR_P - errp = strerror_r(error_number, errbuff, sizeof(errbuff)); -#else - strerror_r(error_number, errbuff, sizeof(errbuff)); - errp = errbuff; -#endif -#else - /* Note: this is not threadsafe! */ - errp = strerror(error_number); -#endif - archive_strcat(&(a->error_string), errp); - } - a->error = a->error_string.s; - va_end(ap); -} - - void -__archive_errx(int retvalue, const char *msg) -{ - static const char *msg1 = "Fatal Internal Error in libarchive: "; - write(2, msg1, strlen(msg1)); - write(2, msg, strlen(msg)); - write(2, "\n", 1); - exit(retvalue); -} - -//--- archive_string.c - struct archive_string * -__archive_string_append(struct archive_string *as, const char *p, size_t s) -{ - __archive_string_ensure(as, as->length + s + 1); - memcpy(as->s + as->length, p, s); - as->s[as->length + s] = 0; - as->length += s; - return (as); -} - - void -__archive_string_free(struct archive_string *as) -{ - as->length = 0; - as->buffer_length = 0; - if (as->s != NULL) - free(as->s); -} - - struct archive_string * -__archive_string_ensure(struct archive_string *as, size_t s) -{ - if (as->s && (s <= as->buffer_length)) - return (as); - - if (as->buffer_length < 32) - as->buffer_length = 32; - while (as->buffer_length < s) - as->buffer_length *= 2; - as->s = (char*)realloc(as->s, as->buffer_length); - /* TODO: Return null instead and fix up all of our callers to - * handle this correctly. */ - if (as->s == NULL) - __archive_errx(1, "Out of memory"); - return (as); -} - - struct archive_string * -__archive_strncat(struct archive_string *as, const char *p, size_t n) -{ - size_t s; - const char *pp; - - /* Like strlen(p), except won't examine positions beyond p[n]. */ - s = 0; - pp = p; - while (*pp && s < n) { - pp++; - s++; - } - return (__archive_string_append(as, p, s)); -} - - struct archive_string * -__archive_strappend_char(struct archive_string *as, char c) -{ - return (__archive_string_append(as, &c, 1)); -} - - struct archive_string * -__archive_strappend_int(struct archive_string *as, int d, int base) -{ - static const char *digits = "0123457890abcdef"; - - if (d < 0) { - __archive_strappend_char(as, '-'); - d = -d; - } - if (d >= base) - __archive_strappend_int(as, d/base, base); - __archive_strappend_char(as, digits[d % base]); - return (as); -} - -//--- archive_entry.c -/* Obtain suitable wide-character manipulation functions. */ -#ifdef HAVE_WCHAR_H -#include <wchar.h> -#else -static size_t wcslen(const wchar_t *s) -{ - const wchar_t *p = s; - while (*p != L'\0') - ++p; - return p - s; -} -static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) -{ - wchar_t *dest = s1; - while((*s1 = *s2) != L'\0') - ++s1, ++s2; - return dest; -} -#define wmemcpy(a,b,i) (wchar_t *)memcpy((a),(b),(i)*sizeof(wchar_t)) -/* Good enough for simple equality testing, but not for sorting. */ -#define wmemcmp(a,b,i) memcmp((a),(b),(i)*sizeof(wchar_t)) -#endif - -#undef max -#define max(a, b) ((a)>(b)?(a):(b)) - - /* - * Handle wide character (i.e., Unicode) and non-wide character - * strings transparently. - * - */ - - struct aes { - const char *aes_mbs; - char *aes_mbs_alloc; - const wchar_t *aes_wcs; - wchar_t *aes_wcs_alloc; - }; - -struct ae_acl { - struct ae_acl *next; - int type; /* E.g., access or default */ - int tag; /* E.g., user/group/other/mask */ - int permset; /* r/w/x bits */ - int id; /* uid/gid for user/group */ - struct aes name; /* uname/gname */ -}; - -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 void aes_set_wcs(struct aes *, const wchar_t *wcs); */ -static void aes_copy_wcs(struct aes *, const wchar_t *wcs); - -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 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); - -static int acl_special(struct archive_entry *entry, - int type, int permset, int tag); -static struct ae_acl *acl_new_entry(struct archive_entry *entry, - int type, int permset, int tag, int id); -static void next_field_w(const wchar_t **wp, const wchar_t **start, - const wchar_t **end, wchar_t *sep); -static int prefix_w(const wchar_t *start, const wchar_t *end, - const wchar_t *test); - - -/* - * Description of an archive entry. - * - * Basically, this is a "struct stat" with a few text fields added in. - * - * TODO: Add "comment", "charset", and possibly other entries - * that are supported by "pax interchange" format. However, GNU, ustar, - * cpio, and other variants don't support these features, so they're not an - * excruciatingly high priority right now. - * - * TODO: "pax interchange" format allows essentially arbitrary - * key/value attributes to be attached to any entry. Supporting - * such extensions may make this library useful for special - * applications (e.g., a package manager could attach special - * package-management attributes to each entry). There are tricky - * API issues involved, so this is not going to happen until - * there's a real demand for it. - * - * TODO: Design a good API for handling sparse files. - */ -struct archive_entry { - /* - * Note that ae_stat.st_mode & S_IFMT can be 0! - * - * This occurs when the actual file type of the object is not - * in the archive. For example, 'tar' archives store - * hardlinks without marking the type of the underlying - * object. - */ - struct stat ae_stat; - - /* - * Use aes here so that we get transparent mbs<->wcs conversions. - */ - struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */ - unsigned long ae_fflags_set; /* Bitmap fflags */ - unsigned long ae_fflags_clear; - struct aes ae_gname; /* Name of owning group */ - struct aes ae_hardlink; /* Name of target for hardlink */ - struct aes ae_pathname; /* Name of entry */ - struct aes ae_symlink; /* symlink contents */ - struct aes ae_uname; /* Name of owner */ - - struct ae_acl *acl_head; - struct ae_acl *acl_p; - int acl_state; /* See acl_next for details. */ - wchar_t *acl_text_w; -}; - - 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_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = NULL; - } - memset(aes, 0, sizeof(*aes)); -} - - 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()"); - } - - if (src->aes_wcs != NULL) { - dest->aes_wcs_alloc = (wchar_t*)malloc((wcslen(src->aes_wcs) + 1) - * sizeof(wchar_t)); - dest->aes_wcs = dest->aes_wcs_alloc; - if (dest->aes_wcs == NULL) - __archive_errx(1, "No memory for aes_copy()"); - wcscpy(dest->aes_wcs_alloc, src->aes_wcs); - } -} - - 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 - */ - int 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; - } - return (aes->aes_mbs); -} - - static const wchar_t * -aes_get_wcs(struct aes *aes) -{ - if (aes->aes_wcs == NULL && aes->aes_mbs == NULL) - return NULL; - if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) { - /* - * No single byte will be more than one wide character, - * so this length estimate will always be big enough. - */ - int wcs_length = strlen(aes->aes_mbs); - 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) - __archive_errx(1, "No memory for aes_get_wcs()"); - mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length); - aes->aes_wcs_alloc[wcs_length] = 0; - } - return (aes->aes_wcs); -} - - static void -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; -} - - static void -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 (aes->aes_wcs_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = 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; -} - -#if 0 - static void -aes_set_wcs(struct aes *aes, const wchar_t *wcs) -{ - 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 = NULL; - aes->aes_wcs = wcs; -} -#endif - - static void -aes_copy_wcs(struct aes *aes, const wchar_t *wcs) -{ - 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 = NULL; - aes->aes_wcs_alloc = (wchar_t*)malloc((wcslen(wcs) + 1) * sizeof(wchar_t)); - if (aes->aes_wcs_alloc == NULL) - __archive_errx(1, "No memory for aes_copy_wcs()"); - wcscpy(aes->aes_wcs_alloc, wcs); - aes->aes_wcs = aes->aes_wcs_alloc; -} - - struct archive_entry * -archive_entry_clear(struct archive_entry *entry) -{ - aes_clean(&entry->ae_fflags_text); - aes_clean(&entry->ae_gname); - aes_clean(&entry->ae_hardlink); - aes_clean(&entry->ae_pathname); - aes_clean(&entry->ae_symlink); - aes_clean(&entry->ae_uname); - archive_entry_acl_clear(entry); - memset(entry, 0, sizeof(*entry)); - return entry; -} - - struct archive_entry * -archive_entry_clone(struct archive_entry *entry) -{ - struct archive_entry *entry2; - - /* Allocate new structure and copy over all of the fields. */ - entry2 = (struct archive_entry*)malloc(sizeof(*entry2)); - if (entry2 == NULL) - return (NULL); - memset(entry2, 0, sizeof(*entry2)); - entry2->ae_stat = entry->ae_stat; - entry2->ae_fflags_set = entry->ae_fflags_set; - entry2->ae_fflags_clear = entry->ae_fflags_clear; - - aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); - aes_copy(&entry2->ae_gname, &entry->ae_gname); - aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink); - aes_copy(&entry2->ae_pathname, &entry->ae_pathname); - aes_copy(&entry2->ae_symlink, &entry->ae_symlink); - aes_copy(&entry2->ae_uname, &entry->ae_uname); - - /* XXX TODO: Copy ACL data over as well. XXX */ - return (entry2); -} - - void -archive_entry_free(struct archive_entry *entry) -{ - archive_entry_clear(entry); - free(entry); -} - - struct archive_entry * -archive_entry_new(void) -{ - struct archive_entry *entry; - - entry = (struct archive_entry*)malloc(sizeof(*entry)); - if (entry == NULL) - return (NULL); - memset(entry, 0, sizeof(*entry)); - return (entry); -} - -/* - * Functions for reading fields from an archive_entry. - */ - - time_t -archive_entry_atime(struct archive_entry *entry) -{ - return (entry->ae_stat.st_atime); -} - - long -archive_entry_atime_nsec(struct archive_entry *entry) -{ - (void)entry; /* entry can be unused here. */ - return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat)); -} - - time_t -archive_entry_ctime(struct archive_entry *entry) -{ - return (entry->ae_stat.st_ctime); -} - - long -archive_entry_ctime_nsec(struct archive_entry *entry) -{ - (void)entry; /* entry can be unused here. */ - return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat)); -} - - dev_t -archive_entry_dev(struct archive_entry *entry) -{ - return (entry->ae_stat.st_dev); -} - - void -archive_entry_fflags(struct archive_entry *entry, - unsigned long *set, unsigned long *clear) -{ - *set = entry->ae_fflags_set; - *clear = entry->ae_fflags_clear; -} - -/* - * Note: if text was provided, this just returns that text. If you - * really need the text to be rebuilt in a canonical form, set the - * text, ask for the bitmaps, then set the bitmaps. (Setting the - * bitmaps clears any stored text.) This design is deliberate: if - * we're editing archives, we don't want to discard flags just because - * they aren't supported on the current system. The bitmap<->text - * conversions are platform-specific (see below). - */ - const char * -archive_entry_fflags_text(struct archive_entry *entry) -{ - const char *f; - char *p; - - f = aes_get_mbs(&entry->ae_fflags_text); - if (f != NULL) - return (f); - - if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) - return (NULL); - - p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); - if (p == NULL) - return (NULL); - - aes_copy_mbs(&entry->ae_fflags_text, p); - free(p); - f = aes_get_mbs(&entry->ae_fflags_text); - return (f); -} - - gid_t -archive_entry_gid(struct archive_entry *entry) -{ - return (entry->ae_stat.st_gid); -} - - const char * -archive_entry_gname(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_gname)); -} - - const wchar_t * -archive_entry_gname_w(struct archive_entry *entry) -{ - return (aes_get_wcs(&entry->ae_gname)); -} - - const char * -archive_entry_hardlink(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_hardlink)); -} - - const wchar_t * -archive_entry_hardlink_w(struct archive_entry *entry) -{ - return (aes_get_wcs(&entry->ae_hardlink)); -} - - ino_t -archive_entry_ino(struct archive_entry *entry) -{ - return (entry->ae_stat.st_ino); -} - - mode_t -archive_entry_mode(struct archive_entry *entry) -{ - return (entry->ae_stat.st_mode); -} - - time_t -archive_entry_mtime(struct archive_entry *entry) -{ - return (entry->ae_stat.st_mtime); -} - - long -archive_entry_mtime_nsec(struct archive_entry *entry) -{ - (void)entry; /* entry can be unused here. */ - return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat)); -} - - const char * -archive_entry_pathname(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_pathname)); -} - - const wchar_t * -archive_entry_pathname_w(struct archive_entry *entry) -{ - return (aes_get_wcs(&entry->ae_pathname)); -} - - dev_t -archive_entry_rdev(struct archive_entry *entry) -{ - return (entry->ae_stat.st_rdev); -} - - int64_t -archive_entry_size(struct archive_entry *entry) -{ - return (entry->ae_stat.st_size); -} - - const struct stat * -archive_entry_stat(struct archive_entry *entry) -{ - return (&entry->ae_stat); -} - - const char * -archive_entry_symlink(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_symlink)); -} - - const wchar_t * -archive_entry_symlink_w(struct archive_entry *entry) -{ - return (aes_get_wcs(&entry->ae_symlink)); -} - - uid_t -archive_entry_uid(struct archive_entry *entry) -{ - return (entry->ae_stat.st_uid); -} - - const char * -archive_entry_uname(struct archive_entry *entry) -{ - return (aes_get_mbs(&entry->ae_uname)); -} - - const wchar_t * -archive_entry_uname_w(struct archive_entry *entry) -{ - return (aes_get_wcs(&entry->ae_uname)); -} - -/* - * Functions to set archive_entry properties. - */ - -/* - * Note "copy" not "set" here. The "set" functions that accept a pointer - * only store the pointer; they don't copy the underlying object. - */ - void -archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) -{ - entry->ae_stat = *st; -} - - void -archive_entry_set_fflags(struct archive_entry *entry, - unsigned long set, unsigned long clear) -{ - aes_clean(&entry->ae_fflags_text); - entry->ae_fflags_set = set; - entry->ae_fflags_clear = clear; -} - - const wchar_t * -archive_entry_copy_fflags_text_w(struct archive_entry *entry, - const wchar_t *flags) -{ - aes_copy_wcs(&entry->ae_fflags_text, flags); - return (ae_wcstofflags(flags, - &entry->ae_fflags_set, &entry->ae_fflags_clear)); -} - - void -archive_entry_set_gid(struct archive_entry *entry, gid_t g) -{ - entry->ae_stat.st_gid = g; -} - - void -archive_entry_set_gname(struct archive_entry *entry, const char *name) -{ - aes_set_mbs(&entry->ae_gname, name); -} - - void -archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) -{ - aes_copy_wcs(&entry->ae_gname, name); -} - - void -archive_entry_set_hardlink(struct archive_entry *entry, const char *target) -{ - aes_set_mbs(&entry->ae_hardlink, target); -} - - void -archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) -{ - aes_copy_mbs(&entry->ae_hardlink, target); -} - - void -archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) -{ - aes_copy_wcs(&entry->ae_hardlink, target); -} - - void -archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) -{ - (void)ns; - entry->ae_stat.st_atime = t; - ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns); -} - - void -archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) -{ - (void)ns; - entry->ae_stat.st_ctime = t; - ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns); -} - -/* Set symlink if symlink is already set, else set hardlink. */ - 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) - aes_set_mbs(&entry->ae_symlink, target); - else - aes_set_mbs(&entry->ae_hardlink, target); -} - - void -archive_entry_set_mode(struct archive_entry *entry, mode_t m) -{ - entry->ae_stat.st_mode = m; -} - - void -archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns) -{ - (void)ns; - entry->ae_stat.st_mtime = m; - ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns); -} - - void -archive_entry_set_pathname(struct archive_entry *entry, const char *name) -{ - aes_set_mbs(&entry->ae_pathname, name); -} - - void -archive_entry_copy_pathname(struct archive_entry *entry, const char *name) -{ - aes_copy_mbs(&entry->ae_pathname, name); -} - - void -archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) -{ - aes_copy_wcs(&entry->ae_pathname, name); -} - - void -archive_entry_set_size(struct archive_entry *entry, int64_t s) -{ - entry->ae_stat.st_size = s; -} - - void -archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) -{ - aes_set_mbs(&entry->ae_symlink, linkname); -} - - void -archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) -{ - aes_copy_wcs(&entry->ae_symlink, linkname); -} - - void -archive_entry_set_uid(struct archive_entry *entry, uid_t u) -{ - entry->ae_stat.st_uid = u; -} - - void -archive_entry_set_uname(struct archive_entry *entry, const char *name) -{ - aes_set_mbs(&entry->ae_uname, name); -} - - void -archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) -{ - aes_copy_wcs(&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 - * complete standard that addressed the needs of ACL archiving and 2) - * everyone followed it faithfully. Alas, neither is true, so the - * following is a lot more complex than might seem necessary to the - * uninitiated. - */ - - void -archive_entry_acl_clear(struct archive_entry *entry) -{ - struct ae_acl *ap; - - while (entry->acl_head != NULL) { - ap = entry->acl_head->next; - aes_clean(&entry->acl_head->name); - free(entry->acl_head); - entry->acl_head = ap; - } - if (entry->acl_text_w != NULL) { - free(entry->acl_text_w); - entry->acl_text_w = NULL; - } - entry->acl_p = NULL; - entry->acl_state = 0; /* Not counting. */ -} - -/* - * Add a single ACL entry to the internal list of ACL data. - */ - void -archive_entry_acl_add_entry(struct archive_entry *entry, - int type, int permset, int tag, int id, const char *name) -{ - struct ae_acl *ap; - - if (acl_special(entry, type, permset, tag) == 0) - return; - ap = acl_new_entry(entry, type, permset, tag, id); - if (ap == NULL) { - /* XXX Error XXX */ - return; - } - if (name != NULL && *name != '\0') - aes_copy_mbs(&ap->name, name); - else - aes_clean(&ap->name); -} - -/* - * As above, but with a wide-character name. - */ - void -archive_entry_acl_add_entry_w(struct archive_entry *entry, - int type, int permset, int tag, int id, const wchar_t *name) -{ - struct ae_acl *ap; - - if (acl_special(entry, type, permset, tag) == 0) - return; - ap = acl_new_entry(entry, type, permset, tag, id); - if (ap == NULL) { - /* XXX Error XXX */ - return; - } - if (name != NULL && *name != L'\0') - aes_copy_wcs(&ap->name, name); - else - aes_clean(&ap->name); -} - -/* - * If this ACL entry is part of the standard POSIX permissions set, - * store the permissions in the stat structure and return zero. - */ - static int -acl_special(struct archive_entry *entry, int type, int permset, int tag) -{ - if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { - switch (tag) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - entry->ae_stat.st_mode &= ~0700; - entry->ae_stat.st_mode |= (permset & 7) << 6; - return (0); - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - entry->ae_stat.st_mode &= ~0070; - entry->ae_stat.st_mode |= (permset & 7) << 3; - return (0); - case ARCHIVE_ENTRY_ACL_OTHER: - entry->ae_stat.st_mode &= ~0007; - entry->ae_stat.st_mode |= permset & 7; - return (0); - } - } - return (1); -} - -/* - * Allocate and populate a new ACL entry with everything but the - * name. - */ - static struct ae_acl * -acl_new_entry(struct archive_entry *entry, - int type, int permset, int tag, int id) -{ - struct ae_acl *ap; - - if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && - type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) - return (NULL); - if (entry->acl_text_w != NULL) { - free(entry->acl_text_w); - entry->acl_text_w = NULL; - } - - /* XXX TODO: More sanity-checks on the arguments XXX */ - - /* If there's a matching entry already in the list, overwrite it. */ - for (ap = entry->acl_head; ap != NULL; ap = ap->next) { - if (ap->type == type && ap->tag == tag && ap->id == id) { - ap->permset = permset; - return (ap); - } - } - - /* Add a new entry to the list. */ - ap = (struct ae_acl *)malloc(sizeof(*ap)); - if (ap == NULL) - return (NULL); - memset(ap, 0, sizeof(*ap)); - ap->next = entry->acl_head; - entry->acl_head = ap; - ap->type = type; - ap->tag = tag; - ap->id = id; - ap->permset = permset; - return (ap); -} - -/* - * Return a count of entries matching "want_type". - */ - int -archive_entry_acl_count(struct archive_entry *entry, int want_type) -{ - int count; - struct ae_acl *ap; - - count = 0; - ap = entry->acl_head; - while (ap != NULL) { - if ((ap->type & want_type) != 0) - count++; - ap = ap->next; - } - - if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) - count += 3; - return (count); -} - -/* - * Prepare for reading entries from the ACL data. Returns a count - * of entries matching "want_type", or zero if there are no - * non-extended ACL entries of that type. - */ - int -archive_entry_acl_reset(struct archive_entry *entry, int want_type) -{ - int count, cutoff; - - count = archive_entry_acl_count(entry, want_type); - - /* - * If the only entries are the three standard ones, - * then don't return any ACL data. (In this case, - * client can just use chmod(2) to set permissions.) - */ - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) - cutoff = 3; - else - cutoff = 0; - - if (count > cutoff) - entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; - else - entry->acl_state = 0; - entry->acl_p = entry->acl_head; - return (count); -} - -/* - * Return the next ACL entry in the list. Fake entries for the - * standard permissions and include them in the returned list. - */ - - int -archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, - int *permset, int *tag, int *id, const char **name) -{ - *name = NULL; - *id = -1; - - /* - * The acl_state is either zero (no entries available), -1 - * (reading from list), or an entry type (retrieve that type - * from ae_stat.st_mode). - */ - if (entry->acl_state == 0) - return (ARCHIVE_WARN); - - /* The first three access entries are special. */ - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - switch (entry->acl_state) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - *permset = (entry->ae_stat.st_mode >> 6) & 7; - *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - return (ARCHIVE_OK); - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - *permset = (entry->ae_stat.st_mode >> 3) & 7; - *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER; - return (ARCHIVE_OK); - case ARCHIVE_ENTRY_ACL_OTHER: - *permset = entry->ae_stat.st_mode & 7; - *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - *tag = ARCHIVE_ENTRY_ACL_OTHER; - entry->acl_state = -1; - entry->acl_p = entry->acl_head; - return (ARCHIVE_OK); - default: - break; - } - } - - while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0) - entry->acl_p = entry->acl_p->next; - if (entry->acl_p == NULL) { - entry->acl_state = 0; - return (ARCHIVE_WARN); - } - *type = entry->acl_p->type; - *permset = entry->acl_p->permset; - *tag = entry->acl_p->tag; - *id = entry->acl_p->id; - *name = aes_get_mbs(&entry->acl_p->name); - entry->acl_p = entry->acl_p->next; - return (ARCHIVE_OK); -} - -/* - * Generate a text version of the ACL. The flags parameter controls - * the style of the generated ACL. - */ - const wchar_t * -archive_entry_acl_text_w(struct archive_entry *entry, int flags) -{ - int count; - int length; - const wchar_t *wname; - const wchar_t *prefix; - wchar_t separator; - struct ae_acl *ap; - int id; - wchar_t *wp; - - if (entry->acl_text_w != NULL) { - free (entry->acl_text_w); - entry->acl_text_w = NULL; - } - - separator = L','; - count = 0; - length = 0; - ap = entry->acl_head; - while (ap != NULL) { - if ((ap->type & flags) != 0) { - count++; - if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && - (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) - length += 8; /* "default:" */ - length += 5; /* tag name */ - length += 1; /* colon */ - wname = aes_get_wcs(&ap->name); - if (wname != NULL) - length += wcslen(wname); - length ++; /* colon */ - length += 3; /* rwx */ - length += 1; /* colon */ - length += max(sizeof(uid_t),sizeof(gid_t)) * 3 + 1; - length ++; /* newline */ - } - ap = ap->next; - } - - if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { - length += 10; /* "user::rwx\n" */ - length += 11; /* "group::rwx\n" */ - length += 11; /* "other::rwx\n" */ - } - - if (count == 0) - return (NULL); - - /* Now, allocate the string and actually populate it. */ - wp = entry->acl_text_w = (wchar_t*)malloc(length * sizeof(wchar_t)); - if (wp == NULL) - __archive_errx(1, "No memory to generate the text version of the ACL"); - count = 0; - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, - entry->ae_stat.st_mode & 0700, -1); - *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, - entry->ae_stat.st_mode & 0070, -1); - *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, - entry->ae_stat.st_mode & 0007, -1); - count += 3; - - ap = entry->acl_head; - while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - wname = aes_get_wcs(&ap->name); - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry_w(&wp, NULL, ap->tag, wname, - ap->permset, id); - count++; - } - ap = ap->next; - } - } - - - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) - prefix = L"default:"; - else - prefix = NULL; - ap = entry->acl_head; - count = 0; - while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - wname = aes_get_wcs(&ap->name); - if (count > 0) - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry_w(&wp, prefix, ap->tag, - wname, ap->permset, id); - count ++; - } - ap = ap->next; - } - } - - return (entry->acl_text_w); -} - - static void -append_id_w(wchar_t **wp, int id) -{ - if (id > 9) - append_id_w(wp, id / 10); - *(*wp)++ = L"0123456789"[id % 10]; -} - - static void -append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id) -{ - if (prefix != NULL) { - wcscpy(*wp, prefix); - *wp += wcslen(*wp); - } - switch (tag) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - wname = NULL; - id = -1; - /* FALL THROUGH */ - case ARCHIVE_ENTRY_ACL_USER: - wcscpy(*wp, L"user"); - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - wname = NULL; - id = -1; - /* FALL THROUGH */ - case ARCHIVE_ENTRY_ACL_GROUP: - wcscpy(*wp, L"group"); - break; - case ARCHIVE_ENTRY_ACL_MASK: - wcscpy(*wp, L"mask"); - wname = NULL; - id = -1; - break; - case ARCHIVE_ENTRY_ACL_OTHER: - wcscpy(*wp, L"other"); - wname = NULL; - id = -1; - break; - } - *wp += wcslen(*wp); - *(*wp)++ = L':'; - if (wname != NULL) { - wcscpy(*wp, wname); - *wp += wcslen(*wp); - } - *(*wp)++ = L':'; - *(*wp)++ = (perm & 0444) ? L'r' : L'-'; - *(*wp)++ = (perm & 0222) ? L'w' : L'-'; - *(*wp)++ = (perm & 0111) ? L'x' : L'-'; - if (id != -1) { - *(*wp)++ = L':'; - append_id_w(wp, id); - } - **wp = L'\0'; -} - -/* - * Parse a textual ACL. This automatically recognizes and supports - * extensions described above. The 'type' argument is used to - * indicate the type that should be used for any entries not - * explicitly marked as "default:". - */ - int -__archive_entry_acl_parse_w(struct archive_entry *entry, - const wchar_t *text, int default_type) -{ - int type, tag, permset, id; - const wchar_t *start, *end; - const wchar_t *name_start, *name_end; - wchar_t sep; - wchar_t *namebuff; - int namebuff_length; - - name_start = name_end = NULL; - namebuff = NULL; - namebuff_length = 0; - - while (text != NULL && *text != L'\0') { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. - */ - if (end-start > 7 && wmemcmp(start, L"default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - start += 7; - } else - type = default_type; - - if (prefix_w(start, end, L"user")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) { - tag = ARCHIVE_ENTRY_ACL_USER; - name_start = start; - name_end = end; - } else - tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (prefix_w(start, end, L"group")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) { - tag = ARCHIVE_ENTRY_ACL_GROUP; - name_start = start; - name_end = end; - } else - tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (prefix_w(start, end, L"other")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) - goto fail; - tag = ARCHIVE_ENTRY_ACL_OTHER; - } else if (prefix_w(start, end, L"mask")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) - goto fail; - tag = ARCHIVE_ENTRY_ACL_MASK; - } else - goto fail; - - next_field_w(&text, &start, &end, &sep); - permset = 0; - while (start < end) { - switch (*start++) { - case 'r': case 'R': - permset |= ARCHIVE_ENTRY_ACL_READ; - break; - case 'w': case 'W': - permset |= ARCHIVE_ENTRY_ACL_WRITE; - break; - case 'x': case 'X': - permset |= ARCHIVE_ENTRY_ACL_EXECUTE; - break; - case '-': - break; - default: - goto fail; - } - } - - /* - * Support star-compatible numeric UID/GID extension. - * This extension adds a ":" followed by the numeric - * ID so that "group:groupname:rwx", for example, - * becomes "group:groupname:rwx:999", where 999 is the - * numeric GID. This extension makes it possible, for - * example, to correctly restore ACLs on a system that - * might have a damaged passwd file or be disconnected - * from a central NIS server. This extension is compatible - * with POSIX.1e draft 17. - */ - if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER || - tag == ARCHIVE_ENTRY_ACL_GROUP)) { - next_field_w(&text, &start, &end, &sep); - - id = 0; - while (start < end && *start >= '0' && *start <= '9') { - if (id > (INT_MAX / 10)) - id = INT_MAX; - else { - id *= 10; - id += *start - '0'; - start++; - } - } - } else - id = -1; /* No id specified. */ - - /* Skip any additional entries. */ - while (sep == L':') { - next_field_w(&text, &start, &end, &sep); - } - - /* Add entry to the internal list. */ - if (name_end == name_start) { - archive_entry_acl_add_entry_w(entry, type, permset, - tag, id, NULL); - } else { - if (namebuff_length <= name_end - name_start) { - if (namebuff != NULL) - free(namebuff); - namebuff_length = name_end - name_start + 256; - namebuff = - (wchar_t*)malloc(namebuff_length * sizeof(wchar_t)); - if (namebuff == NULL) - goto fail; - } - wmemcpy(namebuff, name_start, name_end - name_start); - namebuff[name_end - name_start] = L'\0'; - archive_entry_acl_add_entry_w(entry, type, - permset, tag, id, namebuff); - } - } - if (namebuff != NULL) - free(namebuff); - return (ARCHIVE_OK); - -fail: - if (namebuff != NULL) - free(namebuff); - return (ARCHIVE_WARN); -} - -/* - * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated - * to point to just after the separator. *start points to the first - * character of the matched text and *end just after the last - * character of the matched identifier. In particular *end - *start - * is the length of the field body, not including leading or trailing - * whitespace. - */ - static void -next_field_w(const wchar_t **wp, const wchar_t **start, - const wchar_t **end, wchar_t *sep) -{ - /* Skip leading whitespace to find start of field. */ - while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { - (*wp)++; - } - *start = *wp; - - /* Scan for the separator. */ - while (**wp != L'\0' && **wp != L',' && **wp != L':' && - **wp != L'\n') { - (*wp)++; - } - *sep = **wp; - - /* Trim trailing whitespace to locate end of field. */ - *end = *wp - 1; - while (**end == L' ' || **end == L'\t' || **end == L'\n') { - (*end)--; - } - (*end)++; - - /* Adjust scanner location. */ - if (**wp != L'\0') - (*wp)++; -} - - static int -prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) -{ - if (start == end) - return (0); - - if (*start++ != *test++) - return (0); - - while (start < end && *start++ == *test++) - ; - - if (start < end) - return (0); - - return (1); -} - - -/* - * Following code is modified from UC Berkeley sources, and - * is subject to the following copyright notice. - */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -static struct flag { - const char *name; - const wchar_t *wname; - unsigned long set; - unsigned long clear; -} flags[] = { - /* Preferred (shorter) names per flag first, all prefixed by "no" */ -#ifdef SF_APPEND - { "nosappnd", L"nosappnd", SF_APPEND, 0 }, - { "nosappend", L"nosappend", SF_APPEND, 0 }, -#endif -#ifdef EXT2_APPEND_FL /* 'a' */ - { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, - { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, -#endif -#ifdef SF_ARCHIVED - { "noarch", L"noarch", SF_ARCHIVED, 0 }, - { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, -#endif -#ifdef SF_IMMUTABLE - { "noschg", L"noschg", SF_IMMUTABLE, 0 }, - { "noschange", L"noschange", SF_IMMUTABLE, 0 }, - { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, -#endif -#ifdef EXT2_IMMUTABLE_FL /* 'i' */ - { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, - { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, - { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, -#endif -#ifdef SF_NOUNLINK - { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, - { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, -#endif -#ifdef SF_SNAPSHOT - { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, -#endif -#ifdef UF_APPEND - { "nouappnd", L"nouappnd", UF_APPEND, 0 }, - { "nouappend", L"nouappend", UF_APPEND, 0 }, -#endif -#ifdef UF_IMMUTABLE - { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, - { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, - { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, -#endif -#ifdef UF_NODUMP - { "nodump", L"nodump", 0, UF_NODUMP}, -#endif -#ifdef EXT2_NODUMP_FL /* 'd' */ - { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, -#endif -#ifdef UF_OPAQUE - { "noopaque", L"noopaque", UF_OPAQUE, 0 }, -#endif -#ifdef UF_NOUNLINK - { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, - { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, -#endif -#ifdef EXT2_COMPR_FL /* 'c' */ - { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, -#endif - -#ifdef EXT2_NOATIME_FL /* 'A' */ - { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, -#endif - { NULL, NULL, 0, 0 } -}; - -/* - * fflagstostr -- - * Convert file flags to a comma-separated string. If no flags - * are set, return the empty string. - */ - char * -ae_fflagstostr(unsigned long bitset, unsigned long bitclear) -{ - char *string, *dp; - const char *sp; - unsigned long bits; - struct flag *flag; - int length; - - bits = bitset | bitclear; - length = 0; - for (flag = flags; flag->name != NULL; flag++) - if (bits & (flag->set | flag->clear)) { - length += strlen(flag->name) + 1; - bits &= ~(flag->set | flag->clear); - } - - if (length == 0) - return (NULL); - string = (char*)malloc(length); - if (string == NULL) - return (NULL); - - dp = string; - for (flag = flags; flag->name != NULL; flag++) { - if (bitset & flag->set || bitclear & flag->clear) { - sp = flag->name + 2; - } else if (bitset & flag->clear || bitclear & flag->set) { - sp = flag->name; - } else - continue; - bitset &= ~(flag->set | flag->clear); - bitclear &= ~(flag->set | flag->clear); - if (dp > string) - *dp++ = ','; - while ((*dp++ = *sp++) != '\0') - ; - dp--; - } - - *dp = '\0'; - return (string); -} - -/* - * wcstofflags -- - * 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. - */ - const wchar_t * -ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) -{ - const wchar_t *start, *end; - struct flag *flag; - unsigned long set, clear; - const wchar_t *failed; - - set = clear = 0; - start = s; - failed = NULL; - /* Find start of first token. */ - while (*start == L'\t' || *start == L' ' || *start == L',') - start++; - while (*start != L'\0') { - /* Locate end of token. */ - end = start; - while (*end != L'\0' && *end != L'\t' && - *end != L' ' && *end != L',') - end++; - for (flag = flags; flag->wname != NULL; flag++) { - if (wmemcmp(start, flag->wname, end - start) == 0) { - /* Matched "noXXXX", so reverse the sense. */ - clear |= flag->set; - set |= flag->clear; - break; - } else if (wmemcmp(start, flag->wname + 2, end - start) - == 0) { - /* Matched "XXXX", so don't reverse. */ - set |= flag->set; - clear |= flag->clear; - break; - } - } - /* Ignore unknown flag names. */ - if (flag->wname == NULL && failed == NULL) - failed = start; - - /* Find start of next token. */ - start = end; - while (*start == L'\t' || *start == L' ' || *start == L',') - start++; - - } - - if (setp) - *setp = set; - if (clrp) - *clrp = clear; - - /* Return location of first failure. */ - return (failed); -} - -//--- archive_string_sprintf.c -/* - * Like 'vsprintf', but ensures the target is big enough, resizing if - * necessary. - */ - void -__archive_string_vsprintf(struct archive_string *as, const char *fmt, - va_list ap) -{ - char long_flag; - intmax_t s; /* Signed integer temp. */ - uintmax_t u; /* Unsigned integer temp. */ - const char *p, *p2; - - __archive_string_ensure(as, 64); - - if (fmt == NULL) { - as->s[0] = 0; - return; - } - - long_flag = '\0'; - for (p = fmt; *p != '\0'; p++) { - const char *saved_p = p; - - if (*p != '%') { - archive_strappend_char(as, *p); - continue; - } - - p++; - - switch(*p) { - case 'j': - long_flag = 'j'; - p++; - break; - case 'l': - long_flag = 'l'; - p++; - break; - } - - switch (*p) { - case '%': - __archive_strappend_char(as, '%'); - break; - case 'c': - s = va_arg(ap, int); - __archive_strappend_char(as, s); - break; - case 'd': - switch(long_flag) { - case 'j': s = va_arg(ap, intmax_t); break; - case 'l': s = va_arg(ap, long); break; - default: s = va_arg(ap, int); break; - } - archive_strappend_int(as, s, 10); - break; - case 's': - p2 = va_arg(ap, char *); - archive_strcat(as, p2); - break; - case 'o': case 'u': case 'x': case 'X': - /* Common handling for unsigned integer formats. */ - switch(long_flag) { - case 'j': u = va_arg(ap, uintmax_t); break; - case 'l': u = va_arg(ap, unsigned long); break; - default: u = va_arg(ap, unsigned int); break; - } - /* Format it in the correct base. */ - switch (*p) { - case 'o': archive_strappend_int(as, u, 8); break; - case 'u': archive_strappend_int(as, u, 10); break; - default: archive_strappend_int(as, u, 16); break; - } - break; - default: - /* Rewind and print the initial '%' literally. */ - p = saved_p; - archive_strappend_char(as, *p); - } - } -} - -//---------------------------------------------------------------------- class cmCPackTGZ_Data { public: @@ -3160,29 +65,32 @@ public: }; //---------------------------------------------------------------------- -int cmCPackTGZGenerator::TGZ_Open(struct archive *a, void *client_data) +int cmCPackTGZGenerator::TGZ_Open(void *client_data, const char* pathname, int, mode_t) { cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data; - (void)a; - mydata->OutputStream = new cmGeneratedFileStream(mydata->Name); - if ( *mydata->OutputStream && - mydata->Generator->GenerateHeader(mydata->OutputStream)) + cmGeneratedFileStream* gf = new cmGeneratedFileStream(pathname); + mydata->OutputStream = gf; + if ( !*mydata->OutputStream ) { - return (ARCHIVE_OK); + return -1; } - else + + gf->SetCompression(true); + gf->SetCompressionExtraExtension(false); + + if ( !mydata->Generator->GenerateHeader(mydata->OutputStream)) { - return (ARCHIVE_FATAL); + return -1; } + return 0; } //---------------------------------------------------------------------- -ssize_t cmCPackTGZGenerator::TGZ_Write(struct archive *a, void *client_data, void *buff, size_t n) +ssize_t cmCPackTGZGenerator::TGZ_Write(void *client_data, void *buff, size_t n) { cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data; - (void)a; mydata->OutputStream->write(reinterpret_cast<const char*>(buff), n); if ( !*mydata->OutputStream ) { @@ -3192,12 +100,12 @@ ssize_t cmCPackTGZGenerator::TGZ_Write(struct archive *a, void *client_data, voi } //---------------------------------------------------------------------- -int cmCPackTGZGenerator::TGZ_Close(struct archive *a, void *client_data) +int cmCPackTGZGenerator::TGZ_Close(void *client_data) { cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data; - (void)a; delete mydata->OutputStream; + mydata->OutputStream = 0; return (0); } @@ -3207,861 +115,61 @@ int cmCPackTGZGenerator::CompressFiles(const char* outFileName, const char* topl { std::cout << "Toplevel: " << toplevel << std::endl; cmCPackTGZ_Data mydata(this); - struct archive *a; - struct archive_entry *entry; - struct stat st; - char buff[8192]; - int len; - int fd; + TAR *t; + char buf[TAR_MAXPATHLEN]; + char pathname[TAR_MAXPATHLEN]; + + tartype_t gztype = { + (openfunc_t)cmCPackTGZGenerator::TGZ_Open, + (closefunc_t)cmCPackTGZGenerator::TGZ_Close, + (readfunc_t)0, + (writefunc_t)cmCPackTGZGenerator::TGZ_Write, + &mydata + }; + + // Ok, this libtar is not const safe. for now use auto_ptr hack + char* realName = new char[ strlen(outFileName) + 1 ]; + std::auto_ptr<char> realNamePtr(realName); + strcpy(realName, outFileName); + int flags = O_WRONLY | O_CREAT; + if (tar_open(&t, realName, + &gztype, + flags, 0644, + (m_GeneratorVerbose?TAR_VERBOSE:0) + | 0) == -1) + { + cmSystemTools::Error("Problem with tar_open(): ", strerror(errno)); + return 0; + } - a = archive_write_new(); - mydata.Name = outFileName; - archive_write_set_compression_gzip(a); - archive_write_set_format_ustar(a); - archive_write_open(a, &mydata, cmCPackTGZGenerator::TGZ_Open, - cmCPackTGZGenerator::TGZ_Write, cmCPackTGZGenerator::TGZ_Close); std::vector<std::string>::const_iterator fileIt; for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt ) { - std::string fname = cmSystemTools::RelativePath(toplevel, fileIt->c_str()); - const char* filename = fileIt->c_str(); - stat(filename, &st); - entry = archive_entry_new(); - archive_entry_copy_stat(entry, &st); - archive_entry_set_pathname(entry, fname.c_str()); - archive_write_header(a, entry); - fd = open(filename, O_RDONLY); - len = read(fd, buff, sizeof(buff)); - while ( len > 0 ) + strncpy(pathname, fileIt->c_str(), sizeof(pathname)); + pathname[sizeof(pathname)-1] = 0; + strncpy(buf, pathname, sizeof(buf)); + buf[sizeof(buf)-1] = 0; + if (tar_append_tree(t, buf, pathname) != 0) { - archive_write_data(a, buff, len); - len = read(fd, buff, sizeof(buff)); + cmOStringStream ostr; + ostr << "Problem with tar_append_tree(\"" << buf << "\", \"" << pathname << "\"): " + << strerror(errno); + cmSystemTools::Error(ostr.str().c_str()); + tar_close(t); + return 0; } - archive_entry_free(entry); - } - archive_write_finish(a); - return 1; -} - -//--- archive_write_set_format_ustar.c -struct ustar { - uint64_t entry_bytes_remaining; - uint64_t entry_padding; - char written; -}; - -/* - * Define structure of POSIX 'ustar' tar header. - */ -struct archive_entry_header_ustar { - char name[100]; - char mode[6]; - char mode_padding[2]; - char uid[6]; - char uid_padding[2]; - char gid[6]; - char gid_padding[2]; - char size[11]; - char size_padding[1]; - char mtime[11]; - char mtime_padding[1]; - char checksum[8]; - char typeflag[1]; - char linkname[100]; - char magic[6]; /* For POSIX: "ustar\0" */ - char version[2]; /* For POSIX: "00" */ - char uname[32]; - char gname[32]; - char rdevmajor[6]; - char rdevmajor_padding[2]; - char rdevminor[6]; - char rdevminor_padding[2]; - char prefix[155]; - char padding[12]; -}; - -/* - * A filled-in copy of the header for initialization. - */ -static const struct archive_entry_header_ustar template_header = { - { "" }, /* name */ - { '0','0','0','0','0','0' }, { ' ', '\0' }, /* mode, space-null termination. */ - { '0','0','0','0','0','0' }, { ' ', '\0' }, /* uid, space-null termination. */ - { '0','0','0','0','0','0' }, { ' ', '\0' }, /* gid, space-null termination. */ - { '0','0','0','0','0','0','0','0','0','0','0' }, { ' ' }, /* size, space termination. */ - { '0','0','0','0','0','0','0','0','0','0','0' }, { ' ' }, /* mtime, space termination. */ - { ' ',' ',' ',' ',' ',' ',' ',' ' }, /* Initial checksum value. */ - { '0' }, /* default: regular file */ - { "" }, /* linkname */ - { 'u','s','t','a','r' }, /* magic */ - { '0', '0' }, /* version */ - { "" }, /* uname */ - { "" }, /* gname */ - {'0','0','0','0','0','0'}, { ' ', '\0' }, /* rdevmajor, space-null termination */ - {'0','0','0','0','0','0'}, { ' ', '\0' }, /* rdevminor, space-null termination */ - { "" }, /* prefix */ - { "" } /* padding */ -}; - -static int archive_write_ustar_data(struct archive *a, const void *buff, - size_t s); -static int archive_write_ustar_finish(struct archive *); -static int archive_write_ustar_finish_entry(struct archive *); -static int archive_write_ustar_header(struct archive *, - struct archive_entry *entry); -static int format_256(int64_t, char *, int); -static int format_number(int64_t, char *, int size, int max, int strict); -static int format_octal(int64_t, char *, int); -static int write_nulls(struct archive *a, size_t); - -/* - * Set output format to 'ustar' format. - */ - int -archive_write_set_format_ustar(struct archive *a) -{ - struct ustar *ustar; - - /* If someone else was already registered, unregister them. */ - if (a->format_finish != NULL) - (a->format_finish)(a); - - ustar = (struct ustar*)malloc(sizeof(*ustar)); - if (ustar == NULL) { - archive_set_error(a, ENOMEM, "Can't allocate ustar data"); - return (ARCHIVE_FATAL); - } - memset(ustar, 0, sizeof(*ustar)); - a->format_data = ustar; - - a->pad_uncompressed = 1; /* Mimic gtar in this respect. */ - a->format_write_header = archive_write_ustar_header; - a->format_write_data = archive_write_ustar_data; - a->format_finish = archive_write_ustar_finish; - a->format_finish_entry = archive_write_ustar_finish_entry; - a->archive_format = ARCHIVE_FORMAT_TAR_USTAR; - a->archive_format_name = "POSIX ustar"; - return (ARCHIVE_OK); -} - - static int -archive_write_ustar_header(struct archive *a, struct archive_entry *entry) -{ - char buff[512]; - int ret; - struct ustar *ustar; - - ustar = (struct ustar*)a->format_data; - ustar->written = 1; - - /* Only regular files (not hardlinks) have data. */ - if (archive_entry_hardlink(entry) != NULL || - archive_entry_symlink(entry) != NULL || - !S_ISREG(archive_entry_mode(entry))) - archive_entry_set_size(entry, 0); - - ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1); - if (ret != ARCHIVE_OK) - return (ret); - ret = (a->compression_write)(a, buff, 512); - if (ret != ARCHIVE_OK) - return (ret); - - ustar->entry_bytes_remaining = archive_entry_size(entry); - ustar->entry_padding = 0x1ff & (- ustar->entry_bytes_remaining); - return (ARCHIVE_OK); -} - -/* - * Format a basic 512-byte "ustar" header. - * - * Returns -1 if format failed (due to field overflow). - * Note that this always formats as much of the header as possible. - * If "strict" is set to zero, it will extend numeric fields as - * necessary (overwriting terminators or using base-256 extensions). - * - * This is exported so that other 'tar' formats can use it. - */ - int -__archive_write_format_header_ustar(struct archive *a, char buff[512], - struct archive_entry *entry, int tartype, int strict) -{ - unsigned int checksum; - struct archive_entry_header_ustar *h; - int i, ret; - size_t copy_length; - const char *p, *pp; - const struct stat *st; - int mytartype; - - ret = 0; - mytartype = -1; - /* - * The "template header" already includes the "ustar" - * signature, various end-of-field markers and other required - * elements. - */ - memcpy(buff, &template_header, 512); - - h = (struct archive_entry_header_ustar *)buff; - - /* - * Because the block is already null-filled, and strings - * are allowed to exactly fill their destination (without null), - * I use memcpy(dest, src, strlen()) here a lot to copy strings. - */ - - pp = archive_entry_pathname(entry); - if (strlen(pp) <= sizeof(h->name)) - memcpy(h->name, pp, strlen(pp)); - else { - /* Store in two pieces, splitting at a '/'. */ - p = strchr(pp + strlen(pp) - sizeof(h->name) - 1, '/'); - /* - * If there is no path separator, or the prefix or - * remaining name are too large, return an error. - */ - if (!p) { - archive_set_error(a, ENAMETOOLONG, - "Pathname too long"); - ret = ARCHIVE_WARN; - } else if (p > pp + sizeof(h->prefix)) { - archive_set_error(a, ENAMETOOLONG, - "Pathname too long"); - ret = ARCHIVE_WARN; - } else { - /* Copy prefix and remainder to appropriate places */ - memcpy(h->prefix, pp, p - pp); - memcpy(h->name, p + 1, pp + strlen(pp) - p - 1); - } - } - - p = archive_entry_hardlink(entry); - if (p != NULL) - mytartype = '1'; - else - p = archive_entry_symlink(entry); - if (p != NULL && p[0] != '\0') { - copy_length = strlen(p); - if (copy_length > sizeof(h->linkname)) { - archive_set_error(a, ENAMETOOLONG, - "Link contents too long"); - ret = ARCHIVE_WARN; - copy_length = sizeof(h->linkname); - } - memcpy(h->linkname, p, copy_length); - } - - p = archive_entry_uname(entry); - if (p != NULL && p[0] != '\0') { - copy_length = strlen(p); - if (copy_length > sizeof(h->uname)) { - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Username too long"); - ret = ARCHIVE_WARN; - copy_length = sizeof(h->uname); - } - memcpy(h->uname, p, copy_length); - } - - p = archive_entry_gname(entry); - if (p != NULL && p[0] != '\0') { - copy_length = strlen(p); - if (strlen(p) > sizeof(h->gname)) { - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Group name too long"); - ret = ARCHIVE_WARN; - copy_length = sizeof(h->gname); - } - memcpy(h->gname, p, copy_length); - } - - st = archive_entry_stat(entry); - - if (format_number(st->st_mode & 07777, h->mode, sizeof(h->mode), 8, strict)) { - archive_set_error(a, ERANGE, "Numeric mode too large"); - ret = ARCHIVE_WARN; - } - - if (format_number(st->st_uid, h->uid, sizeof(h->uid), 8, strict)) { - archive_set_error(a, ERANGE, "Numeric user ID too large"); - ret = ARCHIVE_WARN; - } - - if (format_number(st->st_gid, h->gid, sizeof(h->gid), 8, strict)) { - archive_set_error(a, ERANGE, "Numeric group ID too large"); - ret = ARCHIVE_WARN; - } - - if (format_number(st->st_size, h->size, sizeof(h->size), 12, strict)) { - archive_set_error(a, ERANGE, "File size out of range"); - ret = ARCHIVE_WARN; - } - - if (format_number(st->st_mtime, h->mtime, sizeof(h->mtime), 12, strict)) { - archive_set_error(a, ERANGE, - "File modification time too large"); - ret = ARCHIVE_WARN; - } - -#if defined(S_ISBLK) - if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { - if (format_number(major(st->st_rdev), h->rdevmajor, - sizeof(h->rdevmajor), 8, strict)) { - archive_set_error(a, ERANGE, - "Major device number too large"); - ret = ARCHIVE_WARN; - } - - if (format_number(minor(st->st_rdev), h->rdevminor, - sizeof(h->rdevminor), 8, strict)) { - archive_set_error(a, ERANGE, - "Minor device number too large"); - ret = ARCHIVE_WARN; - } - } -#endif - - if (tartype >= 0) { - h->typeflag[0] = tartype; - } else if (mytartype >= 0) { - h->typeflag[0] = mytartype; - } else { - switch (st->st_mode & S_IFMT) { - case S_IFREG: h->typeflag[0] = '0' ; break; -#if defined(S_IFLNK) - case S_IFLNK: h->typeflag[0] = '2' ; break; -#endif - case S_IFCHR: h->typeflag[0] = '3' ; break; -#if defined(S_IFBLK) - case S_IFBLK: h->typeflag[0] = '4' ; break; -#endif - case S_IFDIR: h->typeflag[0] = '5' ; break; -#if defined(S_IFIFO) - case S_IFIFO: h->typeflag[0] = '6' ; break; -#endif -#if defined(S_IFSOCK) - case S_IFSOCK: - archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive socket"); - ret = ARCHIVE_WARN; - break; -#endif - default: - archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive this (mode=0%lo)", - (unsigned long)st->st_mode); - ret = ARCHIVE_WARN; - } - } - - checksum = 0; - for (i = 0; i < 512; i++) - checksum += 255 & (unsigned int)buff[i]; - h->checksum[6] = '\0'; /* Can't be pre-set in the template. */ - /* h->checksum[7] = ' '; */ /* This is pre-set in the template. */ - format_octal(checksum, h->checksum, 6); - return (ret); -} - -/* - * Format a number into a field, with some intelligence. - */ - static int -format_number(int64_t v, char *p, int s, int maxsize, int strict) -{ - int64_t limit; - - limit = ((int64_t)1 << (s*3)); - - /* "Strict" only permits octal values with proper termination. */ - if (strict) - return (format_octal(v, p, s)); - - /* - * In non-strict mode, we allow the number to overwrite one or - * more bytes of the field termination. Even old tar - * implementations should be able to handle this with no - * problem. - */ - if (v >= 0) { - while (s <= maxsize) { - if (v < limit) - return (format_octal(v, p, s)); - s++; - limit <<= 3; } - } - - /* Base-256 can handle any number, positive or negative. */ - return (format_256(v, p, maxsize)); -} - -/* - * Format a number into the specified field using base-256. - */ - static int -format_256(int64_t v, char *p, int s) -{ - p += s; - while (s-- > 0) { - *--p = (char)(v & 0xff); - v >>= 8; - } - *p |= 0x80; /* Set the base-256 marker bit. */ - return (0); -} - -/* - * Format a number into the specified field. - */ - static int -format_octal(int64_t v, char *p, int s) -{ - int len; - - len = s; - - /* Octal values can't be negative, so use 0. */ - if (v < 0) { - while (len-- > 0) - *p++ = '0'; - return (-1); - } - - p += s; /* Start at the end and work backwards. */ - while (s-- > 0) { - *--p = '0' + (v & 7); - v >>= 3; - } - - if (v == 0) - return (0); - - /* If it overflowed, fill field with max value. */ - while (len-- > 0) - *p++ = '7'; - - return (-1); -} - - static int -archive_write_ustar_finish(struct archive *a) -{ - struct ustar *ustar; - int r; - - r = ARCHIVE_OK; - ustar = (struct ustar*)a->format_data; - /* - * Suppress end-of-archive if nothing else was ever written. - * This fixes a problem where setting one format, then another - * ends up writing a gratuitous end-of-archive marker. - */ - if (ustar->written && a->compression_write != NULL) - r = write_nulls(a, 512*2); - free(ustar); - a->format_data = NULL; - return (r); -} - - static int -archive_write_ustar_finish_entry(struct archive *a) -{ - struct ustar *ustar; - int ret; - - ustar = (struct ustar*) a->format_data; - ret = write_nulls(a, - ustar->entry_bytes_remaining + ustar->entry_padding); - ustar->entry_bytes_remaining = ustar->entry_padding = 0; - return (ret); -} - - static int -write_nulls(struct archive *a, size_t padding) -{ - int ret, to_write; - - while (padding > 0) { - to_write = padding < a->null_length ? padding : a->null_length; - ret = (a->compression_write)(a, a->nulls, to_write); - if (ret != ARCHIVE_OK) - return (ret); - padding -= to_write; - } - return (ARCHIVE_OK); -} - - static int -archive_write_ustar_data(struct archive *a, const void *buff, size_t s) -{ - struct ustar *ustar; - int ret; - - ustar = (struct ustar*)a->format_data; - if (s > ustar->entry_bytes_remaining) - s = ustar->entry_bytes_remaining; - ret = (a->compression_write)(a, buff, s); - ustar->entry_bytes_remaining -= s; - return (ret); -} - -//--- archive_write_set_compression_gzip.c - -struct private_data { - z_stream stream; - int64_t total_in; - unsigned char *compressed; - size_t compressed_buffer_size; - unsigned long crc; -}; - - -/* - * Yuck. zlib.h is not const-correct, so I need this one bit - * of ugly hackery to convert a const * pointer to a non-const pointer. - */ -#define SET_NEXT_IN(st,src) \ - (st)->stream.next_in = (Bytef*)(void *)(uintptr_t)(const void *)(src) - -static int archive_compressor_gzip_finish(struct archive *); -static int archive_compressor_gzip_init(struct archive *); -static int archive_compressor_gzip_write(struct archive *, const void *, - size_t); -static int drive_compressor(struct archive *, struct private_data *, - int finishing); - - -/* - * Allocate, initialize and return a archive object. - */ - int -archive_write_set_compression_gzip(struct archive *a) -{ - __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip"); - a->compression_init = &archive_compressor_gzip_init; - a->compression_code = ARCHIVE_COMPRESSION_GZIP; - a->compression_name = "gzip"; - return (ARCHIVE_OK); -} - -/* - * Setup callback. - */ - static int -archive_compressor_gzip_init(struct archive *a) -{ - int ret; - struct private_data *state; - time_t t; - - a->compression_code = ARCHIVE_COMPRESSION_GZIP; - a->compression_name = "gzip"; - - if (a->client_opener != NULL) { - ret = (a->client_opener)(a, a->client_data); - if (ret != ARCHIVE_OK) - return (ret); - } - - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { - archive_set_error(a, ENOMEM, - "Can't allocate data for compression"); - return (ARCHIVE_FATAL); - } - memset(state, 0, sizeof(*state)); - - state->compressed_buffer_size = a->bytes_per_block; - state->compressed = (unsigned char*)malloc(state->compressed_buffer_size); - state->crc = crc32(0L, NULL, 0); - - if (state->compressed == NULL) { - archive_set_error(a, ENOMEM, - "Can't allocate data for compression buffer"); - free(state); - return (ARCHIVE_FATAL); - } - - state->stream.next_out = state->compressed; - state->stream.avail_out = state->compressed_buffer_size; - - /* Prime output buffer with a gzip header. */ - t = time(NULL); - state->compressed[0] = 0x1f; /* GZip signature bytes */ - state->compressed[1] = 0x8b; - state->compressed[2] = 0x08; /* "Deflate" compression */ - state->compressed[3] = 0; /* No options */ - state->compressed[4] = (t)&0xff; /* Timestamp */ - state->compressed[5] = (t>>8)&0xff; - state->compressed[6] = (t>>16)&0xff; - state->compressed[7] = (t>>24)&0xff; - state->compressed[8] = 0; /* No deflate options */ - state->compressed[9] = 3; /* OS=Unix */ - state->stream.next_out += 10; - state->stream.avail_out -= 10; - - a->compression_write = archive_compressor_gzip_write; - a->compression_finish = archive_compressor_gzip_finish; - - /* Initialize compression library. */ - ret = deflateInit2(&(state->stream), - Z_DEFAULT_COMPRESSION, - Z_DEFLATED, - -15 /* < 0 to suppress zlib header */, - 8, - Z_DEFAULT_STRATEGY); - - if (ret == Z_OK) { - a->compression_data = state; - return (0); - } - - /* Library setup failed: clean up. */ - archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error " - "initializing compression library"); - free(state->compressed); - free(state); - - /* Override the error message if we know what really went wrong. */ - switch (ret) { - case Z_STREAM_ERROR: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Internal error initializing " - "compression library: invalid setup parameter"); - break; - case Z_MEM_ERROR: - archive_set_error(a, ENOMEM, "Internal error initializing " - "compression library"); - break; - case Z_VERSION_ERROR: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Internal error initializing " - "compression library: invalid library version"); - break; - } - - return (ARCHIVE_FATAL); -} - -/* - * Write data to the compressed stream. - */ - static int -archive_compressor_gzip_write(struct archive *a, const void *buff, - size_t length) -{ - struct private_data *state; - int ret; - - state = (struct private_data*)a->compression_data; - if (a->client_writer == NULL) { - archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER, - "No write callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } - - /* Update statistics */ - state->crc = crc32(state->crc, (const Bytef*)buff, length); - state->total_in += length; - - /* Compress input data to output buffer */ - SET_NEXT_IN(state, buff); - state->stream.avail_in = length; - if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK) - return (ret); - - a->file_position += length; - return (ARCHIVE_OK); -} - - -/* - * Finish the compression... - */ - static int -archive_compressor_gzip_finish(struct archive *a) -{ - ssize_t block_length, target_block_length, bytes_written; - int ret; - struct private_data *state; - unsigned tocopy; - unsigned char trailer[8]; - - state = (struct private_data*)a->compression_data; - ret = 0; - if (a->client_writer == NULL) { - archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER, - "No write callback is registered? " - "This is probably an internal programming error."); - ret = ARCHIVE_FATAL; - goto cleanup; - } - - /* By default, always pad the uncompressed data. */ - if (a->pad_uncompressed) { - tocopy = a->bytes_per_block - - (state->total_in % a->bytes_per_block); - while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { - SET_NEXT_IN(state, a->nulls); - state->stream.avail_in = tocopy < a->null_length ? - tocopy : a->null_length; - state->crc = crc32(state->crc, a->nulls, - state->stream.avail_in); - state->total_in += state->stream.avail_in; - tocopy -= state->stream.avail_in; - ret = drive_compressor(a, state, 0); - if (ret != ARCHIVE_OK) - goto cleanup; - } - } - - /* Finish compression cycle */ - if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK) - goto cleanup; - - /* Build trailer: 4-byte CRC and 4-byte length. */ - trailer[0] = (state->crc)&0xff; - trailer[1] = (state->crc >> 8)&0xff; - trailer[2] = (state->crc >> 16)&0xff; - trailer[3] = (state->crc >> 24)&0xff; - trailer[4] = (state->total_in)&0xff; - trailer[5] = (state->total_in >> 8)&0xff; - trailer[6] = (state->total_in >> 16)&0xff; - trailer[7] = (state->total_in >> 24)&0xff; - - /* Add trailer to current block. */ - tocopy = 8; - if (tocopy > state->stream.avail_out) - tocopy = state->stream.avail_out; - memcpy(state->stream.next_out, trailer, tocopy); - state->stream.next_out += tocopy; - state->stream.avail_out -= tocopy; - - /* If it overflowed, flush and start a new block. */ - if (tocopy < 8) { - bytes_written = (a->client_writer)(a, a->client_data, - state->compressed, state->compressed_buffer_size); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - a->raw_position += bytes_written; - state->stream.next_out = state->compressed; - state->stream.avail_out = state->compressed_buffer_size; - memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy); - state->stream.next_out += 8-tocopy; - state->stream.avail_out -= 8-tocopy; - } - - /* Optionally, pad the final compressed block. */ - block_length = state->stream.next_out - state->compressed; - - - /* Tricky calculation to determine size of last block. */ - target_block_length = block_length; - if (a->bytes_in_last_block <= 0) - /* Default or Zero: pad to full block */ - target_block_length = a->bytes_per_block; - else - /* Round length to next multiple of bytes_in_last_block. */ - target_block_length = a->bytes_in_last_block * - ( (block_length + a->bytes_in_last_block - 1) / - a->bytes_in_last_block); - if (target_block_length > a->bytes_per_block) - target_block_length = a->bytes_per_block; - if (block_length < target_block_length) { - memset(state->stream.next_out, 0, - target_block_length - block_length); - block_length = target_block_length; - } - - /* Write the last block */ - bytes_written = (a->client_writer)(a, a->client_data, - state->compressed, block_length); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - a->raw_position += bytes_written; - - /* Cleanup: shut down compressor, release memory, etc. */ -cleanup: - switch (deflateEnd(&(state->stream))) { - case Z_OK: - break; - default: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Failed to clean up compressor"); - ret = ARCHIVE_FATAL; - } - free(state->compressed); - free(state); - - /* Close the output */ - if (a->client_closer != NULL) - (a->client_closer)(a, a->client_data); - - return (ret); -} - -/* - * Utility function to push input data through compressor, - * writing full output blocks as necessary. - * - * Note that this handles both the regular write case (finishing == - * false) and the end-of-archive case (finishing == true). - */ - static int -drive_compressor(struct archive *a, struct private_data *state, int finishing) -{ - ssize_t bytes_written; - int ret; - - for (;;) { - if (state->stream.avail_out == 0) { - bytes_written = (a->client_writer)(a, a->client_data, - state->compressed, state->compressed_buffer_size); - if (bytes_written <= 0) { - /* TODO: Handle this write failure */ - return (ARCHIVE_FATAL); - } else if ((size_t)bytes_written < state->compressed_buffer_size) { - /* Short write: Move remaining to - * front of block and keep filling */ - memmove(state->compressed, - state->compressed + bytes_written, - state->compressed_buffer_size - bytes_written); - } - a->raw_position += bytes_written; - state->stream.next_out - = state->compressed + - state->compressed_buffer_size - bytes_written; - state->stream.avail_out = bytes_written; + if (tar_append_eof(t) != 0) + { + cmSystemTools::Error("Problem with tar_append_eof(): ", strerror(errno)); + tar_close(t); + return 0; } - ret = deflate(&(state->stream), - finishing ? Z_FINISH : Z_NO_FLUSH ); - - switch (ret) { - case Z_OK: - /* In non-finishing case, check if compressor - * consumed everything */ - if (!finishing && state->stream.avail_in == 0) - return (ARCHIVE_OK); - /* In finishing case, this return always means - * there's more work */ - break; - case Z_STREAM_END: - /* This return can only occur in finishing case. */ - return (ARCHIVE_OK); - default: - /* Any other return value indicates an error. */ - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "GZip compression failed"); - return (ARCHIVE_FATAL); + if (tar_close(t) != 0) + { + cmSystemTools::Error("Problem with tar_close(): ", strerror(errno)); + return 0; } - } + return 1; } - diff --git a/Source/CPack/cmCPackTGZGenerator.h b/Source/CPack/cmCPackTGZGenerator.h index 1dd86082e9..c536a8e239 100644 --- a/Source/CPack/cmCPackTGZGenerator.h +++ b/Source/CPack/cmCPackTGZGenerator.h @@ -50,9 +50,9 @@ public: virtual ~cmCPackTGZGenerator(); protected: - static int TGZ_Open(struct archive *a, void *client_data); - static ssize_t TGZ_Write(struct archive *a, void *client_data, void *buff, size_t n); - static int TGZ_Close(struct archive *a, void *client_data); + static int TGZ_Open(void *client_data, const char* name, int oflags, mode_t mode); + static ssize_t TGZ_Write(void *client_data, void *buff, size_t n); + static int TGZ_Close(void *client_data); int CompressFiles(const char* outFileName, const char* toplevel, const std::vector<std::string>& files); |