diff --git a/lib/libarchive/archive.h b/lib/libarchive/archive.h index 23b9a9de3666..da31d4030e3d 100644 --- a/lib/libarchive/archive.h +++ b/lib/libarchive/archive.h @@ -1,708 +1,708 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_H_INCLUDED #define ARCHIVE_H_INCLUDED /* * Note: archive.h is for use outside of libarchive; the configuration * headers (config.h, archive_platform.h, etc.) are purely internal. * Do NOT use HAVE_XXX configuration macros to control the behavior of * this header! If you must conditionalize, use predefined compiler and/or * platform macros. */ #include #include /* Linux requires this for off_t */ #if !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) /* Header unavailable on Watcom C or MS Visual C++ or SFU. */ #include /* int64_t, etc. */ #endif #include /* For FILE * */ /* Get appropriate definitions of standard POSIX-style types. */ /* These should match the types used in 'struct stat' */ -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #define __LA_INT64_T __int64 # if defined(_WIN64) # define __LA_SSIZE_T __int64 # else # define __LA_SSIZE_T long # endif #define __LA_UID_T unsigned int #define __LA_GID_T unsigned int #else #include /* ssize_t, uid_t, and gid_t */ #define __LA_INT64_T int64_t #define __LA_SSIZE_T ssize_t #define __LA_UID_T uid_t #define __LA_GID_T gid_t #endif /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ -#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC) +#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL __attribute__((dllimport)) extern # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries or non-Windows needs no special declaration. */ # define __LA_DECL #endif #ifdef __cplusplus extern "C" { #endif /* * The version number is provided as both a macro and a function. * The macro identifies the installed header; the function identifies * the library version (which may not be the same if you're using a * dynamically-linked version of the library). Of course, if the * header and library are very different, you should expect some * strangeness. Don't do that. */ /* * The version number is expressed as a single integer that makes it * easy to compare versions at build time: for version a.b.c, the * version number is printf("%d%03d%03d",a,b,c). For example, if you * know your application requires version 2.12.108 or later, you can * assert that ARCHIVE_VERSION >= 2012108. * * This single-number format was introduced with libarchive 1.9.0 in * the libarchive 1.x family and libarchive 2.2.4 in the libarchive * 2.x family. The following may be useful if you really want to do * feature detection for earlier libarchive versions (which defined * ARCHIVE_API_VERSION and ARCHIVE_API_FEATURE instead): * * #ifndef ARCHIVE_VERSION_NUMBER * #define ARCHIVE_VERSION_NUMBER \ * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) * #endif */ #define ARCHIVE_VERSION_NUMBER 2006901 __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ #define ARCHIVE_VERSION_STRING "libarchive 2.6.901a" __LA_DECL const char * archive_version_string(void); #if ARCHIVE_VERSION_NUMBER < 3000000 /* * Deprecated; these are older names that will be removed in favor of * the simpler definitions above. */ #define ARCHIVE_VERSION_STAMP ARCHIVE_VERSION_NUMBER __LA_DECL int archive_version_stamp(void); #define ARCHIVE_LIBRARY_VERSION ARCHIVE_VERSION_STRING __LA_DECL const char * archive_version(void); #define ARCHIVE_API_VERSION (ARCHIVE_VERSION_NUMBER / 1000000) __LA_DECL int archive_api_version(void); #define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_NUMBER / 1000) % 1000) __LA_DECL int archive_api_feature(void); #endif #if ARCHIVE_VERSION_NUMBER < 3000000 /* This should never have been here in the first place. */ /* Legacy of old tar assumptions, will be removed in libarchive 3.0. */ #define ARCHIVE_BYTES_PER_RECORD 512 #define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240 #endif /* 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 success. */ /* For example, if write_header "fails", then you can't push data. */ #define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */ /* But if write_header is "fatal," then this archive is dead and useless. */ #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/skip/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 __LA_SSIZE_T archive_read_callback(struct archive *, void *_client_data, const void **_buffer); /* Skips at most request bytes from archive and returns the skipped amount */ #if ARCHIVE_VERSION_NUMBER < 2000000 /* Libarchive 1.0 used ssize_t for the return, which is only 32 bits * on most 32-bit platforms; not large enough. */ typedef __LA_SSIZE_T archive_skip_callback(struct archive *, void *_client_data, size_t request); #elif ARCHIVE_VERSION_NUMBER < 3000000 /* Libarchive 2.0 used off_t here, but that is a bad idea on Linux and a * few other platforms where off_t varies with build settings. */ typedef off_t archive_skip_callback(struct archive *, void *_client_data, off_t request); #else /* Libarchive 3.0 uses int64_t here, which is actually guaranteed to be * 64 bits on every platform. */ typedef __LA_INT64_T archive_skip_callback(struct archive *, void *_client_data, __LA_INT64_T request); #endif /* Returns size actually written, zero on EOF, -1 on error. */ typedef __LA_SSIZE_T archive_write_callback(struct archive *, void *_client_data, const void *_buffer, size_t _length); #if ARCHIVE_VERSION_NUMBER < 3000000 /* Open callback is actually never needed; remove it in libarchive 3.0. */ typedef int archive_open_callback(struct archive *, void *_client_data); #endif 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 #define ARCHIVE_COMPRESSION_PROGRAM 4 #define ARCHIVE_COMPRESSION_LZMA 5 #define ARCHIVE_COMPRESSION_XZ 6 /* * 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. * In some cases, this variation occurs as libarchive learns more about * the archive (for example, later entries might utilize extensions that * weren't necessary earlier in the archive; in this case, libarchive * will change the format code to indicate the extended format that * was used). In other cases, it's because different tools have * modified the archive and so different parts of the archive * actually have slightly different formts. (Both tar and cpio store * format codes in each entry, so it is quite possible for each * entry to be in a different format.) */ #define ARCHIVE_FORMAT_BASE_MASK 0xff0000 #define ARCHIVE_FORMAT_CPIO 0x10000 #define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) #define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2) #define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) #define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) #define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) #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 #define ARCHIVE_FORMAT_EMPTY 0x60000 #define ARCHIVE_FORMAT_AR 0x70000 #define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) #define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) #define ARCHIVE_FORMAT_MTREE 0x80000 /*- * 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. */ __LA_DECL 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. */ __LA_DECL int archive_read_support_compression_all(struct archive *); __LA_DECL int archive_read_support_compression_bzip2(struct archive *); __LA_DECL int archive_read_support_compression_compress(struct archive *); __LA_DECL int archive_read_support_compression_gzip(struct archive *); __LA_DECL int archive_read_support_compression_none(struct archive *); __LA_DECL int archive_read_support_compression_program(struct archive *, const char *command); __LA_DECL int archive_read_support_compression_program_signature (struct archive *, const char *, const void * /* match */, size_t); __LA_DECL int archive_read_support_format_all(struct archive *); __LA_DECL int archive_read_support_format_ar(struct archive *); __LA_DECL int archive_read_support_format_cpio(struct archive *); __LA_DECL int archive_read_support_format_empty(struct archive *); __LA_DECL int archive_read_support_format_gnutar(struct archive *); __LA_DECL int archive_read_support_format_iso9660(struct archive *); __LA_DECL int archive_read_support_format_mtree(struct archive *); __LA_DECL int archive_read_support_format_tar(struct archive *); __LA_DECL int archive_read_support_format_zip(struct archive *); /* Open the archive using callbacks for archive I/O. */ __LA_DECL int archive_read_open(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_close_callback *); __LA_DECL int archive_read_open2(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_skip_callback *, archive_close_callback *); /* * A variety of shortcuts that invoke archive_read_open() with * canned callbacks suitable for common situations. The ones that * accept a block size handle tape blocking correctly. */ /* Use this if you know the filename. Note: NULL indicates stdin. */ __LA_DECL int archive_read_open_filename(struct archive *, const char *_filename, size_t _block_size); /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ __LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size); /* Read an archive that's stored in memory. */ __LA_DECL int archive_read_open_memory(struct archive *, void * buff, size_t size); /* A more involved version that is only used for internal testing. */ __LA_DECL int archive_read_open_memory2(struct archive *a, void *buff, size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ __LA_DECL int archive_read_open_fd(struct archive *, int _fd, size_t _block_size); /* Read an archive that's already open, using a FILE *. */ /* Note: DO NOT use this with tape drives. */ __LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); /* Parses and returns next entry header. */ __LA_DECL int archive_read_next_header(struct archive *, struct archive_entry **); /* * Retrieve the byte offset in UNCOMPRESSED data where last-read * header started. */ __LA_DECL __LA_INT64_T archive_read_header_position(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ __LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, void *, size_t); /* * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify * the desired size of the block. The API does guarantee that offsets will * be strictly increasing and that returned blocks will not overlap. */ #if ARCHIVE_VERSION_NUMBER < 3000000 __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, off_t *offset); #else __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, __LA_INT64_T *offset); #endif /*- * 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 */ __LA_DECL int archive_read_data_skip(struct archive *); __LA_DECL int archive_read_data_into_buffer(struct archive *, void *buffer, __LA_SSIZE_T len); __LA_DECL int archive_read_data_into_fd(struct archive *, int fd); /* * Set read options. */ /* Apply option string to the format only. */ __LA_DECL int archive_read_set_format_options(struct archive *_a, const char *s); /* Apply option string to the filter only. */ __LA_DECL int archive_read_set_filter_options(struct archive *_a, const char *s); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_read_set_options(struct archive *_a, const char *s); /*- * 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. */ /* Default: Do not try to set owner/group. */ #define ARCHIVE_EXTRACT_OWNER (0x0001) /* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */ #define ARCHIVE_EXTRACT_PERM (0x0002) /* Default: Do not restore mtime/atime. */ #define ARCHIVE_EXTRACT_TIME (0x0004) /* Default: Replace existing files. */ #define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008) /* Default: Try create first, unlink only if create fails with EEXIST. */ #define ARCHIVE_EXTRACT_UNLINK (0x0010) /* Default: Do not restore ACLs. */ #define ARCHIVE_EXTRACT_ACL (0x0020) /* Default: Do not restore fflags. */ #define ARCHIVE_EXTRACT_FFLAGS (0x0040) /* Default: Do not restore xattrs. */ #define ARCHIVE_EXTRACT_XATTR (0x0080) /* Default: Do not try to guard against extracts redirected by symlinks. */ /* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */ #define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100) /* Default: Do not reject entries with '..' as path elements. */ #define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200) /* Default: Create parent directories as needed. */ #define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400) /* Default: Overwrite files, even if one on disk is newer. */ #define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800) /* Detect blocks of 0 and write holes instead. */ #define ARCHIVE_EXTRACT_SPARSE (0x1000) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); __LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, struct archive * /* dest */); __LA_DECL void archive_read_extract_set_progress_callback(struct archive *, void (*_progress_func)(void *), void *_user_data); /* Record the dev/ino of a file that will not be written. This is * generally set to the dev/ino of the archive being read. */ __LA_DECL void archive_read_extract_set_skip_file(struct archive *, dev_t, ino_t); /* Close the file and release most resources. */ __LA_DECL int archive_read_close(struct archive *); /* Release all resources and destroy the object. */ /* Note that archive_read_finish will call archive_read_close for you. */ #if ARCHIVE_VERSION_NUMBER < 2000000 /* Erroneously declared to return void in libarchive 1.x */ __LA_DECL void archive_read_finish(struct archive *); #else __LA_DECL int archive_read_finish(struct archive *); #endif /*- * 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 */ __LA_DECL struct archive *archive_write_new(void); __LA_DECL int archive_write_set_bytes_per_block(struct archive *, int bytes_per_block); __LA_DECL int archive_write_get_bytes_per_block(struct archive *); /* XXX This is badly misnamed; suggestions appreciated. XXX */ __LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, int bytes_in_last_block); __LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); /* The dev/ino of a file that won't be archived. This is used * to avoid recursively adding an archive to itself. */ __LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t); __LA_DECL int archive_write_set_compression_bzip2(struct archive *); __LA_DECL int archive_write_set_compression_compress(struct archive *); __LA_DECL int archive_write_set_compression_gzip(struct archive *); __LA_DECL int archive_write_set_compression_none(struct archive *); __LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd); /* A convenience function to set the format based on the code or name. */ __LA_DECL int archive_write_set_format(struct archive *, int format_code); __LA_DECL int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ __LA_DECL int archive_write_set_format_ar_bsd(struct archive *); __LA_DECL int archive_write_set_format_ar_svr4(struct archive *); __LA_DECL int archive_write_set_format_cpio(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_mtree(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __LA_DECL int archive_write_set_format_pax(struct archive *); __LA_DECL int archive_write_set_format_pax_restricted(struct archive *); __LA_DECL int archive_write_set_format_shar(struct archive *); __LA_DECL int archive_write_set_format_shar_dump(struct archive *); __LA_DECL int archive_write_set_format_ustar(struct archive *); __LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); __LA_DECL int archive_write_open_fd(struct archive *, int _fd); __LA_DECL int archive_write_open_filename(struct archive *, const char *_file); /* A deprecated synonym for archive_write_open_filename() */ __LA_DECL int archive_write_open_file(struct archive *, const char *_file); __LA_DECL int archive_write_open_FILE(struct archive *, FILE *); /* _buffSize is the size of the buffer, _used refers to a variable that * will be updated after each write into the buffer. */ __LA_DECL int archive_write_open_memory(struct archive *, void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided * to archive_write_header or pad if the provided data is short. */ __LA_DECL int archive_write_header(struct archive *, struct archive_entry *); #if ARCHIVE_VERSION_NUMBER < 2000000 /* This was erroneously declared to return "int" in libarchive 1.x. */ __LA_DECL int archive_write_data(struct archive *, const void *, size_t); #else /* Libarchive 2.0 and later return ssize_t here. */ __LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, const void *, size_t); #endif #if ARCHIVE_VERSION_NUMBER < 3000000 /* Libarchive 1.x and 2.x use off_t for the argument, but that's not * stable on Linux. */ __LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, off_t); #else /* Libarchive 3.0 uses explicit int64_t to ensure consistent 64-bit support. */ __LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, __LA_INT64_T); #endif __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); #if ARCHIVE_VERSION_NUMBER < 2000000 /* Return value was incorrect in libarchive 1.x. */ __LA_DECL void archive_write_finish(struct archive *); #else /* Libarchive 2.x and later returns an error if this fails. */ /* It can fail if the archive wasn't already closed, in which case * archive_write_finish() will implicitly call archive_write_close(). */ __LA_DECL int archive_write_finish(struct archive *); #endif /* * Set write options. */ /* Apply option string to the format only. */ __LA_DECL int archive_write_set_format_options(struct archive *_a, const char *s); /* Apply option string to the compressor only. */ __LA_DECL int archive_write_set_compressor_options(struct archive *_a, const char *s); /* Apply option string to both the format and the compressor. */ __LA_DECL int archive_write_set_options(struct archive *_a, const char *s); /*- * To create objects on disk: * 1) Ask archive_write_disk_new for a new archive_write_disk object. * 2) Set any global properties. In particular, you should set * the compression and format to use. * 3) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to create the file/dir/etc on disk * - archive_write_data to write the entry data * 4) archive_write_finish to cleanup the writer and release resources * * In particular, you can use this in conjunction with archive_read() * to pull entries out of an archive and create them on disk. */ __LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ __LA_DECL int archive_write_disk_set_skip_file(struct archive *, dev_t, ino_t); /* Set flags to control how the next item gets created. */ __LA_DECL int archive_write_disk_set_options(struct archive *, int flags); /* * The lookup functions are given uname/uid (or gname/gid) pairs and * return a uid (gid) suitable for this system. These are used for * restoring ownership and for setting ACLs. The default functions * are naive, they just return the uid/gid. These are small, so reasonable * for applications that don't need to preserve ownership; they * are probably also appropriate for applications that are doing * same-system backup and restore. */ /* * The "standard" lookup functions use common system calls to lookup * the uname/gname, falling back to the uid/gid if the names can't be * found. They cache lookups and are reasonably fast, but can be very * large, so they are not used unless you ask for them. In * particular, these match the specifications of POSIX "pax" and old * POSIX "tar". */ __LA_DECL int archive_write_disk_set_standard_lookup(struct archive *); /* * If neither the default (naive) nor the standard (big) functions suit * your needs, you can write your own and register them. Be sure to * include a cleanup function if you have allocated private data. */ __LA_DECL int archive_write_disk_set_group_lookup(struct archive *, void * /* private_data */, __LA_GID_T (*)(void *, const char *, __LA_GID_T), void (* /* cleanup */)(void *)); __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, void * /* private_data */, __LA_UID_T (*)(void *, const char *, __LA_UID_T), void (* /* cleanup */)(void *)); /* * ARCHIVE_READ_DISK API * * This is still evolving and somewhat experimental. */ __LA_DECL struct archive *archive_read_disk_new(void); /* The names for symlink modes here correspond to an old BSD * command-line argument convention: -L, -P, -H */ /* Follow all symlinks. */ __LA_DECL int archive_read_disk_set_symlink_logical(struct archive *); /* Follow no symlinks. */ __LA_DECL int archive_read_disk_set_symlink_physical(struct archive *); /* Follow symlink initially, then not. */ __LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *); /* TODO: Handle Linux stat32/stat64 ugliness. */ __LA_DECL int archive_read_disk_entry_from_file(struct archive *, struct archive_entry *, int /* fd */, const struct stat *); /* Look up gname for gid or uname for uid. */ /* Default implementations are very, very stupid. */ __LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T); __LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T); /* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the * results for performance. */ __LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); /* You can install your own lookups if you like. */ __LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, __LA_GID_T), void (* /* cleanup_fn */)(void *)); __LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, __LA_UID_T), void (* /* cleanup_fn */)(void *)); /* * Accessor functions to read/set various information in * the struct archive object: */ /* Bytes written after compression or read before decompression. */ __LA_DECL __LA_INT64_T archive_position_compressed(struct archive *); /* Bytes written to compressor or read from decompressor. */ __LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *); __LA_DECL const char *archive_compression_name(struct archive *); __LA_DECL int archive_compression(struct archive *); __LA_DECL int archive_errno(struct archive *); __LA_DECL const char *archive_error_string(struct archive *); __LA_DECL const char *archive_format_name(struct archive *); __LA_DECL int archive_format(struct archive *); __LA_DECL void archive_clear_error(struct archive *); __LA_DECL void archive_set_error(struct archive *, int _err, const char *fmt, ...); __LA_DECL void archive_copy_error(struct archive *dest, struct archive *src); #ifdef __cplusplus } #endif /* These are meaningless outside of this header. */ #undef __LA_DECL #undef __LA_GID_T #undef __LA_UID_T /* These need to remain defined because they're used in the * callback type definitions. XXX Fix this. This is ugly. XXX */ /* #undef __LA_INT64_T */ /* #undef __LA_SSIZE_T */ #endif /* !ARCHIVE_H_INCLUDED */ diff --git a/lib/libarchive/archive_check_magic.c b/lib/libarchive/archive_check_magic.c index e1d35c540d0f..8f1fa2f00adb 100644 --- a/lib/libarchive/archive_check_magic.c +++ b/lib/libarchive/archive_check_magic.c @@ -1,126 +1,126 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #include #include #endif #include "archive_private.h" static void errmsg(const char *m) { write(2, m, strlen(m)); } static void diediedie(void) { -#if defined(_WIN32) && defined(_DEBUG) +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) /* Cause a breakpoint exception */ DebugBreak(); #endif *(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(unsigned int states) { unsigned int 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 int magic, unsigned int 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(); } } diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index 37fc37b49911..4cafce27067c 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -1,2155 +1,2155 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef MAJOR_IN_MKDEV #include #else #ifdef MAJOR_IN_SYSMACROS #include #endif #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* for Linux file flags */ #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_entry_private.h" #undef max #define max(a, b) ((a)>(b)?(a):(b)) /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) -#elif defined mkdev || defined _WIN32 || defined __WIN32__ +#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define ae_makedev(maj, min) makedev((maj), (min)) #endif static void aes_clean(struct aes *); static void aes_copy(struct aes *dest, struct aes *src); static const char * aes_get_mbs(struct aes *); static const wchar_t * aes_get_wcs(struct aes *); static int aes_set_mbs(struct aes *, const char *mbs); static int aes_copy_mbs(struct aes *, const char *mbs); /* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ static int aes_copy_wcs(struct aes *, const wchar_t *wcs); static int aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); 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 int isint_w(const wchar_t *start, const wchar_t *end, int *result); 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); static void archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name, size_t); #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p != L'\0') ++p; return p - s; } #endif #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif #ifndef HAVE_WMEMCPY #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif static void aes_clean(struct aes *aes) { if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } archive_string_free(&(aes->aes_mbs)); archive_string_free(&(aes->aes_utf8)); aes->aes_set = 0; } static void aes_copy(struct aes *dest, struct aes *src) { wchar_t *wp; dest->aes_set = src->aes_set; archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); if (src->aes_wcs != NULL) { wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) * sizeof(wchar_t)); if (wp == NULL) __archive_errx(1, "No memory for aes_copy()"); wcscpy(wp, src->aes_wcs); dest->aes_wcs = wp; } } static const char * aes_get_utf8(struct aes *aes) { if (aes->aes_set & AES_SET_UTF8) return (aes->aes_utf8.s); if ((aes->aes_set & AES_SET_WCS) && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) { aes->aes_set |= AES_SET_UTF8; return (aes->aes_utf8.s); } return (NULL); } static const char * aes_get_mbs(struct aes *aes) { /* If we already have an MBS form, return that immediately. */ if (aes->aes_set & AES_SET_MBS) return (aes->aes_mbs.s); /* If there's a WCS form, try converting with the native locale. */ if ((aes->aes_set & AES_SET_WCS) && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) { aes->aes_set |= AES_SET_MBS; return (aes->aes_mbs.s); } /* We'll use UTF-8 for MBS if all else fails. */ return (aes_get_utf8(aes)); } static const wchar_t * aes_get_wcs(struct aes *aes) { wchar_t *w; int r; /* Return WCS form if we already have it. */ if (aes->aes_set & AES_SET_WCS) return (aes->aes_wcs); if (aes->aes_set & AES_SET_MBS) { /* Try converting MBS to WCS using native locale. */ /* * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ size_t wcs_length = aes->aes_mbs.length; w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); if (w == NULL) __archive_errx(1, "No memory for aes_get_wcs()"); r = mbstowcs(w, aes->aes_mbs.s, wcs_length); if (r > 0) { w[r] = 0; aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs = w); } free(w); } if (aes->aes_set & AES_SET_UTF8) { /* Try converting UTF8 to WCS. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); if (aes->aes_wcs != NULL) aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs); } return (NULL); } static int aes_set_mbs(struct aes *aes, const char *mbs) { return (aes_copy_mbs(aes, mbs)); } static int aes_copy_mbs(struct aes *aes, const char *mbs) { if (mbs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ archive_strcpy(&(aes->aes_mbs), mbs); archive_string_empty(&(aes->aes_utf8)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } return (0); } /* * The 'update' form tries to proactively update all forms of * this string (WCS and MBS) and returns an error if any of * them fail. This is used by the 'pax' handler, for instance, * to detect and report character-conversion failures early while * still allowing clients to get potentially useful values from * the more tolerant lazy conversions. (get_mbs and get_wcs will * strive to give the user something useful, so you can get hopefully * usable values even if some of the character conversions are failing.) */ static int aes_update_utf8(struct aes *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; return (1); /* Succeeded in clearing everything. */ } /* Save the UTF8 string. */ archive_strcpy(&(aes->aes_utf8), utf8); /* Empty the mbs and wcs strings. */ archive_string_empty(&(aes->aes_mbs)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ /* TODO: We should just do a direct UTF-8 to MBS conversion * here. That would be faster, use less space, and give the * same information. (If a UTF-8 to MBS conversion succeeds, * then UTF-8->WCS and Unicode->MBS conversions will both * succeed.) */ /* Try converting UTF8 to WCS, return false on failure. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); if (aes->aes_wcs == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ /* Try converting WCS to MBS, return false on failure. */ if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; /* All conversions succeeded. */ return (1); } static int aes_copy_wcs(struct aes *aes, const wchar_t *wcs) { return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs)); } static int aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) { wchar_t *w; if (wcs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_utf8)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); if (w == NULL) __archive_errx(1, "No memory for aes_copy_wcs()"); wmemcpy(w, wcs, len); w[len] = L'\0'; aes->aes_wcs = w; return (0); } /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); 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); archive_entry_xattr_clear(entry); free(entry->stat); memset(entry, 0, sizeof(*entry)); return entry; } struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; struct ae_acl *ap, *ap2; struct ae_xattr *xp; /* 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); entry2->ae_set = entry->ae_set; aes_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy ACL data over. */ ap = entry->acl_head; while (ap != NULL) { ap2 = acl_new_entry(entry2, ap->type, ap->permset, ap->tag, ap->id); if (ap2 != NULL) aes_copy(&ap2->name, &ap->name); ap = ap->next; } /* Copy xattr data over. */ xp = entry->xattr_head; while (xp != NULL) { archive_entry_xattr_add_entry(entry2, xp->name, xp->value, xp->size); xp = xp->next; } 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.aest_atime); } long archive_entry_atime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_atime_nsec); } int archive_entry_atime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_ATIME); } time_t archive_entry_birthtime(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime); } long archive_entry_birthtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime_nsec); } int archive_entry_birthtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_BIRTHTIME); } time_t archive_entry_ctime(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime); } int archive_entry_ctime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_CTIME); } long archive_entry_ctime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime_nsec); } dev_t archive_entry_dev(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return ae_makedev(entry->ae_stat.aest_devmajor, entry->ae_stat.aest_devminor); else return (entry->ae_stat.aest_dev); } dev_t archive_entry_devmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devmajor); else return major(entry->ae_stat.aest_dev); } dev_t archive_entry_devminor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devminor); else return minor(entry->ae_stat.aest_dev); } mode_t archive_entry_filetype(struct archive_entry *entry) { return (AE_IFMT & entry->ae_stat.aest_mode); } 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.aest_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) { if (entry->ae_set & AE_SET_HARDLINK) return (aes_get_mbs(&entry->ae_hardlink)); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { if (entry->ae_set & AE_SET_HARDLINK) return (aes_get_wcs(&entry->ae_hardlink)); return (NULL); } ino_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->ae_stat.aest_mode); } time_t archive_entry_mtime(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime); } long archive_entry_mtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime_nsec); } int archive_entry_mtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_MTIME); } unsigned int archive_entry_nlink(struct archive_entry *entry) { return (entry->ae_stat.aest_nlink); } 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) { if (entry->ae_stat.aest_rdev_is_broken_down) return ae_makedev(entry->ae_stat.aest_rdevmajor, entry->ae_stat.aest_rdevminor); else return (entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevmajor); else return major(entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevminor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevminor); else return minor(entry->ae_stat.aest_rdev); } int64_t archive_entry_size(struct archive_entry *entry) { return (entry->ae_stat.aest_size); } int archive_entry_size_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_SIZE); } const char * archive_entry_sourcepath(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_sourcepath)); } const char * archive_entry_symlink(struct archive_entry *entry) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_get_mbs(&entry->ae_symlink)); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_get_wcs(&entry->ae_symlink)); return (NULL); } uid_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_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. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->ae_stat.aest_mode &= ~AE_IFMT; entry->ae_stat.aest_mode |= AE_IFMT & type; } 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 char * archive_entry_copy_fflags_text(struct archive_entry *entry, const char *flags) { aes_copy_mbs(&entry->ae_fflags_text, flags); return (ae_strtofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) { 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->stat_valid = 0; entry->ae_stat.aest_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(struct archive_entry *entry, const char *name) { aes_copy_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); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_gname, name)); } void archive_entry_set_ino(struct archive_entry *entry, unsigned long ino) { entry->stat_valid = 0; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { aes_set_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { aes_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { aes_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_ATIME; entry->ae_stat.aest_atime = t; entry->ae_stat.aest_atime_nsec = ns; } void archive_entry_unset_atime(struct archive_entry *entry) { archive_entry_set_atime(entry, 0, 0); entry->ae_set &= ~AE_SET_ATIME; } void archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = m; entry->ae_stat.aest_birthtime_nsec = ns; } void archive_entry_unset_birthtime(struct archive_entry *entry) { archive_entry_set_birthtime(entry, 0, 0); entry->ae_set &= ~AE_SET_BIRTHTIME; } void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_CTIME; entry->ae_stat.aest_ctime = t; entry->ae_stat.aest_ctime_nsec = ns; } void archive_entry_unset_ctime(struct archive_entry *entry) { archive_entry_set_ctime(entry, 0, 0); entry->ae_set &= ~AE_SET_CTIME; } void archive_entry_set_dev(struct archive_entry *entry, dev_t d) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 0; entry->ae_stat.aest_dev = d; } void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devmajor = m; } void archive_entry_set_devminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devminor = m; } /* 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_set & AE_SET_SYMLINK) aes_set_mbs(&entry->ae_symlink, target); else aes_set_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_copy_mbs(&entry->ae_symlink, target); else aes_copy_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_copy_wcs(&entry->ae_symlink, target); else aes_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_update_utf8(&entry->ae_symlink, target)); else return (aes_update_utf8(&entry->ae_hardlink, target)); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->ae_stat.aest_mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = m; entry->ae_stat.aest_mtime_nsec = ns; } void archive_entry_unset_mtime(struct archive_entry *entry) { archive_entry_set_mtime(entry, 0, 0); entry->ae_set &= ~AE_SET_MTIME; } void archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) { entry->stat_valid = 0; entry->ae_stat.aest_nlink = nlink; } 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); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_pathname, name)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->ae_stat.aest_mode &= AE_IFMT; entry->ae_stat.aest_mode |= ~AE_IFMT & p; } void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; } void archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevmajor = m; } void archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevminor = m; } void archive_entry_set_size(struct archive_entry *entry, int64_t s) { entry->stat_valid = 0; entry->ae_stat.aest_size = s; entry->ae_set |= AE_SET_SIZE; } void archive_entry_unset_size(struct archive_entry *entry) { archive_entry_set_size(entry, 0); entry->ae_set &= ~AE_SET_SIZE; } void archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) { aes_set_mbs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { aes_set_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { aes_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { aes_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_set_uid(struct archive_entry *entry, uid_t u) { entry->stat_valid = 0; entry->ae_stat.aest_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(struct archive_entry *entry, const char *name) { aes_copy_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); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_uname, name)); } /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and * 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) { archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name)); } void archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name, size_t len) { 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' && len > 0) aes_copy_wcs_len(&ap->name, name, len); 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.aest_mode &= ~0700; entry->ae_stat.aest_mode |= (permset & 7) << 6; return (0); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: entry->ae_stat.aest_mode &= ~0070; entry->ae_stat.aest_mode |= (permset & 7) << 3; return (0); case ARCHIVE_ENTRY_ACL_OTHER: entry->ae_stat.aest_mode &= ~0007; entry->ae_stat.aest_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.aest_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.aest_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.aest_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.aest_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; *type = 0; *permset = 0; *tag = 0; *id = -1; *name = NULL; return (ARCHIVE_EOF); /* End of ACL entries. */ } *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; size_t 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); else length += sizeof(uid_t) * 3 + 1; 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.aest_mode & 0700, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, entry->ae_stat.aest_mode & 0070, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, entry->ae_stat.aest_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 < 0) id = 0; 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; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ 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); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id_w(wp, id); id = -1; } *(*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) { struct { const wchar_t *start; const wchar_t *end; } field[4]; int fields; int type, tag, permset, id; const wchar_t *p; wchar_t sep; while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); if (fields < 4) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == L':'); if (fields < 3) return (ARCHIVE_WARN); /* Check for a numeric ID in field 1 or 3. */ id = -1; isint_w(field[1].start, field[1].end, &id); /* Field 3 is optional. */ if (id == -1 && fields > 3) isint_w(field[3].start, field[3].end, &id); /* Parse the permissions from field 2. */ permset = 0; p = field[2].start; while (p < field[2].end) { switch (*p++) { 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: return (ARCHIVE_WARN); } } /* * Solaris extension: "defaultuser::rwx" is the * default ACL corresponding to "user::rwx", etc. */ if (field[0].end-field[0].start > 7 && wmemcmp(field[0].start, L"default", 7) == 0) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; field[0].start += 7; } else type = default_type; if (prefix_w(field[0].start, field[0].end, L"user")) { if (id != -1 || field[1].start < field[1].end) tag = ARCHIVE_ENTRY_ACL_USER; else tag = ARCHIVE_ENTRY_ACL_USER_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"group")) { if (id != -1 || field[1].start < field[1].end) tag = ARCHIVE_ENTRY_ACL_GROUP; else tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"other")) { if (id != -1 || field[1].start < field[1].end) return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_OTHER; } else if (prefix_w(field[0].start, field[0].end, L"mask")) { if (id != -1 || field[1].start < field[1].end) return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_MASK; } else return (ARCHIVE_WARN); /* Add entry to the internal list. */ archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, field[1].start, field[1].end - field[1].start); } return (ARCHIVE_OK); } /* * extended attribute handling */ void archive_entry_xattr_clear(struct archive_entry *entry) { struct ae_xattr *xp; while (entry->xattr_head != NULL) { xp = entry->xattr_head->next; free(entry->xattr_head->name); free(entry->xattr_head->value); free(entry->xattr_head); entry->xattr_head = xp; } entry->xattr_head = NULL; } void archive_entry_xattr_add_entry(struct archive_entry *entry, const char *name, const void *value, size_t size) { struct ae_xattr *xp; for (xp = entry->xattr_head; xp != NULL; xp = xp->next) ; if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL) /* XXX Error XXX */ return; xp->name = strdup(name); if ((xp->value = malloc(size)) != NULL) { memcpy(xp->value, value, size); xp->size = size; } else xp->size = 0; xp->next = entry->xattr_head; entry->xattr_head = xp; } /* * returns number of the extended attribute entries */ int archive_entry_xattr_count(struct archive_entry *entry) { struct ae_xattr *xp; int count = 0; for (xp = entry->xattr_head; xp != NULL; xp = xp->next) count++; return count; } int archive_entry_xattr_reset(struct archive_entry * entry) { entry->xattr_p = entry->xattr_head; return archive_entry_xattr_count(entry); } int archive_entry_xattr_next(struct archive_entry * entry, const char **name, const void **value, size_t *size) { if (entry->xattr_p) { *name = entry->xattr_p->name; *value = entry->xattr_p->value; *size = entry->xattr_p->size; entry->xattr_p = entry->xattr_p->next; return (ARCHIVE_OK); } else { *name = NULL; *value = NULL; *size = (size_t)0; return (ARCHIVE_WARN); } } /* * end of xattr handling */ /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint_w(const wchar_t *start, const wchar_t *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10)) n = INT_MAX; else { n *= 10; n += *start - '0'; } start++; } *result = n; return (1); } /* * 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)++; } /* * Return true if the characters [start...end) are a prefix of 'test'. * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. */ 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. */ static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; const char *sp; unsigned long bits; struct flag *flag; size_t 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); } /* * strtofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const char * ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) { const char *start, *end; struct flag *flag; unsigned long set, clear; const char *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; for (flag = flags; flag->name != NULL; flag++) { if (memcmp(start, flag->name, end - start) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (memcmp(start, flag->name + 2, end - start) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->name == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == '\t' || *start == ' ' || *start == ',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } /* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const 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); } #ifdef TEST #include int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); } #endif diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h index 93925a94bd50..a2748fdff67a 100644 --- a/lib/libarchive/archive_entry.h +++ b/lib/libarchive/archive_entry.h @@ -1,500 +1,500 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED /* * Note: archive_entry.h is for use outside of libarchive; the * configuration headers (config.h, archive_platform.h, etc.) are * purely internal. Do NOT use HAVE_XXX configuration macros to * control the behavior of this header! If you must conditionalize, * use predefined compiler and/or platform macros. */ #include #include /* for wchar_t */ #include /* Get appropriate definitions of standard POSIX-style types. */ /* These should match the types used in 'struct stat' */ -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #define __LA_INT64_T __int64 #define __LA_UID_T unsigned int #define __LA_GID_T unsigned int #define __LA_DEV_T unsigned int #define __LA_MODE_T unsigned short #else #include #define __LA_INT64_T int64_t #define __LA_UID_T uid_t #define __LA_GID_T gid_t #define __LA_DEV_T dev_t #define __LA_MODE_T mode_t #endif /* * XXX Is this defined for all Windows compilers? If so, in what * header? It would be nice to remove the __LA_INO_T indirection and * just use plain ino_t everywhere. Likewise for the other types just * above. */ #define __LA_INO_T ino_t /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ -#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC) +#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL __attribute__((dllimport)) extern # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries on all platforms and shared libraries on non-Windows. */ # define __LA_DECL #endif #ifdef __cplusplus extern "C" { #endif /* * Description of an archive entry. * * You can think of this as "struct stat" with some text fields added in. * * TODO: Add "comment", "charset", and possibly other entries that are * supported by "pax interchange" format. However, GNU, ustar, cpio, * 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; /* * File-type constants. These are returned from archive_entry_filetype() * and passed to archive_entry_set_filetype(). * * These values match S_XXX defines on every platform I've checked, * including Windows, AIX, Linux, Solaris, and BSD. They're * (re)defined here because platforms generally don't define the ones * they don't support. For example, Windows doesn't define S_IFLNK or * S_IFBLK. Instead of having a mass of conditional logic and system * checks to define any S_XXX values that aren't supported locally, * I've just defined a new set of such constants so that * libarchive-based applications can manipulate and identify archive * entries properly even if the hosting platform can't store them on * disk. * * These values are also used directly within some portable formats, * such as cpio. If you find a platform that varies from these, the * correct solution is to leave these alone and translate from these * portable values to platform-native values when entries are read from * or written to disk. */ #define AE_IFMT 0170000 #define AE_IFREG 0100000 #define AE_IFLNK 0120000 #define AE_IFSOCK 0140000 #define AE_IFCHR 0020000 #define AE_IFBLK 0060000 #define AE_IFDIR 0040000 #define AE_IFIFO 0010000 /* * Basic object manipulation */ __LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *); /* The 'clone' function does a deep copy; all of the strings are copied too. */ __LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *); __LA_DECL void archive_entry_free(struct archive_entry *); __LA_DECL struct archive_entry *archive_entry_new(void); /* * Retrieve fields from an archive_entry. * * There are a number of implicit conversions among these fields. For * example, if a regular string field is set and you read the _w wide * character field, the entry will implicitly convert narrow-to-wide * using the current locale. Similarly, dev values are automatically * updated when you write devmajor or devminor and vice versa. * * In addition, fields can be "set" or "unset." Unset string fields * return NULL, non-string fields have _is_set() functions to test * whether they've been set. You can "unset" a string field by * assigning NULL; non-string fields have _unset() functions to * unset them. * * Note: There is one ambiguity in the above; string fields will * also return NULL when implicit character set conversions fail. * This is usually what you want. */ __LA_DECL time_t archive_entry_atime(struct archive_entry *); __LA_DECL long archive_entry_atime_nsec(struct archive_entry *); __LA_DECL int archive_entry_atime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_birthtime(struct archive_entry *); __LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_ctime(struct archive_entry *); __LA_DECL long archive_entry_ctime_nsec(struct archive_entry *); __LA_DECL int archive_entry_ctime_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_dev(struct archive_entry *); __LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_devminor(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); __LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); __LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); __LA_DECL __LA_GID_T archive_entry_gid(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); __LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); __LA_DECL time_t archive_entry_mtime(struct archive_entry *); __LA_DECL long archive_entry_mtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_mtime_is_set(struct archive_entry *); __LA_DECL unsigned int archive_entry_nlink(struct archive_entry *); __LA_DECL const char *archive_entry_pathname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); __LA_DECL const char *archive_entry_sourcepath(struct archive_entry *); __LA_DECL __LA_INT64_T archive_entry_size(struct archive_entry *); __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL __LA_UID_T archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); /* * Set fields in an archive_entry. * * Note that string 'set' functions do not copy the string, only the pointer. * In contrast, 'copy' functions do copy the object pointed to. * * Note: As of libarchive 2.4, 'set' functions do copy the string and * are therefore exact synonyms for the 'copy' versions. The 'copy' * names will be retired in libarchive 3.0. */ __LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_atime(struct archive_entry *); __LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_birthtime(struct archive_entry *); __LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_ctime(struct archive_entry *); __LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_fflags(struct archive_entry *, unsigned long /* set */, unsigned long /* clear */); /* Returns pointer to start of first invalid token, or NULL if none. */ /* Note that all recognized tokens are processed, regardless. */ __LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *, const char *); __LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_gid(struct archive_entry *, __LA_GID_T); __LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_ino(struct archive_entry *, unsigned long); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_mtime(struct archive_entry *); __LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_size(struct archive_entry *, __LA_INT64_T); __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_UID_T); __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *); /* * Routines to bulk copy fields to/from a platform-native "struct * stat." Libarchive used to just store a struct stat inside of each * archive_entry object, but this created issues when trying to * manipulate archives on systems different than the ones they were * created on. * * TODO: On Linux, provide both stat32 and stat64 versions of these functions. */ __LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); /* * ACL routines. This used to simply store and return text-format ACL * 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 * * This last point, in particular, forces me to implement a reasonably * complete set of ACL support routines. * * TODO: Extend this to support NFSv4/NTFS permissions. That should * allow full ACL support on Mac OS, in particular, which uses * POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions. */ /* * 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. */ __LA_DECL void archive_entry_acl_clear(struct archive_entry *); __LA_DECL void archive_entry_acl_add_entry(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const char * /* name */); __LA_DECL 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. */ __LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */); __LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const char ** /* name */); __LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const wchar_t ** /* name */); /* * 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 __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int /* flags */); /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); /* * Private ACL parser. This is private because it handles some * 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! * * TODO: Move this declaration out of the public header and into * a private header. Warnings above are silly. */ __LA_DECL int __archive_entry_acl_parse_w(struct archive_entry *, const wchar_t *, int /* type */); /* * extended attributes */ __LA_DECL void archive_entry_xattr_clear(struct archive_entry *); __LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *, const char * /* name */, const void * /* value */, size_t /* size */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_xattr_count(struct archive_entry *); __LA_DECL int archive_entry_xattr_reset(struct archive_entry *); __LA_DECL int archive_entry_xattr_next(struct archive_entry *, const char ** /* name */, const void ** /* value */, size_t *); /* * Utility to match up hardlinks. * * The 'struct archive_entry_linkresolver' is a cache of archive entries * for files with multiple links. Here's how to use it: * 1. Create a lookup object with archive_entry_linkresolver_new() * 2. Tell it the archive format you're using. * 3. Hand each archive_entry to archive_entry_linkify(). * That function will return 0, 1, or 2 entries that should * be written. * 4. Call archive_entry_linkify(resolver, NULL) until * no more entries are returned. * 5. Call archive_entry_link_resolver_free(resolver) to free resources. * * The entries returned have their hardlink and size fields updated * appropriately. If an entry is passed in that does not refer to * a file with multiple links, it is returned unchanged. The intention * is that you should be able to simply filter all entries through * this machine. * * To make things more efficient, be sure that each entry has a valid * nlinks value. The hardlink cache uses this to track when all links * have been found. If the nlinks value is zero, it will keep every * name in the cache indefinitely, which can use a lot of memory. * * Note that archive_entry_size() is reset to zero if the file * body should not be written to the archive. Pay attention! */ struct archive_entry_linkresolver; /* * There are three different strategies for marking hardlinks. * The descriptions below name them after the best-known * formats that rely on each strategy: * * "Old cpio" is the simplest, it always returns any entry unmodified. * As far as I know, only cpio formats use this. Old cpio archives * store every link with the full body; the onus is on the dearchiver * to detect and properly link the files as they are restored. * "tar" is also pretty simple; it caches a copy the first time it sees * any link. Subsequent appearances are modified to be hardlink * references to the first one without any body. Used by all tar * formats, although the newest tar formats permit the "old cpio" strategy * as well. This strategy is very simple for the dearchiver, * and reasonably straightforward for the archiver. * "new cpio" is trickier. It stores the body only with the last * occurrence. The complication is that we might not * see every link to a particular file in a single session, so * there's no easy way to know when we've seen the last occurrence. * The solution here is to queue one link until we see the next. * At the end of the session, you can enumerate any remaining * entries by calling archive_entry_linkify(NULL) and store those * bodies. If you have a file with three links l1, l2, and l3, * you'll get the following behavior if you see all three links: * linkify(l1) => NULL (the resolver stores l1 internally) * linkify(l2) => l1 (resolver stores l2, you write l1) * linkify(l3) => l2, l3 (all links seen, you can write both). * If you only see l1 and l2, you'll get this behavior: * linkify(l1) => NULL * linkify(l2) => l1 * linkify(NULL) => l2 (at end, you retrieve remaining links) * As the name suggests, this strategy is used by newer cpio variants. * It's noticably more complex for the archiver, slightly more complex * for the dearchiver than the tar strategy, but makes it straightforward * to restore a file using any link by simply continuing to scan until * you see a link that is stored with a body. In contrast, the tar * strategy requires you to rescan the archive from the beginning to * correctly extract an arbitrary link. */ __LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void); __LA_DECL void archive_entry_linkresolver_set_strategy( struct archive_entry_linkresolver *, int /* format_code */); __LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *); __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); #ifdef __cplusplus } #endif /* This is meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ diff --git a/lib/libarchive/archive_read_disk_set_standard_lookup.c b/lib/libarchive/archive_read_disk_set_standard_lookup.c index 9c96f1946aed..68e80747f62f 100644 --- a/lib/libarchive/archive_read_disk_set_standard_lookup.c +++ b/lib/libarchive/archive_read_disk_set_standard_lookup.c @@ -1,229 +1,229 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) int archive_read_disk_set_standard_lookup(struct archive *a) { archive_set_error(a, -1, "Standard lookups not available on Windows"); return (ARCHIVE_FATAL); } -#else +#else /* ! (_WIN32 && !__CYGWIN__) */ #define name_cache_size 127 static const char * const NO_NAME = "(noname)"; struct name_cache { struct archive *archive; int probes; int hits; size_t size; struct { id_t id; const char *name; } cache[name_cache_size]; }; static const char * lookup_gname(void *, gid_t); static const char * lookup_uname(void *, uid_t); static void cleanup(void *); static const char * lookup_gname_helper(struct archive *, id_t gid); static const char * lookup_uname_helper(struct archive *, id_t uid); /* * Installs functions that use getpwuid()/getgrgid()---along with * a simple cache to accelerate such lookups---into the archive_read_disk * object. This is in a separate file because getpwuid()/getgrgid() * can pull in a LOT of library code (including NIS/LDAP functions, which * pull in DNS resolveers, etc). This can easily top 500kB, which makes * it inappropriate for some space-constrained applications. * * Applications that are size-sensitive may want to just use the * real default functions (defined in archive_read_disk.c) that just * use the uid/gid without the lookup. Or define your own custom functions * if you prefer. */ int archive_read_disk_set_standard_lookup(struct archive *a) { struct name_cache *ucache = malloc(sizeof(struct name_cache)); struct name_cache *gcache = malloc(sizeof(struct name_cache)); if (ucache == NULL || gcache == NULL) { archive_set_error(a, ENOMEM, "Can't allocate uname/gname lookup cache"); free(ucache); free(gcache); return (ARCHIVE_FATAL); } memset(ucache, 0, sizeof(*ucache)); ucache->archive = a; ucache->size = name_cache_size; memset(gcache, 0, sizeof(*gcache)); gcache->archive = a; gcache->size = name_cache_size; archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup); archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup); return (ARCHIVE_OK); } static void cleanup(void *data) { struct name_cache *cache = (struct name_cache *)data; size_t i; if (cache != NULL) { for (i = 0; i < cache->size; i++) { if (cache->cache[i].name != NULL && cache->cache[i].name != NO_NAME) free((void *)(uintptr_t)cache->cache[i].name); } free(cache); } } /* * Lookup uid/gid from uname/gname, return NULL if no match. */ static const char * lookup_name(struct name_cache *cache, const char * (*lookup_fn)(struct archive *, id_t), id_t id) { const char *name; int slot; cache->probes++; slot = id % cache->size; if (cache->cache[slot].name != NULL) { if (cache->cache[slot].id == id) { cache->hits++; if (cache->cache[slot].name == NO_NAME) return (NULL); return (cache->cache[slot].name); } if (cache->cache[slot].name != NO_NAME) free((void *)(uintptr_t)cache->cache[slot].name); cache->cache[slot].name = NULL; } name = (lookup_fn)(cache->archive, id); if (name == NULL) { /* Cache and return the negative response. */ cache->cache[slot].name = NO_NAME; cache->cache[slot].id = id; return (NULL); } cache->cache[slot].name = name; cache->cache[slot].id = id; return (cache->cache[slot].name); } static const char * lookup_uname(void *data, uid_t uid) { struct name_cache *uname_cache = (struct name_cache *)data; return (lookup_name(uname_cache, &lookup_uname_helper, (id_t)uid)); } static const char * lookup_uname_helper(struct archive *a, id_t id) { char buffer[512]; struct passwd pwent, *result; int r; errno = 0; r = getpwuid_r((uid_t)id, &pwent, buffer, sizeof(buffer), &result); if (r != 0) { archive_set_error(a, errno, "Can't lookup user for id %d", (int)id); return (NULL); } if (result == NULL) return (NULL); return strdup(pwent.pw_name); } static const char * lookup_gname(void *data, gid_t gid) { struct name_cache *gname_cache = (struct name_cache *)data; return (lookup_name(gname_cache, &lookup_gname_helper, (id_t)gid)); } static const char * lookup_gname_helper(struct archive *a, id_t id) { char buffer[512]; struct group grent, *result; int r; errno = 0; r = getgrgid_r((gid_t)id, &grent, buffer, sizeof(buffer), &result); if (r != 0) { archive_set_error(a, errno, "Can't lookup group for id %d", (int)id); return (NULL); } if (result == NULL) return (NULL); return strdup(grent.gr_name); } -#endif /* _WIN32 */ +#endif /* ! (_WIN32 && !__CYGWIN__) */ diff --git a/lib/libarchive/archive_read_support_compression_program.c b/lib/libarchive/archive_read_support_compression_program.c index 44632558cfa7..d405e3b78d56 100644 --- a/lib/libarchive/archive_read_support_compression_program.c +++ b/lib/libarchive/archive_read_support_compression_program.c @@ -1,390 +1,390 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" int archive_read_support_compression_program(struct archive *a, const char *cmd) { return (archive_read_support_compression_program_signature(a, cmd, NULL, 0)); } /* This capability is only available on POSIX systems. */ #if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ - !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && !defined(_WIN32) + !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) /* * On non-Posix systems, allow the program to build, but choke if * this function is actually invoked. */ int archive_read_support_compression_program_signature(struct archive *_a, const char *cmd, void *signature, size_t signature_len) { (void)_a; /* UNUSED */ (void)cmd; /* UNUSED */ (void)signature; /* UNUSED */ (void)signature_len; /* UNUSED */ archive_set_error(_a, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } int __archive_read_program(struct archive_read_filter *self, const char *cmd) { (void)self; /* UNUSED */ (void)cmd; /* UNUSED */ archive_set_error(&self->archive->archive, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } #else #include "filter_fork.h" /* * The bidder object stores the command and the signature to watch for. * The 'inhibit' entry here is used to ensure that unchecked filters never * bid twice in the same pipeline. */ struct program_bidder { char *cmd; void *signature; size_t signature_len; int inhibit; }; static int program_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *upstream); static int program_bidder_init(struct archive_read_filter *); static int program_bidder_free(struct archive_read_filter_bidder *); /* * The actual filter needs to track input and output data. */ struct program_filter { char *description; pid_t child; int child_stdin, child_stdout; char *out_buf; size_t out_buf_len; }; static ssize_t program_filter_read(struct archive_read_filter *, const void **); static int program_filter_close(struct archive_read_filter *); int archive_read_support_compression_program_signature(struct archive *_a, const char *cmd, const void *signature, size_t signature_len) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder; struct program_bidder *state; /* * Get a bidder object from the read core. */ bidder = __archive_read_get_bidder(a); if (bidder == NULL) return (ARCHIVE_FATAL); /* * Allocate our private state. */ state = (struct program_bidder *)calloc(sizeof (*state), 1); if (state == NULL) return (ARCHIVE_FATAL); state->cmd = strdup(cmd); if (signature != NULL && signature_len > 0) { state->signature_len = signature_len; state->signature = malloc(signature_len); memcpy(state->signature, signature, signature_len); } /* * Fill in the bidder object. */ bidder->data = state; bidder->bid = program_bidder_bid; bidder->init = program_bidder_init; bidder->options = NULL; bidder->free = program_bidder_free; return (ARCHIVE_OK); } static int program_bidder_free(struct archive_read_filter_bidder *self) { struct program_bidder *state = (struct program_bidder *)self->data; free(state->cmd); free(state->signature); free(self->data); return (ARCHIVE_OK); } /* * If we do have a signature, bid only if that matches. * * If there's no signature, we bid INT_MAX the first time * we're called, then never bid again. */ static int program_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *upstream) { struct program_bidder *state = self->data; const char *p; /* If we have a signature, use that to match. */ if (state->signature_len > 0) { p = __archive_read_filter_ahead(upstream, state->signature_len, NULL); if (p == NULL) return (0); /* No match, so don't bid. */ if (memcmp(p, state->signature, state->signature_len) != 0) return (0); return (state->signature_len * 8); } /* Otherwise, bid once and then never bid again. */ if (state->inhibit) return (0); state->inhibit = 1; return (INT_MAX); } /* * Use select() to decide whether the child is ready for read or write. */ static ssize_t child_read(struct archive_read_filter *self, char *buf, size_t buf_len) { struct program_filter *state = self->data; ssize_t ret, requested, avail; const char *p; requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; for (;;) { do { ret = read(state->child_stdout, buf, requested); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(state->child_stdout); state->child_stdout = -1; return (0); } if (ret == -1 && errno != EAGAIN) return (-1); if (state->child_stdin == -1) { /* Block until child has some I/O ready. */ __archive_check_child(state->child_stdin, state->child_stdout); continue; } /* Get some more data from upstream. */ p = __archive_read_filter_ahead(self->upstream, 1, &avail); if (p == NULL) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); if (avail < 0) return (avail); continue; } do { ret = write(state->child_stdin, p, avail); } while (ret == -1 && errno == EINTR); if (ret > 0) { /* Consume whatever we managed to write. */ __archive_read_filter_consume(self->upstream, ret); } else if (ret == -1 && errno == EAGAIN) { /* Block until child has some I/O ready. */ __archive_check_child(state->child_stdin, state->child_stdout); } else { /* Write failed. */ close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); /* If it was a bad error, we're done; otherwise * it was EPIPE or EOF, and we can still read * from the child. */ if (ret == -1 && errno != EPIPE) return (-1); } } } int __archive_read_program(struct archive_read_filter *self, const char *cmd) { struct program_filter *state; static const size_t out_buf_len = 65536; char *out_buf; char *description; const char *prefix = "Program: "; state = (struct program_filter *)calloc(1, sizeof(*state)); out_buf = (char *)malloc(out_buf_len); description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); if (state == NULL || out_buf == NULL || description == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate input data"); free(state); free(out_buf); free(description); return (ARCHIVE_FATAL); } self->code = ARCHIVE_COMPRESSION_PROGRAM; state->description = description; strcpy(state->description, prefix); strcat(state->description, cmd); self->name = state->description; state->out_buf = out_buf; state->out_buf_len = out_buf_len; if ((state->child = __archive_create_child(cmd, &state->child_stdin, &state->child_stdout)) == -1) { free(state->out_buf); free(state); archive_set_error(&self->archive->archive, EINVAL, "Can't initialise filter"); return (ARCHIVE_FATAL); } self->data = state; self->read = program_filter_read; self->skip = NULL; self->close = program_filter_close; /* XXX Check that we can read at least one byte? */ return (ARCHIVE_OK); } static int program_bidder_init(struct archive_read_filter *self) { struct program_bidder *bidder_state; bidder_state = (struct program_bidder *)self->bidder->data; return (__archive_read_program(self, bidder_state->cmd)); } static ssize_t program_filter_read(struct archive_read_filter *self, const void **buff) { struct program_filter *state; ssize_t bytes; size_t total; char *p; state = (struct program_filter *)self->data; total = 0; p = state->out_buf; while (state->child_stdout != -1 && total < state->out_buf_len) { bytes = child_read(self, p, state->out_buf_len - total); if (bytes < 0) return (bytes); if (bytes == 0) break; total += bytes; p += bytes; } *buff = state->out_buf; return (total); } static int program_filter_close(struct archive_read_filter *self) { struct program_filter *state; int status; state = (struct program_filter *)self->data; /* Shut down the child. */ if (state->child_stdin != -1) close(state->child_stdin); if (state->child_stdout != -1) close(state->child_stdout); while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) continue; /* Release our private data. */ free(state->out_buf); free(state->description); free(state); return (ARCHIVE_OK); } #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/archive_string.c b/lib/libarchive/archive_string.c index 120c12783feb..111dcb1291de 100644 --- a/lib/libarchive/archive_string.c +++ b/lib/libarchive/archive_string.c @@ -1,448 +1,455 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* * Basic resizable string support, to simplify manipulating arbitrary-sized * strings while minimizing heap activity. */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #include #endif #include "archive_private.h" #include "archive_string.h" struct archive_string * __archive_string_append(struct archive_string *as, const char *p, size_t s) { if (__archive_string_ensure(as, as->length + s + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(as->s + as->length, p, s); as->s[as->length + s] = 0; as->length += s; return (as); } void __archive_string_copy(struct archive_string *dest, struct archive_string *src) { if (src->length == 0) dest->length = 0; else { if (__archive_string_ensure(dest, src->length + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(dest->s, src->s, src->length); dest->length = src->length; dest->s[dest->length] = 0; } } void __archive_string_concat(struct archive_string *dest, struct archive_string *src) { if (src->length > 0) { if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(dest->s + dest->length, src->s, src->length); dest->length += src->length; dest->s[dest->length] = 0; } } void __archive_string_free(struct archive_string *as) { as->length = 0; as->buffer_length = 0; if (as->s != NULL) { free(as->s); as->s = NULL; } } /* Returns NULL on any allocation failure. */ struct archive_string * __archive_string_ensure(struct archive_string *as, size_t s) { /* If buffer is already big enough, don't reallocate. */ if (as->s && (s <= as->buffer_length)) return (as); /* * Growing the buffer at least exponentially ensures that * append operations are always linear in the number of * characters appended. Using a smaller growth rate for * larger buffers reduces memory waste somewhat at the cost of * a larger constant factor. */ if (as->buffer_length < 32) /* Start with a minimum 32-character buffer. */ as->buffer_length = 32; else if (as->buffer_length < 8192) /* Buffers under 8k are doubled for speed. */ as->buffer_length += as->buffer_length; else { /* Buffers 8k and over grow by at least 25% each time. */ size_t old_length = as->buffer_length; as->buffer_length += as->buffer_length / 4; /* Be safe: If size wraps, release buffer and return NULL. */ if (as->buffer_length < old_length) { free(as->s); as->s = NULL; return (NULL); } } /* * The computation above is a lower limit to how much we'll * grow the buffer. In any case, we have to grow it enough to * hold the request. */ if (as->buffer_length < s) as->buffer_length = s; /* Now we can reallocate the buffer. */ as->s = (char *)realloc(as->s, as->buffer_length); if (as->s == NULL) return (NULL); return (as); } struct archive_string * __archive_strncat(struct archive_string *as, const void *_p, size_t n) { size_t s; const char *p, *pp; p = (const char *)_p; /* 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)); } -#ifndef _WIN32 /* - * Home-grown wctomb for UTF-8. + * Translates a wide character string into UTF-8 and appends + * to the archive_string. Note: returns NULL if conversion fails, + * but still leaves a best-effort conversion in the argument as. */ -static int -my_wctomb_utf8(char *p, wchar_t wc) -{ - if (p == NULL) - /* UTF-8 doesn't use shift states. */ - return (0); - if (wc <= 0x7f) { - p[0] = (char)wc; - return (1); - } - if (wc <= 0x7ff) { - p[0] = 0xc0 | ((wc >> 6) & 0x1f); - p[1] = 0x80 | (wc & 0x3f); - return (2); - } - if (wc <= 0xffff) { - p[0] = 0xe0 | ((wc >> 12) & 0x0f); - p[1] = 0x80 | ((wc >> 6) & 0x3f); - p[2] = 0x80 | (wc & 0x3f); - return (3); - } - if (wc <= 0x1fffff) { - p[0] = 0xf0 | ((wc >> 18) & 0x07); - p[1] = 0x80 | ((wc >> 12) & 0x3f); - p[2] = 0x80 | ((wc >> 6) & 0x3f); - p[3] = 0x80 | (wc & 0x3f); - return (4); - } - /* Unicode has no codes larger than 0x1fffff. */ - /* - * Awkward point: UTF-8 <-> wchar_t conversions - * can actually fail. - */ - return (-1); -} - -static int -my_wcstombs(struct archive_string *as, const wchar_t *w, - int (*func)(char *, wchar_t)) +struct archive_string * +__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) { - int n; char *p; + unsigned wc; char buff[256]; - - /* Clear the shift state before starting. */ - (*func)(NULL, L'\0'); + struct archive_string *return_val = as; /* * Convert one wide char at a time into 'buff', whenever that * fills, append it to the string. */ p = buff; while (*w != L'\0') { /* Flush the buffer when we have <=16 bytes free. */ /* (No encoding has a single character >16 bytes.) */ if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) { *p = '\0'; archive_strcat(as, buff); p = buff; } - n = (*func)(p, *w++); - if (n == -1) - return (-1); - p += n; + wc = *w++; + /* If this is a surrogate pair, assemble the full code point.*/ + /* Note: wc must not be wchar_t here, because the full code + * point can be more than 16 bits! */ + if (wc >= 0xD800 && wc <= 0xDBff + && *w >= 0xDC00 && *w <= 0xDFFF) { + wc -= 0xD800; + wc *= 0x400; + wc += (*w - 0xDC00); + wc += 0x10000; + ++w; + } + /* Translate code point to UTF8 */ + if (wc <= 0x7f) { + *p++ = (char)wc; + } else if (wc <= 0x7ff) { + *p++ = 0xc0 | ((wc >> 6) & 0x1f); + *p++ = 0x80 | (wc & 0x3f); + } else if (wc <= 0xffff) { + *p++ = 0xe0 | ((wc >> 12) & 0x0f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | (wc & 0x3f); + } else if (wc <= 0x1fffff) { + *p++ = 0xf0 | ((wc >> 18) & 0x07); + *p++ = 0x80 | ((wc >> 12) & 0x3f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | (wc & 0x3f); + } else { + /* Unicode has no codes larger than 0x1fffff. */ + /* TODO: use \uXXXX escape here instead of ? */ + *p++ = '?'; + return_val = NULL; + } } *p = '\0'; archive_strcat(as, buff); - return (0); + return (return_val); } -/* - * Translates a wide character string into UTF-8 and appends - * to the archive_string. Note: returns NULL if conversion fails. - */ -struct archive_string * -__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) -{ - if (my_wcstombs(as, w, my_wctomb_utf8)) - return (NULL); - return (as); -} - -/* - * Translates a wide character string into current locale character set - * and appends to the archive_string. Note: returns NULL if conversion - * fails. - */ -struct archive_string * -__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) -{ -#if HAVE_WCTOMB - if (my_wcstombs(as, w, wctomb)) - return (NULL); -#else - /* TODO: Can we do better than this? Are there platforms - * that have locale support but don't have wctomb()? */ - if (my_wcstombs(as, w, my_wctomb_utf8)) - return (NULL); -#endif - return (as); -} - - -/* - * Home-grown mbtowc for UTF-8. Some systems lack UTF-8 - * (or even lack mbtowc()) and we need UTF-8 support for pax - * format. So please don't replace this with a call to the - * standard mbtowc() function! - */ static int -my_mbtowc_utf8(wchar_t *pwc, const char *s, size_t n) +utf8_to_unicode(int *pwc, const char *s, size_t n) { int ch; - /* Standard behavior: a NULL value for 's' just resets shift state. */ - if (s == NULL) - return (0); - /* If length argument is zero, don't look at the first character. */ - if (n <= 0) - return (-1); - /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) { return (0); /* Standard: return 0 for end-of-string. */ } if ((ch & 0x80) == 0) { *pwc = ch & 0x7f; return (1); } if ((ch & 0xe0) == 0xc0) { if (n < 2) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (2); } if ((ch & 0xf0) == 0xe0) { if (n < 3) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); return (3); } if ((ch & 0xf8) == 0xf0) { if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); return (4); } /* Invalid first byte. */ return (-1); } /* - * Return a wide-character string by converting this archive_string - * from UTF-8. + * Return a wide-character Unicode string by converting this archive_string + * from UTF-8. We assume that systems with 16-bit wchar_t always use + * UTF16 and systems with 32-bit wchar_t can accept UCS4. */ wchar_t * __archive_string_utf8_w(struct archive_string *as) { wchar_t *ws, *dest; + int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */ const char *src; int n; int err; ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t)); if (ws == NULL) __archive_errx(1, "Out of memory"); err = 0; dest = ws; src = as->s; while (*src != '\0') { - n = my_mbtowc_utf8(dest, src, 8); + n = utf8_to_unicode(&wc, src, 8); if (n == 0) break; if (n < 0) { free(ws); return (NULL); } - dest++; src += n; + if (wc >= 0xDC00 && wc <= 0xDBFF) { + /* This is a leading surrogate; some idiot + * has translated UTF16 to UTF8 without combining + * surrogates; rebuild the full code point before + * continuing. */ + n = utf8_to_unicode(&wc2, src, 8); + if (n < 0) { + free(ws); + return (NULL); + } + if (n == 0) /* Ignore the leading surrogate */ + break; + if (wc2 < 0xDC00 || wc2 > 0xDFFF) { + /* If the second character isn't a + * trailing surrogate, then someone + * has really screwed up and this is + * invalid. */ + free(ws); + return (NULL); + } else { + src += n; + wc -= 0xD800; + wc *= 0x400; + wc += wc2 - 0xDC00; + wc += 0x10000; + } + } + if ((sizeof(wchar_t) < 4) && (wc > 0xffff)) { + /* We have a code point that won't fit into a + * wchar_t; convert it to a surrogate pair. */ + wc -= 0x10000; + *dest++ = ((wc >> 10) & 0x3ff) + 0xD800; + *dest++ = (wc & 0x3ff) + 0xDC00; + } else + *dest++ = wc; } *dest++ = L'\0'; return (ws); } -#else +#if defined(_WIN32) && !defined(__CYGWIN__) -static struct archive_string * -my_archive_strappend_w(struct archive_string *as, - unsigned int codepage, const wchar_t *w) +/* + * Translates a wide character string into current locale character set + * and appends to the archive_string. Note: returns NULL if conversion + * fails. + * + * Win32 builds use WideCharToMultiByte from the Windows API. + * (Maybe Cygwin should too? WideCharToMultiByte will know a + * lot more about local character encodings than the wcrtomb() + * wrapper is going to know.) + */ +struct archive_string * +__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) { char *p; int l, wl; BOOL useDefaultChar = FALSE; wl = (int)wcslen(w); l = wl * 4 + 4; p = malloc(l); if (p == NULL) __archive_errx(1, "Out of memory"); /* To check a useDefaultChar is to simulate error handling of * the my_wcstombs() which is running on non Windows system with * wctomb(). * And to set NULL for last argument is necessary when a codepage * is not CP_ACP(current locale). */ - l = WideCharToMultiByte(codepage, 0, w, wl, p, l, NULL, - (codepage == CP_ACP) ? &useDefaultChar : NULL); - if (l == 0 || useDefaultChar) { + l = WideCharToMultiByte(CP_ACP, 0, w, wl, p, l, NULL, &useDefaultChar); + if (l == 0) { free(p); return (NULL); } __archive_string_append(as, p, l); free(p); return (as); } -/* - * Translates a wide character string into UTF-8 and appends - * to the archive_string. Note: returns NULL if conversion fails. - */ -struct archive_string * -__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) -{ - - return (my_archive_strappend_w(as, CP_UTF8, w)); -} +#else /* * Translates a wide character string into current locale character set * and appends to the archive_string. Note: returns NULL if conversion * fails. + * + * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion + * one character at a time. If a non-Windows platform doesn't have + * either of these, fall back to the built-in UTF8 conversion. */ struct archive_string * __archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) { - - return (my_archive_strappend_w(as, CP_ACP, w)); -} - -/* - * Return a wide-character string by converting this archive_string - * from UTF-8. - */ -wchar_t * -__archive_string_utf8_w(struct archive_string *as) -{ - wchar_t *ws; +#if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB) + /* If there's no built-in locale support, fall back to UTF8 always. */ + return __archive_strappend_w_utf8(as, w); +#else + /* We cannot use the standard wcstombs() here because it + * cannot tell us how big the output buffer should be. So + * I've built a loop around wcrtomb() or wctomb() that + * converts a character at a time and resizes the string as + * needed. We prefer wcrtomb() when it's available because + * it's thread-safe. */ int n; + char *p; + char buff[256]; +#if HAVE_WCRTOMB + mbstate_t shift_state; - ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t)); - if (ws == NULL) - __archive_errx(1, "Out of memory"); - n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - as->s, (int)as->length, ws, (int)as->length); - if (n == 0) { - free(ws); - return (NULL); + memset(&shift_state, 0, sizeof(shift_state)); +#else + /* Clear the shift state before starting. */ + wctomb(NULL, L'\0'); +#endif + + /* + * Convert one wide char at a time into 'buff', whenever that + * fills, append it to the string. + */ + p = buff; + while (*w != L'\0') { + /* Flush the buffer when we have <=16 bytes free. */ + /* (No encoding has a single character >16 bytes.) */ + if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - MB_CUR_MAX)) { + *p = '\0'; + archive_strcat(as, buff); + p = buff; + } +#if HAVE_WCRTOMB + n = wcrtomb(p, *w++, &shift_state); +#else + n = wctomb(p, *w++); +#endif + if (n == -1) + return (NULL); + p += n; } - ws[n] = L'\0'; - return (ws); + *p = '\0'; + archive_strcat(as, buff); + return (as); +#endif } -#endif /* !_WIN32 */ +#endif /* _WIN32 && ! __CYGWIN__ */ diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c index 0d841561ee55..60fa227ad586 100644 --- a/lib/libarchive/archive_write_disk.c +++ b/lib/libarchive/archive_write_disk.c @@ -1,2567 +1,2575 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * 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. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif +#ifdef HAVE_SYS_XATTR_H +#include +#endif #ifdef HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_UTIME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PWD_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_UTIME_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct fixup_entry { struct fixup_entry *next; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long fflags_set; int fixup; /* bitmask of what needs fixing */ char *name; }; /* * We use a bitmask to track which operations remain to be done for * this file. In particular, this helps us avoid unnecessary * operations when it's possible to take care of one step as a * side-effect of another. For example, mkdir() can specify the mode * for the newly-created object but symlink() cannot. This means we * can skip chmod() if mkdir() succeeded, but we must explicitly * chmod() if we're trying to create a directory that already exists * (mkdir() failed) or if we're restoring a symlink. Similarly, we * need to verify UID/GID before trying to restore SUID/SGID bits; * that verification can occur explicitly through a stat() call or * implicitly because of a successful chown() call. */ #define TODO_MODE_FORCE 0x40000000 #define TODO_MODE_BASE 0x20000000 #define TODO_SUID 0x10000000 #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 #define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) #define TODO_TIMES ARCHIVE_EXTRACT_TIME #define TODO_OWNER ARCHIVE_EXTRACT_OWNER #define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS #define TODO_ACLS ARCHIVE_EXTRACT_ACL #define TODO_XATTR ARCHIVE_EXTRACT_XATTR struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; uid_t user_uid; dev_t skip_file_dev; ino_t skip_file_ino; time_t start_time; gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; uid_t (*lookup_uid)(void *private, const char *gname, gid_t gid); void (*cleanup_uid)(void *private); void *lookup_uid_data; /* * Full path of last file to satisfy symlink checks. */ struct archive_string path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ struct stat st; struct stat *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ char *name; /* Name of entry, possibly edited. */ struct archive_string _name_data; /* backing store for 'name' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ int deferred; /* Options requested by the client. */ int flags; /* Handle for the file we're restoring. */ int fd; /* Current offset for writing data to the file. */ off_t offset; /* Last offset actually written to disk. */ off_t fd_offset; /* Maximum size of file, -1 if unknown. */ off_t filesize; /* Dir we were in before this restore; only for deep paths. */ int restore_pwd; /* Mode we should use for this entry; affected by _PERM and umask. */ mode_t mode; /* UID/GID to use in restoring this entry. */ uid_t uid; gid_t gid; }; /* * Default mode for dirs created automatically (will be modified by umask). * Note that POSIX specifies 0777 for implicity-created dirs, "modified * by the process' file creation mask." */ #define DEFAULT_DIR_MODE 0777 /* * Dir modes are restored in two steps: During the extraction, the permissions * in the archive are modified to match the following limits. During * the post-extract fixup pass, the permissions from the archive are * applied. */ #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); #ifdef HAVE_FCHDIR static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); #ifdef HAVE_POSIX_ACL static int set_acl(struct archive_write_disk *, int fd, struct archive_entry *, acl_type_t, int archive_entry_acl_type, const char *tn); #endif static int set_acls(struct archive_write_disk *); static int set_xattrs(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_fflags_platform(struct archive_write_disk *, int fd, const char *name, mode_t mode, unsigned long fflags_set, unsigned long fflags_clear); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_time(int, int, const char *, time_t, long, time_t, long); static int set_times(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static gid_t trivial_lookup_gid(void *, const char *, gid_t); static uid_t trivial_lookup_uid(void *, const char *, uid_t); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); static struct archive_vtable *archive_write_disk_vtable(void); static int _archive_write_close(struct archive *); static int _archive_write_finish(struct archive *); static int _archive_write_header(struct archive *, struct archive_entry *); static int _archive_write_finish_entry(struct archive *); static ssize_t _archive_write_data(struct archive *, const void *, size_t); static ssize_t _archive_write_data_block(struct archive *, const void *, size_t, off_t); static int _archive_write_disk_lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ return (ARCHIVE_OK); } #ifdef HAVE_FSTAT if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } #endif /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occured. Do we want to check explicitly for that? */ if (lstat(a->name, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static struct archive_vtable * archive_write_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_close; av.archive_finish = _archive_write_finish; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; av.archive_write_data = _archive_write_data; av.archive_write_data_block = _archive_write_data_block; } return (&av); } int archive_write_disk_set_options(struct archive *_a, int flags) { struct archive_write_disk *a = (struct archive_write_disk *)_a; a->flags = flags; return (ARCHIVE_OK); } /* * Extract this entry to disk. * * TODO: Validate hardlinks. According to the standards, we're * supposed to check each extracted hardlink and squawk if it refers * to a file that we didn't restore. I'm not entirely convinced this * is a good idea, but more importantly: Is there any way to validate * hardlinks without keeping a complete list of filenames from the * entire archive?? Ugh. * */ static int _archive_write_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; int ret, r; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_header"); archive_clear_error(&a->archive); if (a->archive.state & ARCHIVE_STATE_DATA) { r = _archive_write_finish_entry(&a->archive); if (r == ARCHIVE_FATAL) return (r); } /* Set up for this particular entry. */ a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->entry = archive_entry_clone(entry); a->fd = -1; a->fd_offset = 0; a->offset = 0; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); if (archive_entry_size_is_set(a->entry)) a->filesize = archive_entry_size(a->entry); else a->filesize = -1; archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry)); a->name = a->_name_data.s; archive_clear_error(&a->archive); /* * Clean up the requested path. This is necessary for correct * dir restores; the dir restore logic otherwise gets messed * up by nonsense like "dir/.". */ ret = cleanup_pathname(a); if (ret != ARCHIVE_OK) return (ret); /* * Set the umask to zero so we get predictable mode settings. * This gets done on every call to _write_header in case the * user edits their umask during the extraction for some * reason. This will be reset before we return. Note that we * don't need to do this in _finish_entry, as the chmod(), etc, * system calls don't obey umask. */ a->user_umask = umask(0); /* From here on, early exit requires "goto done" to clean up. */ /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; if (a->flags & ARCHIVE_EXTRACT_PERM) { a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */ /* * SGID requires an extra "check" step because we * cannot easily predict the GID that the system will * assign. (Different systems assign GIDs to files * based on a variety of criteria, including process * credentials and the gid of the enclosing * directory.) We can only restore the SGID bit if * the file has the right GID, and we only know the * GID if we either set it (see set_ownership) or if * we've actually called stat() on the file after it * was restored. Since there are several places at * which we might verify the GID, we need a TODO bit * to keep track. */ if (a->mode & S_ISGID) a->todo |= TODO_SGID | TODO_SGID_CHECK; /* * Verifying the SUID is simpler, but can still be * done in multiple ways, hence the separate "check" bit. */ if (a->mode & S_ISUID) a->todo |= TODO_SUID | TODO_SUID_CHECK; } else { /* * User didn't request full permissions, so don't * restore SUID, SGID bits and obey umask. */ a->mode &= ~S_ISUID; a->mode &= ~S_ISGID; a->mode &= ~S_ISVTX; a->mode &= ~a->user_umask; } -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; #endif if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) a->todo |= TODO_ACLS; if (a->flags & ARCHIVE_EXTRACT_XATTR) a->todo |= TODO_XATTR; if (a->flags & ARCHIVE_EXTRACT_FFLAGS) a->todo |= TODO_FFLAGS; if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { ret = check_symlinks(a); if (ret != ARCHIVE_OK) goto done; } #ifdef HAVE_FCHDIR /* If path exceeds PATH_MAX, shorten the path. */ edit_deep_directories(a); #endif ret = restore_entry(a); /* * On the GNU tar mailing list, some people working with new * Linux filesystems observed that system xattrs used as * layout hints need to be restored before the file contents * are written, so this can't be done at file close. */ if (a->todo & TODO_XATTR) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { fchdir(a->restore_pwd); close(a->restore_pwd); a->restore_pwd = -1; } #endif /* * Fixup uses the unedited pathname from archive_entry_pathname(), * because it is relative to the base dir and the edited path * might be relative to some intermediate dir as a result of the * deep restore logic. */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname(entry)); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } if ((a->deferred & TODO_TIMES) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname(entry)); fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { fe->atime = archive_entry_atime(entry); fe->atime_nanos = archive_entry_atime_nsec(entry); } else { /* If atime is unset, use start time. */ fe->atime = a->start_time; fe->atime_nanos = 0; } if (archive_entry_mtime_is_set(entry)) { fe->mtime = archive_entry_mtime(entry); fe->mtime_nanos = archive_entry_mtime_nsec(entry); } else { /* If mtime is unset, use start time. */ fe->mtime = a->start_time; fe->mtime_nanos = 0; } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); fe->birthtime_nanos = archive_entry_birthtime_nsec(entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; fe->birthtime_nanos = fe->mtime_nanos; } } if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } /* We've created the object and are ready to pour data into it. */ if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. * In particular, dirs, links, etc, don't get written to. */ if (a->fd < 0) { archive_entry_set_size(entry, 0); a->filesize = 0; } done: /* Restore the user's umask before returning. */ umask(a->user_umask); return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, dev_t d, ino_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file"); a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } static ssize_t write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t block_size = 0, bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } if (a->flags & ARCHIVE_EXTRACT_SPARSE) { #if HAVE_STRUCT_STAT_ST_BLKSIZE int r; if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; #else /* XXX TODO XXX Is there a more appropriate choice here ? */ /* This needn't match the filesystem allocation size. */ block_size = 16*1024; #endif } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (off_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { if (block_size == 0) { bytes_to_write = size; } else { /* We're sparsifying the file. */ const char *p, *end; off_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { if (*p != '\0') break; } a->offset += p - buff; size -= p - buff; buff = p; if (size == 0) break; /* Calculate next block boundary after offset. */ block_end = (a->offset / block_size + 1) * block_size; /* If the adjusted write would cross block boundary, * truncate it to the block boundary. */ bytes_to_write = size; if (a->offset + bytes_to_write > block_end) bytes_to_write = block_end - a->offset; } /* Seek if necessary to the specified offset. */ if (a->offset != a->fd_offset) { if (lseek(a->fd, a->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } a->fd_offset = a->offset; a->archive.file_position = a->offset; a->archive.raw_position = a->offset; } bytes_written = write(a->fd, buff, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } buff += bytes_written; size -= bytes_written; a->offset += bytes_written; a->archive.file_position += bytes_written; a->archive.raw_position += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } static ssize_t _archive_write_data_block(struct archive *_a, const void *buff, size_t size, off_t offset) { struct archive_write_disk *a = (struct archive_write_disk *)_a; ssize_t r; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_disk_block"); a->offset = offset; r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, "Write request too large"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); return (write_data_block(a, buff, size)); } static int _archive_write_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_HEADER) return (ARCHIVE_OK); archive_clear_error(&a->archive); /* Pad or truncate file to the right size. */ if (a->fd < 0) { /* There's no file. */ } else if (a->filesize < 0) { /* File size is unknown, so we can't set the size. */ } else if (a->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ } else { #if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } #endif /* * Explicitly stat the file as some platforms might not * implement the XSI option to extend files via ftruncate. */ a->pst = NULL; if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (ret); if (a->st.st_size != a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); return (ARCHIVE_FATAL); } a->pst = NULL; } } /* Restore metadata. */ /* * Look up the "real" UID only if we're going to need it. * TODO: the TODO_SGID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) { a->uid = a->lookup_uid(a->lookup_uid_data, archive_entry_uname(a->entry), archive_entry_uid(a->entry)); } /* Look up the "real" GID only if we're going to need it. */ /* TODO: the TODO_SUID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) { a->gid = a->lookup_gid(a->lookup_gid_data, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * If restoring ownership, do it before trying to restore suid/sgid * bits. If we set the owner, we know what it is and can skip * a stat() call to examine the ownership of the file on disk. */ if (a->todo & TODO_OWNER) ret = set_ownership(a); if (a->todo & TODO_MODE) { int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } if (a->todo & TODO_ACLS) { int r2 = set_acls(a); if (r2 < ret) ret = r2; } /* * Some flags prevent file modification; they must be restored after * file contents are written. */ if (a->todo & TODO_FFLAGS) { int r2 = set_fflags(a); if (r2 < ret) ret = r2; } /* * Time has to be restored after all other metadata; * otherwise atime will get changed. */ if (a->todo & TODO_TIMES) { int r2 = set_times(a); if (r2 < ret) ret = r2; } /* If there's an fd, we can close it now. */ if (a->fd >= 0) { close(a->fd); a->fd = -1; } /* If there's an entry, we can release it now. */ if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup"); a->lookup_gid = lookup_gid; a->cleanup_gid = cleanup_gid; a->lookup_gid_data = private_data; return (ARCHIVE_OK); } int archive_write_disk_set_user_lookup(struct archive *_a, void *private_data, uid_t (*lookup_uid)(void *private, const char *uname, uid_t uid), void (*cleanup_uid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup"); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_write_disk object and initialize it with global state. */ struct archive * archive_write_disk_new(void) { struct archive_write_disk *a; a = (struct archive_write_disk *)malloc(sizeof(*a)); if (a == NULL) return (NULL); memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = archive_write_disk_vtable(); a->lookup_uid = trivial_lookup_uid; a->lookup_gid = trivial_lookup_gid; a->start_time = time(NULL); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ if (archive_string_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } return (&a->archive); } /* * If pathname is longer than PATH_MAX, chdir to a suitable * intermediate dir and edit the path down to a shorter suffix. Note * that this routine never returns an error; if the chdir() attempt * fails for any reason, we just go ahead with the long pathname. The * object creation is likely to fail, but any error will get handled * at that time. */ #ifdef HAVE_FCHDIR static void edit_deep_directories(struct archive_write_disk *a) { int ret; char *tail = a->name; a->restore_pwd = -1; /* If path is short, avoid the open() below. */ if (strlen(tail) <= PATH_MAX) return; /* Try to record our starting dir. */ a->restore_pwd = open(".", O_RDONLY | O_BINARY); if (a->restore_pwd < 0) return; /* As long as the path is too long... */ while (strlen(tail) > PATH_MAX) { /* Locate a dir prefix shorter than PATH_MAX. */ tail += PATH_MAX - 8; while (tail > a->name && *tail != '/') tail--; /* Exit if we find a too-long path component. */ if (tail <= a->name) return; /* Create the intermediate dir and chdir to it. */ *tail = '\0'; /* Terminate dir portion */ ret = create_dir(a, a->name); if (ret == ARCHIVE_OK && chdir(a->name) != 0) ret = ARCHIVE_FAILED; *tail = '/'; /* Restore the / we removed. */ if (ret != ARCHIVE_OK) return; tail++; /* The chdir() succeeded; we've now shortened the path. */ a->name = tail; } return; } #endif /* * The main restore function. */ static int restore_entry(struct archive_write_disk *a) { int ret = ARCHIVE_OK, en; if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { /* * TODO: Fix this. Apparently, there are platforms * that still allow root to hose the entire filesystem * by unlinking a dir. The S_ISDIR() test above * prevents us from using unlink() here if the new * object is a dir, but that doesn't mean the old * object isn't a dir. */ if (unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); return(ARCHIVE_FAILED); } } /* Try creating it first; if this fails, we'll try to recover. */ en = create_filesystem_object(a); if ((en == ENOTDIR || en == ENOENT) && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) { /* If the parent dir doesn't exist, try creating it. */ create_parent_dir(a, a->name); /* Now try to create the object again. */ en = create_filesystem_object(a); } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ archive_set_error(&a->archive, en, "Already exists"); return (ARCHIVE_FAILED); } /* * Some platforms return EISDIR if you call * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some * return EEXIST. POSIX is ambiguous, requiring EISDIR * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) * on an existing item. */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; /* * The SECURE_SYMLINK logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) r = stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) r = lstat(a->name, &a->st); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); } /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) && !S_ISDIR(a->st.st_mode)) { if (!older(&(a->st), a->entry)) { archive_set_error(&a->archive, 0, "File on disk is not older; skipping."); return (ARCHIVE_FAILED); } } /* If it's our archive, we're done. */ if (a->skip_file_dev > 0 && a->skip_file_ino > 0 && a->st.st_dev == a->skip_file_dev && a->st.st_ino == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } if (!S_ISDIR(a->st.st_mode)) { /* A non-dir is in the way, unlink it. */ if (unlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } /* Try again. */ en = create_filesystem_object(a); } else { /* * There's a dir in the way of a dir. Don't * waste time with rmdir()/mkdir(), just fix * up the permissions on the existing dir. * Note that we don't change perms on existing * dirs unless _EXTRACT_PERM is specified. */ if ((a->mode != a->st.st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ en = 0; /* Forget the EEXIST. */ } } if (en) { /* Everything failed; give up here. */ archive_set_error(&a->archive, en, "Can't create '%s'", a->name); return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ return (ret); } /* * Returns 0 if creation succeeds, or else returns errno value from * the failed system call. Note: This function should only ever perform * a single system call. */ int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const char *linkname; mode_t final_mode, mode; int r; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ linkname = archive_entry_hardlink(a->entry); if (linkname != NULL) { r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. * * If the hardlink was successfully created and * the archive doesn't have carry data for it, * consider it to be non-authoritive for meta data. * This is consistent with GNU tar and BSD pax. * If the hardlink does carry data, let the last * archive entry decide ownership. */ if (r == 0 && a->filesize <= 0) { a->todo = 0; a->deferred = 0; } if (r == 0 && a->filesize > 0) { a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY); if (a->fd < 0) r = errno; } return (r); } linkname = archive_entry_symlink(a->entry); if (linkname != NULL) return symlink(linkname, a->name) ? errno : 0; /* * The remaining system calls all set permissions, so let's * try to take advantage of that to avoid an extra chmod() * call. (Recall that umask is set to zero right now!) */ /* Mode we want for the final restored object (w/o file type bits). */ final_mode = a->mode & 07777; /* * The mode that will actually be restored in this step. Note * that SUID, SGID, etc, require additional work to ensure * security, so we never restore them at this point. */ mode = final_mode & 0777; switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: a->fd = open(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode); r = (a->fd < 0); break; case AE_IFCHR: #ifdef HAVE_MKNOD /* Note: we use AE_IFCHR for the case label, and * S_IFCHR for the mknod() call. This is correct. */ r = mknod(a->name, mode | S_IFCHR, archive_entry_rdev(a->entry)); #else /* TODO: Find a better way to warn about our inability * to restore a char device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ break; case AE_IFBLK: #ifdef HAVE_MKNOD r = mknod(a->name, mode | S_IFBLK, archive_entry_rdev(a->entry)); #else /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ break; case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; r = mkdir(a->name, mode); if (r == 0) { /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; /* Never use an immediate chmod(). */ /* We can't avoid the chmod() entirely if EXTRACT_PERM * because of SysV SGID inheritance. */ if ((mode != final_mode) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; } break; case AE_IFIFO: #ifdef HAVE_MKFIFO r = mkfifo(a->name, mode); #else /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); #endif /* HAVE_MKFIFO */ break; } /* All the system calls above set errno on failure. */ if (r) return (errno); /* If we managed to set the final mode, we've avoided a chmod(). */ if (mode == final_mode) a->todo &= ~TODO_MODE; return (0); } /* * Cleanup function for archive_extract. Mostly, this involves processing * the fixup list, which is used to address a number of problems: * * Dir permissions might prevent us from restoring a file in that * dir, so we restore the dir with minimum 0700 permissions first, * then correct the mode at the end. * * Similarly, the act of restoring a file touches the directory * and changes the timestamp on the dir, so we have to touch-up dir * timestamps at the end as well. * * Some file flags can interfere with the restore by, for example, * preventing the creation of hardlinks to those files. * * Note that tar/cpio do not require that archives be in a particular * order; there is no way to know when the last file has been restored * within a directory, so there's no way to optimize the memory usage * here by fixing up the directory any earlier than the * end-of-archive. * * XXX TODO: Directory ACLs should be restored here, for the same * reason we set directory perms here. XXX */ static int _archive_write_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; int ret; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_close"); ret = _archive_write_finish_entry(&a->archive); /* Sort dir list so directories are fixed up in depth-first order. */ p = sort_dir_list(a->fixup_list); while (p != NULL) { a->pst = NULL; /* Mark stat cache as out-of-date. */ if (p->fixup & TODO_TIMES) { #ifdef HAVE_UTIMES /* {f,l,}utimes() are preferred, when available. */ -#ifdef __timeval +#if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif times[0].tv_sec = p->atime; times[0].tv_usec = p->atime_nanos / 1000; #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME /* if it's valid and not mtime, push the birthtime first */ if (((times[1].tv_sec = p->birthtime) < p->mtime) && (p->birthtime > 0)) { times[1].tv_usec = p->birthtime_nanos / 1000; utimes(p->name, times); } #endif times[1].tv_sec = p->mtime; times[1].tv_usec = p->mtime_nanos / 1000; #ifdef HAVE_LUTIMES lutimes(p->name, times); #else utimes(p->name, times); #endif #else /* utime() is more portable, but less precise. */ struct utimbuf times; times.modtime = p->mtime; times.actime = p->atime; utime(p->name, ×); #endif } if (p->fixup & TODO_MODE_BASE) chmod(p->name, p->mode); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, -1, p->name, p->mode, p->fflags_set, 0); next = p->next; free(p->name); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_finish(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret; ret = _archive_write_close(&a->archive); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); free(a); return (ret); } /* * Simple O(n log n) merge sort to order the fixup list. In * particular, we want to restore dir timestamps depth-first. */ static struct fixup_entry * sort_dir_list(struct fixup_entry *p) { struct fixup_entry *a, *b, *t; if (p == NULL) return (NULL); /* A one-item list is already sorted. */ if (p->next == NULL) return (p); /* Step 1: split the list. */ t = p; a = p->next->next; while (a != NULL) { /* Step a twice, t once. */ a = a->next; if (a != NULL) a = a->next; t = t->next; } /* Now, t is at the mid-point, so break the list here. */ b = t->next; t->next = NULL; a = p; /* Step 2: Recursively sort the two sub-lists. */ a = sort_dir_list(a); b = sort_dir_list(b); /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ if (strcmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { t = p = b; b = b->next; } /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { if (strcmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { t->next = b; b = b->next; } t = t->next; } /* Only one list is non-empty, so just splice it on. */ if (a != NULL) t->next = a; if (b != NULL) t->next = b; return (p); } /* * Returns a new, initialized fixup entry. * * TODO: Reduce the memory requirements for this list by using a tree * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive_write_disk *a, const char *pathname) { struct fixup_entry *fe; fe = (struct fixup_entry *)malloc(sizeof(struct fixup_entry)); if (fe == NULL) return (NULL); fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; fe->name = strdup(pathname); return (fe); } /* * Returns a fixup structure for the current entry. */ static struct fixup_entry * current_fixup(struct archive_write_disk *a, const char *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); return (a->current_fixup); } /* TODO: Make this work. */ /* * TODO: The deep-directory support bypasses this; disable deep directory * support if we're doing symlink checks. */ /* * TODO: Someday, integrate this with the deep dir support; they both * scan the path and both can be optimized by comparing against other * recent paths. */ static int check_symlinks(struct archive_write_disk *a) { char *pn, *p; char c; int r; struct stat st; /* * Guard against symlink tricks. Reject any archive entry whose * destination would be altered by a symlink. */ /* Whatever we checked last time doesn't need to be re-checked. */ pn = a->name; p = a->path_safe.s; while ((*pn != '\0') && (*p == *pn)) ++p, ++pn; c = pn[0]; /* Keep going until we've checked the entire name. */ while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) { /* Skip the next path element. */ while (*pn != '\0' && *pn != '/') ++pn; c = pn[0]; pn[0] = '\0'; /* Check that we haven't hit a symlink. */ r = lstat(a->name, &st); if (r != 0) { /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) break; } else if (S_ISLNK(st.st_mode)) { if (c == '\0') { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ if (unlink(a->name)) { archive_set_error(&a->archive, errno, "Could not remove symlink %s", a->name); pn[0] = c; return (ARCHIVE_FAILED); } a->pst = NULL; /* * Even if we did remove it, a warning * is in order. The warning is silly, * though, if we're just replacing one * symlink with another symlink. */ if (!S_ISLNK(a->mode)) { archive_set_error(&a->archive, 0, "Removing symlink %s", a->name); } /* Symlink gone. No more problem! */ pn[0] = c; return (0); } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ if (unlink(a->name) != 0) { archive_set_error(&a->archive, 0, "Cannot remove intervening symlink %s", a->name); pn[0] = c; return (ARCHIVE_FAILED); } a->pst = NULL; } else { archive_set_error(&a->archive, 0, "Cannot extract through symlink %s", a->name); pn[0] = c; return (ARCHIVE_FAILED); } } } pn[0] = c; /* We've checked and/or cleaned the whole path, so remember it. */ archive_strcpy(&a->path_safe, a->name); return (ARCHIVE_OK); } -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) /* * 1. Convert a path separator from '\' to '/' . * We shouldn't check multi-byte character directly because some * character-set have been using the '\' character for a part of * its multibyte character code. * 2. Replace unusable characters in Windows with underscore('_'). * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static void cleanup_pathname_win(struct archive_write_disk *a) { wchar_t wc; char *p; size_t alen, l; alen = 0; l = 0; for (p = a->name; *p != '\0'; p++) { ++alen; if (*p == '\\') l = 1; /* Rewrite the path name if its character is a unusable. */ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; } if (alen == 0 || l == 0) return; /* * Convert path separator. */ p = a->name; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == -1) { while (*p != '\0') { if (*p == '\\') *p = '/'; ++p; } break; } if (l == 1 && wc == L'\\') *p = '/'; p += l; alen -= l; } } #endif /* * Canonicalize the pathname. In particular, this strips duplicate * '/' characters, '.' elements, and trailing '/'. It also raises an * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is * set) any '..' in the path. */ static int cleanup_pathname(struct archive_write_disk *a) { char *dest, *src; char separator = '\0'; dest = src = a->name; if (*src == '\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid empty pathname"); return (ARCHIVE_FAILED); } -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) cleanup_pathname_win(a); #endif /* Skip leading '/'. */ if (*src == '/') separator = *src++; /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '/' */ if (src[0] == '\0') { break; } else if (src[0] == '/') { /* Found '//', ignore second one. */ src++; continue; } else if (src[0] == '.') { if (src[1] == '\0') { /* Ignore trailing '.' */ break; } else if (src[1] == '/') { /* Skip './'. */ src += 2; continue; } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Path contains '..'"); return (ARCHIVE_FAILED); } } /* * Note: Under no circumstances do we * remove '..' elements. In * particular, restoring * '/foo/../bar/' should create the * 'foo' dir as a side-effect. */ } } /* Copy current element, including leading '/'. */ if (separator) *dest++ = '/'; while (*src != '\0' && *src != '/') { *dest++ = *src++; } if (*src == '\0') break; /* Skip '/' separator. */ separator = *src++; } /* * We've just copied zero or more path elements, not including the * final '/'. */ if (dest == a->name) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. */ if (separator) *dest++ = '/'; else *dest++ = '.'; } /* Terminate the result. */ *dest = '\0'; return (ARCHIVE_OK); } /* * Create the parent directory of the specified path, assuming path * is already in mutable storage. */ static int create_parent_dir(struct archive_write_disk *a, char *path) { char *slash; int r; /* Remove tail element to obtain parent name. */ slash = strrchr(path, '/'); if (slash == NULL) return (ARCHIVE_OK); *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } /* * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int create_dir(struct archive_write_disk *a, char *path) { struct stat st; struct fixup_entry *le; char *slash, *base; mode_t mode_final, mode; int r; r = ARCHIVE_OK; /* Check for special names and just skip them. */ slash = strrchr(path, '/'); if (slash == NULL) base = path; else base = slash + 1; if (base[0] == '\0' || (base[0] == '.' && base[1] == '\0') || (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } return (ARCHIVE_OK); } /* * Yes, this should be stat() and not lstat(). Using lstat() * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ if (stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); return (ARCHIVE_FAILED); } if (unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed"); return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; if (r != ARCHIVE_OK) return (r); } /* * Mode we want for the final restored directory. Per POSIX, * implicitly-created dirs must be created obeying the umask. * There's no mention whether this is different for privileged * restores (which the rest of this code handles by pretending * umask=0). I've chosen here to always obey the user's umask for * implicit dirs, even if _EXTRACT_PERM was specified. */ mode_final = DEFAULT_DIR_MODE & ~a->user_umask; /* Mode we want on disk during the restore process. */ mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; if (mkdir(path, mode) == 0) { if (mode != mode_final) { le = new_fixup(a, path); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); } /* * Without the following check, a/b/../b/c/d fails at the * second visit to 'b', so 'd' can't be created. Note that we * don't add it to the fixup list here, as it's already been * added. */ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); return (ARCHIVE_FAILED); } /* * Note: Although we can skip setting the user id if the desired user * id matches the current user, we cannot skip setting the group, as * many systems set the gid based on the containing directory. So * we have to perform a chown syscall if we want to set the SGID * bit. (The alternative is to stat() and then possibly chown(); it's * more efficient to skip the stat() and just always chown().) Note * that a successful chown() here clears the TODO_SGID_CHECK bit, which * allows set_mode to skip the stat() check for the GID. */ static int set_ownership(struct archive_write_disk *a) { +#ifndef __CYGWIN__ +/* unfortunately, on win32 there is no 'root' user with uid 0, + so we just have to try the chown and see if it works */ + /* If we know we can't change it, don't bother trying. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%d", a->uid); return (ARCHIVE_WARN); } +#endif #ifdef HAVE_FCHOWN /* If we have an fd, we can avoid a race. */ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif /* We prefer lchown() but will use chown() if that's all we have. */ /* Of course, if we have neither, this will always fail. */ #ifdef HAVE_LCHOWN if (lchown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #elif HAVE_CHOWN if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif archive_set_error(&a->archive, errno, "Can't set user=%d/group=%d for %s", a->uid, a->gid, a->name); return (ARCHIVE_WARN); } #ifdef HAVE_UTIMES /* * The utimes()-family functions provide high resolution and * a way to set time on an fd or a symlink. We prefer them * when they're available. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { -#ifdef __timeval +#if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif times[0].tv_sec = atime; times[0].tv_usec = atime_nsec / 1000; times[1].tv_sec = mtime; times[1].tv_usec = mtime_nsec / 1000; #ifdef HAVE_FUTIMES if (fd >= 0) return (futimes(fd, times)); #else (void)fd; /* UNUSED */ #endif #ifdef HAVE_LUTIMES (void)mode; /* UNUSED */ return (lutimes(name, times)); #else if (S_ISLNK(mode)) return (0); return (utimes(name, times)); #endif } #elif defined(HAVE_UTIME) /* * utime() is an older, more standard interface that we'll use * if utimes() isn't available. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { struct utimbuf times; (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ times.actime = atime; times.modtime = mtime; if (S_ISLNK(mode)) return (ARCHIVE_OK); return (utime(name, ×)); } #else static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { return (ARCHIVE_WARN); } #endif static int set_times(struct archive_write_disk *a) { time_t atime = a->start_time, mtime = a->start_time; long atime_nsec = 0, mtime_nsec = 0; /* If no time was provided, we're done. */ if (!archive_entry_atime_is_set(a->entry) #if HAVE_STRUCT_STAT_ST_BIRTHTIME && !archive_entry_birthtime_is_set(a->entry) #endif && !archive_entry_mtime_is_set(a->entry)) return (ARCHIVE_OK); /* If no atime was specified, use start time instead. */ /* In theory, it would be marginally more correct to use * time(NULL) here, but that would cost us an extra syscall * for little gain. */ if (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } /* * If you have struct stat.st_birthtime, we assume BSD birthtime * semantics, in which {f,l,}utimes() updates birthtime to earliest * mtime. So we set the time twice, first using the birthtime, * then using the mtime. */ #if HAVE_STRUCT_STAT_ST_BIRTHTIME /* If birthtime is set, flush that through to disk first. */ if (archive_entry_birthtime_is_set(a->entry)) if (set_time(a->fd, a->mode, a->name, atime, atime_nsec, archive_entry_birthtime(a->entry), archive_entry_birthtime_nsec(a->entry))) { archive_set_error(&a->archive, errno, "Can't update time for %s", a->name); return (ARCHIVE_WARN); } #endif if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (set_time(a->fd, a->mode, a->name, atime, atime_nsec, mtime, mtime_nsec)) { archive_set_error(&a->archive, errno, "Can't update time for %s", a->name); return (ARCHIVE_WARN); } /* * Note: POSIX does not provide a portable way to restore ctime. * (Apart from resetting the system clock, which is distasteful.) * So, any restoration of ctime will necessarily be OS-specific. */ return (ARCHIVE_OK); } static int set_mode(struct archive_write_disk *a, int mode) { int r = ARCHIVE_OK; mode &= 07777; /* Strip off file type bits. */ if (a->todo & TODO_SGID_CHECK) { /* * If we don't know the GID is right, we must stat() * to verify it. We can't just check the GID of this * process, since systems sometimes set GID from * the enclosing dir or based on ACLs. */ if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { /* * This is only an error if you * requested owner restore. If you * didn't, we'll try to restore * sgid/suid, but won't consider it a * problem if we can't. */ archive_set_error(&a->archive, -1, "Can't restore SGID bit"); r = ARCHIVE_WARN; } #endif } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } #endif } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; } else if (a->todo & TODO_SUID_CHECK) { /* * If we don't know the UID is right, we can just check * the user, since all systems set the file UID from * the process UID. */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } #endif } a->todo &= ~TODO_SUID_CHECK; } if (S_ISLNK(a->mode)) { #ifdef HAVE_LCHMOD /* * If this is a symlink, use lchmod(). If the * platform doesn't support lchmod(), just skip it. A * platform that doesn't provide a way to set * permissions on symlinks probably ignores * permissions on symlinks, so a failure here has no * impact. */ if (lchmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } #endif } else if (!S_ISDIR(a->mode)) { /* * If it's not a symlink and not a dir, then use * fchmod() or chmod(), depending on whether we have * an fd. Dirs get their perms set during the * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD if (a->fd >= 0) { if (fchmod(a->fd, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } else #endif /* If this platform lacks fchmod(), then * we'll just use chmod(). */ if (chmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } return (r); } static int set_fflags(struct archive_write_disk *a) { struct fixup_entry *le; unsigned long set, clear; int r; int critical_flags; mode_t mode = archive_entry_mode(a->entry); /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, * SF_IMMUTABLE prevents hardlinks from being created, so * should not be set until after any hardlinks are created. To * preserve some semblance of portability, this uses #ifdef * extensively. Ugly, but it works. * * Yes, Virginia, this does create a security race. It's mitigated * somewhat by the practice of creating dirs 0700 until the extract * is done, but it would be nice if we could do more than that. * People restoring critical file systems should be wary of * other programs that might try to muck with files as they're * being restored. */ /* Hopefully, the compiler will optimize this mess into a constant. */ critical_flags = 0; #ifdef SF_IMMUTABLE critical_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE critical_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND critical_flags |= SF_APPEND; #endif #ifdef UF_APPEND critical_flags |= UF_APPEND; #endif #ifdef EXT2_APPEND_FL critical_flags |= EXT2_APPEND_FL; #endif #ifdef EXT2_IMMUTABLE_FL critical_flags |= EXT2_IMMUTABLE_FL; #endif if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); /* * The first test encourages the compiler to eliminate * all of this if it's not necessary. */ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, a->name); le->fixup |= TODO_FFLAGS; le->fflags_set = set; /* Store the mode if it's not already there. */ if ((le->fixup & TODO_MODE) == 0) le->mode = mode; } else { r = set_fflags_platform(a, a->fd, a->name, mode, set, clear); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS) /* * BSD reads flags using stat() and sets them with one of {f,l,}chflags() */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; (void)mode; /* UNUSED */ if (set == 0 && clear == 0) return (ARCHIVE_OK); /* * XXX Is the stat here really necessary? Or can I just use * the 'set' flags directly? In particular, I'm not sure * about the correct approach if we're overwriting an existing * file that already has flags on it. XXX */ if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags &= ~clear; a->st.st_flags |= set; #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif /* * If we can't use the fd to set the flags, we'll use the * pathname to set flags. We prefer lchflags() but will use * chflags() if we must. */ #ifdef HAVE_LCHFLAGS if (lchflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #elif defined(HAVE_CHFLAGS) if (S_ISLNK(a->st.st_mode)) { archive_set_error(&a->archive, errno, "Can't set file flags on symlink."); return (ARCHIVE_WARN); } if (chflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif archive_set_error(&a->archive, errno, "Failed to set file flags"); return (ARCHIVE_WARN); } #elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) /* * Linux uses ioctl() to read and write file flags. */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int ret; int myfd = fd; unsigned long newflags, oldflags; unsigned long sf_mask = 0; if (set == 0 && clear == 0) return (ARCHIVE_OK); /* Only regular files and dirs can have flags. */ if (!S_ISREG(mode) && !S_ISDIR(mode)) return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ if (myfd < 0) myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY); if (myfd < 0) return (ARCHIVE_OK); /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but * there seem to be some Linux systems that lack these * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ #ifdef EXT2_IMMUTABLE_FL sf_mask |= EXT2_IMMUTABLE_FL; #endif #ifdef EXT2_APPEND_FL sf_mask |= EXT2_APPEND_FL; #endif /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX */ ret = ARCHIVE_OK; /* Try setting the flags as given. */ if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { newflags = (oldflags & ~clear) | set; if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) goto cleanup; if (errno != EPERM) goto fail; } /* If we couldn't set all the flags, try again with a subset. */ if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { newflags &= ~sf_mask; oldflags &= sf_mask; newflags |= oldflags; if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) goto cleanup; } /* We couldn't set the flags, so report the failure. */ fail: archive_set_error(&a->archive, errno, "Failed to set file flags"); ret = ARCHIVE_WARN; cleanup: if (fd < 0) close(myfd); return (ret); } #else /* * Of course, some systems have neither BSD chflags() nor Linux' flags * support through ioctl(). */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)mode; /* UNUSED */ (void)set; /* UNUSED */ (void)clear; /* UNUSED */ return (ARCHIVE_OK); } #endif /* __linux */ #ifndef HAVE_POSIX_ACL /* Default empty function body to satisfy mainline code. */ static int set_acls(struct archive_write_disk *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } #else /* * XXX TODO: What about ACL types other than ACCESS and DEFAULT? */ static int set_acls(struct archive_write_disk *a) { int ret; ret = set_acl(a, a->fd, a->entry, ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); if (ret != ARCHIVE_OK) return (ret); ret = set_acl(a, a->fd, a->entry, ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); return (ret); } static int set_acl(struct archive_write_disk *a, int fd, struct archive_entry *entry, acl_type_t acl_type, int ae_requested_type, const char *tname) { acl_t acl; acl_entry_t acl_entry; acl_permset_t acl_permset; int ret; int ae_type, ae_permset, ae_tag, ae_id; uid_t ae_uid; gid_t ae_gid; const char *ae_name; int entries; const char *name; ret = ARCHIVE_OK; entries = archive_entry_acl_reset(entry, ae_requested_type); if (entries == 0) return (ARCHIVE_OK); acl = acl_init(entries); while (archive_entry_acl_next(entry, ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { acl_create_entry(&acl, &acl_entry); switch (ae_tag) { case ARCHIVE_ENTRY_ACL_USER: acl_set_tag_type(acl_entry, ACL_USER); ae_uid = a->lookup_uid(a->lookup_uid_data, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_uid); break; case ARCHIVE_ENTRY_ACL_GROUP: acl_set_tag_type(acl_entry, ACL_GROUP); ae_gid = a->lookup_gid(a->lookup_gid_data, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_gid); break; case ARCHIVE_ENTRY_ACL_USER_OBJ: acl_set_tag_type(acl_entry, ACL_USER_OBJ); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); break; case ARCHIVE_ENTRY_ACL_MASK: acl_set_tag_type(acl_entry, ACL_MASK); break; case ARCHIVE_ENTRY_ACL_OTHER: acl_set_tag_type(acl_entry, ACL_OTHER); break; default: /* XXX */ break; } acl_get_permset(acl_entry, &acl_permset); acl_clear_perms(acl_permset); if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) acl_add_perm(acl_permset, ACL_EXECUTE); if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) acl_add_perm(acl_permset, ACL_WRITE); if (ae_permset & ARCHIVE_ENTRY_ACL_READ) acl_add_perm(acl_permset, ACL_READ); } name = archive_entry_pathname(entry); /* Try restoring the ACL through 'fd' if we can. */ #if HAVE_ACL_SET_FD if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) ret = ARCHIVE_OK; else #else #if HAVE_ACL_SET_FD_NP if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) ret = ARCHIVE_OK; else #endif #endif if (acl_set_file(name, acl_type, acl) != 0) { archive_set_error(&a->archive, errno, "Failed to set %s acl", tname); ret = ARCHIVE_WARN; } acl_free(acl); return (ret); } #endif #if HAVE_LSETXATTR /* * Restore extended attributes - Linux implementation */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL && strncmp(name, "xfsroot.", 8) != 0 && strncmp(name, "system.", 7) != 0) { int e; #if HAVE_FSETXATTR if (a->fd >= 0) e = fsetxattr(a->fd, name, value, size, 0); else #endif { e = lsetxattr(archive_entry_pathname(entry), name, value, size, 0); } if (e == -1) { if (errno == ENOTSUP) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else archive_set_error(&a->archive, errno, "Failed to set extended attribute"); ret = ARCHIVE_WARN; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended attribute encountered"); ret = ARCHIVE_WARN; } } return (ret); } #elif HAVE_EXTATTR_SET_FILE /* * Restore extended attributes - FreeBSD implementation */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL) { int e; int namespace; if (strncmp(name, "user.", 5) == 0) { /* "user." attributes go to user namespace */ name += 5; namespace = EXTATTR_NAMESPACE_USER; } else { /* Warn about other extended attributes. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't restore extended attribute ``%s''", name); ret = ARCHIVE_WARN; continue; } errno = 0; #if HAVE_EXTATTR_SET_FD if (a->fd >= 0) e = extattr_set_fd(a->fd, namespace, name, value, size); else #endif /* TODO: should we use extattr_set_link() instead? */ { e = extattr_set_file(archive_entry_pathname(entry), namespace, name, value, size); } if (e != (int)size) { if (errno == ENOTSUP) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else { archive_set_error(&a->archive, errno, "Failed to set extended attribute"); } ret = ARCHIVE_WARN; } } } return (ret); } #else /* * Restore extended attributes - stub implementation for unsupported systems */ static int set_xattrs(struct archive_write_disk *a) { static int warning_done = 0; /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); } /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); } #endif /* * Trivial implementations of gid/uid lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static gid_t trivial_lookup_gid(void *private_data, const char *gname, gid_t gid) { (void)private_data; /* UNUSED */ (void)gname; /* UNUSED */ return (gid); } static uid_t trivial_lookup_uid(void *private_data, const char *uname, uid_t uid) { (void)private_data; /* UNUSED */ (void)uname; /* UNUSED */ return (uid); } /* * Test if file on disk is older than entry. */ static int older(struct stat *st, struct archive_entry *entry) { /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (st->st_mtime < archive_entry_mtime(entry)) return (1); /* Definitely younger. */ if (st->st_mtime > archive_entry_mtime(entry)) return (0); /* If this platform supports fractional seconds, try those. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC /* Definitely older. */ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Definitely older. */ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_N /* older. */ if (st->st_mtime_n < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_UMTIME /* older. */ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC /* older. */ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry)) return (1); #else /* This system doesn't have high-res timestamps. */ #endif /* Same age or newer, so not older. */ return (0); } diff --git a/lib/libarchive/archive_write_disk_set_standard_lookup.c b/lib/libarchive/archive_write_disk_set_standard_lookup.c index 39f58910bad6..532942539d94 100644 --- a/lib/libarchive/archive_write_disk_set_standard_lookup.c +++ b/lib/libarchive/archive_write_disk_set_standard_lookup.c @@ -1,196 +1,196 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_write_disk_private.h" struct bucket { char *name; int hash; id_t id; }; static const size_t cache_size = 127; static unsigned int hash(const char *); static gid_t lookup_gid(void *, const char *uname, gid_t); static uid_t lookup_uid(void *, const char *uname, uid_t); static void cleanup(void *); /* * Installs functions that use getpwnam()/getgrnam()---along with * a simple cache to accelerate such lookups---into the archive_write_disk * object. This is in a separate file because getpwnam()/getgrnam() * can pull in a LOT of library code (including NIS/LDAP functions, which * pull in DNS resolveers, etc). This can easily top 500kB, which makes * it inappropriate for some space-constrained applications. * * Applications that are size-sensitive may want to just use the * real default functions (defined in archive_write_disk.c) that just * use the uid/gid without the lookup. Or define your own custom functions * if you prefer. * * TODO: Replace these hash tables with simpler move-to-front LRU * lists with a bounded size (128 items?). The hash is a bit faster, * but has a bad pathology in which it thrashes a single bucket. Even * walking a list of 128 items is a lot faster than calling * getpwnam()! */ int archive_write_disk_set_standard_lookup(struct archive *a) { struct bucket *ucache = malloc(cache_size * sizeof(struct bucket)); struct bucket *gcache = malloc(cache_size * sizeof(struct bucket)); memset(ucache, 0, cache_size * sizeof(struct bucket)); memset(gcache, 0, cache_size * sizeof(struct bucket)); archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup); archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup); return (ARCHIVE_OK); } static gid_t lookup_gid(void *private_data, const char *gname, gid_t gid) { int h; struct bucket *b; struct bucket *gcache = (struct bucket *)private_data; /* If no gname, just use the gid provided. */ if (gname == NULL || *gname == '\0') return (gid); /* Try to find gname in the cache. */ h = hash(gname); b = &gcache[h % cache_size ]; if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0) return ((gid_t)b->id); /* Free the cache slot for a new entry. */ if (b->name != NULL) free(b->name); b->name = strdup(gname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; #if HAVE_GRP_H { struct group *grent = getgrnam(gname); if (grent != NULL) gid = grent->gr_gid; } -#elif _WIN32 +#elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a gname->gid lookup for Windows. */ #endif b->id = gid; return (gid); } static uid_t lookup_uid(void *private_data, const char *uname, uid_t uid) { int h; struct bucket *b; struct bucket *ucache = (struct bucket *)private_data; /* If no uname, just use the uid provided. */ if (uname == NULL || *uname == '\0') return (uid); /* Try to find uname in the cache. */ h = hash(uname); b = &ucache[h % cache_size ]; if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0) return ((uid_t)b->id); /* Free the cache slot for a new entry. */ if (b->name != NULL) free(b->name); b->name = strdup(uname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; #if HAVE_PWD_H { struct passwd *pwent = getpwnam(uname); if (pwent != NULL) uid = pwent->pw_uid; } -#elif _WIN32 +#elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a uname->uid lookup for Windows. */ #endif b->id = uid; return (uid); } static void cleanup(void *private) { size_t i; struct bucket *cache = (struct bucket *)private; for (i = 0; i < cache_size; i++) free(cache[i].name); free(cache); } static unsigned int hash(const char *p) { /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, as used by ELF for hashing function names. */ unsigned g, h = 0; while (*p != '\0') { h = ( h << 4 ) + *p++; if (( g = h & 0xF0000000 )) { h ^= g >> 24; h &= 0x0FFFFFFF; } } return h; } diff --git a/lib/libarchive/archive_write_set_compression_program.c b/lib/libarchive/archive_write_set_compression_program.c index 3615021881ec..e612f3be9225 100644 --- a/lib/libarchive/archive_write_set_compression_program.c +++ b/lib/libarchive/archive_write_set_compression_program.c @@ -1,349 +1,349 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* This capability is only available on POSIX systems. */ #if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ - !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && !defined(_WIN32) + !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) #include "archive.h" /* * On non-Posix systems, allow the program to build, but choke if * this function is actually invoked. */ int archive_write_set_compression_program(struct archive *_a, const char *cmd) { archive_set_error(_a, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } #else #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_write_private.h" #include "filter_fork.h" struct private_data { char *description; pid_t child; int child_stdin, child_stdout; char *child_buf; size_t child_buf_len, child_buf_avail; }; static int archive_compressor_program_finish(struct archive_write *); static int archive_compressor_program_init(struct archive_write *); static int archive_compressor_program_write(struct archive_write *, const void *, size_t); /* * Allocate, initialize and return a archive object. */ int archive_write_set_compression_program(struct archive *_a, const char *cmd) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_program"); a->compressor.init = &archive_compressor_program_init; a->compressor.config = strdup(cmd); return (ARCHIVE_OK); } /* * Setup callback. */ static int archive_compressor_program_init(struct archive_write *a) { int ret; struct private_data *state; static const char *prefix = "Program: "; char *cmd = a->compressor.config; if (a->client_opener != NULL) { ret = (a->client_opener)(&a->archive, a->client_data); if (ret != ARCHIVE_OK) return (ret); } state = (struct private_data *)malloc(sizeof(*state)); if (state == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for compression"); return (ARCHIVE_FATAL); } memset(state, 0, sizeof(*state)); a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); strcpy(state->description, prefix); strcat(state->description, cmd); a->archive.compression_name = state->description; state->child_buf_len = a->bytes_per_block; state->child_buf_avail = 0; state->child_buf = malloc(state->child_buf_len); if (state->child_buf == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for compression buffer"); free(state); return (ARCHIVE_FATAL); } if ((state->child = __archive_create_child(cmd, &state->child_stdin, &state->child_stdout)) == -1) { archive_set_error(&a->archive, EINVAL, "Can't initialise filter"); free(state->child_buf); free(state); return (ARCHIVE_FATAL); } a->compressor.write = archive_compressor_program_write; a->compressor.finish = archive_compressor_program_finish; a->compressor.data = state; return (0); } static ssize_t child_write(struct archive_write *a, const char *buf, size_t buf_len) { struct private_data *state = a->compressor.data; ssize_t ret; if (state->child_stdin == -1) return (-1); if (buf_len == 0) return (-1); restart_write: do { ret = write(state->child_stdin, buf, buf_len); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); return (0); } if (ret == -1 && errno != EAGAIN) return (-1); if (state->child_stdout == -1) { fcntl(state->child_stdin, F_SETFL, 0); __archive_check_child(state->child_stdin, state->child_stdout); goto restart_write; } do { ret = read(state->child_stdout, state->child_buf + state->child_buf_avail, state->child_buf_len - state->child_buf_avail); } while (ret == -1 && errno == EINTR); if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(state->child_stdout); state->child_stdout = -1; fcntl(state->child_stdin, F_SETFL, 0); goto restart_write; } if (ret == -1 && errno == EAGAIN) { __archive_check_child(state->child_stdin, state->child_stdout); goto restart_write; } if (ret == -1) return (-1); state->child_buf_avail += ret; ret = (a->client_writer)(&a->archive, a->client_data, state->child_buf, state->child_buf_avail); if (ret <= 0) return (-1); if ((size_t)ret < state->child_buf_avail) { memmove(state->child_buf, state->child_buf + ret, state->child_buf_avail - ret); } state->child_buf_avail -= ret; a->archive.raw_position += ret; goto restart_write; } /* * Write data to the compressed stream. */ static int archive_compressor_program_write(struct archive_write *a, const void *buff, size_t length) { struct private_data *state; ssize_t ret; const char *buf; state = (struct private_data *)a->compressor.data; if (a->client_writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No write callback is registered? " "This is probably an internal programming error."); return (ARCHIVE_FATAL); } buf = buff; while (length > 0) { ret = child_write(a, buf, length); if (ret == -1 || ret == 0) { archive_set_error(&a->archive, EIO, "Can't write to filter"); return (ARCHIVE_FATAL); } length -= ret; buf += ret; } a->archive.file_position += length; return (ARCHIVE_OK); } /* * Finish the compression... */ static int archive_compressor_program_finish(struct archive_write *a) { int ret, status; ssize_t bytes_read, bytes_written; struct private_data *state; state = (struct private_data *)a->compressor.data; ret = 0; if (a->client_writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No write callback is registered? " "This is probably an internal programming error."); ret = ARCHIVE_FATAL; goto cleanup; } /* XXX pad compressed data. */ close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); for (;;) { do { bytes_read = read(state->child_stdout, state->child_buf + state->child_buf_avail, state->child_buf_len - state->child_buf_avail); } while (bytes_read == -1 && errno == EINTR); if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) break; if (bytes_read == -1) { archive_set_error(&a->archive, errno, "Read from filter failed unexpectedly."); ret = ARCHIVE_FATAL; goto cleanup; } state->child_buf_avail += bytes_read; bytes_written = (a->client_writer)(&a->archive, a->client_data, state->child_buf, state->child_buf_avail); if (bytes_written <= 0) { ret = ARCHIVE_FATAL; goto cleanup; } if ((size_t)bytes_written < state->child_buf_avail) { memmove(state->child_buf, state->child_buf + bytes_written, state->child_buf_avail - bytes_written); } state->child_buf_avail -= bytes_written; a->archive.raw_position += bytes_written; } /* XXX pad final compressed block. */ cleanup: /* Shut down the child. */ if (state->child_stdin != -1) close(state->child_stdin); if (state->child_stdout != -1) close(state->child_stdout); while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) continue; if (status != 0) { archive_set_error(&a->archive, EIO, "Filter exited with failure."); ret = ARCHIVE_FATAL; } /* Release our configuration data. */ free(a->compressor.config); a->compressor.config = NULL; /* Release our private state data. */ free(state->child_buf); free(state->description); free(state); return (ret); } #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/archive_write_set_format_mtree.c b/lib/libarchive/archive_write_set_format_mtree.c index 2910a0d82c74..0043030dd244 100644 --- a/lib/libarchive/archive_write_set_format_mtree.c +++ b/lib/libarchive/archive_write_set_format_mtree.c @@ -1,1095 +1,1095 @@ /*- * Copyright (c) 2009 Michihiro NAKAJIMA * Copyright (c) 2008 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #ifdef HAVE_OPENSSL_MD5_H #include #else /* HAVE_OPENSSL_MD5_H */ #ifdef HAVE_MD5_H #include #endif #endif /* HAVE_OPENSSL_MD5_H */ #ifdef HAVE_OPENSSL_RIPEMD_H #include #else /* HAVE_OPENSSL_RIPEMD_H */ #ifdef HAVE_RIPEMD_H #include #endif #ifdef HAVE_RMD160_H #include #endif #endif /* HAVE_OPENSSL_RIPEMD_H */ #ifdef HAVE_OPENSSL_SHA_H #include #else /* HAVE_OPENSSL_SHA_H */ #ifdef HAVE_SHA_H #include #endif #ifdef HAVE_SHA1_H #include #endif #ifdef HAVE_SHA2_H #include #endif #ifdef HAVE_SHA256_H #include #endif #endif /* HAVE_OPENSSL_SHA_H */ #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" #define INDENTNAMELEN 15 #define MAXLINELEN 80 struct mtree_writer { struct archive_entry *entry; struct archive_string ebuf; struct archive_string buf; int first; uint64_t entry_bytes_remaining; struct { int output; int processed; struct archive_string parent; mode_t type; int keys; uid_t uid; gid_t gid; mode_t mode; unsigned long fflags_set; unsigned long fflags_clear; } set; /* chekc sum */ int compute_sum; uint32_t crc; uint64_t crc_len; #ifdef HAVE_MD5 MD5_CTX md5ctx; #endif #if defined(HAVE_OPENSSL_RIPEMD_H) || defined(HAVE_RIPEMD_H) RIPEMD160_CTX rmd160ctx; #elif defined(HAVE_RMD160_H) RMD160_CTX rmd160ctx; #endif #ifdef HAVE_SHA1 #if defined(HAVE_OPENSSL_SHA_H) || defined(HAVE_SHA_H) SHA_CTX sha1ctx; #else SHA1_CTX sha1ctx; #endif #endif #ifdef HAVE_SHA256 SHA256_CTX sha256ctx; #endif #ifdef HAVE_SHA384 #if defined(HAVE_OPENSSL_SHA_H) SHA512_CTX sha384ctx; #else SHA384_CTX sha384ctx; #endif #endif #ifdef HAVE_SHA512 SHA512_CTX sha512ctx; #endif /* Keyword options */ int keys; #define F_CKSUM 0x00000001 /* check sum */ #define F_DEV 0x00000002 /* device type */ #define F_DONE 0x00000004 /* directory done */ #define F_FLAGS 0x00000008 /* file flags */ #define F_GID 0x00000010 /* gid */ #define F_GNAME 0x00000020 /* group name */ #define F_IGN 0x00000040 /* ignore */ #define F_MAGIC 0x00000080 /* name has magic chars */ #define F_MD5 0x00000100 /* MD5 digest */ #define F_MODE 0x00000200 /* mode */ #define F_NLINK 0x00000400 /* number of links */ #define F_NOCHANGE 0x00000800 /* If owner/mode "wrong", do * not change */ #define F_OPT 0x00001000 /* existence optional */ #define F_RMD160 0x00002000 /* RIPEMD160 digest */ #define F_SHA1 0x00004000 /* SHA-1 digest */ #define F_SIZE 0x00008000 /* size */ #define F_SLINK 0x00010000 /* symbolic link */ #define F_TAGS 0x00020000 /* tags */ #define F_TIME 0x00040000 /* modification time */ #define F_TYPE 0x00080000 /* file type */ #define F_UID 0x00100000 /* uid */ #define F_UNAME 0x00200000 /* user name */ #define F_VISIT 0x00400000 /* file visited */ #define F_SHA256 0x00800000 /* SHA-256 digest */ #define F_SHA384 0x01000000 /* SHA-384 digest */ #define F_SHA512 0x02000000 /* SHA-512 digest */ /* Options */ int dironly; /* if the dironly is 1, ignore everything except * directory type files. like mtree(8) -d option. */ int indent; /* if the indent is 1, indent writing data. */ }; #define DEFAULT_KEYS (F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\ | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\ | F_UNAME) #define COMPUTE_CRC(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] static const uint32_t crctab[] = { 0x0, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; static int mtree_safe_char(char c) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return 1; if (c >= '0' && c <= '9') return 1; if (c == 35 || c == 61 || c == 92) return 0; /* #, = and \ are always quoted */ if (c >= 33 && c <= 47) /* !"$%&'()*+,-./ */ return 1; if (c >= 58 && c <= 64) /* :;<>?@ */ return 1; if (c >= 91 && c <= 96) /* []^_` */ return 1; if (c >= 123 && c <= 126) /* {|}~ */ return 1; return 0; } static void mtree_quote(struct archive_string *s, const char *str) { const char *start; char buf[4]; unsigned char c; for (start = str; *str != '\0'; ++str) { if (mtree_safe_char(*str)) continue; if (start != str) archive_strncat(s, start, str - start); c = (unsigned char)*str; buf[0] = '\\'; buf[1] = (c / 64) + '0'; buf[2] = (c / 8 % 8) + '0'; buf[3] = (c % 8) + '0'; archive_strncat(s, buf, 4); start = str + 1; } if (start != str) archive_strncat(s, start, str - start); } static void mtree_indent(struct mtree_writer *mtree) { int i, fn; const char *r, *s, *x; fn = 1; s = r = mtree->ebuf.s; x = NULL; while (*r == ' ') r++; while ((r = strchr(r, ' ')) != NULL) { if (fn) { fn = 0; archive_strncat(&mtree->buf, s, r - s); if (r -s > INDENTNAMELEN) { archive_strncat(&mtree->buf, " \\\n", 3); for (i = 0; i < (INDENTNAMELEN + 1); i++) archive_strappend_char(&mtree->buf, ' '); } else { for (i = r -s; i < (INDENTNAMELEN + 1); i++) archive_strappend_char(&mtree->buf, ' '); } s = ++r; x = NULL; continue; } if (r - s <= MAXLINELEN - 3 - INDENTNAMELEN) x = r++; else { if (x == NULL) x = r; archive_strncat(&mtree->buf, s, x - s); archive_strncat(&mtree->buf, " \\\n", 3); for (i = 0; i < (INDENTNAMELEN + 1); i++) archive_strappend_char(&mtree->buf, ' '); s = r = ++x; x = NULL; } } if (x != NULL && strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) { /* Last keyword is longer. */ archive_strncat(&mtree->buf, s, x - s); archive_strncat(&mtree->buf, " \\\n", 3); for (i = 0; i < (INDENTNAMELEN + 1); i++) archive_strappend_char(&mtree->buf, ' '); s = ++x; } archive_strcat(&mtree->buf, s); archive_string_empty(&mtree->ebuf); } -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) static size_t dir_len(struct archive_entry *entry) { const char *path, *r; path = archive_entry_pathname(entry); r = strrchr(path, '/'); if (r == NULL) return (0); /* Include a separator size */ return (r - path + 1); } -#else +#else /* _WIN32 && !__CYGWIN__ */ /* * Note: We should use wide-character for findng '\' character, * a directory separator on Windows, because some character-set have * been using the '\' character for a part of its multibyte character * code. */ static size_t dir_len(struct archive_entry *entry) { wchar_t wc; const char *path; const char *p, *rp; size_t al, l, size; path = archive_entry_pathname(entry); al = l = -1; for (p = path; *p != '\0'; ++p) { if (*p == '\\') al = l = p - path; else if (*p == '/') al = p - path; } if (l == -1) goto alen; size = p - path; rp = p = path; while (*p != '\0') { l = mbtowc(&wc, p, size); if (l == -1) goto alen; if (l == 1 && (wc == L'/' || wc == L'\\')) rp = p; p += l; size -= l; } return (rp - path + 1); alen: if (al == -1) return (0); return (al + 1); } -#endif /* _WIN32 */ +#endif /* _WIN32 && !__CYGWIN__ */ static int parent_dir_changed(struct archive_string *dir, struct archive_entry *entry) { const char *path; size_t l; l = dir_len(entry); path = archive_entry_pathname(entry); if (archive_strlen(dir) > 0) { if (l == 0) { archive_string_empty(dir); return (1); } if (strncmp(dir->s, path, l) == 0) return (0); /* The parent directory is the same. */ } else if (l == 0) return (0); /* The parent directory is the same. */ archive_strncpy(dir, path, l); return (1); } /* * Write /set keyword. It means set global datas. * [directory-only mode] * - It is only once to write /set keyword. It is using values of the * first entry. * [normal mode] * - Write /set keyword. It is using values of the first entry whose * filetype is a regular file. * - When a parent directory of the entry whose filetype is the regular * file is changed, check the global datas and write it again if its * values are different from the entry's. */ static void set_global(struct mtree_writer *mtree, struct archive_entry *entry) { struct archive_string setstr; struct archive_string unsetstr; const char *name; int keys, oldkeys, effkeys; mode_t set_type = 0; switch (archive_entry_filetype(entry)) { case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: case AE_IFBLK: case AE_IFIFO: break; case AE_IFDIR: if (mtree->dironly) set_type = AE_IFDIR; break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if (!mtree->dironly) set_type = AE_IFREG; break; } if (set_type == 0) return; if (mtree->set.processed && !parent_dir_changed(&mtree->set.parent, entry)) return; /* At first, save a parent directory of the entry for following * entries. */ if (!mtree->set.processed && set_type == AE_IFREG) parent_dir_changed(&mtree->set.parent, entry); archive_string_init(&setstr); archive_string_init(&unsetstr); keys = mtree->keys & (F_FLAGS | F_GID | F_GNAME | F_NLINK | F_MODE | F_TYPE | F_UID | F_UNAME); oldkeys = mtree->set.keys; effkeys = keys; if (mtree->set.processed) { /* * Check the global datas for whether it needs updating. */ effkeys &= ~F_TYPE; if ((oldkeys & (F_UNAME | F_UID)) != 0 && mtree->set.uid == archive_entry_uid(entry)) effkeys &= ~(F_UNAME | F_UID); if ((oldkeys & (F_GNAME | F_GID)) != 0 && mtree->set.gid == archive_entry_gid(entry)) effkeys &= ~(F_GNAME | F_GID); if ((oldkeys & F_MODE) != 0 && mtree->set.mode == (archive_entry_mode(entry) & 07777)) effkeys &= ~F_MODE; if ((oldkeys & F_FLAGS) != 0) { unsigned long fflags_set; unsigned long fflags_clear; archive_entry_fflags(entry, &fflags_set, &fflags_clear); if (fflags_set == mtree->set.fflags_set && fflags_clear == mtree->set.fflags_clear) effkeys &= ~F_FLAGS; } } if ((keys & effkeys & F_TYPE) != 0) { mtree->set.type = set_type; if (set_type == AE_IFDIR) archive_strcat(&setstr, " type=dir"); else archive_strcat(&setstr, " type=file"); } if ((keys & effkeys & F_UNAME) != 0) { if ((name = archive_entry_uname(entry)) != NULL) { archive_strcat(&setstr, " uname="); mtree_quote(&setstr, name); } else if ((oldkeys & F_UNAME) != 0) archive_strcat(&unsetstr, " uname"); else keys &= ~F_UNAME; } if ((keys & effkeys & F_UID) != 0) { mtree->set.uid = archive_entry_uid(entry); archive_string_sprintf(&setstr, " uid=%jd", (intmax_t)mtree->set.uid); } if ((keys & effkeys & F_GNAME) != 0) { if ((name = archive_entry_gname(entry)) != NULL) { archive_strcat(&setstr, " gname="); mtree_quote(&setstr, name); } else if ((oldkeys & F_GNAME) != 0) archive_strcat(&unsetstr, " gname"); else keys &= ~F_GNAME; } if ((keys & effkeys & F_GID) != 0) { mtree->set.gid = archive_entry_gid(entry); archive_string_sprintf(&setstr, " gid=%jd", (intmax_t)mtree->set.gid); } if ((keys & effkeys & F_MODE) != 0) { mtree->set.mode = archive_entry_mode(entry) & 07777; archive_string_sprintf(&setstr, " mode=%o", mtree->set.mode); } if ((keys & effkeys & F_FLAGS) != 0) { if ((name = archive_entry_fflags_text(entry)) != NULL) { archive_strcat(&setstr, " flags="); mtree_quote(&setstr, name); archive_entry_fflags(entry, &mtree->set.fflags_set, &mtree->set.fflags_clear); } else if ((oldkeys & F_FLAGS) != 0) archive_strcat(&unsetstr, " flags"); else keys &= ~F_FLAGS; } if (unsetstr.length > 0) archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s); archive_string_free(&unsetstr); if (setstr.length > 0) archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s); archive_string_free(&setstr); mtree->set.keys = keys; mtree->set.processed = 1; /* On directory-only mode, it is only once to write /set keyword. */ if (mtree->dironly) mtree->set.output = 0; } static int get_keys(struct mtree_writer *mtree, struct archive_entry *entry) { int keys; keys = mtree->keys; if (mtree->set.keys == 0) return (keys); if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 && mtree->set.gid == archive_entry_gid(entry)) keys &= ~(F_GNAME | F_GID); if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 && mtree->set.uid == archive_entry_uid(entry)) keys &= ~(F_UNAME | F_UID); if (mtree->set.keys & F_FLAGS) { unsigned long set, clear; archive_entry_fflags(entry, &set, &clear); if (mtree->set.fflags_set == set && mtree->set.fflags_clear == clear) keys &= ~F_FLAGS; } if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == (archive_entry_mode(entry) & 07777)) keys &= ~F_MODE; switch (archive_entry_filetype(entry)) { case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: case AE_IFBLK: case AE_IFIFO: break; case AE_IFDIR: if ((mtree->set.keys & F_TYPE) != 0 && mtree->set.type == AE_IFDIR) keys &= ~F_TYPE; break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((mtree->set.keys & F_TYPE) != 0 && mtree->set.type == AE_IFREG) keys &= ~F_TYPE; break; } return (keys); } static int archive_write_mtree_header(struct archive_write *a, struct archive_entry *entry) { struct mtree_writer *mtree= a->format_data; struct archive_string *str; const char *path; mtree->entry = archive_entry_clone(entry); path = archive_entry_pathname(mtree->entry); if (mtree->first) { mtree->first = 0; archive_strcat(&mtree->buf, "#mtree\n"); } if (mtree->set.output) set_global(mtree, entry); archive_string_empty(&mtree->ebuf); str = (mtree->indent)? &mtree->ebuf : &mtree->buf; if (!mtree->dironly || archive_entry_filetype(entry) == AE_IFDIR) mtree_quote(str, path); mtree->entry_bytes_remaining = archive_entry_size(entry); if ((mtree->keys & F_CKSUM) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_CKSUM; mtree->crc = 0; mtree->crc_len = 0; } else mtree->compute_sum &= ~F_CKSUM; #ifdef HAVE_MD5 if ((mtree->keys & F_MD5) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_MD5; MD5_Init(&mtree->md5ctx); } else mtree->compute_sum &= ~F_MD5; #endif #ifdef HAVE_RMD160 if ((mtree->keys & F_RMD160) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_RMD160; RIPEMD160_Init(&mtree->rmd160ctx); } else mtree->compute_sum &= ~F_RMD160; #endif #ifdef HAVE_SHA1 if ((mtree->keys & F_SHA1) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA1; SHA1_Init(&mtree->sha1ctx); } else mtree->compute_sum &= ~F_SHA1; #endif #ifdef HAVE_SHA256 if ((mtree->keys & F_SHA256) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA256; SHA256_Init(&mtree->sha256ctx); } else mtree->compute_sum &= ~F_SHA256; #endif #ifdef HAVE_SHA384 if ((mtree->keys & F_SHA384) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA384; SHA384_Init(&mtree->sha384ctx); } else mtree->compute_sum &= ~F_SHA384; #endif #ifdef HAVE_SHA512 if ((mtree->keys & F_SHA512) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA512; SHA512_Init(&mtree->sha512ctx); } else mtree->compute_sum &= ~F_SHA512; #endif return (ARCHIVE_OK); } #if defined(HAVE_MD5) || defined(HAVE_RMD160) || defined(HAVE_SHA1) || defined(HAVE_SHA256) || defined(HAVE_SHA384) || defined(HAVE_SHA512) static void strappend_bin(struct archive_string *s, const unsigned char *bin, int n) { static const char hex[] = "0123456789abcdef"; int i; for (i = 0; i < n; i++) { archive_strappend_char(s, hex[bin[i] >> 4]); archive_strappend_char(s, hex[bin[i] & 0x0f]); } } #endif static int archive_write_mtree_finish_entry(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; struct archive_entry *entry; struct archive_string *str; const char *name; int keys, ret; entry = mtree->entry; if (entry == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Finished entry without being open first."); return (ARCHIVE_FATAL); } mtree->entry = NULL; if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_free(entry); return (ARCHIVE_OK); } str = (mtree->indent)? &mtree->ebuf : &mtree->buf; keys = get_keys(mtree, entry); if ((keys & F_NLINK) != 0 && archive_entry_nlink(entry) != 1 && archive_entry_filetype(entry) != AE_IFDIR) archive_string_sprintf(str, " nlink=%u", archive_entry_nlink(entry)); if ((keys & F_GNAME) != 0 && (name = archive_entry_gname(entry)) != NULL) { archive_strcat(str, " gname="); mtree_quote(str, name); } if ((keys & F_UNAME) != 0 && (name = archive_entry_uname(entry)) != NULL) { archive_strcat(str, " uname="); mtree_quote(str, name); } if ((keys & F_FLAGS) != 0 && (name = archive_entry_fflags_text(entry)) != NULL) { archive_strcat(str, " flags="); mtree_quote(str, name); } if ((keys & F_TIME) != 0) archive_string_sprintf(str, " time=%jd.%jd", (intmax_t)archive_entry_mtime(entry), (intmax_t)archive_entry_mtime_nsec(entry)); if ((keys & F_MODE) != 0) archive_string_sprintf(str, " mode=%o", archive_entry_mode(entry) & 07777); if ((keys & F_GID) != 0) archive_string_sprintf(str, " gid=%jd", (intmax_t)archive_entry_gid(entry)); if ((keys & F_UID) != 0) archive_string_sprintf(str, " uid=%jd", (intmax_t)archive_entry_uid(entry)); switch (archive_entry_filetype(entry)) { case AE_IFLNK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=link"); if ((keys & F_SLINK) != 0) { archive_strcat(str, " link="); mtree_quote(str, archive_entry_symlink(entry)); } break; case AE_IFSOCK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=socket"); break; case AE_IFCHR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=char"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%d,%d", archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); } break; case AE_IFBLK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=block"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%d,%d", archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); } break; case AE_IFDIR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=dir"); break; case AE_IFIFO: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=fifo"); break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((keys & F_TYPE) != 0) archive_strcat(str, " type=file"); if ((keys & F_SIZE) != 0) archive_string_sprintf(str, " size=%jd", (intmax_t)archive_entry_size(entry)); break; } if (mtree->compute_sum & F_CKSUM) { uint64_t len; /* Include the length of the file. */ for (len = mtree->crc_len; len != 0; len >>= 8) COMPUTE_CRC(mtree->crc, len & 0xff); mtree->crc = ~mtree->crc; archive_string_sprintf(str, " cksum=%ju", (uintmax_t)mtree->crc); } #ifdef HAVE_MD5 if (mtree->compute_sum & F_MD5) { unsigned char buf[16]; MD5_Final(buf, &mtree->md5ctx); archive_strcat(str, " md5digest="); strappend_bin(str, buf, sizeof(buf)); } #endif #ifdef HAVE_RMD160 if (mtree->compute_sum & F_RMD160) { unsigned char buf[20]; RIPEMD160_Final(buf, &mtree->rmd160ctx); archive_strcat(str, " rmd160digest="); strappend_bin(str, buf, sizeof(buf)); } #endif #ifdef HAVE_SHA1 if (mtree->compute_sum & F_SHA1) { unsigned char buf[20]; SHA1_Final(buf, &mtree->sha1ctx); archive_strcat(str, " sha1digest="); strappend_bin(str, buf, sizeof(buf)); } #endif #ifdef HAVE_SHA256 if (mtree->compute_sum & F_SHA256) { unsigned char buf[32]; SHA256_Final(buf, &mtree->sha256ctx); archive_strcat(str, " sha256digest="); strappend_bin(str, buf, sizeof(buf)); } #endif #ifdef HAVE_SHA384 if (mtree->compute_sum & F_SHA384) { unsigned char buf[48]; SHA384_Final(buf, &mtree->sha384ctx); archive_strcat(str, " sha384digest="); strappend_bin(str, buf, sizeof(buf)); } #endif #ifdef HAVE_SHA512 if (mtree->compute_sum & F_SHA512) { unsigned char buf[64]; SHA512_Final(buf, &mtree->sha512ctx); archive_strcat(str, " sha512digest="); strappend_bin(str, buf, sizeof(buf)); } #endif archive_strcat(str, "\n"); if (mtree->indent) mtree_indent(mtree); archive_entry_free(entry); if (mtree->buf.length > 32768) { ret = (a->compressor.write)(a, mtree->buf.s, mtree->buf.length); archive_string_empty(&mtree->buf); } else ret = ARCHIVE_OK; return (ret == ARCHIVE_OK ? ret : ARCHIVE_FATAL); } static int archive_write_mtree_finish(struct archive_write *a) { struct mtree_writer *mtree= a->format_data; archive_write_set_bytes_in_last_block(&a->archive, 1); return (a->compressor.write)(a, mtree->buf.s, mtree->buf.length); } static ssize_t archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n) { struct mtree_writer *mtree= a->format_data; if (n > mtree->entry_bytes_remaining) n = mtree->entry_bytes_remaining; if (mtree->dironly) /* We don't need compute a regular file sum */ return (n); if (mtree->compute_sum & F_CKSUM) { /* * Compute a POSIX 1003.2 checksum */ const unsigned char *p; int nn; for (nn = n, p = buff; nn--; ++p) COMPUTE_CRC(mtree->crc, *p); mtree->crc_len += n; } #ifdef HAVE_MD5 if (mtree->compute_sum & F_MD5) MD5_Update(&mtree->md5ctx, buff, n); #endif #ifdef HAVE_RMD160 if (mtree->compute_sum & F_RMD160) RIPEMD160_Update(&mtree->rmd160ctx, buff, n); #endif #ifdef HAVE_SHA1 if (mtree->compute_sum & F_SHA1) SHA1_Update(&mtree->sha1ctx, buff, n); #endif #ifdef HAVE_SHA256 if (mtree->compute_sum & F_SHA256) SHA256_Update(&mtree->sha256ctx, buff, n); #endif #ifdef HAVE_SHA384 if (mtree->compute_sum & F_SHA384) SHA384_Update(&mtree->sha384ctx, buff, n); #endif #ifdef HAVE_SHA512 if (mtree->compute_sum & F_SHA512) SHA512_Update(&mtree->sha512ctx, buff, n); #endif return (n); } static int archive_write_mtree_destroy(struct archive_write *a) { struct mtree_writer *mtree= a->format_data; if (mtree == NULL) return (ARCHIVE_OK); archive_entry_free(mtree->entry); archive_string_free(&mtree->ebuf); archive_string_free(&mtree->buf); archive_string_free(&mtree->set.parent); free(mtree); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_mtree_options(struct archive_write *a, const char *key, const char *value) { struct mtree_writer *mtree= a->format_data; int keybit = 0; switch (key[0]) { case 'a': if (strcmp(key, "all") == 0) keybit = ~0; break; case 'c': if (strcmp(key, "cksum") == 0) keybit = F_CKSUM; break; case 'd': if (strcmp(key, "device") == 0) keybit = F_DEV; else if (strcmp(key, "dironly") == 0) mtree->dironly = (value != NULL)? 1: 0; break; case 'f': if (strcmp(key, "flags") == 0) keybit = F_FLAGS; break; case 'g': if (strcmp(key, "gid") == 0) keybit = F_GID; else if (strcmp(key, "gname") == 0) keybit = F_GNAME; break; case 'i': if (strcmp(key, "indent") == 0) mtree->indent = (value != NULL)? 1: 0; break; case 'l': if (strcmp(key, "link") == 0) keybit = F_SLINK; break; case 'm': #ifdef HAVE_MD5 if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) keybit = F_MD5; #endif if (strcmp(key, "mode") == 0) keybit = F_MODE; break; case 'n': if (strcmp(key, "nlink") == 0) keybit = F_NLINK; break; #ifdef HAVE_RMD160 case 'r': if (strcmp(key, "ripemd160digest") == 0 || strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) keybit = F_RMD160; break; #endif case 's': #ifdef HAVE_SHA1 if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) keybit = F_SHA1; #endif #ifdef HAVE_SHA256 if (strcmp(key, "sha256") == 0 || strcmp(key, "sha256digest") == 0) keybit = F_SHA256; #endif #ifdef HAVE_SHA384 if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) keybit = F_SHA384; #endif #ifdef HAVE_SHA384 if (strcmp(key, "sha512") == 0 || strcmp(key, "sha512digest") == 0) keybit = F_SHA512; #endif if (strcmp(key, "size") == 0) keybit = F_SIZE; break; case 't': if (strcmp(key, "time") == 0) keybit = F_TIME; else if (strcmp(key, "type") == 0) keybit = F_TYPE; break; case 'u': if (strcmp(key, "uid") == 0) keybit = F_UID; else if (strcmp(key, "uname") == 0) keybit = F_UNAME; else if (strcmp(key, "use-set") == 0) mtree->set.output = (value != NULL)? 1: 0; break; } if (keybit != 0) { if (value != NULL) mtree->keys |= keybit; else mtree->keys &= ~keybit; return (ARCHIVE_OK); } return (ARCHIVE_WARN); } int archive_write_set_format_mtree(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct mtree_writer *mtree; if (a->format_destroy != NULL) (a->format_destroy)(a); if ((mtree = malloc(sizeof(*mtree))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } mtree->entry = NULL; mtree->first = 1; memset(&(mtree->set), 0, sizeof(mtree->set)); archive_string_init(&mtree->set.parent); mtree->keys = DEFAULT_KEYS; mtree->dironly = 0; mtree->indent = 0; archive_string_init(&mtree->ebuf); archive_string_init(&mtree->buf); a->format_data = mtree; a->format_destroy = archive_write_mtree_destroy; a->pad_uncompressed = 0; a->format_name = "mtree"; a->format_options = archive_write_mtree_options; a->format_write_header = archive_write_mtree_header; a->format_finish = archive_write_mtree_finish; a->format_write_data = archive_write_mtree_data; a->format_finish_entry = archive_write_mtree_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_MTREE; a->archive.archive_format_name = "mtree"; return (ARCHIVE_OK); } diff --git a/lib/libarchive/test/main.c b/lib/libarchive/test/main.c index 62a0a3f0f9ce..01a081705ef1 100644 --- a/lib/libarchive/test/main.c +++ b/lib/libarchive/test/main.c @@ -1,1180 +1,1180 @@ /* * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Various utility routines useful for test programs. * Each test program is linked against this file. */ #include "test.h" #include #include #include #include -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #endif /* * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. */ #undef PROGRAM /* Testing a library, not a program. */ #define LIBRARY "libarchive" #define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */ #define EXTRA_DUMP(x) archive_error_string((struct archive *)(x)) #define EXTRA_VERSION archive_version() #define KNOWNREF "test_compat_gtar_1.tar.uu" __FBSDID("$FreeBSD$"); /* * "list.h" is simply created by "grep DEFINE_TEST"; it has * a line like * DEFINE_TEST(test_function) * for each test. * Include it here with a suitable DEFINE_TEST to declare all of the * test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #ifdef LIST_H #include LIST_H #else #include "list.h" #endif /* Interix doesn't define these in a standard header. */ #if __INTERIX__ extern char *optarg; extern int optind; #endif /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs for successful tests. */ static int keep_temp_files = 0; /* Default is to print some basic information about each test. */ static int quiet_flag = 0; /* Default is to summarize repeated failures. */ static int verbose = 0; /* Cumulative count of component failures. */ static int failures = 0; /* Cumulative count of skipped component tests. */ static int skips = 0; /* Cumulative count of assertions. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ } #endif /* * My own implementation of the standard assert() macro emits the * message in the same format as GCC (file:line: message). * It also includes some additional useful information. * This makes it a lot easier to skim through test failures in * Emacs. ;-) * * It also supports a few special features specifically to simplify * test harnesses: * failure(fmt, args) -- Stores a text string that gets * printed if the following assertion fails, good for * explaining subtle tests. */ static char msg[4096]; /* * For each test source file, we remember how many times each * failure was reported. */ static const char *failed_filename = NULL; static struct line { int line; int count; } failed_lines[1000]; /* * Count this failure; return the number of previous failures. */ static int previous_failures(const char *filename, int line) { unsigned int i; int count; if (failed_filename == NULL || strcmp(failed_filename, filename) != 0) memset(failed_lines, 0, sizeof(failed_lines)); failed_filename = filename; for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].line == line) { count = failed_lines[i].count; failed_lines[i].count++; return (count); } if (failed_lines[i].line == 0) { failed_lines[i].line = line; failed_lines[i].count = 1; return (0); } } return (0); } /* * Copy arguments into file-local variables. */ static const char *test_filename; static int test_line; static void *test_extra; void test_setup(const char *filename, int line) { test_filename = filename; test_line = line; } /* * Inform user that we're skipping a test. */ void test_skipping(const char *fmt, ...) { va_list ap; if (previous_failures(test_filename, test_line)) return; va_start(ap, fmt); fprintf(stderr, " *** SKIPPING: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); ++skips; } /* Common handling of failed tests. */ static void report_failure(void *extra) { if (msg[0] != '\0') { fprintf(stderr, " Description: %s\n", msg); msg[0] = '\0'; } #ifdef EXTRA_DUMP if (extra != NULL) fprintf(stderr, " detail: %s\n", EXTRA_DUMP(extra)); #else (void)extra; /* UNUSED */ #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); *(char *)(NULL) = 0; exit(1); } } /* * Summarize repeated failures in the just-completed test file. * The reports above suppress multiple failures from the same source * line; this reports on any tests that did fail multiple times. */ static int summarize_comparator(const void *a0, const void *b0) { const struct line *a = a0, *b = b0; if (a->line == 0 && b->line == 0) return (0); if (a->line == 0) return (1); if (b->line == 0) return (-1); return (a->line - b->line); } static void summarize(void) { unsigned int i; qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]), sizeof(failed_lines[0]), summarize_comparator); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].line == 0) break; if (failed_lines[i].count > 1) fprintf(stderr, "%s:%d: Failed %d times\n", failed_filename, failed_lines[i].line, failed_lines[i].count); } /* Clear the failure history for the next file. */ memset(failed_lines, 0, sizeof(failed_lines)); } /* Set up a message to display only after a test fails. */ void failure(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsprintf(msg, fmt, ap); va_end(ap); } /* Generic assert() just displays the failed condition. */ int test_assert(const char *file, int line, int value, const char *condition, void *extra) { ++assertions; if (value) { msg[0] = '\0'; return (value); } failures ++; if (!verbose && previous_failures(file, line)) return (value); fprintf(stderr, "%s:%d: Assertion failed\n", file, line); fprintf(stderr, " Condition: %s\n", condition); report_failure(extra); return (value); } /* assertEqualInt() displays the values of the two integers. */ int test_assert_equal_int(const char *file, int line, int v1, const char *e1, int v2, const char *e2, void *extra) { ++assertions; if (v1 == v2) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n", file, line); fprintf(stderr, " %s=%d\n", e1, v1); fprintf(stderr, " %s=%d\n", e2, v2); report_failure(extra); return (0); } static void strdump(const char *p) { if (p == NULL) { fprintf(stderr, "(null)"); return; } fprintf(stderr, "\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': fprintf(stderr, "\a"); break; case '\b': fprintf(stderr, "\b"); break; case '\n': fprintf(stderr, "\n"); break; case '\r': fprintf(stderr, "\r"); break; default: if (c >= 32 && c < 127) fprintf(stderr, "%c", c); else fprintf(stderr, "\\x%02X", c); } } fprintf(stderr, "\""); } /* assertEqualString() displays the values of the two strings. */ int test_assert_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra) { ++assertions; if (v1 == NULL || v2 == NULL) { if (v1 == v2) { msg[0] = '\0'; return (1); } } else if (strcmp(v1, v2) == 0) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n", file, line); fprintf(stderr, " %s = ", e1); strdump(v1); fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : (int)strlen(v1)); fprintf(stderr, " %s = ", e2); strdump(v2); fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : (int)strlen(v2)); report_failure(extra); return (0); } static void wcsdump(const wchar_t *w) { if (w == NULL) { fprintf(stderr, "(null)"); return; } fprintf(stderr, "\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) fprintf(stderr, "%c", c); else if (c < 256) fprintf(stderr, "\\x%02X", c); else if (c < 0x10000) fprintf(stderr, "\\u%04X", c); else fprintf(stderr, "\\U%08X", c); } fprintf(stderr, "\""); } /* assertEqualWString() displays the values of the two strings. */ int test_assert_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { ++assertions; if (v1 == NULL) { if (v2 == NULL) { msg[0] = '\0'; return (1); } } else if (v2 == NULL) { if (v1 == NULL) { msg[0] = '\0'; return (1); } } else if (wcscmp(v1, v2) == 0) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n", file, line); fprintf(stderr, " %s = ", e1); wcsdump(v1); fprintf(stderr, "\n"); fprintf(stderr, " %s = ", e2); wcsdump(v2); fprintf(stderr, "\n"); report_failure(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; for(i=0; i < l; i+=16) { fprintf(stderr, "%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { fprintf(stderr, "%c ", sep); sep = ' '; } fprintf(stderr, "%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) fprintf(stderr, "%c", c); else fprintf(stderr, "."); } fprintf(stderr, "\n"); } } /* assertEqualMem() displays the values of the two memory blocks. */ /* TODO: For long blocks, hexdump the first bytes that actually differ. */ int test_assert_equal_mem(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, size_t l, const char *ld, void *extra) { ++assertions; if (v1 == NULL || v2 == NULL) { if (v1 == v2) { msg[0] = '\0'; return (1); } } else if (memcmp(v1, v2, l) == 0) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n", file, line); fprintf(stderr, " size %s = %d\n", ld, (int)l); fprintf(stderr, " Dump of %s\n", e1); hexdump(v1, v2, l < 32 ? l : 32, 0); fprintf(stderr, " Dump of %s\n", e2); hexdump(v2, v1, l < 32 ? l : 32, 0); fprintf(stderr, "\n"); report_failure(extra); return (0); } int test_assert_empty_file(const char *f1fmt, ...) { char buff[1024]; char f1[1024]; struct stat st; va_list ap; ssize_t s; int fd; va_start(ap, f1fmt); vsprintf(f1, f1fmt, ap); va_end(ap); if (stat(f1, &st) != 0) { fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1); report_failure(NULL); return (0); } if (st.st_size == 0) return (1); failures ++; if (!verbose && previous_failures(test_filename, test_line)) return (0); fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1); fprintf(stderr, " File size: %d\n", (int)st.st_size); fprintf(stderr, " Contents:\n"); fd = open(f1, O_RDONLY); if (fd < 0) { fprintf(stderr, " Unable to open %s\n", f1); } else { s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size; s = read(fd, buff, s); hexdump(buff, NULL, s, 0); } report_failure(NULL); return (0); } /* assertEqualFile() asserts that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int test_assert_equal_file(const char *f1, const char *f2pattern, ...) { char f2[1024]; va_list ap; char buff1[1024]; char buff2[1024]; int fd1, fd2; int n1, n2; va_start(ap, f2pattern); vsprintf(f2, f2pattern, ap); va_end(ap); fd1 = open(f1, O_RDONLY); fd2 = open(f2, O_RDONLY); for (;;) { n1 = read(fd1, buff1, sizeof(buff1)); n2 = read(fd2, buff2, sizeof(buff2)); if (n1 != n2) break; if (n1 == 0 && n2 == 0) return (1); if (memcmp(buff1, buff2, n1) != 0) break; } failures ++; if (!verbose && previous_failures(test_filename, test_line)) return (0); fprintf(stderr, "%s:%d: Files are not identical\n", test_filename, test_line); fprintf(stderr, " file1=\"%s\"\n", f1); fprintf(stderr, " file2=\"%s\"\n", f2); report_failure(test_extra); return (0); } int test_assert_file_exists(const char *fpattern, ...) { char f[1024]; va_list ap; va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); if (!access(f, F_OK)) return (1); if (!previous_failures(test_filename, test_line)) { fprintf(stderr, "%s:%d: File doesn't exist\n", test_filename, test_line); fprintf(stderr, " file=\"%s\"\n", f); report_failure(test_extra); } return (0); } int test_assert_file_not_exists(const char *fpattern, ...) { char f[1024]; va_list ap; va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); if (access(f, F_OK)) return (1); if (!previous_failures(test_filename, test_line)) { fprintf(stderr, "%s:%d: File exists and shouldn't\n", test_filename, test_line); fprintf(stderr, " file=\"%s\"\n", f); report_failure(test_extra); } return (0); } /* assertFileContents() asserts the contents of a file. */ int test_assert_file_contents(const void *buff, int s, const char *fpattern, ...) { char f[1024]; va_list ap; char *contents; int fd; int n; va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); fd = open(f, O_RDONLY); contents = malloc(s * 2); n = read(fd, contents, s * 2); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failures ++; if (!previous_failures(test_filename, test_line)) { fprintf(stderr, "%s:%d: File contents don't match\n", test_filename, test_line); fprintf(stderr, " file=\"%s\"\n", f); if (n > 0) hexdump(contents, buff, n, 0); else { fprintf(stderr, " File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } report_failure(test_extra); } free(contents); return (0); } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; int fd; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); fd = open(filename, O_RDONLY); if (fd < 0) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fd, &st); if (r != 0) { fprintf(stderr, "Can't stat file %s\n", filename); close(fd); return (NULL); } p = malloc(st.st_size + 1); if (p == NULL) { fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); close(fd); return (NULL); } bytes_read = read(fd, p, st.st_size); if (bytes_read < st.st_size) { fprintf(stderr, "Can't read file %s\n", filename); close(fd); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; close(fd); return (p); } /* * "list.h" is automatically generated; it just has a lot of lines like: * DEFINE_TEST(function_name) * It's used above to declare all of the test functions. * We reuse it here to define a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n }, struct { void (*func)(void); const char *name; } tests[] = { #ifdef LIST_H #include LIST_H #else #include "list.h" #endif }; static void close_descriptors(int warn) { int i; int left_open = 0; for (i = 3; i < 100; ++i) { if (close(i) == 0) ++left_open; } if (warn && left_open > 0) { fprintf(stderr, " ** %d descriptors unclosed\n", left_open); failures += left_open; report_failure(NULL); } } /* * Each test is run in a private work dir. Those work dirs * do have consistent and predictable names, in case a group * of tests need to collaborate. However, there is no provision * for requiring that tests run in a certain order. */ static int test_run(int i, const char *tmpdir) { int failures_before = failures; if (!quiet_flag) { printf("%d: %s\n", i, tests[i].name); fflush(stdout); } /* * Always explicitly chdir() in case the last test moved us to * a strange place. */ if (chdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Create a temp directory for this specific test. */ if (mkdir(tests[i].name, 0755)) { fprintf(stderr, "ERROR: Couldn't create temp dir ``%s''\n", tests[i].name); exit(1); } /* Chdir() to that work directory. */ if (chdir(tests[i].name)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir ``%s''\n", tests[i].name); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Make sure there are no stray descriptors going into the test. */ close_descriptors(0); /* Run the actual test. */ (*tests[i].func)(); /* Close stray descriptors, record as errors against this test. */ close_descriptors(1); /* Summarize the results of this test. */ summarize(); /* If there were no failures, we can remove the work dir. */ if (failures == failures_before) { if (!keep_temp_files && chdir(tmpdir) == 0) { -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) systemf("rm -rf %s", tests[i].name); #else systemf("rmdir /S /Q %s", tests[i].name); #endif } } /* Return appropriate status. */ return (failures == failures_before ? 0 : 1); } static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "w"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #define DEV_NULL "NUL" #else #define DEV_NULL "/dev/null" #endif const char * external_gzip_program(int un) { const char *extprog; if (un) { extprog = "gunzip"; if (systemf("%s -V >" DEV_NULL " 2>" DEV_NULL, extprog) == 0) return (extprog); extprog = "gzip -d"; if (systemf("%s -V >" DEV_NULL " 2>" DEV_NULL, extprog) == 0) return (extprog); } else { extprog = "gzip"; if (systemf("%s -V >" DEV_NULL " 2>" DEV_NULL, extprog) == 0) return (extprog); } return (NULL); } static char * get_refdir(void) { char tried[512] = { '\0' }; char buff[128]; char *pwd, *p; /* Get the current dir. */ pwd = getcwd(NULL, 0); while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; printf("PWD: %s\n", pwd); /* Look for a known file. */ snprintf(buff, sizeof(buff), "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); snprintf(buff, sizeof(buff), "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); if (memcmp(pwd, "/usr/obj", 8) == 0) { snprintf(buff, sizeof(buff), "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); snprintf(buff, sizeof(buff), "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); } -#if defined(_WIN32) && defined(_DEBUG) +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); exit(1); success: free(p); free(pwd); return strdup(buff); } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname = LIBRARY "_test"; const char *tmp, *option_arg, *p; char tmpdir[256]; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* for open() to a binary mode. */ _set_fmode(_O_BINARY); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif #ifdef PROGRAM /* Get the target program from environment, if available. */ testprog = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprog = option_arg; #else usage(progname); #endif break; case 'q': quiet_flag++; break; case 'r': refdir = option_arg; break; case 'v': verbose = 1; break; default: usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprog == NULL) usage(progname); #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; i < 1000; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (mkdir(tmpdir,0755) == 0) break; if (errno == EEXIST) continue; fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ if (refdir == NULL) refdir = refdir_alloc = get_refdir(); /* * Banner with basic information. */ if (!quiet_flag) { printf("Running tests in: %s\n", tmpdir); printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } /* * Run some or all of the individual tests. */ if (*argv == NULL) { /* Default: Run all tests. */ for (i = 0; i < limit; i++) { if (test_run(i, tmpdir)) tests_failed++; tests_run++; } } else { while (*(argv) != NULL) { i = atoi(*argv); if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); usage(progname); } else { if (test_run(i, tmpdir)) tests_failed++; tests_run++; } argv++; } } /* * Report summary statistics. */ if (!quiet_flag) { printf("\n"); printf("%d of %d tests reported failures\n", tests_failed, tests_run); printf(" Total of %d assertions checked.\n", assertions); printf(" Total of %d assertions failed.\n", failures); printf(" Total of %d reported skips.\n", skips); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ rmdir(tmpdir); return (tests_failed); } diff --git a/lib/libarchive/test/test.h b/lib/libarchive/test/test.h index fcac91ad285e..539c8262cb5f 100644 --- a/lib/libarchive/test/test.h +++ b/lib/libarchive/test/test.h @@ -1,202 +1,207 @@ /* * Copyright (c) 2003-2006 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* Every test program should #include "test.h" as the first thing. */ /* * The goal of this file (and the matching test.c) is to * simplify the very repetitive test-*.c test programs. */ #if defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ #include "config.h" #elif defined(__FreeBSD__) /* Building as part of FreeBSD system requires a pre-built config.h. */ #include "config_freebsd.h" -#elif defined(_WIN32) +#elif defined(_WIN32) && !defined(__CYGWIN__) /* Win32 can't run the 'configure' script. */ #include "config_windows.h" #else /* Warn if the library hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no pre-built configuration in test.h. #endif -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) #include #else #include #endif #include #include #include #include #include #include -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) #include #endif #include #ifdef USE_DMALLOC #include #endif /* No non-FreeBSD platform will have __FBSDID, so just define it here. */ #ifdef __FreeBSD__ #include /* For __FBSDID */ #else +/* Some non-FreeBSD platforms such as newlib-derived ones like + * cygwin, have __FBSDID, so this definition must be guarded. + */ +#ifndef __FBSDID #define __FBSDID(a) /* null */ #endif +#endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) #define snprintf sprintf_s #define LOCALE_DE "deu" #else #define LOCALE_DE "de_DE.UTF-8" #endif #ifndef O_BINARY #define O_BINARY 0 #endif /* * Redefine DEFINE_TEST for use in defining the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); void name(void) /* An implementation of the standard assert() macro */ #define assert(e) test_assert(__FILE__, __LINE__, (e), #e, NULL) /* Assert two integers are the same. Reports value of each one if not. */ #define assertEqualInt(v1,v2) \ test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* Assert two strings are the same. Reports value of each one if not. */ #define assertEqualString(v1,v2) \ test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* As above, but v1 and v2 are wchar_t * */ #define assertEqualWString(v1,v2) \ test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* As above, but raw blocks of bytes. */ #define assertEqualMem(v1, v2, l) \ test_assert_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) /* Assert two files are the same; allow printf-style expansion of second name. * See below for comments about variable arguments here... */ #define assertEqualFile \ test_setup(__FILE__, __LINE__);test_assert_equal_file /* Assert that a file is empty; supports printf-style arguments. */ #define assertEmptyFile \ test_setup(__FILE__, __LINE__);test_assert_empty_file /* Assert that a file exists; supports printf-style arguments. */ #define assertFileExists \ test_setup(__FILE__, __LINE__);test_assert_file_exists /* Assert that a file exists; supports printf-style arguments. */ #define assertFileNotExists \ test_setup(__FILE__, __LINE__);test_assert_file_not_exists /* Assert that file contents match a string; supports printf-style arguments. */ #define assertFileContents \ test_setup(__FILE__, __LINE__);test_assert_file_contents /* * This would be simple with C99 variadic macros, but I don't want to * require that. Instead, I insert a function call before each * skipping() call to pass the file and line information down. Crude, * but effective. */ #define skipping \ test_setup(__FILE__, __LINE__);test_skipping /* Function declarations. These are defined in test_utility.c. */ void failure(const char *fmt, ...); void test_setup(const char *, int); void test_skipping(const char *fmt, ...); int test_assert(const char *, int, int, const char *, void *); int test_assert_empty_file(const char *, ...); int test_assert_equal_file(const char *, const char *, ...); int test_assert_equal_int(const char *, int, int, const char *, int, const char *, void *); int test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *); int test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); int test_assert_equal_mem(const char *, int, const char *, const char *, const char *, const char *, size_t, const char *, void *); int test_assert_file_contents(const void *, int, const char *, ...); int test_assert_file_exists(const char *, ...); int test_assert_file_not_exists(const char *, ...); /* Like sprintf, then system() */ int systemf(const char * fmt, ...); /* Suck file into string allocated via malloc(). Call free() when done. */ /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */ char *slurpfile(size_t *, const char *fmt, ...); /* Extracts named reference file to the current directory. */ void extract_reference_file(const char *); /* Get external gzip program name */ const char *external_gzip_program(int un); /* * Special interfaces for libarchive test harness. */ #include "archive.h" #include "archive_entry.h" /* Special customized read-from-memory interface. */ int read_open_memory(struct archive *, void *, size_t, size_t); /* "2" version exercises a slightly different set of libarchive APIs. */ int read_open_memory2(struct archive *, void *, size_t, size_t); /* * ARCHIVE_VERSION_STAMP first appeared in 1.9 and libarchive 2.2.4. * We can approximate it for earlier versions, though. * This is used to disable tests of features not present in the current * version. */ #ifndef ARCHIVE_VERSION_STAMP #define ARCHIVE_VERSION_STAMP \ (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) #endif /* Versions of above that accept an archive argument for additional info. */ #define assertA(e) test_assert(__FILE__, __LINE__, (e), #e, (a)) #define assertEqualIntA(a,v1,v2) \ test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a)) #define assertEqualStringA(a,v1,v2) \ test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a)) /* * A compression is not supported * Use this define after archive_read_next_header() is called */ #define UnsupportedCompress(r, a) \ (r != ARCHIVE_OK && \ (strcmp(archive_error_string(a), \ "Unrecognized archive format") == 0 && \ archive_compression(a) == ARCHIVE_COMPRESSION_NONE)) diff --git a/lib/libarchive/test/test_read_disk.c b/lib/libarchive/test/test_read_disk.c index e368398e4689..ce10440df54b 100644 --- a/lib/libarchive/test/test_read_disk.c +++ b/lib/libarchive/test/test_read_disk.c @@ -1,162 +1,168 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); static void gname_cleanup(void *d) { int *mp = d; assertEqualInt(*mp, 0x13579); *mp = 0x2468; } static const char * gname_lookup(void *d, gid_t g) { int *mp = d; assertEqualInt(*mp, 0x13579); if (g == 1) return ("FOOGROUP"); return ("NOTFOOGROUP"); } static void uname_cleanup(void *d) { int *mp = d; assertEqualInt(*mp, 0x1234); *mp = 0x2345; } static const char * uname_lookup(void *d, uid_t u) { int *mp = d; assertEqualInt(*mp, 0x1234); if (u == 1) return ("FOO"); return ("NOTFOO"); } /* We test GID lookup by looking up the name of group number zero and * checking it against the following list. If your system uses a * different conventional name for group number zero, please extend * this array and send us a patch. As always, please keep this list * sorted alphabetically. */ static const char *zero_groups[] = { "root", /* Linux */ "wheel" /* BSD */ }; DEFINE_TEST(test_read_disk) { struct archive *a; int gmagic = 0x13579, umagic = 0x1234; const char *p; size_t i; assert((a = archive_read_disk_new()) != NULL); /* Default uname/gname lookups always return NULL. */ assert(archive_read_disk_gname(a, 0) == NULL); assert(archive_read_disk_uname(a, 0) == NULL); /* Register some weird lookup functions. */ assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a, &gmagic, &gname_lookup, &gname_cleanup)); /* Verify that our new function got called. */ assertEqualString(archive_read_disk_gname(a, 0), "NOTFOOGROUP"); assertEqualString(archive_read_disk_gname(a, 1), "FOOGROUP"); /* De-register. */ assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a, NULL, NULL, NULL)); /* Ensure our cleanup function got called. */ assertEqualInt(gmagic, 0x2468); /* Same thing with uname lookup.... */ assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a, &umagic, &uname_lookup, &uname_cleanup)); assertEqualString(archive_read_disk_uname(a, 0), "NOTFOO"); assertEqualString(archive_read_disk_uname(a, 1), "FOO"); assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a, NULL, NULL, NULL)); assertEqualInt(umagic, 0x2345); /* Try the standard lookup functions. */ if (archive_read_disk_set_standard_lookup(a) != ARCHIVE_OK) { skipping("standard uname/gname lookup"); } else { +#if defined(__CYGWIN__) + skipping("standard uname/gname lookup; typically no user with uid=0 on cygwin platform"); + i = 0; + p = zero_groups[0]; /* avoid unused warnings */ +#else /* XXX Someday, we may need to generalize this the * same way we generalized the group name check below. * That's needed only if we encounter a system where * uid 0 is not "root". XXX */ assertEqualString(archive_read_disk_uname(a, 0), "root"); /* Get the group name for group 0 and see if it makes sense. */ p = archive_read_disk_gname(a, 0); i = 0; while (i < sizeof(zero_groups)/sizeof(zero_groups[0])) { if (strcmp(zero_groups[i], p) == 0) break; ++i; } if (i == sizeof(zero_groups)/sizeof(zero_groups[0])) { /* If you get a failure here, either * archive_read_disk_gname() isn't working or * your system uses a different name for group * number zero. If the latter, please add a * new entry to the zero_groups[] array above. */ failure("group 0 didn't have any of the expected names"); assertEqualString(p, zero_groups[0]); } +#endif } /* Deregister again and verify the default lookups again. */ assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a, NULL, NULL, NULL)); assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a, NULL, NULL, NULL)); assert(archive_read_disk_gname(a, 0) == NULL); assert(archive_read_disk_uname(a, 0) == NULL); /* Re-register our custom handlers. */ gmagic = 0x13579; umagic = 0x1234; assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a, &gmagic, &gname_lookup, &gname_cleanup)); assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a, &umagic, &uname_lookup, &uname_cleanup)); /* Destroy the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); /* Verify our cleanup functions got called. */ assertEqualInt(gmagic, 0x2468); assertEqualInt(umagic, 0x2345); } diff --git a/lib/libarchive/test/test_read_extract.c b/lib/libarchive/test/test_read_extract.c index b58b364a8418..887ddfde6852 100644 --- a/lib/libarchive/test/test_read_extract.c +++ b/lib/libarchive/test/test_read_extract.c @@ -1,200 +1,200 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #define BUFF_SIZE 1000000 #define FILE_BUFF_SIZE 100000 DEFINE_TEST(test_read_extract) { struct archive_entry *ae; struct archive *a; -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) struct stat st; #endif size_t used; int i; char *buff, *file_buff; -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) int fd; ssize_t bytes_read; #endif buff = malloc(BUFF_SIZE); file_buff = malloc(FILE_BUFF_SIZE); /* Force the umask to something predictable. */ umask(022); /* Create a new archive in memory containing various types of entries. */ assert((a = archive_write_new()) != NULL); assertA(0 == archive_write_set_format_ustar(a)); assertA(0 == archive_write_set_compression_none(a)); assertA(0 == archive_write_open_memory(a, buff, BUFF_SIZE, &used)); /* A directory to be restored with EXTRACT_PERM. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir_0775"); archive_entry_set_mode(ae, S_IFDIR | 0775); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* A regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0755); for (i = 0; i < FILE_BUFF_SIZE; i++) file_buff[i] = (unsigned char)rand(); archive_entry_set_size(ae, FILE_BUFF_SIZE); assertA(0 == archive_write_header(a, ae)); assertA(FILE_BUFF_SIZE == archive_write_data(a, file_buff, FILE_BUFF_SIZE)); archive_entry_free(ae); /* A directory that should obey umask when restored. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir"); archive_entry_set_mode(ae, S_IFDIR | 0777); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* A file in the directory. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir/file"); archive_entry_set_mode(ae, S_IFREG | 0700); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* A file in a dir that is not already in the archive. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir2/file"); archive_entry_set_mode(ae, S_IFREG | 0000); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* A dir with a trailing /. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir3/."); archive_entry_set_mode(ae, S_IFDIR | 0710); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Multiple dirs with a single entry. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir4/a/../b/../c/"); archive_entry_set_mode(ae, S_IFDIR | 0711); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* A symlink. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "symlink"); archive_entry_set_mode(ae, S_IFLNK | 0755); archive_entry_set_symlink(ae, "file"); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertA(0 == archive_write_close(a)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(a); #else assertA(0 == archive_write_finish(a)); #endif /* Extract the entries to disk. */ assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_support_compression_all(a)); assertA(0 == archive_read_open_memory(a, buff, BUFF_SIZE)); /* Restore first entry with _EXTRACT_PERM. */ failure("Error reading first entry", i); assertA(0 == archive_read_next_header(a, &ae)); assertA(0 == archive_read_extract(a, ae, ARCHIVE_EXTRACT_PERM)); /* Rest of entries get restored with no flags. */ for (i = 0; i < 7; i++) { failure("Error reading entry %d", i+1); assertA(0 == archive_read_next_header(a, &ae)); assertA(0 == archive_read_extract(a, ae, 0)); } assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); assert(0 == archive_read_close(a)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_read_finish(a); #else assert(0 == archive_read_finish(a)); #endif -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) /* Test the entries on disk. */ /* This first entry was extracted with ARCHIVE_EXTRACT_PERM, * so the permissions should have been restored exactly, * including resetting the gid bit on those platforms * where gid is inherited by subdirs. */ assert(0 == stat("dir_0775", &st)); failure("This was 0775 in archive, and should be 0775 on disk"); assertEqualInt(st.st_mode, S_IFDIR | 0775); /* Everything else was extracted without ARCHIVE_EXTRACT_PERM, * so there may be some sloppiness about gid bits on directories. */ assert(0 == stat("file", &st)); failure("st.st_mode=%o should be %o", st.st_mode, S_IFREG | 0755); assertEqualInt(st.st_mode, S_IFREG | 0755); failure("The file extracted to disk is the wrong size."); assert(st.st_size == FILE_BUFF_SIZE); fd = open("file", O_RDONLY); failure("The file on disk could not be opened."); assert(fd != 0); bytes_read = read(fd, buff, FILE_BUFF_SIZE); close(fd); failure("The file contents read from disk are the wrong size"); assert(bytes_read == FILE_BUFF_SIZE); failure("The file contents on disk do not match the file contents that were put into the archive."); assert(memcmp(buff, file_buff, FILE_BUFF_SIZE) == 0); assert(0 == stat("dir", &st)); failure("This was 0777 in archive, but umask should make it 0755"); /* If EXTRACT_PERM wasn't used, be careful to ignore sgid bit * when checking dir modes, as some systems inherit sgid bit * from the parent dir. */ assertEqualInt(0755, st.st_mode & 0777); assert(0 == stat("dir/file", &st)); assert(st.st_mode == (S_IFREG | 0700)); assert(0 == stat("dir2", &st)); assertEqualInt(0755, st.st_mode & 0777); assert(0 == stat("dir2/file", &st)); assert(st.st_mode == (S_IFREG | 0000)); assert(0 == stat("dir3", &st)); assertEqualInt(0710, st.st_mode & 0777); assert(0 == stat("dir4", &st)); assertEqualInt(0755, st.st_mode & 0777); assert(0 == stat("dir4/a", &st)); assertEqualInt(0755, st.st_mode & 0777); assert(0 == stat("dir4/b", &st)); assertEqualInt(0755, st.st_mode & 0777); assert(0 == stat("dir4/c", &st)); assertEqualInt(0711, st.st_mode & 0777); assert(0 == lstat("symlink", &st)); assert(S_ISLNK(st.st_mode)); #if HAVE_LCHMOD /* Systems that lack lchmod() can't set symlink perms, so skip this. */ assert((st.st_mode & 07777) == 0755); #endif assert(0 == stat("symlink", &st)); assert(st.st_mode == (S_IFREG | 0755)); #endif free(buff); free(file_buff); } diff --git a/lib/libarchive/test/test_tar_large.c b/lib/libarchive/test/test_tar_large.c index f56dc6c0fe81..b2b8e5dbfdc7 100644 --- a/lib/libarchive/test/test_tar_large.c +++ b/lib/libarchive/test/test_tar_large.c @@ -1,315 +1,312 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #include #include #include /* * This is a somewhat tricky test that verifies the ability to * write and read very large entries to tar archives. It * writes entries from 2GB up to 1TB to an archive in memory. * The memory storage here carefully avoids actually storing * any part of the file bodies, so it runs very quickly and requires * very little memory. If you're willing to wait a few minutes, * you should be able to exercise petabyte entries with this code. */ /* * Each file is built up by duplicating the following block. */ static size_t filedatasize; static void *filedata; /* * We store the archive as blocks of data generated by libarchive, * each possibly followed by bytes of file data. */ struct memblock { struct memblock *next; size_t size; void *buff; - off_t filebytes; + int64_t filebytes; }; /* * The total memory store is just a list of memblocks plus * some accounting overhead. */ struct memdata { - off_t filebytes; + int64_t filebytes; void *buff; struct memblock *first; struct memblock *last; }; /* The following size definitions simplify things below. */ -#define KB ((off_t)1024) -#define MB ((off_t)1024 * KB) -#define GB ((off_t)1024 * MB) -#define TB ((off_t)1024 * GB) +#define KB ((int64_t)1024) +#define MB ((int64_t)1024 * KB) +#define GB ((int64_t)1024 * MB) +#define TB ((int64_t)1024 * GB) #if ARCHIVE_VERSION_NUMBER < 2000000 static ssize_t memory_read_skip(struct archive *, void *, size_t request); #else static off_t memory_read_skip(struct archive *, void *, off_t request); #endif static ssize_t memory_read(struct archive *, void *, const void **buff); static ssize_t memory_write(struct archive *, void *, const void *, size_t); static ssize_t memory_write(struct archive *a, void *_private, const void *buff, size_t size) { struct memdata *private = _private; struct memblock *block; (void)a; /* * Since libarchive tries to behave in a zero-copy manner, if * you give a pointer to filedata to the library, a pointer * into that data will (usually) pop out here. This way, we * can tell the difference between filedata and library header * and metadata. */ if ((const char *)filedata <= (const char *)buff && (const char *)buff < (const char *)filedata + filedatasize) { /* We don't need to store a block of file data. */ - private->last->filebytes += (off_t)size; + private->last->filebytes += (int64_t)size; } else { /* Yes, we're assuming the very first write is metadata. */ /* It's header or metadata, copy and save it. */ block = (struct memblock *)malloc(sizeof(*block)); memset(block, 0, sizeof(*block)); block->size = size; block->buff = malloc(size); memcpy(block->buff, buff, size); if (private->last == NULL) { private->first = private->last = block; } else { private->last->next = block; private->last = block; } block->next = NULL; } return ((long)size); } static ssize_t memory_read(struct archive *a, void *_private, const void **buff) { struct memdata *private = _private; struct memblock *block; ssize_t size; (void)a; free(private->buff); private->buff = NULL; if (private->first == NULL) { private->last = NULL; return (ARCHIVE_EOF); } if (private->filebytes > 0) { /* * We're returning file bytes, simulate it by * passing blocks from the template data. */ - if (private->filebytes > (off_t)filedatasize) + if (private->filebytes > (int64_t)filedatasize) size = (ssize_t)filedatasize; else size = (ssize_t)private->filebytes; private->filebytes -= size; *buff = filedata; } else { /* * We need to get some real data to return. */ block = private->first; private->first = block->next; size = (ssize_t)block->size; if (block->buff != NULL) { private->buff = block->buff; *buff = block->buff; } else { private->buff = NULL; *buff = filedata; } private->filebytes = block->filebytes; free(block); } return (size); } #if ARCHIVE_VERSION_NUMBER < 2000000 static ssize_t memory_read_skip(struct archive *a, void *private, size_t skip) { (void)a; /* UNUSED */ (void)private; /* UNUSED */ (void)skip; /* UNUSED */ return (0); } #else static off_t memory_read_skip(struct archive *a, void *_private, off_t skip) { struct memdata *private = _private; (void)a; if (private->first == NULL) { private->last = NULL; return (0); } if (private->filebytes > 0) { if (private->filebytes < skip) skip = private->filebytes; private->filebytes -= skip; } else { skip = 0; } return (skip); } #endif DEFINE_TEST(test_tar_large) { /* The sizes of the entries we're going to generate. */ - static off_t tests[] = { + static int64_t tests[] = { /* Test for 32-bit signed overflow. */ 2 * GB - 1, 2 * GB, 2 * GB + 1, /* Test for 32-bit unsigned overflow. */ 4 * GB - 1, 4 * GB, 4 * GB + 1, /* 8GB is the "official" max for ustar. */ 8 * GB - 1, 8 * GB, 8 * GB + 1, /* Bend ustar a tad and you can get 64GB (12 octal digits). */ 64 * GB - 1, 64 * GB, /* And larger entries that require non-ustar extensions. */ 256 * GB, 1 * TB, 0 }; int i; char namebuff[64]; struct memdata memdata; struct archive_entry *ae; struct archive *a; - off_t filesize, writesize; + int64_t filesize; + size_t writesize; filedatasize = (size_t)(1 * MB); filedata = malloc(filedatasize); memset(filedata, 0xAA, filedatasize); memset(&memdata, 0, sizeof(memdata)); /* * Open an archive for writing. */ a = archive_write_new(); archive_write_set_format_pax_restricted(a); archive_write_set_bytes_per_block(a, 0); /* No buffering. */ archive_write_open(a, &memdata, NULL, memory_write, NULL); /* * Write a series of large files to it. */ for (i = 0; tests[i] != 0; i++) { assert((ae = archive_entry_new()) != NULL); sprintf(namebuff, "file_%d", i); archive_entry_copy_pathname(ae, namebuff); archive_entry_set_mode(ae, S_IFREG | 0755); filesize = tests[i]; - if (filesize < 0) { - archive_entry_free(ae); - skipping("32-bit off_t doesn't permit testing of very large files."); - return; - } archive_entry_set_size(ae, filesize); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* * Write the actual data to the archive. */ while (filesize > 0) { - writesize = (off_t)filedatasize; - if (writesize > filesize) - writesize = filesize; - assertA(writesize == archive_write_data(a, filedata, writesize)); + writesize = filedatasize; + if ((int64_t)writesize > filesize) + writesize = (size_t)filesize; + assertA((int)writesize + == archive_write_data(a, filedata, writesize)); filesize -= writesize; } } assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "lastfile"); archive_entry_set_mode(ae, S_IFREG | 0755); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertA(0 == archive_write_close(a)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(a); #else assertA(0 == archive_write_finish(a)); #endif /* * Open the same archive for reading. */ a = archive_read_new(); archive_read_support_format_tar(a); archive_read_open2(a, &memdata, NULL, memory_read, memory_read_skip, NULL); /* * Read entries back. */ for (i = 0; tests[i] > 0; i++) { assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); sprintf(namebuff, "file_%d", i); assertEqualString(namebuff, archive_entry_pathname(ae)); assert(tests[i] == archive_entry_size(ae)); } assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); assertEqualString("lastfile", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close out the archive. */ assertA(0 == archive_read_close(a)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_read_finish(a); #else assertA(0 == archive_read_finish(a)); #endif free(memdata.buff); free(filedata); } diff --git a/lib/libarchive/test/test_write_disk.c b/lib/libarchive/test/test_write_disk.c index 72c94443cc43..c0b22e58a125 100644 --- a/lib/libarchive/test/test_write_disk.c +++ b/lib/libarchive/test/test_write_disk.c @@ -1,347 +1,347 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #if ARCHIVE_VERSION_NUMBER >= 1009000 #define UMASK 022 static void create(struct archive_entry *ae, const char *msg) { struct archive *ad; struct stat st; /* Write the entry to disk. */ assert((ad = archive_write_disk_new()) != NULL); failure("%s", msg); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entries on disk. */ assert(0 == stat(archive_entry_pathname(ae), &st)); failure("st.st_mode=%o archive_entry_mode(ae)=%o", st.st_mode, archive_entry_mode(ae)); /* When verifying a dir, ignore the S_ISGID bit, as some systems set * that automatically. */ if (archive_entry_filetype(ae) == AE_IFDIR) st.st_mode &= ~S_ISGID; -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(st.st_mode, archive_entry_mode(ae) & ~UMASK); #endif } static void create_reg_file(struct archive_entry *ae, const char *msg) { static const char data[]="abcdefghijklmnopqrstuvwxyz"; struct archive *ad; struct stat st; time_t now; /* Write the entry to disk. */ assert((ad = archive_write_disk_new()) != NULL); archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); failure("%s", msg); /* * A touchy API design issue: archive_write_data() does (as of * 2.4.12) enforce the entry size as a limit on the data * written to the file. This was not enforced prior to * 2.4.12. The change was prompted by the refined * hardlink-restore semantics introduced at that time. In * short, libarchive needs to know whether a "hardlink entry" * is going to overwrite the contents so that it can know * whether or not to open the file for writing. This implies * that there is a fundamental semantic difference between an * entry with a zero size and one with a non-zero size in the * case of hardlinks and treating the hardlink case * differently from the regular file case is just asking for * trouble. So, a zero size must always mean that no data * will be accepted, which is consistent with the file size in * the entry being a maximum size. */ archive_entry_set_size(ae, sizeof(data)); archive_entry_set_mtime(ae, 123456789, 0); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entries on disk. */ assert(0 == stat(archive_entry_pathname(ae), &st)); failure("st.st_mode=%o archive_entry_mode(ae)=%o", st.st_mode, archive_entry_mode(ae)); -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); #endif assertEqualInt(st.st_size, sizeof(data)); /* test_write_disk_times has more detailed tests of this area. */ assertEqualInt(st.st_mtime, 123456789); failure("No atime was specified, so atime should get set to current time"); now = time(NULL); assert(st.st_atime <= now && st.st_atime > now - 5); } static void create_reg_file2(struct archive_entry *ae, const char *msg) { const int datasize = 100000; char *data; char *compare; struct archive *ad; struct stat st; int i, fd; data = malloc(datasize); for (i = 0; i < datasize; i++) data[i] = (char)(i % 256); /* Write the entry to disk. */ assert((ad = archive_write_disk_new()) != NULL); failure("%s", msg); /* * See above for an explanation why this next call * is necessary. */ archive_entry_set_size(ae, datasize); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); for (i = 0; i < datasize - 999; i += 1000) { assertEqualIntA(ad, ARCHIVE_OK, archive_write_data_block(ad, data + i, 1000, i)); } assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entries on disk. */ assert(0 == stat(archive_entry_pathname(ae), &st)); failure("st.st_mode=%o archive_entry_mode(ae)=%o", st.st_mode, archive_entry_mode(ae)); -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); #endif assertEqualInt(st.st_size, i); compare = malloc(datasize); fd = open(archive_entry_pathname(ae), O_RDONLY); assertEqualInt(datasize, read(fd, compare, datasize)); close(fd); assert(memcmp(compare, data, datasize) == 0); free(compare); free(data); } static void create_reg_file3(struct archive_entry *ae, const char *msg) { static const char data[]="abcdefghijklmnopqrstuvwxyz"; struct archive *ad; struct stat st; /* Write the entry to disk. */ assert((ad = archive_write_disk_new()) != NULL); failure("%s", msg); /* Set the size smaller than the data and verify the truncation. */ archive_entry_set_size(ae, 5); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(5, archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entry on disk. */ assert(0 == stat(archive_entry_pathname(ae), &st)); failure("st.st_mode=%o archive_entry_mode(ae)=%o", st.st_mode, archive_entry_mode(ae)); -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); #endif assertEqualInt(st.st_size, 5); } static void create_reg_file4(struct archive_entry *ae, const char *msg) { static const char data[]="abcdefghijklmnopqrstuvwxyz"; struct archive *ad; struct stat st; /* Write the entry to disk. */ assert((ad = archive_write_disk_new()) != NULL); /* Leave the size unset. The data should not be truncated. */ assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(ARCHIVE_OK, archive_write_data_block(ad, data, sizeof(data), 0)); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entry on disk. */ assert(0 == stat(archive_entry_pathname(ae), &st)); failure("st.st_mode=%o archive_entry_mode(ae)=%o", st.st_mode, archive_entry_mode(ae)); -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); #endif failure(msg); assertEqualInt(st.st_size, sizeof(data)); } -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) static void create_reg_file_win(struct archive_entry *ae, const char *msg) { static const char data[]="abcdefghijklmnopqrstuvwxyz"; struct archive *ad; struct stat st; char *p, *fname; size_t l; /* Write the entry to disk. */ assert((ad = archive_write_disk_new()) != NULL); archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); failure("%s", msg); archive_entry_set_size(ae, sizeof(data)); archive_entry_set_mtime(ae, 123456789, 0); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entries on disk. */ l = strlen(archive_entry_pathname(ae)); fname = malloc(l + 1); assert(NULL != fname); strcpy(fname, archive_entry_pathname(ae)); /* Replace unusable characters in Windows to '_' */ for (p = fname; *p != '\0'; p++) if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; assert(0 == stat(fname, &st)); failure("st.st_mode=%o archive_entry_mode(ae)=%o", st.st_mode, archive_entry_mode(ae)); assertEqualInt(st.st_size, sizeof(data)); } -#endif /* _WIN32 */ +#endif /* _WIN32 && !__CYGWIN__ */ #endif DEFINE_TEST(test_write_disk) { #if ARCHIVE_VERSION_NUMBER < 1009000 skipping("archive_write_disk interface"); #else struct archive_entry *ae; /* Force the umask to something predictable. */ umask(UMASK); /* A regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0755); create_reg_file(ae, "Test creating a regular file"); archive_entry_free(ae); /* Another regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file2"); archive_entry_set_mode(ae, S_IFREG | 0755); create_reg_file2(ae, "Test creating another regular file"); archive_entry_free(ae); /* A regular file with a size restriction */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file3"); archive_entry_set_mode(ae, S_IFREG | 0755); create_reg_file3(ae, "Regular file with size restriction"); archive_entry_free(ae); /* A regular file with an unspecified size */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file3"); archive_entry_set_mode(ae, S_IFREG | 0755); create_reg_file4(ae, "Regular file with unspecified size"); archive_entry_free(ae); /* A regular file over an existing file */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0724); create(ae, "Test creating a file over an existing file."); archive_entry_free(ae); /* A directory. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir"); archive_entry_set_mode(ae, S_IFDIR | 0555); create(ae, "Test creating a regular dir."); archive_entry_free(ae); /* A directory over an existing file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFDIR | 0742); create(ae, "Test creating a dir over an existing file."); archive_entry_free(ae); /* A file over an existing dir. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0744); create(ae, "Test creating a file over an existing dir."); archive_entry_free(ae); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) /* A file with unusable characters in its file name. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "f:i*l?e\"fl|e"); archive_entry_set_mode(ae, S_IFREG | 0755); create_reg_file_win(ae, "Test creating a regular file" " with unusable characters in its file name"); archive_entry_free(ae); /* A file with unusable characters in its directory name. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "d:i*r?e\"co|ry/file1"); archive_entry_set_mode(ae, S_IFREG | 0755); create_reg_file_win(ae, "Test creating a regular file" " with unusable characters in its file name"); archive_entry_free(ae); -#endif /* _WIN32 */ +#endif /* _WIN32 && !__CYGWIN__ */ #endif } diff --git a/lib/libarchive/test/test_write_disk_failures.c b/lib/libarchive/test/test_write_disk_failures.c index 03304d4f0037..0d7b894b4ecd 100644 --- a/lib/libarchive/test/test_write_disk_failures.c +++ b/lib/libarchive/test/test_write_disk_failures.c @@ -1,62 +1,62 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #if ARCHIVE_VERSION_NUMBER >= 1009000 #define UMASK 022 #endif DEFINE_TEST(test_write_disk_failures) { -#if ARCHIVE_VERSION_NUMBER < 1009000 || defined(_WIN32) +#if ARCHIVE_VERSION_NUMBER < 1009000 || (defined(_WIN32) && !defined(__CYGWIN__)) skipping("archive_write_disk interface"); #else struct archive_entry *ae; struct archive *a; /* Force the umask to something predictable. */ umask(UMASK); /* A directory that we can't write to. */ assertEqualInt(0, mkdir("dir", 0555)); /* Try to extract a regular file into the directory above. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir/file"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, 8); assert((a = archive_write_disk_new()) != NULL); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME); archive_entry_set_mtime(ae, 123456789, 0); assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); assertEqualIntA(a, 0, archive_write_finish_entry(a)); assertEqualInt(0, archive_write_finish(a)); archive_entry_free(ae); #endif } diff --git a/lib/libarchive/test/test_write_disk_hardlink.c b/lib/libarchive/test/test_write_disk_hardlink.c index f9bcfae6cbc2..2ab242093a61 100644 --- a/lib/libarchive/test/test_write_disk_hardlink.c +++ b/lib/libarchive/test/test_write_disk_hardlink.c @@ -1,234 +1,241 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); +#if defined(_WIN32) && !defined(__CYGWIN__) +/* Execution bits, Group members bits and others bits do not work. */ +#define UMASK 0177 +#define E_MASK (~0177) +#else #define UMASK 022 +#define E_MASK (~0) +#endif /* * Exercise hardlink recreation. * * File permissions are chosen so that the authoritive entry * has the correct permission and the non-authoritive versions * are just writeable files. */ DEFINE_TEST(test_write_disk_hardlink) { -#if ARCHIVE_VERSION_NUMBER < 1009000 || defined(_WIN32) +#if ARCHIVE_VERSION_NUMBER < 1009000 skipping("archive_write_disk_hardlink tests"); #else static const char data[]="abcdefghijklmnopqrstuvwxyz"; struct archive *ad; struct archive_entry *ae; struct stat st, st2; /* Force the umask to something predictable. */ umask(UMASK); /* Write entries to disk. */ assert((ad = archive_write_disk_new()) != NULL); /* * First, use a tar-like approach; a regular file, then * a separate "hardlink" entry. */ /* Regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link1a"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, sizeof(data)); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* Link. Size of zero means this doesn't carry data. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link1b"); archive_entry_set_mode(ae, S_IFREG | 0642); archive_entry_set_size(ae, 0); archive_entry_copy_hardlink(ae, "link1a"); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* * Repeat tar approach test, but use unset to mark the * hardlink as having no data. */ /* Regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link2a"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, sizeof(data)); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* Link. Unset size means this doesn't carry data. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link2b"); archive_entry_set_mode(ae, S_IFREG | 0642); archive_entry_unset_size(ae); archive_entry_copy_hardlink(ae, "link2a"); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* * Second, try an old-cpio-like approach; a regular file, then * another identical one (which has been marked hardlink). */ /* Regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link3a"); archive_entry_set_mode(ae, S_IFREG | 0600); archive_entry_set_size(ae, sizeof(data)); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* Link. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link3b"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, sizeof(data)); archive_entry_copy_hardlink(ae, "link3a"); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* * Finally, try a new-cpio-like approach, where the initial * regular file is empty and the hardlink has the data. */ /* Regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link4a"); archive_entry_set_mode(ae, S_IFREG | 0600); archive_entry_set_size(ae, 0); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); #if ARCHIVE_VERSION_NUMBER < 3000000 assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1)); #else assertEqualInt(-1, archive_write_data(ad, data, 1)); #endif assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); /* Link. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link4b"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, sizeof(data)); archive_entry_copy_hardlink(ae, "link4a"); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(ad); #else assertEqualInt(0, archive_write_finish(ad)); #endif /* Test the entries on disk. */ /* Test #1 */ assert(0 == stat("link1a", &st)); /* If the hardlink was successfully created and the archive * doesn't carry data for it, we consider it to be * non-authoritive for meta data as well. This is consistent * with GNU tar and BSD pax. */ - assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK); + assertEqualInt(st.st_mode & E_MASK, (S_IFREG | 0755) & ~UMASK); assertEqualInt(st.st_size, sizeof(data)); assertEqualInt(st.st_nlink, 2); assert(0 == stat("link1b", &st2)); assertEqualInt(st.st_mode, st2.st_mode); assertEqualInt(st.st_size, st2.st_size); assertEqualInt(st.st_nlink, st2.st_nlink); assertEqualInt(st.st_ino, st2.st_ino); assertEqualInt(st.st_dev, st2.st_dev); /* Test #2: Should produce identical results to test #1 */ /* Note that marking a hardlink with size = 0 is treated the * same as having an unset size. This is partly for backwards * compatibility (we used to not have unset tracking, so * relied on size == 0) and partly to match the model used by * common file formats that store a size of zero for * hardlinks. */ assert(0 == stat("link2a", &st)); - assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK); + assertEqualInt(st.st_mode & E_MASK, (S_IFREG | 0755) & ~UMASK); assertEqualInt(st.st_size, sizeof(data)); assertEqualInt(st.st_nlink, 2); assert(0 == stat("link2b", &st2)); assertEqualInt(st.st_mode, st2.st_mode); assertEqualInt(st.st_size, st2.st_size); assertEqualInt(st.st_nlink, st2.st_nlink); assertEqualInt(st.st_ino, st2.st_ino); assertEqualInt(st.st_dev, st2.st_dev); /* Test #3 */ assert(0 == stat("link3a", &st)); - assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK); + assertEqualInt(st.st_mode & E_MASK, (S_IFREG | 0755) & ~UMASK); assertEqualInt(st.st_size, sizeof(data)); assertEqualInt(st.st_nlink, 2); assert(0 == stat("link3b", &st2)); - assertEqualInt(st2.st_mode, (S_IFREG | 0755) & ~UMASK); + assertEqualInt(st2.st_mode & E_MASK, (S_IFREG | 0755) & ~UMASK); assertEqualInt(st2.st_size, sizeof(data)); assertEqualInt(st2.st_nlink, 2); assertEqualInt(st.st_ino, st2.st_ino); assertEqualInt(st.st_dev, st2.st_dev); /* Test #4 */ assert(0 == stat("link4a", &st)); - assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK); + assertEqualInt(st.st_mode & E_MASK, (S_IFREG | 0755) & ~UMASK); assertEqualInt(st.st_size, sizeof(data)); assertEqualInt(st.st_nlink, 2); assert(0 == stat("link4b", &st2)); - assertEqualInt(st2.st_mode, (S_IFREG | 0755) & ~UMASK); + assertEqualInt(st2.st_mode & E_MASK, (S_IFREG | 0755) & ~UMASK); assertEqualInt(st2.st_size, sizeof(data)); assertEqualInt(st2.st_nlink, 2); assertEqualInt(st.st_ino, st2.st_ino); assertEqualInt(st.st_dev, st2.st_dev); #endif } diff --git a/lib/libarchive/test/test_write_disk_perms.c b/lib/libarchive/test/test_write_disk_perms.c index 87e2a2d168aa..3d9be27e5964 100644 --- a/lib/libarchive/test/test_write_disk_perms.c +++ b/lib/libarchive/test/test_write_disk_perms.c @@ -1,455 +1,455 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); -#if ARCHIVE_VERSION_NUMBER >= 1009000 && !defined(_WIN32) +#if ARCHIVE_VERSION_NUMBER >= 1009000 && (!defined(_WIN32) || defined(__CYGWIN__)) #define UMASK 022 static long _default_gid = -1; static long _invalid_gid = -1; static long _alt_gid = -1; /* * To fully test SGID restores, we need three distinct GIDs to work * with: * * the GID that files are created with by default (for the * current user in the current directory) * * An "alt gid" that this user can create files with * * An "invalid gid" that this user is not permitted to create * files with. * The second fails if this user doesn't belong to at least two groups; * the third fails if the current user is root. */ static void searchgid(void) { static int _searched = 0; uid_t uid = getuid(); gid_t gid = 0; unsigned int n; struct stat st; int fd; /* If we've already looked this up, we're done. */ if (_searched) return; _searched = 1; /* Create a file on disk in the current default dir. */ fd = open("test_gid", O_CREAT, 0664); failure("Couldn't create a file for gid testing."); assert(fd > 0); /* See what GID it ended up with. This is our "valid" GID. */ assert(fstat(fd, &st) == 0); _default_gid = st.st_gid; /* Find a GID for which fchown() fails. This is our "invalid" GID. */ _invalid_gid = -1; /* This loop stops when we wrap the gid or examine 10,000 gids. */ for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) { if (fchown(fd, uid, gid) != 0) { _invalid_gid = gid; break; } } /* * Find a GID for which fchown() succeeds, but which isn't the * default. This is the "alternate" gid. */ _alt_gid = -1; for (gid = 0, n = 0; gid == n && n < 10000 ; n++, gid++) { /* _alt_gid must be different than _default_gid */ if (gid == (gid_t)_default_gid) continue; if (fchown(fd, uid, gid) == 0) { _alt_gid = gid; break; } } close(fd); } static int altgid(void) { searchgid(); return (_alt_gid); } static int invalidgid(void) { searchgid(); return (_invalid_gid); } static int defaultgid(void) { searchgid(); return (_default_gid); } #endif /* * Exercise permission and ownership restores. * In particular, try to exercise a bunch of border cases related * to files/dirs that already exist, SUID/SGID bits, etc. */ DEFINE_TEST(test_write_disk_perms) { -#if ARCHIVE_VERSION_NUMBER < 1009000 || defined(_WIN32) +#if ARCHIVE_VERSION_NUMBER < 1009000 || (defined(_WIN32) && !defined(__CYGWIN__)) skipping("archive_write_disk interface"); #else struct archive *a; struct archive_entry *ae; struct stat st; /* * Set ownership of the current directory to the group of this * process. Otherwise, the SGID tests below fail if the * /tmp directory is owned by a group to which we don't belong * and we're on a system where group ownership is inherited. * (Because we're not allowed to SGID files with defaultgid().) */ assertEqualInt(0, chown(".", getuid(), getgid())); /* Create an archive_write_disk object. */ assert((a = archive_write_disk_new()) != NULL); /* Write a regular file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file_0755"); archive_entry_set_mode(ae, S_IFREG | 0777); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); archive_entry_free(ae); /* Write a regular file, then write over it. */ /* For files, the perms should get updated. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file_overwrite_0144"); archive_entry_set_mode(ae, S_IFREG | 0777); assert(0 == archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); /* Check that file was created with different perms. */ assert(0 == stat("file_overwrite_0144", &st)); failure("file_overwrite_0144: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) != 0144); /* Overwrite, this should change the perms. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file_overwrite_0144"); archive_entry_set_mode(ae, S_IFREG | 0144); assert(0 == archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); /* Write a regular dir. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir_0514"); archive_entry_set_mode(ae, S_IFDIR | 0514); assert(0 == archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); /* Overwrite an existing dir. */ /* For dir, the first perms should get left. */ assert(mkdir("dir_overwrite_0744", 0744) == 0); /* Check original perms. */ assert(0 == stat("dir_overwrite_0744", &st)); failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); assert((st.st_mode & 0777) == 0744); /* Overwrite shouldn't edit perms. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir_overwrite_0744"); archive_entry_set_mode(ae, S_IFDIR | 0777); assert(0 == archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); /* Make sure they're unchanged. */ assert(0 == stat("dir_overwrite_0744", &st)); failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); assert((st.st_mode & 0777) == 0744); /* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file_no_suid"); archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0777); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Write a regular file with ARCHIVE_EXTRACT_PERM. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_0777"); archive_entry_set_mode(ae, S_IFREG | 0777); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_4742"); archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); archive_entry_set_uid(ae, getuid()); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* * Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit, * but wrong uid. POSIX says you shouldn't restore SUID bit * unless the UID could be restored. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_bad_suid"); archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); archive_entry_set_uid(ae, getuid() + 1); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); assertA(0 == archive_write_header(a, ae)); /* * Because we didn't ask for owner, the failure to * restore SUID shouldn't return a failure. * We check below to make sure SUID really wasn't set. * See more detailed comments below. */ failure("Opportunistic SUID failure shouldn't return error."); assertEqualInt(0, archive_write_finish_entry(a)); if (getuid() != 0) { assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_bad_suid2"); archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); archive_entry_set_uid(ae, getuid() + 1); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); assertA(0 == archive_write_header(a, ae)); /* Owner change should fail here. */ failure("Non-opportunistic SUID failure should return error."); assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a)); } /* Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_perm_sgid"); archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); archive_entry_set_gid(ae, defaultgid()); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); assert(0 == archive_write_header(a, ae)); failure("Setting SGID bit should succeed here."); assertEqualIntA(a, 0, archive_write_finish_entry(a)); if (altgid() == -1) { /* * Current user must belong to at least two groups or * else we can't test setting the GID to another group. */ skipping("Current user can't test gid restore: must belong to more than one group."); } else { /* * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit * but without ARCHIVE_EXTRACT_OWNER. */ /* * This is a weird case: The user has asked for permissions to * be restored but not asked for ownership to be restored. As * a result, the default file creation will create a file with * the wrong group. There are several possible behaviors for * libarchive in this scenario: * = Set the SGID bit. It is wrong and a security hole to * set SGID with the wrong group. Even POSIX thinks so. * = Implicitly set the group. I don't like this. * = drop the SGID bit and warn (the old libarchive behavior) * = drop the SGID bit and don't warn (the current libarchive * behavior). * The current behavior sees SGID/SUID restore when you * don't ask for owner restore as an "opportunistic" * action. That is, libarchive should do it if it can, * but if it can't, it's not an error. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_alt_sgid"); archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); archive_entry_set_uid(ae, getuid()); archive_entry_set_gid(ae, altgid()); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); assert(0 == archive_write_header(a, ae)); failure("Setting SGID bit should fail because of group mismatch but the failure should be silent because we didn't ask for the group to be set."); assertEqualIntA(a, 0, archive_write_finish_entry(a)); /* * As above, but add _EXTRACT_OWNER to verify that it * does succeed. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_alt_sgid_owner"); archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); archive_entry_set_uid(ae, getuid()); archive_entry_set_gid(ae, altgid()); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); assert(0 == archive_write_header(a, ae)); failure("Setting SGID bit should succeed here."); assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); } /* * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit, * but wrong GID. POSIX says you shouldn't restore SGID bit * unless the GID could be restored. */ if (invalidgid() == -1) { /* This test always fails for root. */ printf("Running as root: Can't test SGID failures.\n"); } else { assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_bad_sgid"); archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); archive_entry_set_gid(ae, invalidgid()); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); assertA(0 == archive_write_header(a, ae)); failure("This SGID restore should fail without an error."); assertEqualIntA(a, 0, archive_write_finish_entry(a)); assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_bad_sgid2"); archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); archive_entry_set_gid(ae, invalidgid()); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); assertA(0 == archive_write_header(a, ae)); failure("This SGID restore should fail with an error."); assertEqualIntA(a, ARCHIVE_WARN, archive_write_finish_entry(a)); } /* Set ownership should fail if we're not root. */ if (getuid() == 0) { printf("Running as root: Can't test setuid failures.\n"); } else { assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "file_bad_owner"); archive_entry_set_mode(ae, S_IFREG | 0744); archive_entry_set_uid(ae, getuid() + 1); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_OWNER); assertA(0 == archive_write_header(a, ae)); assertEqualIntA(a,ARCHIVE_WARN,archive_write_finish_entry(a)); } #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(a); #else assert(0 == archive_write_finish(a)); #endif archive_entry_free(ae); /* Test the entries on disk. */ assert(0 == stat("file_0755", &st)); failure("file_0755: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0755); assert(0 == stat("file_overwrite_0144", &st)); failure("file_overwrite_0144: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0144); assert(0 == stat("dir_0514", &st)); failure("dir_0514: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0514); assert(0 == stat("dir_overwrite_0744", &st)); failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); assert((st.st_mode & 0777) == 0744); assert(0 == stat("file_no_suid", &st)); failure("file_0755: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0755); assert(0 == stat("file_0777", &st)); failure("file_0777: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0777); /* SUID bit should get set here. */ assert(0 == stat("file_4742", &st)); failure("file_4742: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (S_ISUID | 0742)); /* SUID bit should NOT have been set here. */ assert(0 == stat("file_bad_suid", &st)); failure("file_bad_suid: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (0742)); /* Some things don't fail if you're root, so suppress this. */ if (getuid() != 0) { /* SUID bit should NOT have been set here. */ assert(0 == stat("file_bad_suid2", &st)); failure("file_bad_suid2: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (0742)); } /* SGID should be set here. */ assert(0 == stat("file_perm_sgid", &st)); failure("file_perm_sgid: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (S_ISGID | 0742)); if (altgid() != -1) { /* SGID should not be set here. */ assert(0 == stat("file_alt_sgid", &st)); failure("file_alt_sgid: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (0742)); /* SGID should be set here. */ assert(0 == stat("file_alt_sgid_owner", &st)); failure("file_alt_sgid: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (S_ISGID | 0742)); } if (invalidgid() != -1) { /* SGID should NOT be set here. */ assert(0 == stat("file_bad_sgid", &st)); failure("file_bad_sgid: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (0742)); /* SGID should NOT be set here. */ assert(0 == stat("file_bad_sgid2", &st)); failure("file_bad_sgid2: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (0742)); } if (getuid() != 0) { assert(0 == stat("file_bad_owner", &st)); failure("file_bad_owner: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == (0744)); failure("file_bad_owner: st.st_uid=%d getuid()=%d", st.st_uid, getuid()); /* The entry had getuid()+1, but because we're * not root, we should not have been able to set that. */ assert(st.st_uid == getuid()); } #endif } diff --git a/lib/libarchive/test/test_write_disk_secure.c b/lib/libarchive/test/test_write_disk_secure.c index 4c014b164057..d41748907b8f 100644 --- a/lib/libarchive/test/test_write_disk_secure.c +++ b/lib/libarchive/test/test_write_disk_secure.c @@ -1,224 +1,224 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #define UMASK 022 /* * Exercise security checks that should prevent certain * writes. */ DEFINE_TEST(test_write_disk_secure) { #if ARCHIVE_VERSION_NUMBER < 1009000 skipping("archive_write_disk interface"); #else struct archive *a; struct archive_entry *ae; struct stat st; /* Start with a known umask. */ umask(UMASK); /* Create an archive_write_disk object. */ assert((a = archive_write_disk_new()) != NULL); /* Write a regular dir to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "dir"); archive_entry_set_mode(ae, S_IFDIR | 0777); assert(0 == archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) /* Write a symlink to the dir above. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link_to_dir"); archive_entry_set_mode(ae, S_IFLNK | 0777); archive_entry_set_symlink(ae, "dir"); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* * Without security checks, we should be able to * extract a file through the link. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "link_to_dir/filea"); archive_entry_set_mode(ae, S_IFREG | 0777); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* But with security checks enabled, this should fail. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "link_to_dir/fileb"); archive_entry_set_mode(ae, S_IFREG | 0777); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS); failure("Extracting a file through a symlink should fail here."); assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); /* Create another link. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link_to_dir2"); archive_entry_set_mode(ae, S_IFLNK | 0777); archive_entry_set_symlink(ae, "dir"); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* * With symlink check and unlink option, it should remove * the link and create the dir. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "link_to_dir2/filec"); archive_entry_set_mode(ae, S_IFREG | 0777); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_UNLINK); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); assert(0 == archive_write_finish_entry(a)); /* * Without security checks, extracting a dir over a link to a * dir should follow the link. */ /* Create a symlink to a dir. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link_to_dir3"); archive_entry_set_mode(ae, S_IFLNK | 0777); archive_entry_set_symlink(ae, "dir"); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Extract a dir whose name matches the symlink. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "link_to_dir3"); archive_entry_set_mode(ae, S_IFDIR | 0777); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Verify link was followed. */ assertEqualInt(0, lstat("link_to_dir3", &st)); assert(S_ISLNK(st.st_mode)); archive_entry_free(ae); /* * As above, but a broken link, so the link should get replaced. */ /* Create a symlink to a dir. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link_to_dir4"); archive_entry_set_mode(ae, S_IFLNK | 0777); archive_entry_set_symlink(ae, "nonexistent_dir"); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Extract a dir whose name matches the symlink. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "link_to_dir4"); archive_entry_set_mode(ae, S_IFDIR | 0777); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Verify link was replaced. */ assertEqualInt(0, lstat("link_to_dir4", &st)); assert(S_ISDIR(st.st_mode)); archive_entry_free(ae); #endif /* * As above, but a link to a non-dir, so the link should get replaced. */ /* Create a regular file and a symlink to it */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "non_dir"); archive_entry_set_mode(ae, S_IFREG | 0777); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Create symlink to the file. */ archive_entry_copy_pathname(ae, "link_to_dir5"); archive_entry_set_mode(ae, S_IFLNK | 0777); archive_entry_set_symlink(ae, "non_dir"); archive_write_disk_set_options(a, 0); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Extract a dir whose name matches the symlink. */ assert(archive_entry_clear(ae) != NULL); archive_entry_copy_pathname(ae, "link_to_dir5"); archive_entry_set_mode(ae, S_IFDIR | 0777); assert(0 == archive_write_header(a, ae)); assert(0 == archive_write_finish_entry(a)); /* Verify link was replaced. */ assertEqualInt(0, lstat("link_to_dir5", &st)); assert(S_ISDIR(st.st_mode)); archive_entry_free(ae); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(a); #else assert(0 == archive_write_finish(a)); #endif -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__CYGWIN__) /* Test the entries on disk. */ assert(0 == lstat("dir", &st)); failure("dir: st.st_mode=%o", st.st_mode); assert((st.st_mode & 0777) == 0755); assert(0 == lstat("link_to_dir", &st)); failure("link_to_dir: st.st_mode=%o", st.st_mode); assert(S_ISLNK(st.st_mode)); #if HAVE_LCHMOD /* Systems that lack lchmod() can't set symlink perms, so skip this. */ failure("link_to_dir: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0755); #endif assert(0 == lstat("dir/filea", &st)); failure("dir/filea: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0755); failure("dir/fileb: This file should not have been created"); assert(0 != lstat("dir/fileb", &st)); assert(0 == lstat("link_to_dir2", &st)); failure("link_to_dir2 should have been re-created as a true dir"); assert(S_ISDIR(st.st_mode)); failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode); assert((st.st_mode & 0777) == 0755); assert(0 == lstat("link_to_dir2/filec", &st)); assert(S_ISREG(st.st_mode)); failure("link_to_dir2/filec: st.st_mode=%o", st.st_mode); assert((st.st_mode & 07777) == 0755); #endif #endif }