/*========================================================================= Program: CMake - Cross-Platform Makefile Generator Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cmCPackTGZGenerator.h" #include "cmake.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmSystemTools.h" #include "cmMakefile.h" #include "cmGeneratedFileStream.h" #include #include #include // auto_ptr #include // O_WRONLY, O_CREAT //---------------------------------------------------------------------- cmCPackTGZGenerator::cmCPackTGZGenerator() { } //---------------------------------------------------------------------- cmCPackTGZGenerator::~cmCPackTGZGenerator() { } //---------------------------------------------------------------------- int cmCPackTGZGenerator::ProcessGenerator() { return this->Superclass::ProcessGenerator(); } //---------------------------------------------------------------------- int cmCPackTGZGenerator::Initialize(const char* name) { return this->Superclass::Initialize(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 #endif #if defined(HAVE_UNISTD_H) # include // for geteuid #endif #if defined(HAVE_INTTYPES_H) # include // for uintptr_t #endif #include #include // for ENOMEM #include // for struct stat #include // 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. */ 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 #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: cmCPackTGZ_Data(cmCPackTGZGenerator* gen) : Name(0), OutputStream(0), Generator(gen) {} const char *Name; std::ostream* OutputStream; cmCPackTGZGenerator* Generator; }; //---------------------------------------------------------------------- int cmCPackTGZGenerator::TGZ_Open(struct archive *a, void *client_data) { cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data; (void)a; mydata->OutputStream = new cmGeneratedFileStream(mydata->Name); if ( *mydata->OutputStream && mydata->Generator->GenerateHeader(mydata->OutputStream)) { return (ARCHIVE_OK); } else { return (ARCHIVE_FATAL); } } //---------------------------------------------------------------------- ssize_t cmCPackTGZGenerator::TGZ_Write(struct archive *a, void *client_data, void *buff, size_t n) { cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data; (void)a; mydata->OutputStream->write(reinterpret_cast(buff), n); if ( !*mydata->OutputStream ) { return 0; } return n; } //---------------------------------------------------------------------- int cmCPackTGZGenerator::TGZ_Close(struct archive *a, void *client_data) { cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data; (void)a; delete mydata->OutputStream; return (0); } //---------------------------------------------------------------------- int cmCPackTGZGenerator::CompressFiles(const char* outFileName, const char* toplevel, const std::vector& files) { 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; 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::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 ) { archive_write_data(a, buff, len); len = read(fd, buff, sizeof(buff)); } 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; } 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); } } }