Index: vendor/libarchive/dist/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/CMakeLists.txt (revision 314564) +++ vendor/libarchive/dist/CMakeLists.txt (revision 314565) @@ -1,1719 +1,1726 @@ # CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR) # PROJECT(libarchive C) # SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${libarchive_BINARY_DIR}/bin) endif() # # Set the Build type for make based generators. # You can choose following types: # Debug : Debug build # Release : Release build # RelWithDebInfo : Release build with Debug Info # MinSizeRel : Release Min Size build IF(NOT CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build Type" FORCE) + SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE) ENDIF(NOT CMAKE_BUILD_TYPE) # Set a value type to properly display CMAKE_BUILD_TYPE on GUI if the # value type is "UNINITIALIZED". GET_PROPERTY(cached_type CACHE CMAKE_BUILD_TYPE PROPERTY TYPE) IF("${cached_type}" STREQUAL "UNINITIALIZED") SET(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build Type" FORCE) ENDIF("${cached_type}" STREQUAL "UNINITIALIZED") # Check the Build Type. IF(NOT "${CMAKE_BUILD_TYPE}" MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$") MESSAGE(FATAL_ERROR "Unknown keyword for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" "Acceptable keywords: Debug,Release,RelWithDebInfo,MinSizeRel") ENDIF(NOT "${CMAKE_BUILD_TYPE}" MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$") # On MacOS, prefer MacPorts libraries to system libraries. # I haven't come up with a compelling argument for this to be conditional. list(APPEND CMAKE_PREFIX_PATH /opt/local) # Enable @rpath in the install name. # detail in "cmake --help-policy CMP0042" SET(CMAKE_MACOSX_RPATH ON) # # Version - read from 'version' file. # FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/build/version _version) STRING(REGEX REPLACE - "^([0-9])[0-9][0-9][0-9][0-9][0-9][0-9][a-z]?$" "\\1" _major ${_version}) + "^([0-9])[0-9][0-9][0-9][0-9][0-9][0-9][a-z]*$" "\\1" _major ${_version}) STRING(REGEX REPLACE - "^[0-9]([0-9][0-9][0-9])[0-9][0-9][0-9][a-z]?$" "\\1" _minor ${_version}) + "^[0-9]([0-9][0-9][0-9])[0-9][0-9][0-9][a-z]*$" "\\1" _minor ${_version}) STRING(REGEX REPLACE - "^[0-9][0-9][0-9][0-9]([0-9][0-9][0-9])[a-z]?$" "\\1" _revision ${_version}) + "^[0-9][0-9][0-9][0-9]([0-9][0-9][0-9])[a-z]*$" "\\1" _revision ${_version}) STRING(REGEX REPLACE - "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]([a-z]?)$" "\\1" _quality ${_version}) + "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]([a-z]*)$" "\\1" _quality ${_version}) SET(_version_number ${_major}${_minor}${_revision}) STRING(REGEX REPLACE "[0]*([^0]*[0-9])$" "\\1" _trimmed_minor ${_minor}) STRING(REGEX REPLACE "[0]*([^0]*[0-9])$" "\\1" _trimmed_revision ${_revision}) # SET(VERSION "${_major}.${_trimmed_minor}.${_trimmed_revision}${_quality}") SET(BSDCPIO_VERSION_STRING "${VERSION}") SET(BSDTAR_VERSION_STRING "${VERSION}") SET(BSDCAT_VERSION_STRING "${VERSION}") SET(LIBARCHIVE_VERSION_NUMBER "${_version_number}") SET(LIBARCHIVE_VERSION_STRING "${VERSION}") # INTERFACE_VERSION increments with every release # libarchive 2.7 == interface version 9 = 2 + 7 # libarchive 2.8 == interface version 10 = 2 + 8 # libarchive 2.9 == interface version 11 = 2 + 9 # libarchive 3.0 == interface version 12 # libarchive 3.1 == interface version 13 math(EXPR INTERFACE_VERSION "13 + ${_minor}") # Set SOVERSION == Interface version # ?? Should there be more here ?? SET(SOVERSION "${INTERFACE_VERSION}") # Enalbe CMAKE_PUSH_CHECK_STATE() and CMAKE_POP_CHECK_STATE() macros # saving and restoring the state of the variables. INCLUDE(CMakePushCheckState) # Initialize the state of the variables. This initialization is not # necessary but this shows you what value the variables initially have. SET(CMAKE_REQUIRED_DEFINITIONS) SET(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_REQUIRED_LIBRARIES) SET(CMAKE_REQUIRED_FLAGS) # Especially for early development, we want to be a little # aggressive about diagnosing build problems; this can get # relaxed somewhat in final shipping versions. IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$") SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security") ################################################################# # Set compile flags for all build types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wmissing-prototypes") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wcast-qual") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$") IF (CMAKE_C_COMPILER_ID MATCHES "^Clang$") SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security") ################################################################# # Set compile flags for all build types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wmissing-prototypes") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wcast-qual") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^Clang$") IF (CMAKE_C_COMPILER_ID MATCHES "^XL$") SET(CMAKE_C_COMPILER "xlc_r") SET(CMAKE_REQUIRED_FLAGS "-qflag=e:e -qformat=sec") ################################################################# # Set compile flags for all build types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qflag=e:e -qformat=sec") ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qhalt=w") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qflag=w:w") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qinfo=pro:use") ENDIF(CMAKE_C_COMPILER_ID MATCHES "^XL$") IF (MSVC) ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" # Enable level 4 C4061: The enumerate has no associated handler in a switch # statement. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4061") # Enable level 4 C4254: A larger bit field was assigned to a smaller bit # field. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4254") # Enable level 4 C4295: An array was initialized but the last character in # the array is not a null; accessing the array may # produce unexpected results. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4295") # Enable level 4 C4296: An unsigned variable was used in a comparison # operation with zero. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4296") # Enable level 4 C4389: An operation involved signed and unsigned variables. # This could result in a loss of data. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4389") # Enable level 4 C4505: The given function is local and not referenced in # the body of the module; therefore, the function is # dead code. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4505") # Enable level 4 C4514: The optimizer removed an inline function that is not # called. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4514") # Enable level 4 C4702: Unreachable code. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4702") # Enable level 4 C4706: The test value in a conditional expression was the # result of an assignment. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4706") # /WX option is the same as gcc's -Werror option. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /WX") # /Oi option enables built-in functions. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Oi") ################################################################# # Set compile flags for release build. SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oi") ENDIF (MSVC) # Enable CTest/CDash support include(CTest) OPTION(ENABLE_NETTLE "Enable use of Nettle" ON) OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON) OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF) OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON) OPTION(ENABLE_ZLIB "Enable the use of the system ZLIB library if found" ON) OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON) OPTION(ENABLE_LIBXML2 "Enable the use of the system libxml2 library if found" ON) OPTION(ENABLE_EXPAT "Enable the use of the system EXPAT library if found" ON) OPTION(ENABLE_PCREPOSIX "Enable the use of the system PCREPOSIX library if found" ON) OPTION(ENABLE_LibGCC "Enable the use of the system LibGCC library if found" ON) # CNG is used for encrypt/decrypt Zip archives on Windows. OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON) OPTION(ENABLE_TAR "Enable tar building" ON) OPTION(ENABLE_TAR_SHARED "Enable dynamic build of tar" FALSE) OPTION(ENABLE_CPIO "Enable cpio building" ON) OPTION(ENABLE_CPIO_SHARED "Enable dynamic build of cpio" FALSE) OPTION(ENABLE_CAT "Enable cat building" ON) OPTION(ENABLE_CAT_SHARED "Enable dynamic build of cat" FALSE) OPTION(ENABLE_XATTR "Enable extended attribute support" ON) OPTION(ENABLE_ACL "Enable ACL support" ON) OPTION(ENABLE_ICONV "Enable iconv support" ON) OPTION(ENABLE_TEST "Enable unit and regression tests" ON) OPTION(ENABLE_COVERAGE "Enable code coverage (GCC only, automatically sets ENABLE_TEST to ON)" FALSE) OPTION(ENABLE_INSTALL "Enable installing of libraries" ON) SET(POSIX_REGEX_LIB "AUTO" CACHE STRING "Choose what library should provide POSIX regular expression support") SET(ENABLE_SAFESEH "AUTO" CACHE STRING "Enable use of /SAFESEH linker flag (MSVC only)") SET(WINDOWS_VERSION "WIN7" CACHE STRING "Set Windows version to use (Windows only)") IF(ENABLE_COVERAGE) include(LibarchiveCodeCoverage) ENDIF(ENABLE_COVERAGE) IF(ENABLE_TEST) ENABLE_TESTING() ENDIF(ENABLE_TEST) IF(WIN32) IF(WINDOWS_VERSION STREQUAL "WIN8") SET(NTDDI_VERSION 0x06020000) SET(_WIN32_WINNT 0x0602) SET(WINVER 0x0602) ELSEIF(WINDOWS_VERSION STREQUAL "WIN7") SET(NTDDI_VERSION 0x06010000) SET(_WIN32_WINNT 0x0601) SET(WINVER 0x0601) ELSEIF(WINDOWS_VERSION STREQUAL "WS08") SET(NTDDI_VERSION 0x06000100) SET(_WIN32_WINNT 0x0600) SET(WINVER 0x0600) ELSEIF(WINDOWS_VERSION STREQUAL "VISTA") SET(NTDDI_VERSION 0x06000000) SET(_WIN32_WINNT 0x0600) SET(WINVER 0x0600) ELSEIF(WINDOWS_VERSION STREQUAL "WS03") SET(NTDDI_VERSION 0x05020000) SET(_WIN32_WINNT 0x0502) SET(WINVER 0x0502) ELSEIF(WINDOWS_VERSION STREQUAL "WINXP") SET(NTDDI_VERSION 0x05010000) SET(_WIN32_WINNT 0x0501) SET(WINVER 0x0501) ELSE(WINDOWS_VERSION STREQUAL "WIN8") # Default to Windows Server 2003 API if we don't recognize the specifier SET(NTDDI_VERSION 0x05020000) SET(_WIN32_WINNT 0x0502) SET(WINVER 0x0502) ENDIF(WINDOWS_VERSION STREQUAL "WIN8") ENDIF(WIN32) IF(MSVC) IF(ENABLE_SAFESEH STREQUAL "YES") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH") SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH") SET(ENV{LDFLAGS} "$ENV{LDFLAGS} /SAFESEH") ELSEIF(ENABLE_SAFESEH STREQUAL "NO") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO") SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") SET(ENV{LDFLAGS} "$ENV{LDFLAGS} /SAFESEH:NO") ENDIF(ENABLE_SAFESEH STREQUAL "YES") ENDIF(MSVC) IF("${CMAKE_C_PLATFORM_ID}" MATCHES "^(HP-UX)$") ADD_DEFINITIONS(-D_XOPEN_SOURCE=500) # Ask wchar.h for mbstate_t ENDIF() # INCLUDE(CheckCSourceCompiles) INCLUDE(CheckCSourceRuns) INCLUDE(CheckFileOffsetBits) INCLUDE(CheckFuncs) INCLUDE(CheckHeaderDirent) INCLUDE(CheckIncludeFile) INCLUDE(CheckIncludeFiles) INCLUDE(CheckLibraryExists) INCLUDE(CheckStructHasMember) INCLUDE(CheckSymbolExists) INCLUDE(CheckTypeExists) INCLUDE(CheckTypeSize) # # Generate list.h # MACRO (GENERATE_LIST_H _listfile _cmlist __list_sources) SET(_argv ${ARGV}) # Remove _listfile and _cmlist from _argv LIST(REMOVE_AT _argv 0 1) IF (NOT EXISTS "${_listfile}" OR ${_cmlist} IS_NEWER_THAN "${_listfile}") MESSAGE(STATUS "Generating ${_listfile}") FILE(WRITE ${_listfile} "") FOREACH (testfile ${_argv}) IF (testfile MATCHES "^test_[^/]+[.]c$") FILE(STRINGS ${testfile} testvar REGEX "^DEFINE_TEST") FOREACH (deftest ${testvar}) FILE(APPEND ${_listfile} "${deftest}\n") ENDFOREACH (deftest) ENDIF (testfile MATCHES "^test_[^/]+[.]c$") ENDFOREACH (testfile) ENDIF (NOT EXISTS "${_listfile}" OR ${_cmlist} IS_NEWER_THAN "${_listfile}") ENDMACRO (GENERATE_LIST_H) # # Generate installation rules for man pages. # MACRO (INSTALL_MAN __mans) FOREACH (_man ${ARGV}) STRING(REGEX REPLACE "^.+[.]([1-9])" "\\1" _mansect ${_man}) INSTALL(FILES ${_man} DESTINATION "share/man/man${_mansect}") ENDFOREACH (_man) ENDMACRO (INSTALL_MAN __mans) # # Find out what macro is needed to use libraries on Windows. # MACRO (TRY_MACRO_FOR_LIBRARY INCLUDES LIBRARIES TRY_TYPE SAMPLE_SOURCE MACRO_LIST) IF(WIN32 AND NOT CYGWIN) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables SET(CMAKE_REQUIRED_INCLUDES ${INCLUDES}) SET(CMAKE_REQUIRED_LIBRARIES ${LIBRARIES}) FOREACH(VAR ${MACRO_LIST}) # Clear ${VAR} from CACHE If the libraries which ${VAR} was # checked with are changed. SET(VAR_WITH_LIB "${VAR}_WITH_LIB") GET_PROPERTY(PREV_VAR_WITH_LIB VARIABLE PROPERTY ${VAR_WITH_LIB}) IF(NOT "${PREV_VAR_WITH_LIB}" STREQUAL "${LIBRARIES}") UNSET(${VAR} CACHE) ENDIF(NOT "${PREV_VAR_WITH_LIB}" STREQUAL "${LIBRARIES}") # Check if the library can be used with the macro. IF("${TRY_TYPE}" MATCHES "COMPILES") CHECK_C_SOURCE_COMPILES("${SAMPLE_SOURCE}" ${VAR}) ELSEIF("${TRY_TYPE}" MATCHES "RUNS") CHECK_C_SOURCE_RUNS("${SAMPLE_SOURCE}" ${VAR}) ELSE("${TRY_TYPE}" MATCHES "COMPILES") MESSAGE(FATAL_ERROR "UNKNOWN KEYWORD \"${TRY_TYPE}\" FOR TRY_TYPE") ENDIF("${TRY_TYPE}" MATCHES "COMPILES") # Save the libraries which ${VAR} is checked with. SET(${VAR_WITH_LIB} "${LIBRARIES}" CACHE INTERNAL "Macro ${VAR} is checked with") ENDFOREACH(VAR) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(WIN32 AND NOT CYGWIN) ENDMACRO (TRY_MACRO_FOR_LIBRARY) # # Check compress/decompress libraries # IF(WIN32 AND NOT CMAKE_CL_64 AND NOT CYGWIN) # GnuWin32 is only for Win32, not Win64. SET(__GNUWIN32PATH "C:/Program Files/GnuWin32") ENDIF(WIN32 AND NOT CMAKE_CL_64 AND NOT CYGWIN) IF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}") # You have to add a path availabel DLL file into PATH environment variable. # Maybe DLL path is "C:/Program Files/GnuWin32/bin". # The zlib and the bzip2 Setup program have installed programs and DLLs into # "C:/Program Files/GnuWin32" by default. # This is convenience setting for Windows. SET(CMAKE_PREFIX_PATH ${__GNUWIN32PATH} $(CMAKE_PREFIX_PATH)) # # If you didn't use Setup program or installed into nonstandard path, # cmake cannot find out your zlib or bzip2 libraries and include files, # you should execute cmake with -DCMAKE_PREFIX_PATH option. # e.g. # cmake -DCMAKE_PREFIX_PATH= # # If compiling error occurred in zconf.h, You may need patch to zconf.h. #--- zconf.h.orig 2005-07-21 00:40:26.000000000 #+++ zconf.h 2009-01-19 11:39:10.093750000 #@@ -286,7 +286,7 @@ # # #if 1 /* HAVE_UNISTD_H -- this line is updated by ./configure */ # # include /* for off_t */ #-# include /* for SEEK_* and off_t */ #+# include /* for SEEK_* and off_t */ # # ifdef VMS # # include /* for off_t */ # # endif ENDIF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}") SET(ADDITIONAL_LIBS "") # # Find ZLIB # IF(ENABLE_ZLIB) FIND_PACKAGE(ZLIB) ELSE() SET(ZLIB_FOUND FALSE) # Override cached value ENDIF() IF(ZLIB_FOUND) SET(HAVE_LIBZ 1) SET(HAVE_ZLIB_H 1) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${ZLIB_LIBRARIES}) IF(WIN32 AND NOT CYGWIN) # # Test if ZLIB_WINAPI macro is needed to use. # TRY_MACRO_FOR_LIBRARY( "${ZLIB_INCLUDE_DIR}" "${ZLIB_LIBRARIES}" RUNS "#include \nint main() {uLong f = zlibCompileFlags(); return (f&(1U<<10))?0:-1; }" ZLIB_WINAPI) IF(ZLIB_WINAPI) ADD_DEFINITIONS(-DZLIB_WINAPI) ELSE(ZLIB_WINAPI) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${ZLIB_INCLUDE_DIR}" "${ZLIB_LIBRARIES}" COMPILES "#include \nint main() {return zlibVersion()?1:0; }" "ZLIB_DLL;WITHOUT_ZLIB_DLL") IF(ZLIB_DLL) ADD_DEFINITIONS(-DZLIB_DLL) ENDIF(ZLIB_DLL) ENDIF(ZLIB_WINAPI) ENDIF(WIN32 AND NOT CYGWIN) ENDIF(ZLIB_FOUND) MARK_AS_ADVANCED(CLEAR ZLIB_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR ZLIB_LIBRARY) # # Find BZip2 # IF(ENABLE_BZip2) FIND_PACKAGE(BZip2) ELSE() SET(BZIP2_FOUND FALSE) # Override cached value ENDIF() IF(BZIP2_FOUND) SET(HAVE_LIBBZ2 1) SET(HAVE_BZLIB_H 1) INCLUDE_DIRECTORIES(${BZIP2_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${BZIP2_LIBRARIES}) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${BZIP2_INCLUDE_DIR}" "${BZIP2_LIBRARIES}" COMPILES "#include \nint main() {return BZ2_bzlibVersion()?1:0; }" "USE_BZIP2_DLL;USE_BZIP2_STATIC") IF(USE_BZIP2_DLL) ADD_DEFINITIONS(-DUSE_BZIP2_DLL) ELSEIF(USE_BZIP2_STATIC) ADD_DEFINITIONS(-DUSE_BZIP2_STATIC) ENDIF(USE_BZIP2_DLL) ENDIF(BZIP2_FOUND) MARK_AS_ADVANCED(CLEAR BZIP2_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR BZIP2_LIBRARIES) # # Find LZMA # IF(ENABLE_LZMA) FIND_PACKAGE(LibLZMA) ELSE() SET(LIBZMA_FOUND FALSE) # Override cached value ENDIF() IF(LIBLZMA_FOUND) SET(HAVE_LIBLZMA 1) SET(HAVE_LZMA_H 1) + SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR}) + SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES}) INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS}) LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES}) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${LIBLZMA_INCLUDE_DIRS}" "${LIBLZMA_LIBRARIES}" COMPILES "#include \nint main() {return (int)lzma_version_number(); }" "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC") IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) ADD_DEFINITIONS(-DLZMA_API_STATIC) ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) ELSE(LIBLZMA_FOUND) # LZMA not found and will not be used. ENDIF(LIBLZMA_FOUND) # # Find LZO2 # IF(ENABLE_LZO) IF (LZO2_INCLUDE_DIR) # Already in cache, be silent SET(LZO2_FIND_QUIETLY TRUE) ENDIF (LZO2_INCLUDE_DIR) FIND_PATH(LZO2_INCLUDE_DIR lzo/lzoconf.h) FIND_LIBRARY(LZO2_LIBRARY NAMES lzo2 liblzo2) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZO2 DEFAULT_MSG LZO2_LIBRARY LZO2_INCLUDE_DIR) ELSE(ENABLE_LZO) SET(LIBZMA_FOUND FALSE) # Override cached value ENDIF(ENABLE_LZO) IF(LZO2_FOUND) SET(HAVE_LIBLZO2 1) SET(HAVE_LZO_LZOCONF_H 1) SET(HAVE_LZO_LZO1X_H 1) INCLUDE_DIRECTORIES(${LZO2_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${LZO2_LIBRARY}) # # TODO: test for static library. # ENDIF(LZO2_FOUND) MARK_AS_ADVANCED(CLEAR LZO2_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR LZO2_LIBRARY) # # Find LZ4 # IF (LZ4_INCLUDE_DIR) # Already in cache, be silent SET(LZ4_FIND_QUIETLY TRUE) ENDIF (LZ4_INCLUDE_DIR) FIND_PATH(LZ4_INCLUDE_DIR lz4.h) FIND_LIBRARY(LZ4_LIBRARY NAMES lz4 liblz4) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR) IF(LZ4_FOUND) SET(HAVE_LIBLZ4 1) SET(HAVE_LZ4_H 1) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables SET(CMAKE_REQUIRED_INCLUDES ${LZ4_INCLUDE_DIR}) CHECK_INCLUDE_FILES("lz4hc.h" HAVE_LZ4HC_H) CMAKE_POP_CHECK_STATE() # Restore the state of the variables INCLUDE_DIRECTORIES(${LZ4_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${LZ4_LIBRARY}) # # TODO: test for static library. # ENDIF(LZ4_FOUND) MARK_AS_ADVANCED(CLEAR LZ4_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY) # # Check headers # CHECK_HEADER_DIRENT() SET(INCLUDES "") MACRO (LA_CHECK_INCLUDE_FILE header var) CHECK_INCLUDE_FILES("${INCLUDES};${header}" ${var}) IF (${var}) SET(INCLUDES ${INCLUDES} ${header}) ENDIF (${var}) ENDMACRO (LA_CHECK_INCLUDE_FILE) # Some FreeBSD headers assume sys/types.h was already included. LA_CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H) # Alphabetize the rest unless there's a compelling reason LA_CHECK_INCLUDE_FILE("acl/libacl.h" HAVE_ACL_LIBACL_H) LA_CHECK_INCLUDE_FILE("ctype.h" HAVE_CTYPE_H) LA_CHECK_INCLUDE_FILE("copyfile.h" HAVE_COPYFILE_H) LA_CHECK_INCLUDE_FILE("direct.h" HAVE_DIRECT_H) LA_CHECK_INCLUDE_FILE("dlfcn.h" HAVE_DLFCN_H) LA_CHECK_INCLUDE_FILE("errno.h" HAVE_ERRNO_H) LA_CHECK_INCLUDE_FILE("ext2fs/ext2_fs.h" HAVE_EXT2FS_EXT2_FS_H) CHECK_C_SOURCE_COMPILES("#include #include int main(void) { return EXT2_IOC_GETFLAGS; }" HAVE_WORKING_EXT2_IOC_GETFLAGS) LA_CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H) LA_CHECK_INCLUDE_FILE("grp.h" HAVE_GRP_H) LA_CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H) LA_CHECK_INCLUDE_FILE("io.h" HAVE_IO_H) LA_CHECK_INCLUDE_FILE("langinfo.h" HAVE_LANGINFO_H) LA_CHECK_INCLUDE_FILE("limits.h" HAVE_LIMITS_H) LA_CHECK_INCLUDE_FILE("linux/types.h" HAVE_LINUX_TYPES_H) LA_CHECK_INCLUDE_FILE("linux/fiemap.h" HAVE_LINUX_FIEMAP_H) LA_CHECK_INCLUDE_FILE("linux/fs.h" HAVE_LINUX_FS_H) + +CHECK_C_SOURCE_COMPILES("#include +#include +int main(void) { return FS_IOC_GETFLAGS; }" HAVE_WORKING_FS_IOC_GETFLAGS) + LA_CHECK_INCLUDE_FILE("linux/magic.h" HAVE_LINUX_MAGIC_H) LA_CHECK_INCLUDE_FILE("locale.h" HAVE_LOCALE_H) LA_CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H) LA_CHECK_INCLUDE_FILE("paths.h" HAVE_PATHS_H) LA_CHECK_INCLUDE_FILE("poll.h" HAVE_POLL_H) LA_CHECK_INCLUDE_FILE("process.h" HAVE_PROCESS_H) LA_CHECK_INCLUDE_FILE("pthread.h" HAVE_PTHREAD_H) LA_CHECK_INCLUDE_FILE("pwd.h" HAVE_PWD_H) LA_CHECK_INCLUDE_FILE("readpassphrase.h" HAVE_READPASSPHRASE_H) LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H) LA_CHECK_INCLUDE_FILE("signal.h" HAVE_SIGNAL_H) LA_CHECK_INCLUDE_FILE("spawn.h" HAVE_SPAWN_H) LA_CHECK_INCLUDE_FILE("stdarg.h" HAVE_STDARG_H) LA_CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H) LA_CHECK_INCLUDE_FILE("stdlib.h" HAVE_STDLIB_H) LA_CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H) LA_CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H) LA_CHECK_INCLUDE_FILE("sys/acl.h" HAVE_SYS_ACL_H) LA_CHECK_INCLUDE_FILE("sys/cdefs.h" HAVE_SYS_CDEFS_H) LA_CHECK_INCLUDE_FILE("sys/ioctl.h" HAVE_SYS_IOCTL_H) LA_CHECK_INCLUDE_FILE("sys/mkdev.h" HAVE_SYS_MKDEV_H) LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H) LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H) LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H) LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H) LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H) LA_CHECK_INCLUDE_FILE("sys/statfs.h" HAVE_SYS_STATFS_H) LA_CHECK_INCLUDE_FILE("sys/statvfs.h" HAVE_SYS_STATVFS_H) LA_CHECK_INCLUDE_FILE("sys/time.h" HAVE_SYS_TIME_H) LA_CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H) LA_CHECK_INCLUDE_FILE("sys/utsname.h" HAVE_SYS_UTSNAME_H) LA_CHECK_INCLUDE_FILE("sys/vfs.h" HAVE_SYS_VFS_H) LA_CHECK_INCLUDE_FILE("sys/wait.h" HAVE_SYS_WAIT_H) LA_CHECK_INCLUDE_FILE("time.h" HAVE_TIME_H) LA_CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H) LA_CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H) LA_CHECK_INCLUDE_FILE("wchar.h" HAVE_WCHAR_H) LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H) LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H) IF(ENABLE_CNG) LA_CHECK_INCLUDE_FILE("Bcrypt.h" HAVE_BCRYPT_H) ELSE(ENABLE_CNG) UNSET(HAVE_BCRYPT_H CACHE) ENDIF(ENABLE_CNG) # Following files need windows.h, so we should test it after windows.h test. LA_CHECK_INCLUDE_FILE("wincrypt.h" HAVE_WINCRYPT_H) LA_CHECK_INCLUDE_FILE("winioctl.h" HAVE_WINIOCTL_H) # # Check whether use of __EXTENSIONS__ is safe. # We need some macro such as _GNU_SOURCE to use extension functions. # SET(_INCLUDE_FILES) FOREACH (it ${_HEADER}) SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n") ENDFOREACH (it) CHECK_C_SOURCE_COMPILES( "#define __EXTENSIONS__ 1 ${_INCLUDE_FILES} int main() { return 0;}" SAFE_TO_DEFINE_EXTENSIONS) # # Find Nettle # IF(ENABLE_NETTLE) FIND_PACKAGE(Nettle) IF(NETTLE_FOUND) SET(HAVE_LIBNETTLE 1) LIST(APPEND ADDITIONAL_LIBS ${NETTLE_LIBRARIES}) INCLUDE_DIRECTORIES(${NETTLE_INCLUDE_DIR}) LIST(APPEND CMAKE_REQUIRED_INCLUDES ${NETTLE_INCLUDE_DIR}) LA_CHECK_INCLUDE_FILE("nettle/aes.h" HAVE_NETTLE_AES_H) LA_CHECK_INCLUDE_FILE("nettle/hmac.h" HAVE_NETTLE_HMAC_H) LA_CHECK_INCLUDE_FILE("nettle/md5.h" HAVE_NETTLE_MD5_H) LA_CHECK_INCLUDE_FILE("nettle/pbkdf2.h" HAVE_NETTLE_PBKDF2_H) LA_CHECK_INCLUDE_FILE("nettle/ripemd160.h" HAVE_NETTLE_RIPEMD160_H) LA_CHECK_INCLUDE_FILE("nettle/sha.h" HAVE_NETTLE_SHA_H) ENDIF(NETTLE_FOUND) MARK_AS_ADVANCED(CLEAR NETTLE_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR NETTLE_LIBRARIES) ENDIF(ENABLE_NETTLE) # # Find OpenSSL # (Except on Mac, where OpenSSL is deprecated.) # IF(ENABLE_OPENSSL AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") FIND_PACKAGE(OpenSSL) IF(OPENSSL_FOUND) SET(HAVE_LIBCRYPTO 1) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${OPENSSL_CRYPTO_LIBRARY}) ENDIF(OPENSSL_FOUND) ELSE() SET(OPENSSL_FOUND FALSE) # Override cached value ENDIF() # FreeBSD libmd IF(NOT OPENSSL_FOUND) CHECK_LIBRARY_EXISTS(md "MD5Init" "" LIBMD_FOUND) IF(LIBMD_FOUND) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables SET(CMAKE_REQUIRED_LIBRARIES "md") FIND_LIBRARY(LIBMD_LIBRARY NAMES md) LIST(APPEND ADDITIONAL_LIBS ${LIBMD_LIBRARY}) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(LIBMD_FOUND) ENDIF(NOT OPENSSL_FOUND) # # How to prove that CRYPTO functions, which have several names on various # platforms, just see if archive_digest.c can compile and link against # required libraries. # MACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION) FOREACH(ALGORITHM ${ALGORITHMS}) IF(NOT ARCHIVE_CRYPTO_${ALGORITHM}) STRING(TOLOWER "${ALGORITHM}" lower_algorithm) STRING(TOUPPER "${ALGORITHM}" algorithm) IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND) SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE) ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NOT NETTLE_FOUND) SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE) ENDIF("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND) IF(NOT DEFINED ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) # Probe the local implementation for whether this # crypto implementation is available on this platform. SET(TRY_CRYPTO_REQUIRED_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}/libarchive;${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp") SET(TRY_CRYPTO_REQUIRED_LIBS) IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND) SET(TRY_CRYPTO_REQUIRED_INCLUDES "${TRY_CRYPTO_REQUIRED_INCLUDES};${OPENSSL_INCLUDE_DIR}") SET(TRY_CRYPTO_REQUIRED_LIBS "-DLINK_LIBRARIES:STRING=${OPENSSL_LIBRARIES}") ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NETTLE_FOUND) SET(TRY_CRYPTO_REQUIRED_INCLUDES "${TRY_CRYPTO_REQUIRED_INCLUDES};${NETTLE_INCLUDE_DIR}") SET(TRY_CRYPTO_REQUIRED_LIBS "-DLINK_LIBRARIES:STRING=${NETTLE_LIBRARY}") ELSEIF("${IMPLEMENTATION}" MATCHES "^LIBMD$" AND LIBMD_FOUND) SET(TRY_CRYPTO_REQUIRED_LIBS "-DLINK_LIBRARIES:STRING=${LIBMD_LIBRARY}") ENDIF("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h) FILE(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h" CONFDEFS_H) FILE(READ "${CMAKE_CURRENT_SOURCE_DIR}/libarchive/archive_digest.c" ARCHIVE_CRYPTO_C) SET(SOURCE "${CONFDEFS_H} #define ARCHIVE_${algorithm}_COMPILE_TEST #define ARCHIVE_CRYPTO_${algorithm}_${IMPLEMENTATION} #define PLATFORM_CONFIG_H \"check_crypto_md.h\" ${ARCHIVE_CRYPTO_C} int main(int argc, char **argv) { archive_${lower_algorithm}_ctx ctx; archive_${lower_algorithm}_init(&ctx); archive_${lower_algorithm}_update(&ctx, *argv, argc); archive_${lower_algorithm}_final(&ctx, NULL); return 0; } ") FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_md.h" "") FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_md.c" "${SOURCE}") MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}") TRY_COMPILE(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_md.c CMAKE_FLAGS "${TRY_CRYPTO_REQUIRED_LIBS}" "${TRY_CRYPTO_REQUIRED_INCLUDES}" OUTPUT_VARIABLE OUTPUT) # Inform user whether or not we found it; if not, log why we didn't. IF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} -- found") SET(ARCHIVE_CRYPTO_${ALGORITHM} 1) ELSE (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} -- not found") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} failed with the following output:\n" "${OUTPUT}\n" "Source file was:\n${SOURCE}\n") ENDIF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) ENDIF(NOT DEFINED ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) # Add appropriate libs/includes depending on whether the implementation # was found on this platform. IF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${OPENSSL_LIBRARIES}) LIST(REMOVE_DUPLICATES ADDITIONAL_LIBS) ENDIF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND) ENDIF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}) ENDIF(NOT ARCHIVE_CRYPTO_${ALGORITHM}) ENDFOREACH(ALGORITHM ${ALGORITHMS}) ENDMACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION) # # CRYPTO functions on Windows is defined at archive_windows.c, thus we do not # need the test what the functions can be mapped to archive_{crypto name}_init, # archive_{crypto name}_update and archive_{crypto name}_final. # The functions on Windows use CALG_{crypto name} macro to create a crypt object # and then we need to know what CALG_{crypto name} macros is available to show # ARCHIVE_CRYPTO_{crypto name}_WIN macros because Windows 2000 and earlier version # of Windows XP do not support SHA256, SHA384 and SHA512. # MACRO(CHECK_CRYPTO_WIN CRYPTO_LIST) IF(WIN32 AND NOT CYGWIN) FOREACH(CRYPTO ${CRYPTO_LIST}) IF(NOT ARCHIVE_CRYPTO_${CRYPTO}) IF(NOT DEFINED ARCHIVE_CRYPTO_${CRYPTO}_WIN) STRING(TOUPPER "${CRYPTO}" crypto) SET(ALGID "") IF ("${CRYPTO}" MATCHES "^MD5$") SET(ALGID "CALG_MD5") ENDIF ("${CRYPTO}" MATCHES "^MD5$") IF ("${CRYPTO}" MATCHES "^SHA1$") SET(ALGID "CALG_SHA1") ENDIF ("${CRYPTO}" MATCHES "^SHA1$") IF ("${CRYPTO}" MATCHES "^SHA256$") SET(ALGID "CALG_SHA_256") ENDIF ("${CRYPTO}" MATCHES "^SHA256$") IF ("${CRYPTO}" MATCHES "^SHA384$") SET(ALGID "CALG_SHA_384") ENDIF ("${CRYPTO}" MATCHES "^SHA384$") IF ("${CRYPTO}" MATCHES "^SHA512$") SET(ALGID "CALG_SHA_512") ENDIF ("${CRYPTO}" MATCHES "^SHA512$") CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h) FILE(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h" CONFDEFS_H) SET(SOURCE "${CONFDEFS_H} #define ${crypto}_COMPILE_TEST #include #include int main(int argc, char **argv) { return ${ALGID}; } ") SET(SOURCE_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_win.c") FILE(WRITE "${SOURCE_FILE}" "${SOURCE}") MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN") TRY_COMPILE(ARCHIVE_CRYPTO_${CRYPTO}_WIN ${CMAKE_BINARY_DIR} ${SOURCE_FILE} CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}/libarchive" OUTPUT_VARIABLE OUTPUT) IF (ARCHIVE_CRYPTO_${CRYPTO}_WIN) MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN -- found") SET(ARCHIVE_CRYPTO_${CRYPTO} 1) ELSE (ARCHIVE_CRYPTO_${CRYPTO}_WIN) MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN -- not found") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN failed with the following output:\n" "${OUTPUT}\n" "Source file was:\n${SOURCE}\n") ENDIF (ARCHIVE_CRYPTO_${CRYPTO}_WIN) ENDIF(NOT DEFINED ARCHIVE_CRYPTO_${CRYPTO}_WIN) ENDIF(NOT ARCHIVE_CRYPTO_${CRYPTO}) ENDFOREACH(CRYPTO) ENDIF(WIN32 AND NOT CYGWIN) ENDMACRO(CHECK_CRYPTO_WIN CRYPTO_LIST) # # Find iconv # POSIX defines the second arg as const char ** # and requires it to be in libc. But we can accept # a non-const argument here and can support iconv() # being in libiconv. # MACRO(CHECK_ICONV LIB TRY_ICONV_CONST) IF(NOT HAVE_ICONV) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR CMAKE_C_COMPILER_ID MATCHES "^Clang$") # # During checking iconv proto type, we should use -Werror to avoid the # success of iconv detection with a warnig which success is a miss # detection. So this needs for all build mode(even it's a release mode). # SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR CMAKE_C_COMPILER_ID MATCHES "^Clang$") IF (CMAKE_C_COMPILER_ID MATCHES "^XL$") SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -qhalt=w -qflag=w:w") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^XL$") IF (MSVC) # NOTE: /WX option is the same as gcc's -Werror option. SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /WX") ENDIF (MSVC) # CHECK_C_SOURCE_COMPILES( "#include #include int main() { ${TRY_ICONV_CONST} char *ccp; iconv_t cd = iconv_open(\"\", \"\"); iconv(cd, &ccp, (size_t *)0, (char **)0, (size_t *)0); iconv_close(cd); return 0; }" HAVE_ICONV_${LIB}_${TRY_ICONV_CONST}) IF(HAVE_ICONV_${LIB}_${TRY_ICONV_CONST}) SET(HAVE_ICONV true) SET(ICONV_CONST ${TRY_ICONV_CONST}) ENDIF(HAVE_ICONV_${LIB}_${TRY_ICONV_CONST}) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(NOT HAVE_ICONV) ENDMACRO(CHECK_ICONV TRY_ICONV_CONST) IF(ENABLE_ICONV) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables FIND_PATH(ICONV_INCLUDE_DIR iconv.h) IF(ICONV_INCLUDE_DIR) #SET(INCLUDES ${INCLUDES} "iconv.h") SET(HAVE_ICONV_H 1) INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR}) SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) CHECK_ICONV("libc" "const") CHECK_ICONV("libc" "") # If iconv isn't in libc and we have a libiconv, try that. FIND_LIBRARY(LIBICONV_PATH NAMES iconv libiconv) IF(NOT HAVE_ICONV AND LIBICONV_PATH) LIST(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBICONV_PATH}) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${ICONV_INCLUDE_DIR}" "${LIBICONV_PATH}" COMPILES "#include \nint main() {return iconv_close((iconv_t)0);}" "WITHOUT_LIBICONV_STATIC;LIBICONV_STATIC") IF(NOT WITHOUT_LIBICONV_STATIC AND LIBICONV_STATIC) ADD_DEFINITIONS(-DLIBICONV_STATIC) ENDIF(NOT WITHOUT_LIBICONV_STATIC AND LIBICONV_STATIC) # # Set up CMAKE_REQUIRED_* for CHECK_ICONV # SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) SET(CMAKE_REQUIRED_LIBRARIES ${LIBICONV_PATH}) IF(LIBICONV_STATIC) # LIBICONV_STATIC is necessary for the success of CHECK_ICONV # on Windows. SET(CMAKE_REQUIRED_DEFINITIONS "-DLIBICONV_STATIC") ELSE(LIBICONV_STATIC) SET(CMAKE_REQUIRED_DEFINITIONS) ENDIF(LIBICONV_STATIC) CHECK_ICONV("libiconv" "const") CHECK_ICONV("libiconv" "") IF (HAVE_ICONV) LIST(APPEND ADDITIONAL_LIBS ${LIBICONV_PATH}) ENDIF(HAVE_ICONV) ENDIF(NOT HAVE_ICONV AND LIBICONV_PATH) ENDIF(ICONV_INCLUDE_DIR) # # Find locale_charset() for libiconv. # IF(LIBICONV_PATH) SET(CMAKE_REQUIRED_DEFINITIONS) SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) SET(CMAKE_REQUIRED_LIBRARIES) CHECK_INCLUDE_FILES("localcharset.h" HAVE_LOCALCHARSET_H) FIND_LIBRARY(LIBCHARSET_PATH NAMES charset libcharset) IF(LIBCHARSET_PATH) SET(CMAKE_REQUIRED_LIBRARIES ${LIBCHARSET_PATH}) IF(WIN32 AND NOT CYGWIN) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${ICONV_INCLUDE_DIR}" "${LIBCHARSET_PATH}" COMPILES "#include \nint main() {return locale_charset()?1:0;}" "WITHOUT_LIBCHARSET_STATIC;LIBCHARSET_STATIC") IF(NOT WITHOUT_LIBCHARSET_STATIC AND LIBCHARSET_STATIC) ADD_DEFINITIONS(-DLIBCHARSET_STATIC) ENDIF(NOT WITHOUT_LIBCHARSET_STATIC AND LIBCHARSET_STATIC) IF(WITHOUT_LIBCHARSET_STATIC OR LIBCHARSET_STATIC) SET(HAVE_LOCALE_CHARSET ON CACHE INTERNAL "Have function locale_charset") ENDIF(WITHOUT_LIBCHARSET_STATIC OR LIBCHARSET_STATIC) ELSE(WIN32 AND NOT CYGWIN) CHECK_FUNCTION_EXISTS_GLIBC(locale_charset HAVE_LOCALE_CHARSET) ENDIF(WIN32 AND NOT CYGWIN) IF(HAVE_LOCALE_CHARSET) LIST(APPEND ADDITIONAL_LIBS ${LIBCHARSET_PATH}) ENDIF(HAVE_LOCALE_CHARSET) ENDIF(LIBCHARSET_PATH) ENDIF(LIBICONV_PATH) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ELSE(ENABLE_ICONV) # Make sure ICONV variables are not in CACHE after ENABLE_ICONV disabled # (once enabled). UNSET(HAVE_LOCALE_CHARSET CACHE) UNSET(HAVE_ICONV CACHE) UNSET(HAVE_ICONV_libc_ CACHE) UNSET(HAVE_ICONV_libc_const CACHE) UNSET(HAVE_ICONV_libiconv_ CACHE) UNSET(HAVE_ICONV_libiconv_const CACHE) UNSET(ICONV_INCLUDE_DIR CACHE) UNSET(LIBICONV_PATH CACHE) UNSET(LIBICONV_DLL CACHE) UNSET(LIBICONV_STATIC CACHE) UNSET(LIBCHARSET_DLL CACHE) UNSET(LIBCHARSET_STATIC CACHE) ENDIF(ENABLE_ICONV) # # Find Libxml2 # IF(ENABLE_LIBXML2) FIND_PACKAGE(LibXml2) ELSE() SET(LIBXML2_FOUND FALSE) ENDIF() IF(LIBXML2_FOUND) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${LIBXML2_LIBRARIES}) SET(HAVE_LIBXML2 1) # libxml2's include files use iconv.h SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) CHECK_INCLUDE_FILES("libxml/xmlreader.h" HAVE_LIBXML_XMLREADER_H) CHECK_INCLUDE_FILES("libxml/xmlwriter.h" HAVE_LIBXML_XMLWRITER_H) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${ICONV_INCLUDE_DIR};${LIBXML2_INCLUDE_DIR}" "ws2_32.lib;${ZLIB_LIBRARIES};${LIBICONV_PATH};${LIBXML2_LIBRARIES}" COMPILES "#include \n#include \nint main() {return xmlTextReaderRead((xmlTextReaderPtr)(void *)0);}" "WITHOUT_LIBXML_STATIC;LIBXML_STATIC") IF(NOT WITHOUT_LIBXML_STATIC AND LIBXML_STATIC) ADD_DEFINITIONS(-DLIBXML_STATIC) ENDIF(NOT WITHOUT_LIBXML_STATIC AND LIBXML_STATIC) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ELSE(LIBXML2_FOUND) # # Find Expat # IF(ENABLE_EXPAT) FIND_PACKAGE(EXPAT) ELSE() SET(EXPAT_FOUND FALSE) ENDIF() IF(EXPAT_FOUND) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${EXPAT_LIBRARIES}) SET(HAVE_LIBEXPAT 1) LA_CHECK_INCLUDE_FILE("expat.h" HAVE_EXPAT_H) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(EXPAT_FOUND) ENDIF(LIBXML2_FOUND) MARK_AS_ADVANCED(CLEAR LIBXML2_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR LIBXML2_LIBRARIES) # # POSIX Regular Expression support # IF(POSIX_REGEX_LIB MATCHES "^(AUTO|LIBC|LIBREGEX)$") # # If PCREPOSIX is not found or not requested, try using regex # from libc or libregex # FIND_PATH(REGEX_INCLUDE_DIR regex.h) IF(REGEX_INCLUDE_DIR) CHECK_FUNCTION_EXISTS_GLIBC(regcomp HAVE_REGCOMP_LIBC) # # If libc does not provide regex, find libregex. # IF(NOT HAVE_REGCOMP_LIBC) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables FIND_LIBRARY(REGEX_LIBRARY regex) IF(REGEX_LIBRARY) SET(CMAKE_REQUIRED_LIBRARIES ${REGEX_LIBRARY}) CHECK_FUNCTION_EXISTS_GLIBC(regcomp HAVE_REGCOMP_LIBREGEX) IF(HAVE_REGCOMP_LIBREGEX) LIST(APPEND ADDITIONAL_LIBS ${REGEX_LIBRARY}) # # If regex.h is not found, retry looking for regex.h at # REGEX_INCLUDE_DIR # IF(NOT HAVE_REGEX_H) UNSET(HAVE_REGEX_H CACHE) INCLUDE_DIRECTORIES(${REGEX_INCLUDE_DIR}) SET(CMAKE_REQUIRED_INCLUDES ${REGEX_INCLUDE_DIR}) LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H) ENDIF(NOT HAVE_REGEX_H) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${REGEX_INCLUDE_DIR}" "${REGEX_LIBRARY}" COMPILES "#include \n#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" "USE_REGEX_DLL;USE_REGEX_STATIC") IF(USE_REGEX_DLL) ADD_DEFINITIONS(-DUSE_REGEX_DLL) ELSEIF(USE_REGEX_STATIC) ADD_DEFINITIONS(-DUSE_REGEX_STATIC) ENDIF(USE_REGEX_DLL) ENDIF(HAVE_REGCOMP_LIBREGEX) ENDIF(REGEX_LIBRARY) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(NOT HAVE_REGCOMP_LIBC) ENDIF(REGEX_INCLUDE_DIR) IF(HAVE_REGCOMP_LIBC OR HAVE_REGCOMP_LIBREGEX) SET(FOUND_POSIX_REGEX_LIB 1) ENDIF(HAVE_REGCOMP_LIBC OR HAVE_REGCOMP_LIBREGEX) ENDIF(POSIX_REGEX_LIB MATCHES "^(AUTO|LIBC|LIBREGEX)$") IF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$") # # If requested, try finding library for PCREPOSIX # IF(ENABLE_LibGCC) FIND_PACKAGE(LibGCC) ELSE() SET(LIBGCC_FOUND FALSE) # Override cached value ENDIF() IF(ENABLE_PCREPOSIX) FIND_PACKAGE(PCREPOSIX) ELSE() SET(PCREPOSIX_FOUND FALSE) # Override cached value ENDIF() IF(PCREPOSIX_FOUND) INCLUDE_DIRECTORIES(${PCRE_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${PCREPOSIX_LIBRARIES}) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( "${PCRE_INCLUDE_DIR}" "${PCREPOSIX_LIBRARIES}" COMPILES "#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" "WITHOUT_PCRE_STATIC;PCRE_STATIC") IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) ADD_DEFINITIONS(-DPCRE_STATIC) ELSEIF(NOT WITHOUT_PCRE_STATIC AND NOT PCRE_STATIC AND PCRE_FOUND) # Determine if pcre static libraries are to be used. LIST(APPEND ADDITIONAL_LIBS ${PCRE_LIBRARIES}) SET(TMP_LIBRARIES ${PCREPOSIX_LIBRARIES} ${PCRE_LIBRARIES}) MESSAGE(STATUS "trying again with -lpcre included") TRY_MACRO_FOR_LIBRARY( "${PCRE_INCLUDE_DIR}" "${TMP_LIBRARIES}" COMPILES "#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" "WITHOUT_PCRE_STATIC;PCRE_STATIC") IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) ADD_DEFINITIONS(-DPCRE_STATIC) ELSEIF(NOT WITHOUT_PCRE_STATIC AND NOT PCRE_STATIC AND MSVC AND LIBGCC_FOUND) # When doing a Visual Studio build using pcre static libraries # built using the mingw toolchain, -lgcc is needed to resolve # ___chkstk_ms. MESSAGE(STATUS "Visual Studio build detected, trying again with -lgcc included") LIST(APPEND ADDITIONAL_LIBS ${LIBGCC_LIBRARIES}) SET(TMP_LIBRARIES ${PCREPOSIX_LIBRARIES} ${PCRE_LIBRARIES} ${LIBGCC_LIBRARIES}) TRY_MACRO_FOR_LIBRARY( "${PCRE_INCLUDE_DIR}" "${TMP_LIBRARIES}" COMPILES "#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" "WITHOUT_PCRE_STATIC;PCRE_STATIC") IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) ADD_DEFINITIONS(-DPCRE_STATIC) ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) ENDIF(PCREPOSIX_FOUND) MARK_AS_ADVANCED(CLEAR PCRE_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR PCREPOSIX_LIBRARIES) MARK_AS_ADVANCED(CLEAR PCRE_LIBRARIES) MARK_AS_ADVANCED(CLEAR LIBGCC_LIBRARIES) ENDIF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$") # # Check functions # CMAKE_PUSH_CHECK_STATE() # Save the state of the variables IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR CMAKE_C_COMPILER_ID MATCHES "^Clang$") # # During checking functions, we should use -fno-builtin to avoid the # failure of function detection which failure is an error "conflicting # types for built-in function" caused by using -Werror option. # SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-builtin") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR CMAKE_C_COMPILER_ID MATCHES "^Clang$") CHECK_SYMBOL_EXISTS(_CrtSetReportMode "crtdbg.h" HAVE__CrtSetReportMode) CHECK_FUNCTION_EXISTS_GLIBC(arc4random_buf HAVE_ARC4RANDOM_BUF) CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS) CHECK_FUNCTION_EXISTS_GLIBC(chown HAVE_CHOWN) CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT) CHECK_FUNCTION_EXISTS_GLIBC(ctime_r HAVE_CTIME_R) CHECK_FUNCTION_EXISTS_GLIBC(dirfd HAVE_DIRFD) CHECK_FUNCTION_EXISTS_GLIBC(fchdir HAVE_FCHDIR) CHECK_FUNCTION_EXISTS_GLIBC(fchflags HAVE_FCHFLAGS) CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD) CHECK_FUNCTION_EXISTS_GLIBC(fchown HAVE_FCHOWN) CHECK_FUNCTION_EXISTS_GLIBC(fcntl HAVE_FCNTL) CHECK_FUNCTION_EXISTS_GLIBC(fdopendir HAVE_FDOPENDIR) CHECK_FUNCTION_EXISTS_GLIBC(fork HAVE_FORK) CHECK_FUNCTION_EXISTS_GLIBC(fstat HAVE_FSTAT) CHECK_FUNCTION_EXISTS_GLIBC(fstatat HAVE_FSTATAT) CHECK_FUNCTION_EXISTS_GLIBC(fstatfs HAVE_FSTATFS) CHECK_FUNCTION_EXISTS_GLIBC(fstatvfs HAVE_FSTATVFS) CHECK_FUNCTION_EXISTS_GLIBC(ftruncate HAVE_FTRUNCATE) CHECK_FUNCTION_EXISTS_GLIBC(futimens HAVE_FUTIMENS) CHECK_FUNCTION_EXISTS_GLIBC(futimes HAVE_FUTIMES) CHECK_FUNCTION_EXISTS_GLIBC(futimesat HAVE_FUTIMESAT) CHECK_FUNCTION_EXISTS_GLIBC(geteuid HAVE_GETEUID) CHECK_FUNCTION_EXISTS_GLIBC(getgrgid_r HAVE_GETGRGID_R) CHECK_FUNCTION_EXISTS_GLIBC(getgrnam_r HAVE_GETGRNAM_R) CHECK_FUNCTION_EXISTS_GLIBC(getpwnam_r HAVE_GETPWNAM_R) CHECK_FUNCTION_EXISTS_GLIBC(getpwuid_r HAVE_GETPWUID_R) CHECK_FUNCTION_EXISTS_GLIBC(getpid HAVE_GETPID) CHECK_FUNCTION_EXISTS_GLIBC(getvfsbyname HAVE_GETVFSBYNAME) CHECK_FUNCTION_EXISTS_GLIBC(gmtime_r HAVE_GMTIME_R) CHECK_FUNCTION_EXISTS_GLIBC(lchflags HAVE_LCHFLAGS) CHECK_FUNCTION_EXISTS_GLIBC(lchmod HAVE_LCHMOD) CHECK_FUNCTION_EXISTS_GLIBC(lchown HAVE_LCHOWN) CHECK_FUNCTION_EXISTS_GLIBC(link HAVE_LINK) CHECK_FUNCTION_EXISTS_GLIBC(localtime_r HAVE_LOCALTIME_R) CHECK_FUNCTION_EXISTS_GLIBC(lstat HAVE_LSTAT) CHECK_FUNCTION_EXISTS_GLIBC(lutimes HAVE_LUTIMES) CHECK_FUNCTION_EXISTS_GLIBC(mbrtowc HAVE_MBRTOWC) CHECK_FUNCTION_EXISTS_GLIBC(memmove HAVE_MEMMOVE) CHECK_FUNCTION_EXISTS_GLIBC(mkdir HAVE_MKDIR) CHECK_FUNCTION_EXISTS_GLIBC(mkfifo HAVE_MKFIFO) CHECK_FUNCTION_EXISTS_GLIBC(mknod HAVE_MKNOD) CHECK_FUNCTION_EXISTS_GLIBC(mkstemp HAVE_MKSTEMP) CHECK_FUNCTION_EXISTS_GLIBC(nl_langinfo HAVE_NL_LANGINFO) CHECK_FUNCTION_EXISTS_GLIBC(openat HAVE_OPENAT) CHECK_FUNCTION_EXISTS_GLIBC(pipe HAVE_PIPE) CHECK_FUNCTION_EXISTS_GLIBC(poll HAVE_POLL) CHECK_FUNCTION_EXISTS_GLIBC(posix_spawnp HAVE_POSIX_SPAWNP) CHECK_FUNCTION_EXISTS_GLIBC(readlink HAVE_READLINK) CHECK_FUNCTION_EXISTS_GLIBC(readpassphrase HAVE_READPASSPHRASE) CHECK_FUNCTION_EXISTS_GLIBC(select HAVE_SELECT) CHECK_FUNCTION_EXISTS_GLIBC(setenv HAVE_SETENV) CHECK_FUNCTION_EXISTS_GLIBC(setlocale HAVE_SETLOCALE) CHECK_FUNCTION_EXISTS_GLIBC(sigaction HAVE_SIGACTION) CHECK_FUNCTION_EXISTS_GLIBC(statfs HAVE_STATFS) CHECK_FUNCTION_EXISTS_GLIBC(statvfs HAVE_STATVFS) CHECK_FUNCTION_EXISTS_GLIBC(strchr HAVE_STRCHR) CHECK_FUNCTION_EXISTS_GLIBC(strdup HAVE_STRDUP) CHECK_FUNCTION_EXISTS_GLIBC(strerror HAVE_STRERROR) CHECK_FUNCTION_EXISTS_GLIBC(strncpy_s HAVE_STRNCPY_S) CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR) CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK) CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM) CHECK_FUNCTION_EXISTS_GLIBC(tzset HAVE_TZSET) CHECK_FUNCTION_EXISTS_GLIBC(unsetenv HAVE_UNSETENV) CHECK_FUNCTION_EXISTS_GLIBC(utime HAVE_UTIME) CHECK_FUNCTION_EXISTS_GLIBC(utimes HAVE_UTIMES) CHECK_FUNCTION_EXISTS_GLIBC(utimensat HAVE_UTIMENSAT) CHECK_FUNCTION_EXISTS_GLIBC(vfork HAVE_VFORK) CHECK_FUNCTION_EXISTS_GLIBC(wcrtomb HAVE_WCRTOMB) CHECK_FUNCTION_EXISTS_GLIBC(wcscmp HAVE_WCSCMP) CHECK_FUNCTION_EXISTS_GLIBC(wcscpy HAVE_WCSCPY) CHECK_FUNCTION_EXISTS_GLIBC(wcslen HAVE_WCSLEN) CHECK_FUNCTION_EXISTS_GLIBC(wctomb HAVE_WCTOMB) CHECK_FUNCTION_EXISTS_GLIBC(_ctime64_s HAVE__CTIME64_S) CHECK_FUNCTION_EXISTS_GLIBC(_fseeki64 HAVE__FSEEKI64) CHECK_FUNCTION_EXISTS_GLIBC(_get_timezone HAVE__GET_TIMEZONE) CHECK_FUNCTION_EXISTS_GLIBC(_localtime64_s HAVE__LOCALTIME64_S) CHECK_FUNCTION_EXISTS_GLIBC(_mkgmtime64 HAVE__MKGMTIME64) SET(CMAKE_REQUIRED_LIBRARIES "") CHECK_FUNCTION_EXISTS(cygwin_conv_path HAVE_CYGWIN_CONV_PATH) CHECK_FUNCTION_EXISTS(fseeko HAVE_FSEEKO) CHECK_FUNCTION_EXISTS(strerror_r HAVE_STRERROR_R) CHECK_FUNCTION_EXISTS(strftime HAVE_STRFTIME) CHECK_FUNCTION_EXISTS(vprintf HAVE_VPRINTF) CHECK_FUNCTION_EXISTS(wmemcmp HAVE_WMEMCMP) CHECK_FUNCTION_EXISTS(wmemcpy HAVE_WMEMCPY) CHECK_FUNCTION_EXISTS(wmemmove HAVE_WMEMMOVE) CMAKE_POP_CHECK_STATE() # Restore the state of the variables CHECK_C_SOURCE_COMPILES( "#include \n#include \nint main(void) { struct vfsconf v; return sizeof(v);}" HAVE_STRUCT_VFSCONF) CHECK_C_SOURCE_COMPILES( "#include \n#include \nint main(void) { struct xvfsconf v; return sizeof(v);}" HAVE_STRUCT_XVFSCONF) # Make sure we have the POSIX version of readdir_r, not the # older 2-argument version. CHECK_C_SOURCE_COMPILES( "#include \nint main() {DIR *d = opendir(\".\"); struct dirent e,*r; return readdir_r(d,&e,&r);}" HAVE_READDIR_R) # Only detect readlinkat() if we also have AT_FDCWD in unistd.h. # NOTE: linux requires fcntl.h for AT_FDCWD. CHECK_C_SOURCE_COMPILES( "#include \n#include \nint main() {char buf[10]; return readlinkat(AT_FDCWD, \"\", buf, 0);}" HAVE_READLINKAT) # To verify major(), we need to both include the header # of interest and verify that the result can be linked. # CHECK_FUNCTION_EXISTS doesn't accept a header argument, # CHECK_SYMBOL_EXISTS doesn't test linkage. CHECK_C_SOURCE_COMPILES( "#include \nint main() { return major(256); }" MAJOR_IN_MKDEV) CHECK_C_SOURCE_COMPILES( "#include \nint main() { return major(256); }" MAJOR_IN_SYSMACROS) CHECK_C_SOURCE_COMPILES( "#include \n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}" HAVE_LZMA_STREAM_ENCODER_MT) IF(HAVE_STRERROR_R) SET(HAVE_DECL_STRERROR_R 1) ENDIF(HAVE_STRERROR_R) # # Check defines # SET(headers "limits.h") IF(HAVE_STDINT_H) LIST(APPEND headers "stdint.h") ENDIF(HAVE_STDINT_H) IF(HAVE_INTTYPES_H) LIST(APPEND headers "inttypes.h") ENDIF(HAVE_INTTYPES_H) CHECK_SYMBOL_EXISTS(EFTYPE "errno.h" HAVE_EFTYPE) CHECK_SYMBOL_EXISTS(EILSEQ "errno.h" HAVE_EILSEQ) CHECK_SYMBOL_EXISTS(D_MD_ORDER "langinfo.h" HAVE_D_MD_ORDER) CHECK_SYMBOL_EXISTS(INT32_MAX "${headers}" HAVE_DECL_INT32_MAX) CHECK_SYMBOL_EXISTS(INT32_MIN "${headers}" HAVE_DECL_INT32_MIN) CHECK_SYMBOL_EXISTS(INT64_MAX "${headers}" HAVE_DECL_INT64_MAX) CHECK_SYMBOL_EXISTS(INT64_MIN "${headers}" HAVE_DECL_INT64_MIN) CHECK_SYMBOL_EXISTS(INTMAX_MAX "${headers}" HAVE_DECL_INTMAX_MAX) CHECK_SYMBOL_EXISTS(INTMAX_MIN "${headers}" HAVE_DECL_INTMAX_MIN) CHECK_SYMBOL_EXISTS(UINT32_MAX "${headers}" HAVE_DECL_UINT32_MAX) CHECK_SYMBOL_EXISTS(UINT64_MAX "${headers}" HAVE_DECL_UINT64_MAX) CHECK_SYMBOL_EXISTS(UINTMAX_MAX "${headers}" HAVE_DECL_UINTMAX_MAX) CHECK_SYMBOL_EXISTS(SIZE_MAX "${headers}" HAVE_DECL_SIZE_MAX) CHECK_SYMBOL_EXISTS(SSIZE_MAX "limits.h" HAVE_DECL_SSIZE_MAX) # # Check struct members # # Check for tm_gmtoff in struct tm CHECK_STRUCT_HAS_MEMBER("struct tm" tm_gmtoff "time.h" HAVE_STRUCT_TM_TM_GMTOFF) CHECK_STRUCT_HAS_MEMBER("struct tm" __tm_gmtoff "time.h" HAVE_STRUCT_TM___TM_GMTOFF) # Check for f_namemax in struct statfs CHECK_STRUCT_HAS_MEMBER("struct statfs" f_namemax "sys/param.h;sys/mount.h" HAVE_STRUCT_STATFS_F_NAMEMAX) # Check for birthtime in struct stat CHECK_STRUCT_HAS_MEMBER("struct stat" st_birthtime "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_BIRTHTIME) # Check for high-resolution timestamps in struct stat CHECK_STRUCT_HAS_MEMBER("struct stat" st_birthtimespec.tv_nsec "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_n "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIME_N) CHECK_STRUCT_HAS_MEMBER("struct stat" st_umtime "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_UMTIME) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_usec "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIME_USEC) # Check for block size support in struct stat CHECK_STRUCT_HAS_MEMBER("struct stat" st_blksize "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_BLKSIZE) # Check for st_flags in struct stat (BSD fflags) CHECK_STRUCT_HAS_MEMBER("struct stat" st_flags "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_FLAGS) IF(HAVE_SYS_STATVFS_H) CHECK_STRUCT_HAS_MEMBER("struct statvfs" f_iosize "sys/types.h;sys/statvfs.h" HAVE_STRUCT_STATVFS_F_IOSIZE) ENDIF() # # CHECK_STRUCT_HAS_MEMBER("struct tm" tm_sec "sys/types.h;sys/time.h;time.h" TIME_WITH_SYS_TIME) # # Check for integer types # # CHECK_TYPE_SIZE("short" SIZE_OF_SHORT) CHECK_TYPE_SIZE("int" SIZE_OF_INT) CHECK_TYPE_SIZE("long" SIZE_OF_LONG) CHECK_TYPE_SIZE("long long" SIZE_OF_LONG_LONG) CHECK_TYPE_SIZE("unsigned short" SIZE_OF_UNSIGNED_SHORT) CHECK_TYPE_SIZE("unsigned" SIZE_OF_UNSIGNED) CHECK_TYPE_SIZE("unsigned long" SIZE_OF_UNSIGNED_LONG) CHECK_TYPE_SIZE("unsigned long long" SIZE_OF_UNSIGNED_LONG_LONG) CHECK_TYPE_SIZE("__int64" __INT64) CHECK_TYPE_SIZE("unsigned __int64" UNSIGNED___INT64) CHECK_TYPE_SIZE(int16_t INT16_T) CHECK_TYPE_SIZE(int32_t INT32_T) CHECK_TYPE_SIZE(int64_t INT64_T) CHECK_TYPE_SIZE(intmax_t INTMAX_T) CHECK_TYPE_SIZE(uint8_t UINT8_T) CHECK_TYPE_SIZE(uint16_t UINT16_T) CHECK_TYPE_SIZE(uint32_t UINT32_T) CHECK_TYPE_SIZE(uint64_t UINT64_T) CHECK_TYPE_SIZE(uintmax_t UINTMAX_T) CHECK_TYPE_SIZE(dev_t DEV_T) IF(NOT HAVE_DEV_T) IF(MSVC) SET(dev_t "unsigned int") ENDIF(MSVC) ENDIF(NOT HAVE_DEV_T) # CHECK_TYPE_SIZE(gid_t GID_T) IF(NOT HAVE_GID_T) IF(WIN32) SET(gid_t "short") ELSE(WIN32) SET(gid_t "unsigned int") ENDIF(WIN32) ENDIF(NOT HAVE_GID_T) # CHECK_TYPE_SIZE(id_t ID_T) IF(NOT HAVE_ID_T) IF(WIN32) SET(id_t "short") ELSE(WIN32) SET(id_t "unsigned int") ENDIF(WIN32) ENDIF(NOT HAVE_ID_T) # CHECK_TYPE_SIZE(mode_t MODE_T) IF(NOT HAVE_MODE_T) IF(WIN32) SET(mode_t "unsigned short") ELSE(WIN32) SET(mode_t "int") ENDIF(WIN32) ENDIF(NOT HAVE_MODE_T) # CHECK_TYPE_SIZE(off_t OFF_T) IF(NOT HAVE_OFF_T) SET(off_t "__int64") ENDIF(NOT HAVE_OFF_T) # CHECK_TYPE_SIZE(size_t SIZE_T) IF(NOT HAVE_SIZE_T) IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) SET(size_t "uint64_t") ELSE("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) SET(size_t "uint32_t") ENDIF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) ENDIF(NOT HAVE_SIZE_T) # CHECK_TYPE_SIZE(ssize_t SSIZE_T) IF(NOT HAVE_SSIZE_T) IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) SET(ssize_t "int64_t") ELSE("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) SET(ssize_t "long") ENDIF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) ENDIF(NOT HAVE_SSIZE_T) # CHECK_TYPE_SIZE(uid_t UID_T) IF(NOT HAVE_UID_T) IF(WIN32) SET(uid_t "short") ELSE(WIN32) SET(uid_t "unsigned int") ENDIF(WIN32) ENDIF(NOT HAVE_UID_T) # CHECK_TYPE_SIZE(pid_t PID_T) IF(NOT HAVE_PID_T) IF(WIN32) SET(pid_t "int") ELSE(WIN32) MESSAGE(FATAL_ERROR "pid_t doesn't exist on this platform?") ENDIF(WIN32) ENDIF(NOT HAVE_PID_T) # CHECK_TYPE_SIZE(intptr_t INTPTR_T) IF(NOT HAVE_INTPTR_T) IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) SET(intptr_t "int64_t") ELSE() SET(intptr_t "int32_t") ENDIF() ENDIF(NOT HAVE_INTPTR_T) # CHECK_TYPE_SIZE(uintptr_t UINTPTR_T) IF(NOT HAVE_UINTPTR_T) IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) SET(uintptr_t "uint64_t") ELSE() SET(uintptr_t "uint32_t") ENDIF() ENDIF(NOT HAVE_UINTPTR_T) # CHECK_TYPE_SIZE(wchar_t SIZEOF_WCHAR_T) IF(HAVE_SIZEOF_WCHAR_T) SET(HAVE_WCHAR_T 1) ENDIF(HAVE_SIZEOF_WCHAR_T) # # Check if _FILE_OFFSET_BITS macro needed for large files # CHECK_FILE_OFFSET_BITS() # # Check for Extended Attribute libraries, headers, and functions # IF(ENABLE_XATTR) LA_CHECK_INCLUDE_FILE(attr/xattr.h HAVE_ATTR_XATTR_H) LA_CHECK_INCLUDE_FILE(sys/xattr.h HAVE_SYS_XATTR_H) LA_CHECK_INCLUDE_FILE(sys/extattr.h HAVE_SYS_EXTATTR_H) CHECK_LIBRARY_EXISTS(attr "setxattr" "" HAVE_LIBATTR) IF(HAVE_LIBATTR) SET(CMAKE_REQUIRED_LIBRARIES "attr") ENDIF(HAVE_LIBATTR) CHECK_SYMBOL_EXISTS(EXTATTR_NAMESPACE_USER "sys/types.h;sys/extattr.h" HAVE_DECL_EXTATTR_NAMESPACE_USER) CHECK_FUNCTION_EXISTS_GLIBC(extattr_get_file HAVE_EXTATTR_GET_FILE) CHECK_FUNCTION_EXISTS_GLIBC(extattr_list_file HAVE_EXTATTR_LIST_FILE) CHECK_FUNCTION_EXISTS_GLIBC(extattr_set_fd HAVE_EXTATTR_SET_FD) CHECK_FUNCTION_EXISTS_GLIBC(extattr_set_file HAVE_EXTATTR_SET_FILE) CHECK_FUNCTION_EXISTS_GLIBC(fgetxattr HAVE_FGETXATTR) CHECK_FUNCTION_EXISTS_GLIBC(flistxattr HAVE_FLISTXATTR) CHECK_FUNCTION_EXISTS_GLIBC(fsetxattr HAVE_FSETXATTR) CHECK_FUNCTION_EXISTS_GLIBC(getxattr HAVE_GETXATTR) CHECK_FUNCTION_EXISTS_GLIBC(lgetxattr HAVE_LGETXATTR) CHECK_FUNCTION_EXISTS_GLIBC(listxattr HAVE_LISTXATTR) CHECK_FUNCTION_EXISTS_GLIBC(llistxattr HAVE_LLISTXATTR) CHECK_FUNCTION_EXISTS_GLIBC(lsetxattr HAVE_LSETXATTR) CHECK_FUNCTION_EXISTS_GLIBC(fgetea HAVE_FGETEA) CHECK_FUNCTION_EXISTS_GLIBC(flistea HAVE_FLISTEA) CHECK_FUNCTION_EXISTS_GLIBC(fsetea HAVE_FSETEA) CHECK_FUNCTION_EXISTS_GLIBC(getea HAVE_GETEA) CHECK_FUNCTION_EXISTS_GLIBC(lgetea HAVE_LGETEA) CHECK_FUNCTION_EXISTS_GLIBC(listea HAVE_LISTEA) CHECK_FUNCTION_EXISTS_GLIBC(llistea HAVE_LLISTEA) CHECK_FUNCTION_EXISTS_GLIBC(lsetea HAVE_LSETEA) ELSE(ENABLE_XATTR) SET(HAVE_ATTR_LIB FALSE) SET(HAVE_ATTR_XATTR_H FALSE) SET(HAVE_DECL_EXTATTR_NAMESPACE_USER FALSE) SET(HAVE_EXTATTR_GET_FILE FALSE) SET(HAVE_EXTATTR_LIST_FILE FALSE) SET(HAVE_EXTATTR_SET_FD FALSE) SET(HAVE_EXTATTR_SET_FILE FALSE) SET(HAVE_FGETEA FALSE) SET(HAVE_FGETXATTR FALSE) SET(HAVE_FLISTEA FALSE) SET(HAVE_FLISTXATTR FALSE) SET(HAVE_FSETEA FALSE) SET(HAVE_FSETXATTR FALSE) SET(HAVE_GETEA FALSE) SET(HAVE_GETXATTR FALSE) SET(HAVE_LGETEA FALSE) SET(HAVE_LGETXATTR FALSE) SET(HAVE_LISTEA FALSE) SET(HAVE_LISTXATTR FALSE) SET(HAVE_LLISTEA FALSE) SET(HAVE_LLISTXATTR FALSE) SET(HAVE_LSETEA FALSE) SET(HAVE_LSETXATTR FALSE) SET(HAVE_SYS_EXTATTR_H FALSE) SET(HAVE_SYS_XATTR_H FALSE) ENDIF(ENABLE_XATTR) # # Check for ACL libraries, headers, and functions # # The ACL support in libarchive is written against the POSIX1e draft, # which was never officially approved and varies quite a bit across # platforms. Worse, some systems have completely non-POSIX acl functions, # which makes the following checks rather more complex than I would like. # IF(ENABLE_ACL) CHECK_LIBRARY_EXISTS(acl "acl_get_file" "" HAVE_LIBACL) IF(HAVE_LIBACL) SET(CMAKE_REQUIRED_LIBRARIES "acl") FIND_LIBRARY(ACL_LIBRARY NAMES acl) LIST(APPEND ADDITIONAL_LIBS ${ACL_LIBRARY}) ENDIF(HAVE_LIBACL) # CHECK_FUNCTION_EXISTS_GLIBC(acl_create_entry HAVE_ACL_CREATE_ENTRY) CHECK_FUNCTION_EXISTS_GLIBC(acl_init HAVE_ACL_INIT) CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd HAVE_ACL_SET_FD) CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd_np HAVE_ACL_SET_FD_NP) CHECK_FUNCTION_EXISTS_GLIBC(acl_set_file HAVE_ACL_SET_FILE) CHECK_TYPE_EXISTS(acl_permset_t "${INCLUDES}" HAVE_ACL_PERMSET_T) # The "acl_get_perm()" function was omitted from the POSIX draft. # (It's a pretty obvious oversight; otherwise, there's no way to # test for specific permissions in a permset.) Linux uses the obvious # name, FreeBSD adds _np to mark it as "non-Posix extension." # Test for both as a double-check that we really have POSIX-style ACL support. CHECK_FUNCTION_EXISTS(acl_get_fd_np HAVE_ACL_GET_FD_NP) CHECK_FUNCTION_EXISTS(acl_get_perm HAVE_ACL_GET_PERM) CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP) CHECK_FUNCTION_EXISTS(acl_get_link HAVE_ACL_GET_LINK) CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP) CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP) CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP) CHECK_SYMBOL_EXISTS(ACL_TYPE_NFS4 "${INCLUDES}" HAVE_ACL_TYPE_NFS4) # MacOS has an acl.h that isn't POSIX. It can be detected by # checking for ACL_USER CHECK_SYMBOL_EXISTS(ACL_USER "${INCLUDES}" HAVE_ACL_USER) CHECK_C_SOURCE_COMPILES("#include #include int main(void) { return ACL_TYPE_EXTENDED; }" HAVE_ACL_TYPE_EXTENDED) # Solaris and derivates ACLs CHECK_LIBRARY_EXISTS(sec "acl_get" "" HAVE_LIBSEC) IF(HAVE_LIBSEC) SET(CMAKE_REQUIRED_LIBRARIES "sec") FIND_LIBRARY(SEC_LIBRARY NAMES sec) LIST(APPEND ADDITIONAL_LIBS ${SEC_LIBRARY}) ENDIF(HAVE_LIBSEC) # CHECK_TYPE_EXISTS(aclent_t "${INCLUDES}" HAVE_ACLENT_T) CHECK_TYPE_EXISTS(ace_t "${INCLUDES}" HAVE_ACE_T) CHECK_FUNCTION_EXISTS(acl_get HAVE_FACL_GET) CHECK_FUNCTION_EXISTS(facl_get HAVE_FACL_GET) CHECK_FUNCTION_EXISTS(acl_set HAVE_FACL_SET) CHECK_FUNCTION_EXISTS(facl_set HAVE_FACL_SET) ELSE(ENABLE_ACL) # If someone runs cmake, then disables ACL support, we need # to forcibly override the cached values for these. SET(HAVE_ACL_CREATE_ENTRY FALSE) SET(HAVE_ACL_GET_LINK FALSE) SET(HAVE_ACL_GET_LINK_NP FALSE) SET(HAVE_ACL_GET_PERM FALSE) SET(HAVE_ACL_GET_PERM_NP FALSE) SET(HAVE_ACL_INIT FALSE) SET(HAVE_ACL_LIB FALSE) SET(HAVE_ACL_PERMSET_T FALSE) SET(HAVE_ACL_SET_FD FALSE) SET(HAVE_ACL_SET_FD_NP FALSE) SET(HAVE_ACL_SET_FILE FALSE) SET(HAVE_ACL_TYPE_NFS4 FALSE) SET(HAVE_ACL_USER FALSE) SET(HAVE_ACL_TYPE_EXTENDED FALSE) SET(HAVE_ACL_GET FALSE) SET(HAVE_ACLENT_T FALSE) SET(HAVE_ACE_T FALSE) SET(HAVE_FACL_GET FALSE) SET(HAVE_ACL_SET FALSE) SET(HAVE_FACL_SET FALSE) ENDIF(ENABLE_ACL) # # Check MD5/RMD160/SHA support # NOTE: Crypto checks must be run last before generating config.h # CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" LIBC) CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC2) CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC3) CHECK_CRYPTO("MD5;SHA1;SHA256;SHA384;SHA512" LIBSYSTEM) CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" NETTLE) CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" OPENSSL) # Libmd has to be probed after OpenSSL. CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA512" LIBMD) CHECK_CRYPTO_WIN("MD5;SHA1;SHA256;SHA384;SHA512") # Generate "config.h" from "build/cmake/config.h.in" CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) ADD_DEFINITIONS(-DHAVE_CONFIG_H) # Handle generation of the libarchive.pc file for pkg-config INCLUDE(CreatePkgConfigFile) # # Register installation of PDF documents. # IF(WIN32 AND NOT CYGWIN) # # On Windows platform, It's better that we install PDF documents # on one's computer. # These PDF documents are available in the release package. # IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doc/pdf) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/pdf DESTINATION share/man FILES_MATCHING PATTERN "*.pdf" ) ENDIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doc/pdf) ENDIF(WIN32 AND NOT CYGWIN) # # # INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/libarchive) # IF(MSVC) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) ENDIF(MSVC) IF(ENABLE_TEST) ADD_CUSTOM_TARGET(run_all_tests) ENDIF(ENABLE_TEST) add_subdirectory(libarchive) add_subdirectory(cat) add_subdirectory(tar) add_subdirectory(cpio) Index: vendor/libarchive/dist/Makefile.am =================================================================== --- vendor/libarchive/dist/Makefile.am (revision 314564) +++ vendor/libarchive/dist/Makefile.am (revision 314565) @@ -1,1273 +1,1280 @@ ## Process this file with automake to produce Makefile.in AUTOMAKE_OPTIONS= foreign subdir-objects ACLOCAL_AMFLAGS = -I build/autoconf # # What to build and install # lib_LTLIBRARIES= libarchive.la noinst_LTLIBRARIES= libarchive_fe.la bin_PROGRAMS= $(bsdtar_programs) $(bsdcpio_programs) $(bsdcat_programs) man_MANS= $(libarchive_man_MANS) $(bsdtar_man_MANS) $(bsdcpio_man_MANS) $(bsdcat_man_MANS) BUILT_SOURCES= libarchive/test/list.h tar/test/list.h cpio/test/list.h cat/test/list.h # # What to test: We always test libarchive, test bsdtar and bsdcpio only # if we built them. # check_PROGRAMS= libarchive_test $(bsdtar_test_programs) $(bsdcpio_test_programs) $(bsdcat_test_programs) TESTS= libarchive_test $(bsdtar_test_programs) $(bsdcpio_test_programs) $(bsdcat_test_programs) TESTS_ENVIRONMENT= $(libarchive_TESTS_ENVIRONMENT) $(bsdtar_TESTS_ENVIRONMENT) $(bsdcpio_TESTS_ENVIRONMENT) $(bsdcat_TESTS_ENVIRONMENT) # Always build and test both bsdtar and bsdcpio as part of 'distcheck' DISTCHECK_CONFIGURE_FLAGS = --enable-bsdtar --enable-bsdcpio # The next line is commented out by default in shipping libarchive releases. # It is uncommented by default in trunk. -DEV_CFLAGS=-Werror -Wextra -Wunused -Wshadow -Wmissing-prototypes -Wcast-qual -g +# DEV_CFLAGS=-Werror -Wextra -Wunused -Wshadow -Wmissing-prototypes -Wcast-qual -g AM_CFLAGS=$(DEV_CFLAGS) PLATFORMCPPFLAGS = @PLATFORMCPPFLAGS@ AM_CPPFLAGS=$(PLATFORMCPPFLAGS) # # What to include in the distribution # EXTRA_DIST= \ CMakeLists.txt \ README.md \ build/autogen.sh \ build/bump-version.sh \ build/clean.sh \ build/cmake \ build/version \ contrib \ doc \ examples \ $(libarchive_EXTRA_DIST) \ $(libarchive_test_EXTRA_DIST) \ $(bsdtar_EXTRA_DIST) \ $(bsdtar_test_EXTRA_DIST) \ $(bsdcpio_EXTRA_DIST) \ $(bsdcpio_test_EXTRA_DIST) \ $(bsdcat_EXTRA_DIST) \ $(bsdcat_test_EXTRA_DIST) # a) Clean out some unneeded files and directories # b) Collect all documentation and format it for distribution. dist-hook: rm -rf `find $(distdir) -name CVS -type d` rm -rf `find $(distdir) -name .svn -type d` rm -f `find $(distdir) -name '*~'` rm -f `find $(distdir) -name '*.out'` rm -f `find $(distdir) -name '*.core'` -rm -f $(distdir)/*/Makefile $(distdir)/*/*/Makefile cd $(distdir)/doc && /bin/sh update.sh # # Extra rules for cleanup # DISTCLEANFILES= \ libarchive/test/list.h \ tar/test/list.h \ cpio/test/list.h \ cat/test/list.h distclean-local: -rm -rf .ref -rm -rf autom4te.cache/ -rm -f *~ -[ -f libarchive/Makefile ] && cd libarchive && make clean -[ -f libarchive/test/Makefile ] && cd libarchive/test && make clean -[ -f tar/Makefile ] && cd tar && make clean -[ -f tar/test/Makefile ] && cd tar/test && make clean -[ -f cpio/Makefile ] && cd cpio && make clean -[ -f cpio/test/Makefile ] && cd cpio/test && make clean -[ -f cat/Makefile ] && cd cat && make clean -[ -f cpio/test/Makefile ] && cd cat/test && make clean # # Libarchive headers, source, etc. # # include_HEADERS= libarchive/archive.h libarchive/archive_entry.h libarchive_la_SOURCES= \ libarchive/archive_acl.c \ libarchive/archive_acl_private.h \ libarchive/archive_check_magic.c \ libarchive/archive_cmdline.c \ libarchive/archive_cmdline_private.h \ libarchive/archive_crc32.h \ libarchive/archive_cryptor.c \ libarchive/archive_cryptor_private.h \ libarchive/archive_digest.c \ libarchive/archive_digest_private.h \ libarchive/archive_endian.h \ libarchive/archive_entry.c \ libarchive/archive_entry.h \ libarchive/archive_entry_copy_stat.c \ libarchive/archive_entry_link_resolver.c \ libarchive/archive_entry_locale.h \ libarchive/archive_entry_private.h \ libarchive/archive_entry_sparse.c \ libarchive/archive_entry_stat.c \ libarchive/archive_entry_strmode.c \ libarchive/archive_entry_xattr.c \ libarchive/archive_getdate.c \ libarchive/archive_getdate.h \ libarchive/archive_hmac.c \ libarchive/archive_hmac_private.h \ libarchive/archive_match.c \ libarchive/archive_openssl_evp_private.h \ libarchive/archive_openssl_hmac_private.h \ libarchive/archive_options.c \ libarchive/archive_options_private.h \ libarchive/archive_pack_dev.h \ libarchive/archive_pack_dev.c \ libarchive/archive_pathmatch.c \ libarchive/archive_pathmatch.h \ libarchive/archive_platform.h \ libarchive/archive_ppmd_private.h \ libarchive/archive_ppmd7.c \ libarchive/archive_ppmd7_private.h \ libarchive/archive_private.h \ libarchive/archive_random.c \ libarchive/archive_random_private.h \ libarchive/archive_rb.c \ libarchive/archive_rb.h \ libarchive/archive_read.c \ libarchive/archive_read_add_passphrase.c \ libarchive/archive_read_append_filter.c \ libarchive/archive_read_data_into_fd.c \ libarchive/archive_read_disk_entry_from_file.c \ libarchive/archive_read_disk_posix.c \ libarchive/archive_read_disk_private.h \ libarchive/archive_read_disk_set_standard_lookup.c \ libarchive/archive_read_extract.c \ libarchive/archive_read_extract2.c \ libarchive/archive_read_open_fd.c \ libarchive/archive_read_open_file.c \ libarchive/archive_read_open_filename.c \ libarchive/archive_read_open_memory.c \ libarchive/archive_read_private.h \ libarchive/archive_read_set_format.c \ libarchive/archive_read_set_options.c \ libarchive/archive_read_support_filter_all.c \ libarchive/archive_read_support_filter_bzip2.c \ libarchive/archive_read_support_filter_compress.c \ libarchive/archive_read_support_filter_grzip.c \ libarchive/archive_read_support_filter_gzip.c \ libarchive/archive_read_support_filter_lrzip.c \ libarchive/archive_read_support_filter_lz4.c \ libarchive/archive_read_support_filter_lzop.c \ libarchive/archive_read_support_filter_none.c \ libarchive/archive_read_support_filter_program.c \ libarchive/archive_read_support_filter_rpm.c \ libarchive/archive_read_support_filter_uu.c \ libarchive/archive_read_support_filter_xz.c \ libarchive/archive_read_support_format_7zip.c \ libarchive/archive_read_support_format_all.c \ libarchive/archive_read_support_format_ar.c \ libarchive/archive_read_support_format_by_code.c \ libarchive/archive_read_support_format_cab.c \ libarchive/archive_read_support_format_cpio.c \ libarchive/archive_read_support_format_empty.c \ libarchive/archive_read_support_format_iso9660.c \ libarchive/archive_read_support_format_lha.c \ libarchive/archive_read_support_format_mtree.c \ libarchive/archive_read_support_format_rar.c \ libarchive/archive_read_support_format_raw.c \ libarchive/archive_read_support_format_tar.c \ libarchive/archive_read_support_format_warc.c \ libarchive/archive_read_support_format_xar.c \ libarchive/archive_read_support_format_zip.c \ libarchive/archive_string.c \ libarchive/archive_string.h \ libarchive/archive_string_composition.h \ libarchive/archive_string_sprintf.c \ libarchive/archive_util.c \ libarchive/archive_virtual.c \ libarchive/archive_write.c \ libarchive/archive_write_disk_acl.c \ libarchive/archive_write_disk_posix.c \ libarchive/archive_write_disk_private.h \ libarchive/archive_write_disk_set_standard_lookup.c \ libarchive/archive_write_open_fd.c \ libarchive/archive_write_open_file.c \ libarchive/archive_write_open_filename.c \ libarchive/archive_write_open_memory.c \ libarchive/archive_write_private.h \ libarchive/archive_write_add_filter.c \ libarchive/archive_write_add_filter_b64encode.c \ libarchive/archive_write_add_filter_by_name.c \ libarchive/archive_write_add_filter_bzip2.c \ libarchive/archive_write_add_filter_compress.c \ libarchive/archive_write_add_filter_grzip.c \ libarchive/archive_write_add_filter_gzip.c \ libarchive/archive_write_add_filter_lrzip.c \ libarchive/archive_write_add_filter_lz4.c \ libarchive/archive_write_add_filter_lzop.c \ libarchive/archive_write_add_filter_none.c \ libarchive/archive_write_add_filter_program.c \ libarchive/archive_write_add_filter_uuencode.c \ libarchive/archive_write_add_filter_xz.c \ libarchive/archive_write_set_format.c \ libarchive/archive_write_set_format_7zip.c \ libarchive/archive_write_set_format_ar.c \ libarchive/archive_write_set_format_by_name.c \ libarchive/archive_write_set_format_cpio.c \ libarchive/archive_write_set_format_cpio_newc.c \ libarchive/archive_write_set_format_filter_by_ext.c \ libarchive/archive_write_set_format_iso9660.c \ libarchive/archive_write_set_format_mtree.c \ libarchive/archive_write_set_format_pax.c \ libarchive/archive_write_set_format_raw.c \ libarchive/archive_write_set_format_shar.c \ libarchive/archive_write_set_format_ustar.c \ libarchive/archive_write_set_format_v7tar.c \ libarchive/archive_write_set_format_gnutar.c \ libarchive/archive_write_set_format_warc.c \ libarchive/archive_write_set_format_xar.c \ libarchive/archive_write_set_format_zip.c \ libarchive/archive_write_set_options.c \ libarchive/archive_write_set_passphrase.c \ libarchive/archive_xxhash.h \ libarchive/config_freebsd.h \ libarchive/filter_fork_posix.c \ libarchive/filter_fork.h \ libarchive/xxhash.c if INC_WINDOWS_FILES libarchive_la_SOURCES+= \ libarchive/archive_entry_copy_bhfi.c \ libarchive/archive_read_disk_windows.c \ libarchive/archive_windows.h \ libarchive/archive_windows.c \ libarchive/archive_write_disk_windows.c \ libarchive/filter_fork_windows.c endif # -no-undefined marks that libarchive doesn't rely on symbols # defined in the application. This is mandatory for cygwin. libarchive_la_LDFLAGS= -no-undefined -version-info $(ARCHIVE_LIBTOOL_VERSION) libarchive_la_LIBADD= $(LTLIBICONV) # Manpages to install libarchive_man_MANS= \ libarchive/archive_entry.3 \ libarchive/archive_entry_acl.3 \ libarchive/archive_entry_linkify.3 \ libarchive/archive_entry_paths.3 \ libarchive/archive_entry_perms.3 \ libarchive/archive_entry_stat.3 \ libarchive/archive_entry_time.3 \ libarchive/archive_read.3 \ libarchive/archive_read_add_passphrase.3 \ libarchive/archive_read_data.3 \ libarchive/archive_read_disk.3 \ libarchive/archive_read_extract.3 \ libarchive/archive_read_filter.3 \ libarchive/archive_read_format.3 \ libarchive/archive_read_free.3 \ libarchive/archive_read_header.3 \ libarchive/archive_read_new.3 \ libarchive/archive_read_open.3 \ libarchive/archive_read_set_options.3 \ libarchive/archive_util.3 \ libarchive/archive_write.3 \ libarchive/archive_write_blocksize.3 \ libarchive/archive_write_data.3 \ libarchive/archive_write_disk.3 \ libarchive/archive_write_filter.3 \ libarchive/archive_write_finish_entry.3 \ libarchive/archive_write_format.3 \ libarchive/archive_write_free.3 \ libarchive/archive_write_header.3 \ libarchive/archive_write_new.3 \ libarchive/archive_write_open.3 \ libarchive/archive_write_set_options.3 \ libarchive/archive_write_set_passphrase.3 \ libarchive/cpio.5 \ libarchive/libarchive.3 \ libarchive/libarchive_changes.3 \ libarchive/libarchive_internals.3 \ libarchive/libarchive-formats.5 \ libarchive/mtree.5 \ libarchive/tar.5 # Additional libarchive files to include in the distribution libarchive_EXTRA_DIST= \ libarchive/archive_windows.c \ libarchive/archive_windows.h \ libarchive/filter_fork_windows.c \ libarchive/CMakeLists.txt \ $(libarchive_man_MANS) # pkgconfig pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = build/pkgconfig/libarchive.pc # Sources needed by all test programs test_utils_SOURCES= \ test_utils/test_utils.c \ - test_utils/test_utils.h + test_utils/test_utils.h \ + test_utils/test_main.c \ + test_utils/test_common.h # # # libarchive_test program # # libarchive_test_SOURCES= \ $(libarchive_la_SOURCES) \ $(test_utils_SOURCES) \ - libarchive/test/main.c \ libarchive/test/read_open_memory.c \ libarchive/test/test.h \ libarchive/test/test_acl_nfs4.c \ libarchive/test/test_acl_pax.c \ libarchive/test/test_acl_platform_nfs4.c \ libarchive/test/test_acl_platform_posix1e.c \ libarchive/test/test_acl_posix1e.c \ libarchive/test/test_acl_text.c \ libarchive/test/test_archive_api_feature.c \ libarchive/test/test_archive_clear_error.c \ libarchive/test/test_archive_cmdline.c \ libarchive/test/test_archive_digest.c \ libarchive/test/test_archive_getdate.c \ libarchive/test/test_archive_match_owner.c \ libarchive/test/test_archive_match_path.c \ libarchive/test/test_archive_match_time.c \ libarchive/test/test_archive_pathmatch.c \ libarchive/test/test_archive_read_add_passphrase.c \ libarchive/test/test_archive_read_close_twice.c \ libarchive/test/test_archive_read_close_twice_open_fd.c \ libarchive/test/test_archive_read_close_twice_open_filename.c \ libarchive/test/test_archive_read_multiple_data_objects.c \ libarchive/test/test_archive_read_next_header_empty.c \ libarchive/test/test_archive_read_next_header_raw.c \ libarchive/test/test_archive_read_open2.c \ libarchive/test/test_archive_read_set_filter_option.c \ libarchive/test/test_archive_read_set_format_option.c \ libarchive/test/test_archive_read_set_option.c \ libarchive/test/test_archive_read_set_options.c \ libarchive/test/test_archive_read_support.c \ libarchive/test/test_archive_set_error.c \ libarchive/test/test_archive_string.c \ libarchive/test/test_archive_string_conversion.c \ libarchive/test/test_archive_write_add_filter_by_name.c \ libarchive/test/test_archive_write_set_filter_option.c \ libarchive/test/test_archive_write_set_format_by_name.c \ libarchive/test/test_archive_write_set_format_filter_by_ext.c \ libarchive/test/test_archive_write_set_format_option.c \ libarchive/test/test_archive_write_set_option.c \ libarchive/test/test_archive_write_set_options.c \ libarchive/test/test_archive_write_set_passphrase.c \ libarchive/test/test_bad_fd.c \ libarchive/test/test_compat_bzip2.c \ libarchive/test/test_compat_cpio.c \ libarchive/test/test_compat_gtar.c \ libarchive/test/test_compat_gzip.c \ libarchive/test/test_compat_lz4.c \ libarchive/test/test_compat_lzip.c \ libarchive/test/test_compat_lzma.c \ libarchive/test/test_compat_lzop.c \ libarchive/test/test_compat_mac.c \ libarchive/test/test_compat_pax_libarchive_2x.c \ libarchive/test/test_compat_perl_archive_tar.c \ libarchive/test/test_compat_plexus_archiver_tar.c \ libarchive/test/test_compat_solaris_tar_acl.c \ libarchive/test/test_compat_solaris_pax_sparse.c \ libarchive/test/test_compat_star_acl.c \ libarchive/test/test_compat_tar_hardlink.c \ libarchive/test/test_compat_uudecode.c \ libarchive/test/test_compat_uudecode_large.c \ libarchive/test/test_compat_xz.c \ libarchive/test/test_compat_zip.c \ libarchive/test/test_empty_write.c \ libarchive/test/test_entry.c \ libarchive/test/test_entry_strmode.c \ libarchive/test/test_extattr_freebsd.c \ libarchive/test/test_filter_count.c \ libarchive/test/test_fuzz.c \ libarchive/test/test_gnutar_filename_encoding.c \ libarchive/test/test_link_resolver.c \ libarchive/test/test_open_failure.c \ libarchive/test/test_open_fd.c \ libarchive/test/test_open_file.c \ libarchive/test/test_open_filename.c \ libarchive/test/test_pax_filename_encoding.c \ libarchive/test/test_read_data_large.c \ libarchive/test/test_read_disk.c \ libarchive/test/test_read_disk_directory_traversals.c \ libarchive/test/test_read_disk_entry_from_file.c \ libarchive/test/test_read_extract.c \ libarchive/test/test_read_file_nonexistent.c \ libarchive/test/test_read_filter_compress.c \ libarchive/test/test_read_filter_grzip.c \ libarchive/test/test_read_filter_lrzip.c \ libarchive/test/test_read_filter_lzop.c \ libarchive/test/test_read_filter_lzop_multiple_parts.c \ libarchive/test/test_read_filter_program.c \ libarchive/test/test_read_filter_program_signature.c \ libarchive/test/test_read_filter_uudecode.c \ libarchive/test/test_read_format_7zip.c \ libarchive/test/test_read_format_7zip_encryption_data.c \ libarchive/test/test_read_format_7zip_encryption_partially.c \ libarchive/test/test_read_format_7zip_encryption_header.c \ libarchive/test/test_read_format_7zip_malformed.c \ libarchive/test/test_read_format_ar.c \ libarchive/test/test_read_format_cab.c \ libarchive/test/test_read_format_cab_filename.c \ libarchive/test/test_read_format_cpio_afio.c \ libarchive/test/test_read_format_cpio_bin.c \ libarchive/test/test_read_format_cpio_bin_Z.c \ libarchive/test/test_read_format_cpio_bin_be.c \ libarchive/test/test_read_format_cpio_bin_bz2.c \ libarchive/test/test_read_format_cpio_bin_gz.c \ libarchive/test/test_read_format_cpio_bin_le.c \ libarchive/test/test_read_format_cpio_bin_lzip.c \ libarchive/test/test_read_format_cpio_bin_lzma.c \ libarchive/test/test_read_format_cpio_bin_xz.c \ libarchive/test/test_read_format_cpio_filename.c \ libarchive/test/test_read_format_cpio_odc.c \ libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.c \ libarchive/test/test_read_format_cpio_svr4_gzip.c \ libarchive/test/test_read_format_cpio_svr4_gzip_rpm.c \ libarchive/test/test_read_format_cpio_svr4c_Z.c \ libarchive/test/test_read_format_empty.c \ libarchive/test/test_read_format_gtar_filename.c \ libarchive/test/test_read_format_gtar_gz.c \ libarchive/test/test_read_format_gtar_lzma.c \ libarchive/test/test_read_format_gtar_sparse.c \ libarchive/test/test_read_format_gtar_sparse_skip_entry.c \ libarchive/test/test_read_format_iso_Z.c \ libarchive/test/test_read_format_iso_multi_extent.c \ libarchive/test/test_read_format_iso_xorriso.c \ libarchive/test/test_read_format_isojoliet_bz2.c \ libarchive/test/test_read_format_isojoliet_long.c \ libarchive/test/test_read_format_isojoliet_rr.c \ libarchive/test/test_read_format_isojoliet_versioned.c \ libarchive/test/test_read_format_isorr_bz2.c \ libarchive/test/test_read_format_isorr_ce.c \ libarchive/test/test_read_format_isorr_new_bz2.c \ libarchive/test/test_read_format_isorr_rr_moved.c \ libarchive/test/test_read_format_isozisofs_bz2.c \ libarchive/test/test_read_format_lha.c \ libarchive/test/test_read_format_lha_bugfix_0.c \ libarchive/test/test_read_format_lha_filename.c \ libarchive/test/test_read_format_mtree.c \ libarchive/test/test_read_format_mtree_crash747.c \ libarchive/test/test_read_format_pax_bz2.c \ libarchive/test/test_read_format_rar.c \ libarchive/test/test_read_format_rar_encryption_data.c \ libarchive/test/test_read_format_rar_encryption_partially.c \ libarchive/test/test_read_format_rar_encryption_header.c \ libarchive/test/test_read_format_rar_invalid1.c \ libarchive/test/test_read_format_raw.c \ libarchive/test/test_read_format_tar.c \ libarchive/test/test_read_format_tar_concatenated.c \ libarchive/test/test_read_format_tar_empty_pax.c \ libarchive/test/test_read_format_tar_empty_filename.c \ libarchive/test/test_read_format_tar_filename.c \ libarchive/test/test_read_format_tbz.c \ libarchive/test/test_read_format_tgz.c \ libarchive/test/test_read_format_tlz.c \ libarchive/test/test_read_format_txz.c \ libarchive/test/test_read_format_tz.c \ libarchive/test/test_read_format_ustar_filename.c \ libarchive/test/test_read_format_warc.c \ libarchive/test/test_read_format_xar.c \ libarchive/test/test_read_format_zip.c \ libarchive/test/test_read_format_zip_comment_stored.c \ libarchive/test/test_read_format_zip_encryption_data.c \ libarchive/test/test_read_format_zip_encryption_partially.c \ libarchive/test/test_read_format_zip_encryption_header.c \ libarchive/test/test_read_format_zip_filename.c \ libarchive/test/test_read_format_zip_high_compression.c \ libarchive/test/test_read_format_zip_jar.c \ libarchive/test/test_read_format_zip_mac_metadata.c \ libarchive/test/test_read_format_zip_malformed.c \ libarchive/test/test_read_format_zip_msdos.c \ libarchive/test/test_read_format_zip_nested.c \ libarchive/test/test_read_format_zip_nofiletype.c \ libarchive/test/test_read_format_zip_padded.c \ libarchive/test/test_read_format_zip_sfx.c \ libarchive/test/test_read_format_zip_traditional_encryption_data.c \ libarchive/test/test_read_format_zip_winzip_aes.c \ libarchive/test/test_read_format_zip_winzip_aes_large.c \ libarchive/test/test_read_format_zip_zip64.c \ libarchive/test/test_read_large.c \ libarchive/test/test_read_pax_schily_xattr.c \ libarchive/test/test_read_pax_truncated.c \ libarchive/test/test_read_position.c \ libarchive/test/test_read_set_format.c \ libarchive/test/test_read_too_many_filters.c \ libarchive/test/test_read_truncated.c \ libarchive/test/test_read_truncated_filter.c \ libarchive/test/test_sparse_basic.c \ libarchive/test/test_tar_filenames.c \ libarchive/test/test_tar_large.c \ libarchive/test/test_ustar_filenames.c \ libarchive/test/test_ustar_filename_encoding.c \ libarchive/test/test_warn_missing_hardlink_target.c \ libarchive/test/test_write_disk.c \ libarchive/test/test_write_disk_appledouble.c \ libarchive/test/test_write_disk_failures.c \ libarchive/test/test_write_disk_hardlink.c \ libarchive/test/test_write_disk_hfs_compression.c \ libarchive/test/test_write_disk_lookup.c \ libarchive/test/test_write_disk_mac_metadata.c \ libarchive/test/test_write_disk_no_hfs_compression.c \ libarchive/test/test_write_disk_perms.c \ libarchive/test/test_write_disk_secure.c \ libarchive/test/test_write_disk_secure744.c \ libarchive/test/test_write_disk_secure745.c \ libarchive/test/test_write_disk_secure746.c \ libarchive/test/test_write_disk_sparse.c \ libarchive/test/test_write_disk_symlink.c \ libarchive/test/test_write_disk_times.c \ libarchive/test/test_write_filter_b64encode.c \ libarchive/test/test_write_filter_bzip2.c \ libarchive/test/test_write_filter_compress.c \ libarchive/test/test_write_filter_gzip.c \ libarchive/test/test_write_filter_gzip_timestamp.c \ libarchive/test/test_write_filter_lrzip.c \ libarchive/test/test_write_filter_lz4.c \ libarchive/test/test_write_filter_lzip.c \ libarchive/test/test_write_filter_lzma.c \ libarchive/test/test_write_filter_lzop.c \ libarchive/test/test_write_filter_program.c \ libarchive/test/test_write_filter_uuencode.c \ libarchive/test/test_write_filter_xz.c \ libarchive/test/test_write_format_7zip.c \ libarchive/test/test_write_format_7zip_empty.c \ libarchive/test/test_write_format_7zip_large.c \ libarchive/test/test_write_format_ar.c \ libarchive/test/test_write_format_cpio.c \ libarchive/test/test_write_format_cpio_empty.c \ libarchive/test/test_write_format_cpio_newc.c \ libarchive/test/test_write_format_cpio_odc.c \ libarchive/test/test_write_format_gnutar.c \ libarchive/test/test_write_format_gnutar_filenames.c \ libarchive/test/test_write_format_iso9660.c \ libarchive/test/test_write_format_iso9660_boot.c \ libarchive/test/test_write_format_iso9660_empty.c \ libarchive/test/test_write_format_iso9660_filename.c \ libarchive/test/test_write_format_iso9660_zisofs.c \ libarchive/test/test_write_format_mtree.c \ libarchive/test/test_write_format_mtree_absolute_path.c \ libarchive/test/test_write_format_mtree_classic.c \ libarchive/test/test_write_format_mtree_classic_indent.c\ libarchive/test/test_write_format_mtree_fflags.c \ libarchive/test/test_write_format_mtree_no_separator.c \ libarchive/test/test_write_format_mtree_quoted_filename.c\ libarchive/test/test_write_format_pax.c \ libarchive/test/test_write_format_raw.c \ libarchive/test/test_write_format_raw_b64.c \ libarchive/test/test_write_format_shar_empty.c \ libarchive/test/test_write_format_tar.c \ libarchive/test/test_write_format_tar_empty.c \ libarchive/test/test_write_format_tar_sparse.c \ libarchive/test/test_write_format_tar_ustar.c \ libarchive/test/test_write_format_tar_v7tar.c \ libarchive/test/test_write_format_warc.c \ libarchive/test/test_write_format_warc_empty.c \ libarchive/test/test_write_format_xar.c \ libarchive/test/test_write_format_xar_empty.c \ libarchive/test/test_write_format_zip.c \ libarchive/test/test_write_format_zip_compression_store.c \ libarchive/test/test_write_format_zip_empty.c \ libarchive/test/test_write_format_zip_empty_zip64.c \ libarchive/test/test_write_format_zip_file.c \ libarchive/test/test_write_format_zip_file_zip64.c \ libarchive/test/test_write_format_zip_large.c \ libarchive/test/test_write_format_zip_zip64.c \ libarchive/test/test_write_open_memory.c \ libarchive/test/test_write_read_format_zip.c \ libarchive/test/test_zip_filename_encoding.c -libarchive_test_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/test_utils -I$(top_builddir)/libarchive/test -DLIBARCHIVE_STATIC $(PLATFORMCPPFLAGS) +libarchive_test_CPPFLAGS= \ + -I$(top_srcdir)/libarchive \ + -I$(top_srcdir)/libarchive/test \ + -I$(top_srcdir)/test_utils \ + -I$(top_builddir)/libarchive/test \ + -DLIBARCHIVE_STATIC $(PLATFORMCPPFLAGS) + libarchive_test_LDADD= $(LTLIBICONV) # The "list.h" file just lists all of the tests defined in all of the sources. # Building it automatically provides a sanity-check on libarchive_test_SOURCES # above. libarchive/test/list.h: Makefile $(MKDIR_P) libarchive/test cat $(top_srcdir)/libarchive/test/test_*.c | grep '^DEFINE_TEST' > libarchive/test/list.h libarchive_TESTS_ENVIRONMENT= LIBARCHIVE_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/libarchive/test LRZIP=NOCONFIG libarchive_test_EXTRA_DIST=\ libarchive/test/list.h \ libarchive/test/test_acl_pax_posix1e.tar.uu \ libarchive/test/test_acl_pax_nfs4.tar.uu \ libarchive/test/test_archive_string_conversion.txt.Z.uu \ libarchive/test/test_compat_bzip2_1.tbz.uu \ libarchive/test/test_compat_bzip2_2.tbz.uu \ libarchive/test/test_compat_cpio_1.cpio.uu \ libarchive/test/test_compat_gtar_1.tar.uu \ libarchive/test/test_compat_gtar_2.tar.uu \ libarchive/test/test_compat_gzip_1.tgz.uu \ libarchive/test/test_compat_gzip_2.tgz.uu \ libarchive/test/test_compat_lz4_1.tar.lz4.uu \ libarchive/test/test_compat_lz4_2.tar.lz4.uu \ libarchive/test/test_compat_lz4_3.tar.lz4.uu \ libarchive/test/test_compat_lz4_B4.tar.lz4.uu \ libarchive/test/test_compat_lz4_B4BD.tar.lz4.uu \ libarchive/test/test_compat_lz4_B4BDBX.tar.lz4.uu \ libarchive/test/test_compat_lz4_B5.tar.lz4.uu \ libarchive/test/test_compat_lz4_B5BD.tar.lz4.uu \ libarchive/test/test_compat_lz4_B6.tar.lz4.uu \ libarchive/test/test_compat_lz4_B6BD.tar.lz4.uu \ libarchive/test/test_compat_lz4_B7.tar.lz4.uu \ libarchive/test/test_compat_lz4_B7BD.tar.lz4.uu \ libarchive/test/test_compat_lzip_1.tlz.uu \ libarchive/test/test_compat_lzip_2.tlz.uu \ libarchive/test/test_compat_lzma_1.tlz.uu \ libarchive/test/test_compat_lzma_2.tlz.uu \ libarchive/test/test_compat_lzma_3.tlz.uu \ libarchive/test/test_compat_lzop_1.tar.lzo.uu \ libarchive/test/test_compat_lzop_2.tar.lzo.uu \ libarchive/test/test_compat_lzop_3.tar.lzo.uu \ libarchive/test/test_compat_mac-1.tar.Z.uu \ libarchive/test/test_compat_mac-2.tar.Z.uu \ libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu \ libarchive/test/test_compat_perl_archive_tar.tar.uu \ libarchive/test/test_compat_plexus_archiver_tar.tar.uu \ libarchive/test/test_compat_solaris_pax_sparse_1.pax.Z.uu \ libarchive/test/test_compat_solaris_pax_sparse_2.pax.Z.uu \ libarchive/test/test_compat_solaris_tar_acl.tar.uu \ libarchive/test/test_compat_star_acl_nfs4.tar.uu \ libarchive/test/test_compat_star_acl_posix1e.tar.uu \ libarchive/test/test_compat_tar_hardlink_1.tar.uu \ libarchive/test/test_compat_uudecode_large.tar.Z.uu \ libarchive/test/test_compat_xz_1.txz.uu \ libarchive/test/test_compat_zip_1.zip.uu \ libarchive/test/test_compat_zip_2.zip.uu \ libarchive/test/test_compat_zip_3.zip.uu \ libarchive/test/test_compat_zip_4.zip.uu \ libarchive/test/test_compat_zip_5.zip.uu \ libarchive/test/test_compat_zip_6.zip.uu \ libarchive/test/test_compat_zip_7.xps.uu \ libarchive/test/test_fuzz.cab.uu \ libarchive/test/test_fuzz.lzh.uu \ libarchive/test/test_fuzz_1.iso.Z.uu \ libarchive/test/test_pax_filename_encoding.tar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part1.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part2.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part3.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part4.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part5.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part6.rar.uu \ libarchive/test/test_rar_multivolume_single_file.part1.rar.uu \ libarchive/test/test_rar_multivolume_single_file.part2.rar.uu \ libarchive/test/test_rar_multivolume_single_file.part3.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part01.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part02.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part03.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part04.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part05.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part06.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part07.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part08.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part09.rar.uu \ libarchive/test/test_rar_multivolume_uncompressed_files.part10.rar.uu \ libarchive/test/test_read_filter_grzip.tar.grz.uu \ libarchive/test/test_read_filter_lrzip.tar.lrz.uu \ libarchive/test/test_read_filter_lzop.tar.lzo.uu \ libarchive/test/test_read_filter_lzop_multiple_parts.tar.lzo.uu \ libarchive/test/test_read_format_mtree_crash747.mtree.bz2.uu \ libarchive/test/test_read_format_7zip_bcj2_bzip2.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_copy_1.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_copy_2.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_copy_lzma.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_deflate.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma1_1.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma1_2.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma2_1.7z.uu \ libarchive/test/test_read_format_7zip_bcj2_lzma2_2.7z.uu \ libarchive/test/test_read_format_7zip_bcj_bzip2.7z.uu \ libarchive/test/test_read_format_7zip_bcj_copy.7z.uu \ libarchive/test/test_read_format_7zip_bcj_deflate.7z.uu \ libarchive/test/test_read_format_7zip_bcj_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_bzip2.7z.uu \ libarchive/test/test_read_format_7zip_copy.7z.uu \ libarchive/test/test_read_format_7zip_copy_2.7z.uu \ libarchive/test/test_read_format_7zip_deflate.7z.uu \ libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_empty_archive.7z.uu \ libarchive/test/test_read_format_7zip_empty_file.7z.uu \ libarchive/test/test_read_format_7zip_encryption.7z.uu \ libarchive/test/test_read_format_7zip_encryption_header.7z.uu \ libarchive/test/test_read_format_7zip_encryption_partially.7z.uu \ libarchive/test/test_read_format_7zip_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_2.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_malformed.7z.uu \ libarchive/test/test_read_format_7zip_malformed2.7z.uu \ libarchive/test/test_read_format_7zip_ppmd.7z.uu \ libarchive/test/test_read_format_7zip_symbolic_name.7z.uu \ libarchive/test/test_read_format_ar.ar.uu \ libarchive/test/test_read_format_cab_1.cab.uu \ libarchive/test/test_read_format_cab_2.cab.uu \ libarchive/test/test_read_format_cab_3.cab.uu \ libarchive/test/test_read_format_cab_filename_cp932.cab.uu \ libarchive/test/test_read_format_cpio_bin_be.cpio.uu \ libarchive/test/test_read_format_cpio_bin_le.cpio.uu \ libarchive/test/test_read_format_cpio_filename_cp866.cpio.uu \ libarchive/test/test_read_format_cpio_filename_eucjp.cpio.uu \ libarchive/test/test_read_format_cpio_filename_koi8r.cpio.uu \ libarchive/test/test_read_format_cpio_filename_utf8_jp.cpio.uu \ libarchive/test/test_read_format_cpio_filename_utf8_ru.cpio.uu \ libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.rpm.uu \ libarchive/test/test_read_format_cpio_svr4_gzip_rpm.rpm.uu \ libarchive/test/test_read_format_gtar_filename_cp866.tar.Z.uu \ libarchive/test/test_read_format_gtar_filename_eucjp.tar.Z.uu \ libarchive/test/test_read_format_gtar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_gtar_sparse_1_13.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix00.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu \ libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu \ libarchive/test/test_read_format_iso.iso.Z.uu \ libarchive/test/test_read_format_iso_2.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet_by_nero.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet_long.iso.Z.uu \ libarchive/test/test_read_format_iso_joliet_rockridge.iso.Z.uu \ libarchive/test/test_read_format_iso_multi_extent.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_ce.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_new.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_rr_moved.iso.Z.uu \ libarchive/test/test_read_format_iso_xorriso.iso.Z.uu \ libarchive/test/test_read_format_iso_zisofs.iso.Z.uu \ libarchive/test/test_read_format_lha_bugfix_0.lzh.uu \ libarchive/test/test_read_format_lha_filename_cp932.lzh.uu \ libarchive/test/test_read_format_lha_header0.lzh.uu \ libarchive/test/test_read_format_lha_header1.lzh.uu \ libarchive/test/test_read_format_lha_header2.lzh.uu \ libarchive/test/test_read_format_lha_header3.lzh.uu \ libarchive/test/test_read_format_lha_lh0.lzh.uu \ libarchive/test/test_read_format_lha_lh6.lzh.uu \ libarchive/test/test_read_format_lha_lh7.lzh.uu \ libarchive/test/test_read_format_lha_withjunk.lzh.uu \ libarchive/test/test_read_format_mtree.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic2.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic3.mtree.uu \ libarchive/test/test_read_format_rar.rar.uu \ libarchive/test/test_read_format_rar_binary_data.rar.uu \ libarchive/test/test_read_format_rar_compress_best.rar.uu \ libarchive/test/test_read_format_rar_compress_normal.rar.uu \ libarchive/test/test_read_format_rar_encryption_data.rar.uu \ libarchive/test/test_read_format_rar_encryption_header.rar.uu \ libarchive/test/test_read_format_rar_encryption_partially.rar.uu \ libarchive/test/test_read_format_rar_invalid1.rar.uu \ libarchive/test/test_read_format_rar_multi_lzss_blocks.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0001.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0002.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0003.rar.uu \ libarchive/test/test_read_format_rar_multivolume.part0004.rar.uu \ libarchive/test/test_read_format_rar_noeof.rar.uu \ libarchive/test/test_read_format_rar_ppmd_lzss_conversion.rar.uu \ libarchive/test/test_read_format_rar_sfx.exe.uu \ libarchive/test/test_read_format_rar_subblock.rar.uu \ libarchive/test/test_read_format_rar_unicode.rar.uu \ libarchive/test/test_read_format_rar_windows.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ libarchive/test/test_read_format_raw.data.uu \ libarchive/test/test_read_format_tar_concatenated.tar.uu \ libarchive/test/test_read_format_tar_empty_filename.tar.uu \ libarchive/test/test_read_format_tar_empty_pax.tar.Z.uu \ libarchive/test/test_read_format_tar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_cp866.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_eucjp.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_warc.warc.uu \ libarchive/test/test_read_format_zip.zip.uu \ libarchive/test/test_read_format_zip_comment_stored_1.zip.uu \ libarchive/test/test_read_format_zip_comment_stored_2.zip.uu \ libarchive/test/test_read_format_zip_encryption_data.zip.uu \ libarchive/test/test_read_format_zip_encryption_header.zip.uu \ libarchive/test/test_read_format_zip_encryption_partially.zip.uu \ libarchive/test/test_read_format_zip_filename_cp866.zip.uu \ libarchive/test/test_read_format_zip_filename_cp932.zip.uu \ libarchive/test/test_read_format_zip_filename_koi8r.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_jp.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_ru.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_ru2.zip.uu \ libarchive/test/test_read_format_zip_high_compression.zip.uu \ libarchive/test/test_read_format_zip_length_at_end.zip.uu \ libarchive/test/test_read_format_zip_jar.jar.uu \ libarchive/test/test_read_format_zip_mac_metadata.zip.uu \ libarchive/test/test_read_format_zip_malformed1.zip.uu \ libarchive/test/test_read_format_zip_msdos.zip.uu \ libarchive/test/test_read_format_zip_nested.zip.uu \ libarchive/test/test_read_format_zip_nofiletype.zip.uu \ libarchive/test/test_read_format_zip_padded1.zip.uu \ libarchive/test/test_read_format_zip_padded2.zip.uu \ libarchive/test/test_read_format_zip_padded3.zip.uu \ libarchive/test/test_read_format_zip_sfx.uu \ libarchive/test/test_read_format_zip_symlink.zip.uu \ libarchive/test/test_read_format_zip_traditional_encryption_data.zip.uu \ libarchive/test/test_read_format_zip_ux.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes128.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256_large.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256_stored.zip.uu \ libarchive/test/test_read_format_zip_zip64a.zip.uu \ libarchive/test/test_read_format_zip_zip64b.zip.uu \ libarchive/test/test_read_large_splitted_rar_aa.uu \ libarchive/test/test_read_large_splitted_rar_ab.uu \ libarchive/test/test_read_large_splitted_rar_ac.uu \ libarchive/test/test_read_large_splitted_rar_ad.uu \ libarchive/test/test_read_large_splitted_rar_ae.uu \ libarchive/test/test_read_pax_schily_xattr.tar.uu \ libarchive/test/test_read_splitted_rar_aa.uu \ libarchive/test/test_read_splitted_rar_ab.uu \ libarchive/test/test_read_splitted_rar_ac.uu \ libarchive/test/test_read_splitted_rar_ad.uu \ libarchive/test/test_read_too_many_filters.gz.uu \ libarchive/test/test_splitted_rar_seek_support_aa.uu \ libarchive/test/test_splitted_rar_seek_support_ab.uu \ libarchive/test/test_splitted_rar_seek_support_ac.uu \ libarchive/test/test_write_disk_appledouble.cpio.gz.uu \ libarchive/test/test_write_disk_hfs_compression.tgz.uu \ libarchive/test/test_write_disk_mac_metadata.tar.gz.uu \ libarchive/test/test_write_disk_no_hfs_compression.tgz.uu \ libarchive/test/CMakeLists.txt \ libarchive/test/README # # Common code for libarchive frontends (cpio, tar) # libarchive_fe_la_SOURCES= \ libarchive_fe/err.c \ libarchive_fe/err.h \ libarchive_fe/lafe_platform.h \ libarchive_fe/line_reader.c \ libarchive_fe/line_reader.h \ libarchive_fe/passphrase.c \ libarchive_fe/passphrase.h libarchive_fe_la_CPPFLAGS= -I$(top_srcdir)/libarchive # # # bsdtar source, docs, etc. # # bsdtar_SOURCES= \ tar/bsdtar.c \ tar/bsdtar.h \ tar/bsdtar_platform.h \ tar/cmdline.c \ tar/creation_set.c \ tar/read.c \ tar/subst.c \ tar/util.c \ tar/write.c if INC_WINDOWS_FILES bsdtar_SOURCES+= \ tar/bsdtar_windows.h \ tar/bsdtar_windows.c endif bsdtar_DEPENDENCIES= libarchive.la libarchive_fe.la if STATIC_BSDTAR bsdtar_ldstatic= -static bsdtar_ccstatic= -DLIBARCHIVE_STATIC else bsdtar_ldstatic= bsdtar_ccstatic= endif bsdtar_LDADD= libarchive.la libarchive_fe.la $(LTLIBICONV) bsdtar_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdtar_ccstatic) $(PLATFORMCPPFLAGS) bsdtar_LDFLAGS= $(bsdtar_ldstatic) bsdtar_EXTRA_DIST= \ tar/bsdtar.1 \ tar/bsdtar_windows.h \ tar/bsdtar_windows.c \ tar/CMakeLists.txt \ tar/config_freebsd.h if BUILD_BSDTAR bsdtar_man_MANS= tar/bsdtar.1 bsdtar_programs= bsdtar else bsdtar_man_MANS= bsdtar_programs= endif # # bsdtar_test # bsdtar_test_SOURCES= \ $(test_utils_SOURCES) \ - tar/test/main.c \ tar/test/test.h \ tar/test/test_0.c \ tar/test/test_basic.c \ tar/test/test_copy.c \ tar/test/test_empty_mtree.c \ tar/test/test_extract_tar_Z.c \ tar/test/test_extract_tar_bz2.c \ tar/test/test_extract_tar_grz.c \ tar/test/test_extract_tar_gz.c \ tar/test/test_extract_tar_lrz.c \ tar/test/test_extract_tar_lz.c \ tar/test/test_extract_tar_lz4.c \ tar/test/test_extract_tar_lzma.c \ tar/test/test_extract_tar_lzo.c \ tar/test/test_extract_tar_xz.c \ tar/test/test_format_newc.c \ tar/test/test_help.c \ tar/test/test_leading_slash.c \ tar/test/test_missing_file.c \ tar/test/test_option_C_upper.c \ tar/test/test_option_H_upper.c \ tar/test/test_option_L_upper.c \ tar/test/test_option_O_upper.c \ tar/test/test_option_T_upper.c \ tar/test/test_option_U_upper.c \ tar/test/test_option_X_upper.c \ tar/test/test_option_a.c \ tar/test/test_option_b.c \ tar/test/test_option_b64encode.c \ tar/test/test_option_exclude.c \ tar/test/test_option_gid_gname.c \ tar/test/test_option_grzip.c \ tar/test/test_option_j.c \ tar/test/test_option_k.c \ tar/test/test_option_keep_newer_files.c \ tar/test/test_option_lrzip.c \ tar/test/test_option_lz4.c \ tar/test/test_option_lzma.c \ tar/test/test_option_lzop.c \ tar/test/test_option_n.c \ tar/test/test_option_newer_than.c \ tar/test/test_option_nodump.c \ tar/test/test_option_older_than.c \ tar/test/test_option_passphrase.c \ tar/test/test_option_q.c \ tar/test/test_option_r.c \ tar/test/test_option_s.c \ tar/test/test_option_uid_uname.c \ tar/test/test_option_uuencode.c \ tar/test/test_option_xz.c \ tar/test/test_option_z.c \ tar/test/test_patterns.c \ tar/test/test_print_longpath.c \ tar/test/test_stdio.c \ tar/test/test_strip_components.c \ tar/test/test_symlink_dir.c \ tar/test/test_version.c \ tar/test/test_windows.c bsdtar_test_CPPFLAGS=\ -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \ -I$(top_srcdir)/test_utils \ - -I$(top_srcdir)/tar -I$(top_builddir)/tar/test \ + -I$(top_srcdir)/tar -I$(top_srcdir)/tar/test \ + -I$(top_builddir)/tar/test \ $(PLATFORMCPPFLAGS) tar/test/list.h: Makefile $(MKDIR_P) tar/test cat $(top_srcdir)/tar/test/test_*.c | grep '^DEFINE_TEST' > tar/test/list.h if BUILD_BSDTAR bsdtar_test_programs= bsdtar_test bsdtar_TESTS_ENVIRONMENT= BSDTAR=`cd $(top_builddir);/bin/pwd`/bsdtar$(EXEEXT) BSDTAR_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/tar/test else bsdtar_test_programs= bsdtar_TESTS_ENVIRONMENT= endif bsdtar_test_EXTRA_DIST= \ tar/test/list.h \ tar/test/test_extract.tar.Z.uu \ tar/test/test_extract.tar.bz2.uu \ tar/test/test_extract.tar.grz.uu \ tar/test/test_extract.tar.gz.uu \ tar/test/test_extract.tar.lrz.uu \ tar/test/test_extract.tar.lz.uu \ tar/test/test_extract.tar.lz4.uu \ tar/test/test_extract.tar.lzma.uu \ tar/test/test_extract.tar.lzo.uu \ tar/test/test_extract.tar.xz.uu \ tar/test/test_leading_slash.tar.uu \ tar/test/test_option_keep_newer_files.tar.Z.uu \ tar/test/test_option_passphrase.zip.uu \ tar/test/test_option_s.tar.Z.uu \ tar/test/test_patterns_2.tar.uu \ tar/test/test_patterns_3.tar.uu \ tar/test/test_patterns_4.tar.uu \ tar/test/test_print_longpath.tar.Z.uu \ tar/test/CMakeLists.txt # # # bsdcpio source, docs, etc. # # bsdcpio_SOURCES= \ cpio/cmdline.c \ cpio/cpio.c \ cpio/cpio.h \ cpio/cpio_platform.h if INC_WINDOWS_FILES bsdcpio_SOURCES+= \ cpio/cpio_windows.h \ cpio/cpio_windows.c endif bsdcpio_DEPENDENCIES = libarchive.la libarchive_fe.la if STATIC_BSDCPIO bsdcpio_ldstatic= -static bsdcpio_ccstatic= -DLIBARCHIVE_STATIC else bsdcpio_ldstatic= bsdcpio_ccstatic= endif bsdcpio_LDADD= libarchive_fe.la libarchive.la $(LTLIBICONV) bsdcpio_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdcpio_ccstatic) $(PLATFORMCPPFLAGS) bsdcpio_LDFLAGS= $(bsdcpio_ldstatic) bsdcpio_EXTRA_DIST= \ cpio/bsdcpio.1 \ cpio/cpio_windows.h \ cpio/cpio_windows.c \ cpio/CMakeLists.txt \ cpio/config_freebsd.h if BUILD_BSDCPIO # Manpages to install bsdcpio_man_MANS= cpio/bsdcpio.1 bsdcpio_programs= bsdcpio else bsdcpio_man_MANS= bsdcpio_programs= endif # # bsdcpio_test # bsdcpio_test_SOURCES= \ $(test_utils_SOURCES) \ cpio/cmdline.c \ - cpio/test/main.c \ cpio/test/test.h \ cpio/test/test_0.c \ cpio/test/test_basic.c \ cpio/test/test_cmdline.c \ cpio/test/test_extract_cpio_Z.c \ cpio/test/test_extract_cpio_bz2.c \ cpio/test/test_extract_cpio_grz.c \ cpio/test/test_extract_cpio_gz.c \ cpio/test/test_extract_cpio_lrz.c \ cpio/test/test_extract_cpio_lz.c \ cpio/test/test_extract_cpio_lz4.c \ cpio/test/test_extract_cpio_lzma.c \ cpio/test/test_extract_cpio_lzo.c \ cpio/test/test_extract_cpio_xz.c \ cpio/test/test_format_newc.c \ cpio/test/test_gcpio_compat.c \ cpio/test/test_missing_file.c \ cpio/test/test_option_0.c \ cpio/test/test_option_B_upper.c \ cpio/test/test_option_C_upper.c \ cpio/test/test_option_J_upper.c \ cpio/test/test_option_L_upper.c \ cpio/test/test_option_Z_upper.c \ cpio/test/test_option_a.c \ cpio/test/test_option_b64encode.c \ cpio/test/test_option_c.c \ cpio/test/test_option_d.c \ cpio/test/test_option_f.c \ cpio/test/test_option_grzip.c \ cpio/test/test_option_help.c \ cpio/test/test_option_l.c \ cpio/test/test_option_lrzip.c \ cpio/test/test_option_lz4.c \ cpio/test/test_option_lzma.c \ cpio/test/test_option_lzop.c \ cpio/test/test_option_m.c \ cpio/test/test_option_passphrase.c \ cpio/test/test_option_t.c \ cpio/test/test_option_u.c \ cpio/test/test_option_uuencode.c \ cpio/test/test_option_version.c \ cpio/test/test_option_xz.c \ cpio/test/test_option_y.c \ cpio/test/test_option_z.c \ cpio/test/test_owner_parse.c \ cpio/test/test_passthrough_dotdot.c \ cpio/test/test_passthrough_reverse.c bsdcpio_test_CPPFLAGS= \ -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \ -I$(top_srcdir)/test_utils \ - -I$(top_srcdir)/cpio -I$(top_builddir)/cpio/test \ + -I$(top_srcdir)/cpio -I$(top_srcdir)/cpio/test \ + -I$(top_builddir)/cpio/test \ $(PLATFORMCPPFLAGS) bsdcpio_test_LDADD=libarchive_fe.la cpio/test/list.h: Makefile $(MKDIR_P) cpio/test cat $(top_srcdir)/cpio/test/test_*.c | grep '^DEFINE_TEST' > cpio/test/list.h if BUILD_BSDCPIO bsdcpio_test_programs= bsdcpio_test bsdcpio_TESTS_ENVIRONMENT= BSDCPIO=`cd $(top_builddir);/bin/pwd`/bsdcpio$(EXEEXT) BSDCPIO_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/cpio/test else bsdcpio_test_programs= bsdcpio_TESTS_ENVIRONMENT= endif bsdcpio_test_EXTRA_DIST= \ cpio/test/list.h \ cpio/test/test_extract.cpio.Z.uu \ cpio/test/test_extract.cpio.bz2.uu \ cpio/test/test_extract.cpio.grz.uu \ cpio/test/test_extract.cpio.gz.uu \ cpio/test/test_extract.cpio.lrz.uu \ cpio/test/test_extract.cpio.lz.uu \ cpio/test/test_extract.cpio.lz4.uu \ cpio/test/test_extract.cpio.lzma.uu \ cpio/test/test_extract.cpio.lzo.uu \ cpio/test/test_extract.cpio.xz.uu \ cpio/test/test_gcpio_compat_ref.bin.uu \ cpio/test/test_gcpio_compat_ref.crc.uu \ cpio/test/test_gcpio_compat_ref.newc.uu \ cpio/test/test_gcpio_compat_ref.ustar.uu \ cpio/test/test_gcpio_compat_ref_nosym.bin.uu \ cpio/test/test_gcpio_compat_ref_nosym.crc.uu \ cpio/test/test_gcpio_compat_ref_nosym.newc.uu \ cpio/test/test_gcpio_compat_ref_nosym.ustar.uu \ cpio/test/test_option_f.cpio.uu \ cpio/test/test_option_m.cpio.uu \ cpio/test/test_option_passphrase.zip.uu \ cpio/test/test_option_t.cpio.uu \ cpio/test/test_option_t.stdout.uu \ cpio/test/test_option_tv.stdout.uu \ cpio/test/CMakeLists.txt # # # bsdcat source, docs, etc. # # bsdcat_SOURCES= \ cat/bsdcat.c \ cat/bsdcat.h \ cat/bsdcat_platform.h \ cat/cmdline.c if INC_WINDOWS_FILES bsdcat_SOURCES+= endif bsdcat_DEPENDENCIES = libarchive.la libarchive_fe.la if STATIC_BSDCAT bsdcat_ldstatic= -static bsdcat_ccstatic= -DLIBARCHIVE_STATIC else bsdcat_ldstatic= bsdcat_ccstatic= endif bsdcat_LDADD= libarchive_fe.la libarchive.la $(LTLIBICONV) bsdcat_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdcat_ccstatic) $(PLATFORMCPPFLAGS) bsdcat_LDFLAGS= $(bsdcat_ldstatic) bsdcat_EXTRA_DIST= \ cat/bsdcat.1 \ cat/CMakeLists.txt if BUILD_BSDCAT # Manpages to install bsdcat_man_MANS= cat/bsdcat.1 bsdcat_programs= bsdcat else bsdcat_man_MANS= bsdcat_programs= endif # # bsdcat_test # bsdcat_test_SOURCES= \ $(test_utils_SOURCES) \ - cat/test/main.c \ cat/test/test.h \ cat/test/test_0.c \ cat/test/test_empty_gz.c \ cat/test/test_empty_lz4.c \ cat/test/test_empty_xz.c \ cat/test/test_error.c \ cat/test/test_error_mixed.c \ cat/test/test_expand_Z.c \ cat/test/test_expand_bz2.c \ cat/test/test_expand_gz.c \ cat/test/test_expand_lz4.c \ cat/test/test_expand_mixed.c \ cat/test/test_expand_plain.c \ cat/test/test_expand_xz.c \ cat/test/test_help.c \ cat/test/test_version.c bsdcat_test_CPPFLAGS= \ -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \ -I$(top_srcdir)/test_utils \ - -I$(top_srcdir)/cat -I$(top_builddir)/cat/test \ + -I$(top_srcdir)/cat -I$(top_srcdir)/cat/test \ + -I$(top_builddir)/cat/test \ $(PLATFORMCPPFLAGS) bsdcat_test_LDADD=libarchive_fe.la cat/test/list.h: Makefile cat $(top_srcdir)/cat/test/test_*.c | grep '^DEFINE_TEST' > cat/test/list.h if BUILD_BSDCAT bsdcat_test_programs= bsdcat_test bsdcat_TESTS_ENVIRONMENT= BSDCAT=`cd $(top_builddir);/bin/pwd`/bsdcat$(EXEEXT) BSDCAT_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/cat/test else bsdcat_test_programs= bsdcat_TESTS_ENVIRONMENT= endif bsdcat_test_EXTRA_DIST= \ cat/test/list.h \ cat/test/test_empty.gz.uu \ cat/test/test_empty.lz4.uu \ cat/test/test_empty.xz.uu \ cat/test/test_expand.Z.uu \ cat/test/test_expand.bz2.uu \ cat/test/test_expand.gz.uu \ cat/test/test_expand.lz4.uu \ cat/test/test_expand.plain.uu \ cat/test/test_expand.xz.uu \ cat/test/CMakeLists.txt Index: vendor/libarchive/dist/NEWS =================================================================== --- vendor/libarchive/dist/NEWS (revision 314564) +++ vendor/libarchive/dist/NEWS (revision 314565) @@ -1,675 +1,683 @@ +Feb 26, 2017: libarchive 3.3.1 released + Security & Feature release + +Feb 19, 2017: libarchive 3.3.0 released + Security & Feature release + Jan 29, 2017: Limited NFSv4 ACL support for Mac OS (Darwin) Jan 10, 2017: POSIX.1e and NFSv4 ACL support for Solaris and derivates Dec 27, 2016: NFSv4 ACL read and write support for pax Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w() + +Nov, 2016: libarchive is now being tested by the OSS-Fuzz project Oct 26, 2016: Remove liblzmadec support Oct 23, 2016: libarchive 3.2.2 released Security release Jun 20, 2016: libarchive 3.2.1 released This fixes a handful of security and other critical issues with 3.2.0 May 01, 2016: libarchive 3.2.0 released Apr 09, 2016: libarchive 3.1.901a released Another test release in preparation for 3.2.0 Feb 13, 2016: libarchive 3.1.900a released This is a test release in preparation for 3.2.0 Oct 21, 2015: Preliminary port to OSF Apr 11, 2015: libarchive's issue tracker is now hosted at GitHub. https://github.com/libarchive/libarchive/issues Early 2015: Many fixes to crash and overflow bugs thanks to Hanno Boeck Oct 13, 2014: Zip encryption and decryption support Aug 13, 2014: Add support for lz4 compression. Jun 10, 2014: Add warc format support May 3, 2014: Add experimental Zip streaming extension Apr 6, 2014: Add bsdcat command-line tool Jan 12, 2014: Add Zip64 support Dec 1, 2013: Rewrite Zip write logic Jul 1, 2013: Add ability to detect encrypted entries for many formats (This does not add the ability to *decrypt* those entries, however) Feb 23, 2013: "raw" write support added Feb 09, 2013: libarchive 3.1.2 released Jan 28, 2013: libarchive's new website moved to http://www.libarchive.org. Jan 13, 2013: libarchive 3.1.1 released Jan 13, 2013: libarchive 3.1.0 released Dec 07, 2012: Implement functions to manually set the format and filters used. Nov 11, 2012: Add support for __MACOSX directory in Zip archives, which resource forks are stored in. Oct 20, 2012: Add support for writing v7 tar format. Oct 09, 2012: Add support for grzip compression. Oct 07, 2012: Introduce b64encode filter. Oct 07, 2012: Introduce uuencode filter. Oct 06, 2012: Add support for lzop. Sep 27, 2012: Implement function used to seek within data blocks. (Currently only supported for uncompressed RAR archives). Apr 22, 2012: Add basic archive read and write filter support for lrzip. Mar 27, 2012: libarchive 3.0.4 released Feb 05, 2012: libarchive development now hosted at GitHub. http://libarchive.github.com/ Feb 05, 2012: libarchive's issue tracker remains at Google Code. http://code.google.com/p/libarchive/issues/list Feb 05, 2012: libarchive's mailing lists remain at Google Groups. Dec 24, 2011: libarchive 3.0.2 released Dec 23, 2011: Various fixes merged from FreeBSD Dec 23, 2011: Symlink support in Zip reader and writer Dec 23, 2011: Robustness fixes to 7Zip reader Nov 27, 2011: libarchive 3.0.1b released Nov 26, 2011: 7Zip reader Nov 26, 2011: Small fixes to ISO and Zip to improve robustness with corrupted input Nov 24, 2011: Improve streaming Zip reader's support for uncompressed entries Nov 20, 2011: New seeking Zip reader supports SFX Zip archives Nov 20, 2011: Build fixes on Windows Nov 13, 2011: libarchive 3.0.0a released Nov 06, 2011: Update shared-library version calculations for libarchive 3.x Sep 04, 2011: Fix tar -s; follow GNU tar for controlling hardlink/symlink substitutions Aug 18, 2011: Fix reading ISO images built by NetBSD's mkisofs Aug 15, 2011: Old archive_read_support_compression_XXX functions are deprecated and will disappear in libarchive 4.0. Jun 26, 2011: RAR reader Jun 16, 2011: Add tar:compat-2x option to emulate broken libarchive 2.x handling of pax UTF-8 headers Apr 25, 2011: Refactor read_open() into a collection of single-item setters; support the old interfaces as wrappers Apr 12, 2011: Split disk writer into separate POSIX and Windows implementations Apr 10, 2011: Improvements to character translations on Windows. Mar 30, 2011: More work to return errors instead of calling abort() Mar 23, 2011: Add charset option to many writers to control MBCS filenames Mar 17, 2011: Overhauled support for per-format extension options Mar 17, 2011: Track character set used for mbcs strings, support translating to/from user-specified locale Mar 09, 2011: Recognize mtree files without requiring a signature Mar 06, 2011: Use iconv to convert to/from Unicode instead of making bad assumptions about the C90 character set translation functions Feb 17, 2011: Fixes for AIX, TRU64, and other platforms Dec 22, 2010: CAB reader Dec 20, 2010: LHA/LZH reader Jul 03, 2010: minitar example demonstrates archive_read_disk directory traversal Jun 29, 2010: Many improvements to ISO reader compatibility Jun 26, 2010: Use larger buffers when copy files into an archive Jun 18, 2010: Reimplement Mac OS extensions in libarchive Jun 09, 2010: archive_read_disk now supports traversals May 28, 2010: XAR writer May 16, 2010: Fix ^T handling; don't exit on interrupted reads and writes May 09, 2010: Improved detection of platform-specific crypto support May 04, 2010: lzip read and write filters May 01, 2010: New options: tar --gid --gname --uid --uname Apr 28, 2010: Use Red-black tree for ISO reader/writer to improve performance Apr 17, 2010: Minimal writer for legacy GNU tar format Mar 12, 2010: Don't dereference symlinks on Linux when reading ACLs. Mar 06, 2010: Fix build when an older libarchive is already installed Feb 28, 2010: Relax handling of state failures; misuse by clients now generally results in a sticky ARCHIVE_FATAL rather than a visit to abort() Feb 25, 2010: ISO writer Feb 21, 2010: Split many man pages into smaller chunks. Feb 21, 2010: Performance: Cheat on block sizes when reading archives from disk. Feb 21, 2010: Use int64_t instead of off_t, dev_t, ino_t, uid_t, and gid_t Feb 20, 2010: Document new ACL functions. Feb 19, 2010: Support multiple write filters Feb 07, 2010: Remove some legacy libarchive 1.x APIs Feb 04, 2010: Read afio headers Feb 02, 2010: Archive sparse files compatibly with GNU tar Feb 01, 2010: Integrate Apple extensions for Mac OS extended attributes into bsdtar Jan 31, 2010: Support cpio -V Feb 04, 2010: libarchive 2.8.0 released Jan 17, 2010: Fix error handling for 'echo nonexistent | cpio -o' Jan 17, 2010: Don't use futimes() on Cygwin Jan 02, 2010: libarchive 2.7.902a released (test release for 2.8) Jan 02, 2010: Fix tar/test/test_windows on MinGW Jan 02, 2010: Fix memory leaks in libarchive tests Jan 01, 2010: Fix memory leak when filter startup fails Dec 27, 2009: libarchive 2.7.901a released (test release for 2.8) Aug 04, 2009: libarchive 2.7.1 released Jul 20, 2009: Suppress bogus warning about unxz Jul 19, 2009: Support Cygwin 1.7 Jun 11, 2009: Support lzma/xz files compressed with larger buffer sizes. May 24, 2009: Handle gzip files signed with OpenBSD "gzsig" program. May 07, 2009: Avoid false failures when reading from pipe. Apr 16, 2009: libarchive 2.7.0 released Apr 10, 2009: libarchive 2.6.992a released Apr 09, 2009: Fix SIGPIPE issue building with MSVC. Apr 09, 2009: Fix several minor memory leaks in libarchive and libarchive_test Apr 08, 2009: libarchive 2.6.991a released Apr 07, 2009: Additional tests added to bsdcpio_test Apr 01, 2009: libarchive 2.6.990a released Apr 01, 2009: Use command-line gunzip, bunzip2, unxz, unlzma for decompression if the library is built without suitable libraries. The setup functions return ARCHIVE_WARN in this case so clients can adapt if necessary. Apr 01, 2009: Use getpw*_r and getgr*_r functions for thread-safety. Mar 24, 2009: Add archive_read_next_header2(), which is up to 25% more efficient for some clients; from Brian Harring. Mar 22, 2009: PDF versions of manpages are now included in the distribution. Mar, 2009: Major work to improve Cygwin build by Charles Wilson. Feb/Mar, 2009: Major work on cmake build support, mostly by Michihiro NAKAJIMA. Feb/Mar, 2009: Major work on Visual Studio support by Michihiro NAKAJIMA. All tests now pass. Feb 25, 2009: Fix Debian Bug #516577 Feb 21, 2009: Yacc is no longer needed to build; date parser rewritten in C. Jan/Feb, 2009: Mtree work by Michihiro. Feb, 2009: Joliet support by Andreas Henriksson. Jan/Feb, 2009: New options framework by Michihiro. Feb, 2009: High-res timestamps on Tru64, AIX, and GNU Hurd, by Björn Jacke. Jan 18, 2009: Extended attributes work on FreeBSD and Linux now with pax format. Jan 07, 2009: New archive_read_disk_entry_from_file() knows about ACLs, extended attributes, etc so that bsdtar and bsdcpio don't require such system-specific knowledge. Jan 03, 2009: Read filter system extensively refactored. In particular, read filter pipelines are now built out automatically and individual filters should be much easier to implement. Documentation on the Googlecode Wiki explains how to implement new filters. Dec 28, 2008: Many Windows/Visual Studio fixes from Michihiro NAKAJIMA. Dec 28, 2008: Main libarchive development moved from FreeBSD Perforce server to Google Code. This should make it easier for more people to participate in libarchive development. Dec 28, 2008: libarchive 2.6.0 released Dec 25, 2008: libarchive 2.5.905a released Dec 10, 2008: libarchive 2.5.904a released Dec 04, 2008: libarchive 2.5.903a released Nov 09, 2008: libarchive 2.5.902a released Nov 08, 2008: libarchive 2.5.901a released Nov 08, 2008: Start of pre-release testing for libarchive 2.6 Nov 07, 2008: Read filter refactor: The decompression routines just consume and produce arbitrarily-sized blocks. The reblocking from read_support_compression_none() has been pulled into the read core. Also, the decompression bid now makes multiple passes and stacks read filters. Oct 21, 2008: bsdcpio: New command-line parser. Oct 19, 2008: Internal read_ahead change: short reads are now an error Oct 06, 2008: bsdtar: option parser no longer uses getopt_long(), gives consistent option parsing on all platforms. Sep 19, 2008: Jaakko Heinonen: shar utility built on libarchive Sep 17, 2008: Pedro Giffuni: birthtime support Sep 17, 2008: Miklos Vajna: lzma reader and test. Note: I still have some concerns about the auto-detection (LZMA file format doesn't support auto-detection well), so this is not yet enabled under archive_read_support_compression_all(). For now, you must call archive_read_support_compression_lzma() if you want LZMA read support. Sep 11, 2008: Ivailo Petrov: Many fixes to Windows build, new solution files Jul 26, 2008: archive_entry now tracks which values have not been set. This helps zip extraction (file size is often "unknown") and time restores (tar usually doesn't know atime). Jul 26, 2008: Joerg Sonnenberger: Performance improvements to shar writer Jul 25, 2008: Joerg Sonnenberger: mtree write support Jul 02, 2008: libarchive 2.5.5 released Jul 02, 2008: libarchive 2.5.5b released Jul 01, 2008: bsdcpio is being used by enough people, we can call it 1.0.0 now Jun 20, 2008: bsdcpio: If a -l link fails with EXDEV, copy the file instead Jun 19, 2008: bsdcpio: additional long options for better GNU cpio compat Jun 15, 2008: Many small portability and bugfixes since 2.5.4b. May 25, 2008: libarchive 2.5.4b released May 21, 2008: Joerg Sonnenberger: fix bsdtar hardlink handling for newc format May 21, 2008: More progress on Windows building. Thanks to "Scott" for the Windows makefiles, thanks to Kees Zeelenberg for code contributions. May 21, 2008: Fix a number of non-exploitable integer and buffer overflows, thanks to David Remahl at Apple for pointing these out. May 21, 2008: Colin Percival: SIGINFO or SIGUSR1 to bsdtar prints progress info May 16, 2008: bsdtar's test harness no longer depends on file ordering. This was causing spurious test failures on a lot of systems. Thanks to Bernhard R. Link for the diagnosis. May 14, 2008: Joerg Sonnenberger: -s substitution support for bsdtar May 13, 2008: Joerg Sonnenberger: Many mtree improvements May 11, 2008: Joerg Sonnenberger: fix hardlink extraction when hardlinks have different permissions from original file April 30, 2008: Primary libarchive work has been moved into the FreeBSD project's Perforce repository: http://perforce.freebsd.org/ The libarchive project can be browsed at //depot/user/kientzle/libarchive-portable Direct link: http://preview.tinyurl.com/46mdgr May 04, 2008: libarchive 2.5.3b released * libarchive: Several fixes to link resolver to address bsdcpio crashes * bsdcpio: -p hardlink handling fixes * tar/pax: Ensure ustar dirnames end in '/'; be more careful about measuring filenames when deciding what pathname fields to use * libarchive: Mark which entry strings are set; be accurate about distinguishing empty strings ("") from unset ones (NULL) * tar: Don't crash reading entries with empty filenames * libarchive_test, bsdtar_test, bsdcpio_test: Better detaults: run all tests, delete temp dirs, summarize repeated failures * -no-undefined to libtool for Cygwin * libarchive_test: Skip large file tests on systems with 32-bit off_t * iso9660: Don't bother trying to find the body of an empty file; this works around strange behavior from some ISO9660 writers * tar: allow -r -T to be used together * tar: allow --format with -r or -u * libarchive: Don't build archive.h May 04, 2008: Simplified building: archive.h is no longer constructed This may require additional #if conditionals on some platforms. Mar 30, 2008: libarchive 2.5.1b released Mar 15, 2008: libarchive 2.5.0b released Mar 15, 2008: bsdcpio now seems to correctly write hardlinks into newc, ustar, and old cpio archives. Just a little more testing before bsdcpio 1.0 becomes a reality. Mar 15, 2008: I think the new linkify() interface is finally handling all known hardlink strategies. Mar 15, 2008: Mtree read fixes from Joerg Sonnenberger. Mar 15, 2008: Many new bsdtar and bsdcpio options from Joerg Sonnenberger. Mar 15, 2008: test harnesses no longer require uudecode; they now have built-in decoding logic that decodes the reference files as they are needed. Mar 14, 2008: libarchive 2.4.14 released; identical to 2.4.13 except for a point fix for gname/uname mixup in pax format that was introduced with the UTF-8 fixes. Feb 26, 2008: libarchive 2.4.13 released Feb 25, 2008: Handle path, linkname, gname, or uname that can't be converted to/from UTF-8. Implement "hdrcharset" attribute from SUS-2008. Feb 25, 2008: Fix name clash on NetBSD. Feb 18, 2008: Fix writing empty 'ar' archives, per Kai Wang Feb 18, 2008: [bsdtar] Permit appending on block devices. Feb 09, 2008: New "linkify" resolver to help with newc hardlink writing; bsdcpio still needs to be converted to use this. Feb 02, 2008: Windows compatibility fixes from Ivailo Petrov, Kees Zeelenberg Jan 30, 2008: Ignore hardlink size for non-POSIX tar archives. Jan 22, 2008: libarchive 2.4.12 released Jan 22, 2008: Fix bad padding when writing symlinks to newc cpio archives. Jan 22, 2008: Verify bsdcpio_test by getting it to work against GNU cpio 2.9. bsdcpio_test complains about missing options (-y and -z), format of informational messages (--version, --help), and a minor formatting issue in odc format output. After this update, bsdcpio_test uncovered several more cosmetic issues in bsdcpio, all now fixed. Jan 22, 2008: Experimental support for self-extracting Zip archives. Jan 22, 2008: Extend hardlink restore strategy to work correctly with hardlinks extracted from newc cpio files. (Which store the body only with the last occurrence of a link.) Dec 30, 2007: libarchive 2.4.11 released Dec 30, 2007: Fixed a compile error in bsdcpio on some systems. Dec 29, 2007: libarchive 2.4.10 released Dec 29, 2007: bsdcpio 0.9.0 is ready for wider use. Dec 29, 2007: Completed initial test harness for bsdcpio. Dec 22, 2007: libarchive 2.4.9 released Dec 22, 2007: Implement the remaining options for bsdcpio: -a, -q, -L, -f, pattern selection for -i and -it. Dec 13, 2007: libarchive 2.4.8 released Dec 13, 2007: gzip and bzip2 compression now handle zero-byte writes correctly, Thanks to Damien Golding for bringing this to my attention. Dec 12, 2007: libarchive 2.4.7 released Dec 10, 2007: libarchive 2.4.6 released Dec 09, 2007: tar/test/test_copy.c verifies "tar -c | tar -x" copy pipeline Dec 07, 2007: Fix a couple of minor memory leaks. Dec 04, 2007: libarchive 2.4.5 released Dec 04, 2007: Fix cpio/test/test_write_odc by setting the umask first. Dec 03, 2007: libarchive 2.4.4 released Dec 03, 2007: New configure options --disable-xattr and --disable-acl, thanks to Samuli Suominen. Dec 03, 2007: libarchive 2.4.3 released Dec 03, 2007: Thanks to Lapo Luchini for sending me a ZIP file that libarchive couldn't handle. Fixed a bug in handling of "length at end" flags in ZIP files. Dec 03, 2007: Fixed bsdcpio -help, bsdtar -help tests. Dec 02, 2007: First cut at real bsdtar test harness. Dec 02, 2007: libarchive 2.4.2 released Dec 02, 2007: libarchive 2.4.1 released Dec 02, 2007: Minor fixes, rough cut of mdoc-to-man conversion for man pages. Oct 30, 2007: libarchive 2.4.0 released Oct 30, 2007: Minor compile fix thanks to Joerg Schilling. Oct 30, 2007: Only run the format auction once at the beginning of the archive. This is simpler and supports better error recovery. Oct 29, 2007: Test support for very large entries in tar archives: libarchive_test now exercises entries from 2GB up to 1TB. Oct 27, 2007: libarchive 2.3.5 released Oct 27, 2007: Correct some unnecessary internal data copying in the "compression none" reader and writer; this reduces user time by up to 2/3 in some tests. (Thanks to Jan Psota for publishing his performance test results to GNU tar's bug-tar mailing list; those results pointed me towards this problem.) Oct 27, 2007: Fix for skipping archive entries that are exactly a multiple of 4G on 32-bit platforms. Oct 25, 2007: Fix for reading very large (>8G) tar archives; this was broken when I put in support for new GNU tar sparse formats. Oct 20, 2007: Initial work on new pattern-matching code for cpio; I hope this eventually replaces the code currently in bsdtar. Oct 08, 2007: libarchive 2.3.4 released Oct 05, 2007: Continuing work on bsdcpio test suite. Oct 05, 2007: New cpio.5 manpage, updates to "History" of bsdcpio.1 and bsdtar.1 manpages. Oct 05, 2007: Fix zip reader to immediately return EOF if you try to read body of non-regular file. In particular, this fixes bsdtar extraction of zip archives. Sep 30, 2007: libarchive 2.3.3 released Sep 26, 2007: Rework Makefile.am so that the enable/disable options actually do the right things. Sep 26, 2007: cpio-odc and cpio-newc archives no longer write bodies for non-regular files. Sep 26, 2007: Test harness for bsdcpio is in place, needs more tests written. This is much nicer than the ragtag collection of test scripts that bsdtar has. Sep 20, 2007: libarchive 2.3.2 released Sep 20, 2007: libarchive 2.3.1 broke bsdtar because the archive_write_data() fix was implemented incorrectly. Sep 16, 2007: libarchive 2.3.1 released Sep 16, 2007: Many fixes to bsdcpio 0.3: handle hardlinks with -p, recognize block size on writing, fix a couple of segfaults. Sep 16, 2007: Fixed return value from archive_write_data() when used with archive_write_disk() to match the documentation and other instances of this same function. Sep 15, 2007: Add archive_entry_link_resolver, archive_entry_strmode Sep 11, 2007: libarchive 2.2.8 released Sep 09, 2007: bsdcpio 0.2 supports most (not yet all) of the old POSIX spec. Sep 01, 2007: libarchive 2.2.7 released Aug 31, 2007: Support for reading mtree files, including an mtree.5 manpage (A little experimental still.) Aug 18, 2007: Read gtar 1.17 --posix --sparse entries. Aug 13, 2007: Refined suid/sgid restore handling; it is no longer an error if suid/sgid bits are dropped when you request perm restore but don't request owner restore. Aug 06, 2007: Use --enable-bsdcpio if you want to try bsdcpio Aug 05, 2007: libarchive 2.2.6 released Aug 05, 2007: New configure option --disable-bsdtar, thanks to Joerg Sonnenberger. Aug 05, 2007: Several bug fixes from FreeBSD CVS repo. Jul 13, 2007: libarchive 2.2.5 released Jul 12, 2007: libarchive 2.2.4 released Jul 12, 2007: Thanks to Colin Percival's help in diagnosing and fixing several critical security bugs. Details available at http://security.freebsd.org/advisories/FreeBSD-SA-07:05.libarchive.asc May 26, 2007: libarchive 2.2.3 released May 26, 2007: Fix memory leaks in ZIP reader and shar writer, add some missing system headers to archive_entry.h, dead code cleanup from Colin Percival, more tests for gzip/bzip2, fix an EOF anomaly in bzip2 decompression. May 12, 2007: libarchive 2.2.2 released May 12, 2007: Fix archive_write_disk permission restore by cloning entry passed into write_header so that permission info is still available at finish_entry time. (archive_read_extract() worked okay because it held onto the passed-in entry, but direct consumers of archive_write_disk would break). This required fixing archive_entry_clone(), which now works and has a reasonably complete test case. May 10, 2007: Skeletal cpio implementation. May 06, 2007: libarchive 2.2.1 released May 06, 2007: Flesh out a lot more of test_entry.c so as to catch problems such as the device node breakage before releasing . May 05, 2007: Fix a bad bug introduced in 2.1.9 that broke device node entries in tar archives. May 03, 2007: Move 'struct stat' out of archive_entry core as well. This removes some portability headaches and fixes a bunch of corner cases that arise when manipulating archives on dissimilar systems. Apr 30, 2007: libarchive 2.1.10 released Apr 31, 2007: Minor code cleanup. Apr 24, 2007: libarchive 2.1.9 released Apr 24, 2007: Fix some recently-introduced problems with libraries (Just let automake handle it and it all works much better.) Finish isolating major()/minor()/makedev() in archive_entry.c. Apr 23, 2007: libarchive 2.1.8 released Apr 23, 2007: Minor fixes found from building on MacOS X Apr 22, 2007: libarchive 2.1.7 released Apr 22, 2007: Eliminated all uses of 'struct stat' from the format readers/writers. This should improve portability; 'struct stat' is now only used in archive_entry and in code that actually touches the disk. Apr 17, 2007: libarchive 2.1.6 released Libarchive now compiles and passes all tests on Interix. Apr 16, 2007: libarchive 2.1.5 released Apr 15, 2007: libarchive 2.1b2 released Apr 15, 2007: New libarchive_internals.3 documentation of internal APIs. Not complete, but should prove helpful. Apr 15, 2007: Experimental "read_compress_program" and "write_compress_program" for using libarchive with external compression. Not yet well tested, and likely has portability issues. Feedback appreciated. Apr 14, 2007: libarchive 2.0.31 released Apr 14, 2007: More fixes for Interix, more 'ar' work Apr 14, 2007: libarchive 2.0.30 released Apr 13, 2007: libarchive now enforces trailing '/' on dirs written to tar archives Apr 11, 2007: libarchive 2.0.29 released Apr 11, 2007: Make it easier to statically configure for different platforms. Apr 11, 2007: Updated config.guess, config.sub, libtool Apr 06, 2007: libarchive 2.0.28 released Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang. Apr 01, 2007: libarchive 2.0.27 released Mar 31, 2007: Several minor fixes from Colin Percival and Joerg Sonnenberger. Mar 12, 2007: libarchive 2.0.25 released Mar 12, 2007: Fix broken --unlink flag. Mar 11, 2007: libarchive 2.0.24 released Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry that refers to a non-existent user or group to not be restored correctly. The fix both makes the parser more tolerant (so that archives created with the buggy ACLs can be read now) and corrects the ACL formatter. Mar 10, 2007: More work on test portability to Linux. Mar 10, 2007: libarchive 2.0.22 released Mar 10, 2007: Header cleanups; added linux/fs.h, removed some unnecessary headers, added #include guards in bsdtar. If you see any obvious compile failures from this, let me know. Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough to enable as part of "make check", but getting better. Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when a header write fails in a way that only affects this item. Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN. Mar 07, 2007: libarchive 2.0.21 released Mar 07, 2007: Add some ACL tests (only for the system-independent portion of the ACL support for now). Mar 07, 2007: tar's ability to read ACLs off disk got turned off for FreeBSD; re-enable it. (ACL restores and libarchive support for storing/reading ACLs from pax archives was unaffected.) Mar 02, 2007: libarchive 2.0.20 released Mar 2, 2007: It's not perfect, but it's pretty good. Libarchive 2.0 is officially out of beta. Feb 28, 2007: libarchive 2.0b17 released Feb 27, 2007: Make the GID restore checks more robust by checking whether the current user has too few or too many privileges. Feb 26, 2007: libarchive 2.0b15 released Feb 26, 2007: Don't lose symlinks when extracting from ISOs. Thanks to Diego "Flameeyes" Pettenò for telling me about the broken testcase on Gentoo that (finally!) led me to the cause of this long-standing bug. Feb 26, 2007: libarchive 2.0b14 released Feb 26, 2007: Fix a broken test on platforms that lack lchmod(). Feb 25, 2007: libarchive 2.0b13 released Feb 25, 2007: Empty archives were being written as empty files, without a proper end-of-archive marker. Fixed. Feb 23, 2007: libarchive 2.0b12 released Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT and _EXTRACT_SECURE_SYMLINK. These checks used to be in bsdtar, but they belong down in libarchive where they can be used by other tools and where they can be better optimized. Feb 11, 2007: libarchive 2.0b11 released Feb 10, 2007: Fixed a bunch of errors in libarchive's handling of EXTRACT_PERM and EXTRACT_OWNER, especially relating to SUID and SGID bits. Jan 31, 2007: libarchive 2.0b9 released Jan 31, 2007: Added read support for "empty" archives as a distinct archive format. Bsdtar uses this to handle, e.g., "touch foo.tar; tar -rf foo.tar" Jan 22, 2007: libarchive 2.0b6 released Jan 22, 2007: archive_write_disk API is now in place. It provides a finer-grained interface than archive_read_extract. In particular, you can use it to create objects on disk without having an archive around (just feed it archive_entry objects describing what you want to create), you can override the uname/gname-to-uid/gid lookups (minitar uses this to avoid getpwXXX() and getgrXXX() bloat). Jan 09, 2007: libarchive 2.0a3 released Jan 9, 2007: archive_extract is now much better; it handles the most common cases with a minimal number of system calls. Some features still need a lot of testing, especially corner cases involving objects that already exist on disk. I expect the next round of API overhaul will simplify building test cases. Jan 9, 2007: a number of fixes thanks to Colin Percival, especially corrections to the skip() framework and handling of large files. Jan 9, 2007: Fixes for large ISOs. The code should correctly handle very large ISOs with entries up to 4G. Thanks to Robert Sciuk for pointing out these issues. Sep 05, 2006: libarchive 1.3.1 released Sep 5, 2006: Bump version to 1.3 for new I/O wrappers. Sep 4, 2006: New memory and FILE read/write wrappers. Sep 4, 2006: libarchive test harness is now minimally functional; it's located a few minor bugs in error-handling logic Aug 17, 2006: libarchive 1.2.54 released Aug 17, 2006: Outline ABI changes for libarchive 2.0; these are protected behind #ifdef's until I think I've found everything that needs to change. Aug 17, 2006: Fix error-handling in archive_read/write_close() They weren't returning any errors before. Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set Fixes a bug adding files when writing archive to pipe or when using archive_write_open() directly. Jul 2006: New "skip" handling improves performance extracting single files from large uncompressed archives. Mar 21, 2006: 1.2.52 released Mar 21, 2006: Fix -p on platforms that don't have platform-specific extended attribute code. Mar 20, 2006: Add NEWS file; fill in some older history from other files. I'll try to keep this file up-to-date from now on. OLDER NEWS SUMMARIES Mar 19, 2006: libarchive 1.2.51 released Mar 18, 2006: Many fixes to extended attribute support, including a redesign of the storage format to simplify debugging. Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth spending much time on. Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support for extended attributes (Currently Linux-only.). Mar 11, 2006: Reorganized distribution package: There is now one tar.gz file that builds both libarchive and bsdtar. Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write Pax attribute entry names. Nov 7, 2005: Experimental 'tp' format support in libarchive. Feedback appreciated; this is not enabled by archive_read_support_format_all() yet as I'm not quite content with the format detection heuristics. Nov 7, 2005: Some more portability improvements thanks to Darin Broady, minor bugfixes. Oct 12, 2005: Use GNU libtool to build shared libraries on many systems. Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs. Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows: http://gnuwin32.sourceforge.net/ Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson. -L/-h fix from Jaakko Heinonen. Mar 12, 2005: archive_read_extract can now handle very long pathnames (I've tested with pathnames up to 1MB). Mar 12, 2005: Marcus Geiger has written an article about libarchive http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/ including examples of using it from Objective-C. His MoinX http://moinx.antbear.org/ desktop Wiki uses libarchive for archiving and restoring Wiki pages. Jan 22, 2005: Preliminary ZIP extraction support, new directory-walking code for bsdtar. Jan 16, 2005: ISO9660 extraction code added; manpage corrections. May 22, 2004: Many gtar-compatible long options have been added; almost all FreeBSD ports extract correctly with bsdtar. May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar, and pdtar archives. Index: vendor/libarchive/dist/build/autogen.sh =================================================================== --- vendor/libarchive/dist/build/autogen.sh (revision 314564) +++ vendor/libarchive/dist/build/autogen.sh (revision 314565) @@ -1,67 +1,68 @@ #!/bin/sh PATH=/usr/local/gnu-autotools/bin/:$PATH export PATH # Start from one level above the build directory if [ -f version ]; then cd .. fi if [ \! -f build/version ]; then echo "Can't find source directory" exit 1 fi # BSD make's "OBJDIR" support freaks out the automake-generated # Makefile. Effectively disable it. export MAKEOBJDIRPREFIX=/junk # Start from the build directory, where the version file is located if [ -f build/version ]; then cd build fi if [ \! -f version ]; then echo "Can't find version file" exit 1 fi # Update the build number in the 'version' file. # Separate number from additional alpha/beta/etc marker MARKER=`cat version | sed 's/[0-9.]//g'` # Bump the number VN=`cat version | sed 's/[^0-9.]//g'` # Build out the string. VS="$(($VN/1000000)).$(( ($VN/1000)%1000 )).$(( $VN%1000 ))$MARKER" cd .. # Clean up the source dir as much as we can. /bin/sh build/clean.sh # Substitute the versions into Libarchive's archive.h and archive_entry.h perl -p -i -e "s/^(#define\tARCHIVE_VERSION_NUMBER).*/\$1 $VN/" libarchive/archive.h perl -p -i -e "s/^(#define\tARCHIVE_VERSION_NUMBER).*/\$1 $VN/" libarchive/archive_entry.h perl -p -i -e "s/^(#define\tARCHIVE_VERSION_ONLY_STRING).*/\$1 \"$VS\"/" libarchive/archive.h # Substitute versions into configure.ac as well perl -p -i -e 's/(m4_define\(\[LIBARCHIVE_VERSION_S\]),.*\)/$1,['"$VS"'])/' configure.ac perl -p -i -e 's/(m4_define\(\[LIBARCHIVE_VERSION_N\]),.*\)/$1,['"$VN"'])/' configure.ac # Remove developer CFLAGS if a release build is being made if [ -n "${MAKE_LIBARCHIVE_RELEASE}" ]; then perl -p -i -e "s/^(DEV_CFLAGS.*)/# \$1/" Makefile.am + perl -p -i -e 's/CMAKE_BUILD_TYPE "[A-Za-z]*"/CMAKE_BUILD_TYPE "Release"/' CMakeLists.txt fi set -xe aclocal -I build/autoconf # Note: --automake flag needed only for libtoolize from # libtool 1.5.x; in libtool 2.2.x it is a synonym for --quiet case `uname` in Darwin) glibtoolize --automake -c;; *) libtoolize --automake -c;; esac autoconf autoheader automake -a -c Index: vendor/libarchive/dist/build/cmake/config.h.in =================================================================== --- vendor/libarchive/dist/build/cmake/config.h.in (revision 314564) +++ vendor/libarchive/dist/build/cmake/config.h.in (revision 314565) @@ -1,1236 +1,1239 @@ /* config.h. Generated from build/cmake/config.h.in by cmake configure */ /* * Ensure we have C99-style int64_t, etc, all defined. */ /* First, we need to know if the system has already defined them. */ #cmakedefine HAVE_INT16_T #cmakedefine HAVE_INT32_T #cmakedefine HAVE_INT64_T #cmakedefine HAVE_INTMAX_T #cmakedefine HAVE_UINT8_T #cmakedefine HAVE_UINT16_T #cmakedefine HAVE_UINT32_T #cmakedefine HAVE_UINT64_T #cmakedefine HAVE_UINTMAX_T /* We might have the types we want under other spellings. */ #cmakedefine HAVE___INT64 #cmakedefine HAVE_U_INT64_T #cmakedefine HAVE_UNSIGNED___INT64 /* The sizes of various standard integer types. */ @SIZE_OF_SHORT_CODE@ @SIZE_OF_INT_CODE@ @SIZE_OF_LONG_CODE@ @SIZE_OF_LONG_LONG_CODE@ @SIZE_OF_UNSIGNED_SHORT_CODE@ @SIZE_OF_UNSIGNED_CODE@ @SIZE_OF_UNSIGNED_LONG_CODE@ @SIZE_OF_UNSIGNED_LONG_LONG_CODE@ /* * If we lack int64_t, define it to the first of __int64, int, long, and long long * that exists and is the right size. */ #if !defined(HAVE_INT64_T) && defined(HAVE___INT64) typedef __int64 int64_t; #define HAVE_INT64_T #endif #if !defined(HAVE_INT64_T) && SIZE_OF_INT == 8 typedef int int64_t; #define HAVE_INT64_T #endif #if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8 typedef long int64_t; #define HAVE_INT64_T #endif #if !defined(HAVE_INT64_T) && SIZE_OF_LONG_LONG == 8 typedef long long int64_t; #define HAVE_INT64_T #endif #if !defined(HAVE_INT64_T) #error No 64-bit integer type was found. #endif /* * Similarly for int32_t */ #if !defined(HAVE_INT32_T) && SIZE_OF_INT == 4 typedef int int32_t; #define HAVE_INT32_T #endif #if !defined(HAVE_INT32_T) && SIZE_OF_LONG == 4 typedef long int32_t; #define HAVE_INT32_T #endif #if !defined(HAVE_INT32_T) #error No 32-bit integer type was found. #endif /* * Similarly for int16_t */ #if !defined(HAVE_INT16_T) && SIZE_OF_INT == 2 typedef int int16_t; #define HAVE_INT16_T #endif #if !defined(HAVE_INT16_T) && SIZE_OF_SHORT == 2 typedef short int16_t; #define HAVE_INT16_T #endif #if !defined(HAVE_INT16_T) #error No 16-bit integer type was found. #endif /* * Similarly for uint64_t */ #if !defined(HAVE_UINT64_T) && defined(HAVE_UNSIGNED___INT64) typedef unsigned __int64 uint64_t; #define HAVE_UINT64_T #endif #if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED == 8 typedef unsigned uint64_t; #define HAVE_UINT64_T #endif #if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8 typedef unsigned long uint64_t; #define HAVE_UINT64_T #endif #if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG_LONG == 8 typedef unsigned long long uint64_t; #define HAVE_UINT64_T #endif #if !defined(HAVE_UINT64_T) #error No 64-bit unsigned integer type was found. #endif /* * Similarly for uint32_t */ #if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED == 4 typedef unsigned uint32_t; #define HAVE_UINT32_T #endif #if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED_LONG == 4 typedef unsigned long uint32_t; #define HAVE_UINT32_T #endif #if !defined(HAVE_UINT32_T) #error No 32-bit unsigned integer type was found. #endif /* * Similarly for uint16_t */ #if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED == 2 typedef unsigned uint16_t; #define HAVE_UINT16_T #endif #if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED_SHORT == 2 typedef unsigned short uint16_t; #define HAVE_UINT16_T #endif #if !defined(HAVE_UINT16_T) #error No 16-bit unsigned integer type was found. #endif /* * Similarly for uint8_t */ #if !defined(HAVE_UINT8_T) typedef unsigned char uint8_t; #define HAVE_UINT8_T #endif #if !defined(HAVE_UINT16_T) #error No 8-bit unsigned integer type was found. #endif /* Define intmax_t and uintmax_t if they are not already defined. */ #if !defined(HAVE_INTMAX_T) typedef int64_t intmax_t; #endif #if !defined(HAVE_UINTMAX_T) typedef uint64_t uintmax_t; #endif /* Define ZLIB_WINAPI if zlib was built on Visual Studio. */ #cmakedefine ZLIB_WINAPI 1 /* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_LIBC 1 /* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1 /* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_NETTLE 1 /* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_OPENSSL 1 /* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_WIN 1 /* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_LIBC 1 /* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_NETTLE 1 /* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_OPENSSL 1 /* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_LIBC 1 /* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1 /* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_NETTLE 1 /* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_OPENSSL 1 /* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_WIN 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC2 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC3 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_NETTLE 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_OPENSSL 1 /* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_WIN 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_LIBC 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_LIBC2 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_LIBC3 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_NETTLE 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_OPENSSL 1 /* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_WIN 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC2 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC3 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_NETTLE 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_OPENSSL 1 /* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_WIN 1 /* Version number of bsdcpio */ #cmakedefine BSDCPIO_VERSION_STRING "${BSDCPIO_VERSION_STRING}" /* Version number of bsdtar */ #cmakedefine BSDTAR_VERSION_STRING "${BSDTAR_VERSION_STRING}" /* Version number of bsdcat */ #cmakedefine BSDCAT_VERSION_STRING "${BSDCAT_VERSION_STRING}" /* Define to 1 if you have the `acl_create_entry' function. */ #cmakedefine HAVE_ACL_CREATE_ENTRY 1 /* Define to 1 if you have the `acl_get_fd_np' function. */ #cmakedefine HAVE_ACL_GET_FD_NP 1 /* Define to 1 if you have the `acl_get_link' function. */ #cmakedefine HAVE_ACL_GET_LINK 1 /* Define to 1 if you have the `acl_get_link_np' function. */ #cmakedefine HAVE_ACL_GET_LINK_NP 1 /* Define to 1 if you have the `acl_get_perm' function. */ #cmakedefine HAVE_ACL_GET_PERM 1 /* Define to 1 if you have the `acl_get_perm_np' function. */ #cmakedefine HAVE_ACL_GET_PERM_NP 1 /* Define to 1 if you have the `acl_init' function. */ #cmakedefine HAVE_ACL_INIT 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ACL_LIBACL_H 1 /* Define to 1 if the system has the type `acl_permset_t'. */ #cmakedefine HAVE_ACL_PERMSET_T 1 /* Define to 1 if you have the `acl_set_fd' function. */ #cmakedefine HAVE_ACL_SET_FD 1 /* Define to 1 if you have the `acl_set_fd_np' function. */ #cmakedefine HAVE_ACL_SET_FD_NP 1 /* Define to 1 if you have the `acl_set_file' function. */ #cmakedefine HAVE_ACL_SET_FILE 1 /* True for FreeBSD with NFSv4 ACL support */ #cmakedefine HAVE_ACL_TYPE_NFS4 1 /* True for MacOS ACL support */ #cmakedefine HAVE_ACL_TYPE_EXTENDED 1 /* True for systems with POSIX ACL support */ #cmakedefine HAVE_ACL_USER 1 /* Define to 1 if you have the `arc4random_buf' function. */ #cmakedefine HAVE_ARC4RANDOM_BUF 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ATTR_XATTR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_BCRYPT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_BSDXML_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_BZLIB_H 1 /* Define to 1 if you have the `chflags' function. */ #cmakedefine HAVE_CHFLAGS 1 /* Define to 1 if you have the `chown' function. */ #cmakedefine HAVE_CHOWN 1 /* Define to 1 if you have the `chroot' function. */ #cmakedefine HAVE_CHROOT 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_COPYFILE_H 1 /* Define to 1 if you have the `ctime_r' function. */ #cmakedefine HAVE_CTIME_R 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_CTYPE_H 1 /* Define to 1 if you have the `cygwin_conv_path' function. */ #cmakedefine HAVE_CYGWIN_CONV_PATH 1 /* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT32_MAX 1 /* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT32_MIN 1 /* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT64_MAX 1 /* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT64_MIN 1 /* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INTMAX_MAX 1 /* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INTMAX_MIN 1 /* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_SIZE_MAX 1 /* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_SSIZE_MAX 1 /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #cmakedefine HAVE_DECL_STRERROR_R 1 /* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_UINT32_MAX 1 /* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_UINT64_MAX 1 /* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_UINTMAX_MAX 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DIRECT_H 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_DIRENT_H 1 /* Define to 1 if you have the `dirfd' function. */ #cmakedefine HAVE_DIRFD 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #cmakedefine HAVE_DOPRNT 1 /* Define to 1 if nl_langinfo supports D_MD_ORDER */ #cmakedefine HAVE_D_MD_ORDER 1 /* A possible errno value for invalid file format errors */ #cmakedefine HAVE_EFTYPE 1 /* A possible errno value for invalid file format errors */ #cmakedefine HAVE_EILSEQ 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_EXPAT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_EXT2FS_EXT2_FS_H 1 /* Define to 1 if you have the `extattr_get_file' function. */ #cmakedefine HAVE_EXTATTR_GET_FILE 1 /* Define to 1 if you have the `extattr_list_file' function. */ #cmakedefine HAVE_EXTATTR_LIST_FILE 1 /* Define to 1 if you have the `extattr_set_fd' function. */ #cmakedefine HAVE_EXTATTR_SET_FD 1 /* Define to 1 if you have the `extattr_set_file' function. */ #cmakedefine HAVE_EXTATTR_SET_FILE 1 /* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */ #cmakedefine HAVE_DECL_EXTATTR_NAMESPACE_USER 1 /* Define to 1 if you have the `fchdir' function. */ #cmakedefine HAVE_FCHDIR 1 /* Define to 1 if you have the `fchflags' function. */ #cmakedefine HAVE_FCHFLAGS 1 /* Define to 1 if you have the `fchmod' function. */ #cmakedefine HAVE_FCHMOD 1 /* Define to 1 if you have the `fchown' function. */ #cmakedefine HAVE_FCHOWN 1 /* Define to 1 if you have the `fcntl' function. */ #cmakedefine HAVE_FCNTL 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H 1 /* Define to 1 if you have the `fdopendir' function. */ #cmakedefine HAVE_FDOPENDIR 1 /* Define to 1 if you have the `fgetea' function. */ #cmakedefine HAVE_FGETEA 1 /* Define to 1 if you have the `fgetxattr' function. */ #cmakedefine HAVE_FGETXATTR 1 /* Define to 1 if you have the `flistea' function. */ #cmakedefine HAVE_FLISTEA 1 /* Define to 1 if you have the `flistxattr' function. */ #cmakedefine HAVE_FLISTXATTR 1 /* Define to 1 if you have the `fork' function. */ #cmakedefine HAVE_FORK 1 /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ #cmakedefine HAVE_FSEEKO 1 /* Define to 1 if you have the `fsetea' function. */ #cmakedefine HAVE_FSETEA 1 /* Define to 1 if you have the `fsetxattr' function. */ #cmakedefine HAVE_FSETXATTR 1 /* Define to 1 if you have the `fstat' function. */ #cmakedefine HAVE_FSTAT 1 /* Define to 1 if you have the `fstatat' function. */ #cmakedefine HAVE_FSTATAT 1 /* Define to 1 if you have the `fstatfs' function. */ #cmakedefine HAVE_FSTATFS 1 /* Define to 1 if you have the `fstatvfs' function. */ #cmakedefine HAVE_FSTATVFS 1 /* Define to 1 if you have the `ftruncate' function. */ #cmakedefine HAVE_FTRUNCATE 1 /* Define to 1 if you have the `futimens' function. */ #cmakedefine HAVE_FUTIMENS 1 /* Define to 1 if you have the `futimes' function. */ #cmakedefine HAVE_FUTIMES 1 /* Define to 1 if you have the `futimesat' function. */ #cmakedefine HAVE_FUTIMESAT 1 /* Define to 1 if you have the `getea' function. */ #cmakedefine HAVE_GETEA 1 /* Define to 1 if you have the `geteuid' function. */ #cmakedefine HAVE_GETEUID 1 /* Define to 1 if you have the `getgrgid_r' function. */ #cmakedefine HAVE_GETGRGID_R 1 /* Define to 1 if you have the `getgrnam_r' function. */ #cmakedefine HAVE_GETGRNAM_R 1 /* Define to 1 if you have the `getpid' function. */ #cmakedefine HAVE_GETPID 1 /* Define to 1 if you have the `getpwnam_r' function. */ #cmakedefine HAVE_GETPWNAM_R 1 /* Define to 1 if you have the `getpwuid_r' function. */ #cmakedefine HAVE_GETPWUID_R 1 /* Define to 1 if you have the `getvfsbyname' function. */ #cmakedefine HAVE_GETVFSBYNAME 1 /* Define to 1 if you have the `getxattr' function. */ #cmakedefine HAVE_GETXATTR 1 /* Define to 1 if you have the `gmtime_r' function. */ #cmakedefine HAVE_GMTIME_R 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_GRP_H 1 /* Define to 1 if you have the `iconv' function. */ #cmakedefine HAVE_ICONV 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ICONV_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IO_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LANGINFO_H 1 /* Define to 1 if you have the `lchflags' function. */ #cmakedefine HAVE_LCHFLAGS 1 /* Define to 1 if you have the `lchmod' function. */ #cmakedefine HAVE_LCHMOD 1 /* Define to 1 if you have the `lchown' function. */ #cmakedefine HAVE_LCHOWN 1 /* Define to 1 if you have the `lgetea' function. */ #cmakedefine HAVE_LGETEA 1 /* Define to 1 if you have the `lgetxattr' function. */ #cmakedefine HAVE_LGETXATTR 1 /* Define to 1 if you have the `acl' library (-lacl). */ #cmakedefine HAVE_LIBACL 1 /* Define to 1 if you have the `attr' library (-lattr). */ #cmakedefine HAVE_LIBATTR 1 /* Define to 1 if you have the `bsdxml' library (-lbsdxml). */ #cmakedefine HAVE_LIBBSDXML 1 /* Define to 1 if you have the `bz2' library (-lbz2). */ #cmakedefine HAVE_LIBBZ2 1 /* Define to 1 if you have the `charset' library (-lcharset). */ #cmakedefine HAVE_LIBCHARSET 1 /* Define to 1 if you have the `crypto' library (-lcrypto). */ #cmakedefine HAVE_LIBCRYPTO 1 /* Define to 1 if you have the `expat' library (-lexpat). */ #cmakedefine HAVE_LIBEXPAT 1 /* Define to 1 if you have the `gcc' library (-lgcc). */ #cmakedefine HAVE_LIBGCC 1 /* Define to 1 if you have the `lz4' library (-llz4). */ #cmakedefine HAVE_LIBLZ4 1 /* Define to 1 if you have the `lzma' library (-llzma). */ #cmakedefine HAVE_LIBLZMA 1 /* Define to 1 if you have the `lzmadec' library (-llzmadec). */ #cmakedefine HAVE_LIBLZMADEC 1 /* Define to 1 if you have the `lzo2' library (-llzo2). */ #cmakedefine HAVE_LIBLZO2 1 /* Define to 1 if you have the `nettle' library (-lnettle). */ #cmakedefine HAVE_LIBNETTLE 1 /* Define to 1 if you have the `pcre' library (-lpcre). */ #cmakedefine HAVE_LIBPCRE 1 /* Define to 1 if you have the `pcreposix' library (-lpcreposix). */ #cmakedefine HAVE_LIBPCREPOSIX 1 /* Define to 1 if you have the `xml2' library (-lxml2). */ #cmakedefine HAVE_LIBXML2 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIBXML_XMLREADER_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIBXML_XMLWRITER_H 1 /* Define to 1 if you have the `z' library (-lz). */ #cmakedefine HAVE_LIBZ 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H 1 /* Define to 1 if you have the `link' function. */ #cmakedefine HAVE_LINK 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_FIEMAP_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_FS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_MAGIC_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_TYPES_H 1 /* Define to 1 if you have the `listea' function. */ #cmakedefine HAVE_LISTEA 1 /* Define to 1 if you have the `listxattr' function. */ #cmakedefine HAVE_LISTXATTR 1 /* Define to 1 if you have the `llistea' function. */ #cmakedefine HAVE_LLISTEA 1 /* Define to 1 if you have the `llistxattr' function. */ #cmakedefine HAVE_LLISTXATTR 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALCHARSET_H 1 /* Define to 1 if you have the `locale_charset' function. */ #cmakedefine HAVE_LOCALE_CHARSET 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALE_H 1 /* Define to 1 if you have the `localtime_r' function. */ #cmakedefine HAVE_LOCALTIME_R 1 /* Define to 1 if the system has the type `long long int'. */ #cmakedefine HAVE_LONG_LONG_INT 1 /* Define to 1 if you have the `lsetea' function. */ #cmakedefine HAVE_LSETEA 1 /* Define to 1 if you have the `lsetxattr' function. */ #cmakedefine HAVE_LSETXATTR 1 /* Define to 1 if you have the `lstat' function. */ #cmakedefine HAVE_LSTAT 1 /* Define to 1 if `lstat' has the bug that it succeeds when given the zero-length file name argument. */ #cmakedefine HAVE_LSTAT_EMPTY_STRING_BUG 1 /* Define to 1 if you have the `lutimes' function. */ #cmakedefine HAVE_LUTIMES 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZ4HC_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZ4_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZMADEC_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZMA_H 1 /* Define to 1 if you have a working `lzma_stream_encoder_mt' function. */ #cmakedefine HAVE_LZMA_STREAM_ENCODER_MT 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZO_LZO1X_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZO_LZOCONF_H 1 /* Define to 1 if you have the `mbrtowc' function. */ #cmakedefine HAVE_MBRTOWC 1 /* Define to 1 if you have the `memmove' function. */ #cmakedefine HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H 1 /* Define to 1 if you have the `mkdir' function. */ #cmakedefine HAVE_MKDIR 1 /* Define to 1 if you have the `mkfifo' function. */ #cmakedefine HAVE_MKFIFO 1 /* Define to 1 if you have the `mknod' function. */ #cmakedefine HAVE_MKNOD 1 /* Define to 1 if you have the `mkstemp' function. */ #cmakedefine HAVE_MKSTEMP 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_NDIR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_AES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_HMAC_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_MD5_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_PBKDF2_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_RIPEMD160_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_SHA_H 1 /* Define to 1 if you have the `nl_langinfo' function. */ #cmakedefine HAVE_NL_LANGINFO 1 /* Define to 1 if you have the `openat' function. */ #cmakedefine HAVE_OPENAT 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PATHS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PCREPOSIX_H 1 /* Define to 1 if you have the `pipe' function. */ #cmakedefine HAVE_PIPE 1 /* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */ #cmakedefine HAVE_PKCS5_PBKDF2_HMAC_SHA1 1 /* Define to 1 if you have the `poll' function. */ #cmakedefine HAVE_POLL 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_POLL_H 1 /* Define to 1 if you have the `posix_spawnp' function. */ #cmakedefine HAVE_POSIX_SPAWNP 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PROCESS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PTHREAD_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PWD_H 1 /* Define to 1 if you have the `readdir_r' function. */ #cmakedefine HAVE_READDIR_R 1 /* Define to 1 if you have the `readlink' function. */ #cmakedefine HAVE_READLINK 1 /* Define to 1 if you have the `readlinkat' function. */ #cmakedefine HAVE_READLINKAT 1 /* Define to 1 if you have the `readpassphrase' function. */ #cmakedefine HAVE_READPASSPHRASE 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_READPASSPHRASE_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_REGEX_H 1 /* Define to 1 if you have the `select' function. */ #cmakedefine HAVE_SELECT 1 /* Define to 1 if you have the `setenv' function. */ #cmakedefine HAVE_SETENV 1 /* Define to 1 if you have the `setlocale' function. */ #cmakedefine HAVE_SETLOCALE 1 /* Define to 1 if you have the `sigaction' function. */ #cmakedefine HAVE_SIGACTION 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SIGNAL_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SPAWN_H 1 /* Define to 1 if you have the `statfs' function. */ #cmakedefine HAVE_STATFS 1 /* Define to 1 if you have the `statvfs' function. */ #cmakedefine HAVE_STATVFS 1 /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ #cmakedefine HAVE_STAT_EMPTY_STRING_BUG 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDARG_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H 1 /* Define to 1 if you have the `strchr' function. */ #cmakedefine HAVE_STRCHR 1 /* Define to 1 if you have the `strdup' function. */ #cmakedefine HAVE_STRDUP 1 /* Define to 1 if you have the `strerror' function. */ #cmakedefine HAVE_STRERROR 1 /* Define to 1 if you have the `strerror_r' function. */ #cmakedefine HAVE_STRERROR_R 1 /* Define to 1 if you have the `strftime' function. */ #cmakedefine HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H 1 /* Define to 1 if you have the `strrchr' function. */ #cmakedefine HAVE_STRRCHR 1 /* Define to 1 if `f_namemax' is a member of `struct statfs'. */ #cmakedefine HAVE_STRUCT_STATFS_F_NAMEMAX 1 /* Define to 1 if `f_iosize' is a member of `struct statvfs'. */ #cmakedefine HAVE_STRUCT_STATVFS_F_IOSIZE 1 /* Define to 1 if `st_birthtime' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIME 1 /* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 /* Define to 1 if `st_blksize' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_BLKSIZE 1 /* Define to 1 if `st_flags' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_FLAGS 1 /* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 /* Define to 1 if `st_mtime_n' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIME_N 1 /* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIME_USEC 1 /* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 /* Define to 1 if `st_umtime' is a member of `struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_UMTIME 1 /* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */ #cmakedefine HAVE_STRUCT_TM_TM_GMTOFF 1 /* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */ #cmakedefine HAVE_STRUCT_TM___TM_GMTOFF 1 /* Define to 1 if you have `struct vfsconf'. */ #cmakedefine HAVE_STRUCT_VFSCONF 1 /* Define to 1 if you have `struct xvfsconf'. */ #cmakedefine HAVE_STRUCT_XVFSCONF 1 /* Define to 1 if you have the `symlink' function. */ #cmakedefine HAVE_SYMLINK 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_ACL_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_CDEFS_H 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_SYS_DIR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_EA_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_EXTATTR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_IOCTL_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_MKDEV_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_MOUNT_H 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_SYS_NDIR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_PARAM_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_POLL_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SELECT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STATFS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STATVFS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_UTIME_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_UTSNAME_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_VFS_H 1 /* Define to 1 if you have that is POSIX.1 compatible. */ #cmakedefine HAVE_SYS_WAIT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_XATTR_H 1 /* Define to 1 if you have the `timegm' function. */ #cmakedefine HAVE_TIMEGM 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_TIME_H 1 /* Define to 1 if you have the `tzset' function. */ #cmakedefine HAVE_TZSET 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 /* Define to 1 if you have the `unsetenv' function. */ #cmakedefine HAVE_UNSETENV 1 /* Define to 1 if the system has the type `unsigned long long'. */ #cmakedefine HAVE_UNSIGNED_LONG_LONG 1 /* Define to 1 if the system has the type `unsigned long long int'. */ #cmakedefine HAVE_UNSIGNED_LONG_LONG_INT 1 /* Define to 1 if you have the `utime' function. */ #cmakedefine HAVE_UTIME 1 /* Define to 1 if you have the `utimensat' function. */ #cmakedefine HAVE_UTIMENSAT 1 /* Define to 1 if you have the `utimes' function. */ #cmakedefine HAVE_UTIMES 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UTIME_H 1 /* Define to 1 if you have the `vfork' function. */ #cmakedefine HAVE_VFORK 1 /* Define to 1 if you have the `vprintf' function. */ #cmakedefine HAVE_VPRINTF 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WCHAR_H 1 /* Define to 1 if the system has the type `wchar_t'. */ #cmakedefine HAVE_WCHAR_T 1 /* Define to 1 if you have the `wcrtomb' function. */ #cmakedefine HAVE_WCRTOMB 1 /* Define to 1 if you have the `wcscmp' function. */ #cmakedefine HAVE_WCSCMP 1 /* Define to 1 if you have the `wcscpy' function. */ #cmakedefine HAVE_WCSCPY 1 /* Define to 1 if you have the `wcslen' function. */ #cmakedefine HAVE_WCSLEN 1 /* Define to 1 if you have the `wctomb' function. */ #cmakedefine HAVE_WCTOMB 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WCTYPE_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINCRYPT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINDOWS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINIOCTL_H 1 /* Define to 1 if you have _CrtSetReportMode in */ #cmakedefine HAVE__CrtSetReportMode 1 /* Define to 1 if you have the `wmemcmp' function. */ #cmakedefine HAVE_WMEMCMP 1 /* Define to 1 if you have the `wmemcpy' function. */ #cmakedefine HAVE_WMEMCPY 1 /* Define to 1 if you have the `wmemmove' function. */ #cmakedefine HAVE_WMEMMOVE 1 /* Define to 1 if you have a working EXT2_IOC_GETFLAGS */ #cmakedefine HAVE_WORKING_EXT2_IOC_GETFLAGS 1 +/* Define to 1 if you have a working FS_IOC_GETFLAGS */ +#cmakedefine HAVE_WORKING_FS_IOC_GETFLAGS 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ZLIB_H 1 /* Define to 1 if you have the `_ctime64_s' function. */ #cmakedefine HAVE__CTIME64_S 1 /* Define to 1 if you have the `_fseeki64' function. */ #cmakedefine HAVE__FSEEKI64 1 /* Define to 1 if you have the `_get_timezone' function. */ #cmakedefine HAVE__GET_TIMEZONE 1 /* Define to 1 if you have the `_localtime64_s' function. */ #cmakedefine HAVE__LOCALTIME64_S 1 /* Define to 1 if you have the `_mkgmtime64' function. */ #cmakedefine HAVE__MKGMTIME64 1 /* Define as const if the declaration of iconv() needs const. */ #define ICONV_CONST ${ICONV_CONST} /* Version number of libarchive as a single integer */ #cmakedefine LIBARCHIVE_VERSION_NUMBER "${LIBARCHIVE_VERSION_NUMBER}" /* Version number of libarchive */ #cmakedefine LIBARCHIVE_VERSION_STRING "${LIBARCHIVE_VERSION_STRING}" /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ #cmakedefine LSTAT_FOLLOWS_SLASHED_SYMLINK 1 /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #cmakedefine MAJOR_IN_MKDEV 1 /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #cmakedefine MAJOR_IN_SYSMACROS 1 /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #cmakedefine NO_MINUS_C_MINUS_O 1 /* The size of `wchar_t', as computed by sizeof. */ #cmakedefine SIZEOF_WCHAR_T ${SIZEOF_WCHAR_T} /* Define to 1 if strerror_r returns char *. */ #cmakedefine STRERROR_R_CHAR_P 1 /* Define to 1 if you can safely include both and . */ #cmakedefine TIME_WITH_SYS_TIME 1 /* * Some platform requires a macro to use extension functions. */ #cmakedefine SAFE_TO_DEFINE_EXTENSIONS 1 #ifdef SAFE_TO_DEFINE_EXTENSIONS /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # define _ALL_SOURCE 1 #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # define _POSIX_PTHREAD_SEMANTICS 1 #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # define _TANDEM_SOURCE 1 #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 #endif #endif /* SAFE_TO_DEFINE_EXTENSIONS */ /* Version number of package */ #cmakedefine VERSION "${VERSION}" /* Number of bits in a file offset, on hosts where this is settable. */ #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ #cmakedefine _LARGEFILE_SOURCE 1 /* Define for large files, on AIX-style hosts. */ #cmakedefine _LARGE_FILES ${_LARGE_FILES} /* Define to control Windows SDK version */ #ifndef NTDDI_VERSION #cmakedefine NTDDI_VERSION ${NTDDI_VERSION} #endif // NTDDI_VERSION #ifndef _WIN32_WINNT #cmakedefine _WIN32_WINNT ${_WIN32_WINNT} #endif // _WIN32_WINNT #ifndef WINVER #cmakedefine WINVER ${WINVER} #endif // WINVER /* Define to empty if `const' does not conform to ANSI C. */ #cmakedefine const ${const} /* Define to `int' if doesn't define. */ #cmakedefine gid_t ${gid_t} /* Define to `unsigned long' if does not define. */ #cmakedefine id_t ${id_t} /* Define to `int' if does not define. */ #cmakedefine mode_t ${mode_t} /* Define to `long long' if does not define. */ #cmakedefine off_t ${off_t} /* Define to `int' if doesn't define. */ #cmakedefine pid_t ${pid_t} /* Define to `unsigned int' if does not define. */ #cmakedefine size_t ${size_t} /* Define to `int' if does not define. */ #cmakedefine ssize_t ${ssize_t} /* Define to `int' if doesn't define. */ #cmakedefine uid_t ${uid_t} /* Define to `int' if does not define. */ #cmakedefine intptr_t ${intptr_t} /* Define to `unsigned int' if does not define. */ #cmakedefine uintptr_t ${uintptr_t} Index: vendor/libarchive/dist/build/version =================================================================== --- vendor/libarchive/dist/build/version (revision 314564) +++ vendor/libarchive/dist/build/version (revision 314565) @@ -1 +1 @@ -3002002 +3003001 Index: vendor/libarchive/dist/cat/test/main.c =================================================================== --- vendor/libarchive/dist/cat/test/main.c (revision 314564) +++ vendor/libarchive/dist/cat/test/main.c (nonexistent) @@ -1,3072 +0,0 @@ -/* - * 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" -#include "test_utils.h" -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_ICONV_H -#include -#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 -#include -#include -#ifdef HAVE_SIGNAL_H -#include -#endif -#include -#include - -/* - * This same file is used pretty much verbatim for all test harnesses. - * - * The next few lines are the only differences. - * TODO: Move this into a separate configuration header, have all test - * suites share one copy of this file. - */ -#define KNOWNREF "test_expand.Z.uu" -#define ENVBASE "BSDCAT" /* Prefix for environment variables. */ -#define PROGRAM "bsdcat" /* Name of program being tested. */ -#define PROGRAM_ALIAS "cat" /* Generic alias for program */ -#undef LIBRARY /* Not testing a library. */ -#undef EXTRA_DUMP /* How to dump extra data */ -#undef EXTRA_ERRNO /* How to dump errno */ -/* How to generate extra version info. */ -#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") - -/* - * - * Windows support routines - * - * Note: Configuration is a tricky issue. Using HAVE_* feature macros - * in the test harness is dangerous because they cover up - * configuration errors. The classic example of this is omitting a - * configure check. If libarchive and libarchive_test both look for - * the same feature macro, such errors are hard to detect. Platform - * macros (e.g., _WIN32 or __GNUC__) are a little better, but can - * easily lead to very messy code. It's best to limit yourself - * to only the most generic programming techniques in the test harness - * and thus avoid conditionals altogether. Where that's not possible, - * try to minimize conditionals by grouping platform-specific tests in - * one place (e.g., test_acl_freebsd) or by adding new assert() - * functions (e.g., assertMakeHardlink()) to cover up platform - * differences. Platform-specific coding in libarchive_test is often - * a symptom that some capability is missing from libarchive itself. - */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#include -#include -#include -#ifndef F_OK -#define F_OK (0) -#endif -#ifndef S_ISDIR -#define S_ISDIR(m) ((m) & _S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(m) ((m) & _S_IFREG) -#endif -#if !defined(__BORLANDC__) -#define access _access -#undef chdir -#define chdir _chdir -#endif -#ifndef fileno -#define fileno _fileno -#endif -/*#define fstat _fstat64*/ -#if !defined(__BORLANDC__) -#define getcwd _getcwd -#endif -#define lstat stat -/*#define lstat _stat64*/ -/*#define stat _stat64*/ -#define rmdir _rmdir -#if !defined(__BORLANDC__) -#define strdup _strdup -#define umask _umask -#endif -#define int64_t __int64 -#endif - -#if defined(HAVE__CrtSetReportMode) -# include -#endif - -mode_t umasked(mode_t expected_mode) -{ - mode_t mode = umask(0); - umask(mode); - return expected_mode & ~mode; -} - -/* Path to working directory for current test */ -const char *testworkdir; -#ifdef PROGRAM -/* Pathname of exe to be tested. */ -const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -const char *testprog; -#endif - -#if defined(_WIN32) && !defined(__CYGWIN__) -static void *GetFunctionKernel32(const char *); -static int my_CreateSymbolicLinkA(const char *, const char *, int); -static int my_CreateHardLinkA(const char *, const char *); -static int my_GetFileInformationByName(const char *, - BY_HANDLE_FILE_INFORMATION *); - -static void * -GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary("kernel32.dll"); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} - -static int -my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateSymbolicLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, flags); -} - -static int -my_CreateHardLinkA(const char *linkname, const char *target) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateHardLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, NULL); -} - -static int -my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) -{ - HANDLE h; - int r; - - memset(bhfi, 0, sizeof(*bhfi)); - h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) - return (0); - r = GetFileInformationByHandle(h, bhfi); - CloseHandle(h); - return (r); -} -#endif - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) -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 - -/* - * - * OPTIONS FLAGS - * - */ - -/* Enable core dump on failure. */ -static int dump_on_failure = 0; -/* Default is to remove temp dirs and log data for successful tests. */ -static int keep_temp_files = 0; -/* Default is to run the specified tests once and report errors. */ -static int until_failure = 0; -/* Default is to just report pass/fail for each test. */ -static int verbosity = 0; -#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ -#define VERBOSITY_PASSFAIL 0 /* Default */ -#define VERBOSITY_LIGHT_REPORT 1 /* -v */ -#define VERBOSITY_FULL 2 /* -vv */ -/* A few places generate even more output for verbosity > VERBOSITY_FULL, - * mostly for debugging the test harness itself. */ -/* Cumulative count of assertion failures. */ -static int failures = 0; -/* Cumulative count of reported skips. */ -static int skips = 0; -/* Cumulative count of assertions checked. */ -static int assertions = 0; - -/* Directory where uuencoded reference files can be found. */ -static const char *refdir; - -/* - * Report log information selectively to console and/or disk log. - */ -static int log_console = 0; -static FILE *logfile; -static void -vlogprintf(const char *fmt, va_list ap) -{ -#ifdef va_copy - va_list lfap; - va_copy(lfap, ap); -#endif - if (log_console) - vfprintf(stdout, fmt, ap); - if (logfile != NULL) -#ifdef va_copy - vfprintf(logfile, fmt, lfap); - va_end(lfap); -#else - vfprintf(logfile, fmt, ap); -#endif -} - -static void -logprintf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vlogprintf(fmt, ap); - va_end(ap); -} - -/* Set up a message to display only if next assertion fails. */ -static char msgbuff[4096]; -static const char *msg, *nextmsg; -void -failure(const char *fmt, ...) -{ - va_list ap; - if (fmt == NULL) { - nextmsg = NULL; - } else { - va_start(ap, fmt); - vsprintf(msgbuff, fmt, ap); - va_end(ap); - nextmsg = msgbuff; - } -} - -/* - * Copy arguments into file-local variables. - * This was added to permit vararg assert() functions without needing - * variadic wrapper macros. Turns out that the vararg capability is almost - * never used, so almost all of the vararg assertions can be simplified - * by removing the vararg capability and reworking the wrapper macro to - * pass __FILE__, __LINE__ directly into the function instead of using - * this hook. I suspect this machinery is used so rarely that we - * would be better off just removing it entirely. That would simplify - * the code here noticeably. - */ -static const char *skipping_filename; -static int skipping_line; -void skipping_setup(const char *filename, int line) -{ - skipping_filename = filename; - skipping_line = line; -} - -/* Called at the beginning of each assert() function. */ -static void -assertion_count(const char *file, int line) -{ - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - ++assertions; - /* Proper handling of "failure()" message. */ - msg = nextmsg; - nextmsg = NULL; - /* Uncomment to print file:line after every assertion. - * Verbose, but occasionally useful in tracking down crashes. */ - /* printf("Checked %s:%d\n", file, line); */ -} - -/* - * For each test source file, we remember how many times each - * assertion was reported. Cleared before each new test, - * used by test_summarize(). - */ -static struct line { - int count; - int skip; -} failed_lines[10000]; -const char *failed_filename; - -/* Count this failure, setup up log destination and handle initial report. */ -static void -failure_start(const char *filename, int line, const char *fmt, ...) -{ - va_list ap; - - /* Record another failure for this line. */ - ++failures; - failed_filename = filename; - failed_lines[line].count++; - - /* Determine whether to log header to console. */ - switch (verbosity) { - case VERBOSITY_LIGHT_REPORT: - log_console = (failed_lines[line].count < 2); - break; - default: - log_console = (verbosity >= VERBOSITY_FULL); - } - - /* Log file:line header for this failure */ - va_start(ap, fmt); -#if _MSC_VER - logprintf("%s(%d): ", filename, line); -#else - logprintf("%s:%d: ", filename, line); -#endif - vlogprintf(fmt, ap); - va_end(ap); - logprintf("\n"); - - if (msg != NULL && msg[0] != '\0') { - logprintf(" Description: %s\n", msg); - msg = NULL; - } - - /* Determine whether to log details to console. */ - if (verbosity == VERBOSITY_LIGHT_REPORT) - log_console = 0; -} - -/* Complete reporting of failed tests. */ -/* - * The 'extra' hook here is used by libarchive to include libarchive - * error messages with assertion failures. It could also be used - * to add strerror() output, for example. Just define the EXTRA_DUMP() - * macro appropriately. - */ -static void -failure_finish(void *extra) -{ - (void)extra; /* UNUSED (maybe) */ -#ifdef EXTRA_DUMP - if (extra != NULL) { - logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); - logprintf(" detail: %s\n", EXTRA_DUMP(extra)); - } -#endif - - if (dump_on_failure) { - fprintf(stderr, - " *** forcing core dump so failure can be debugged ***\n"); - abort(); - } -} - -/* Inform user that we're skipping some checks. */ -void -test_skipping(const char *fmt, ...) -{ - char buff[1024]; - va_list ap; - - va_start(ap, fmt); - vsprintf(buff, fmt, ap); - va_end(ap); - /* Use failure() message if set. */ - msg = nextmsg; - nextmsg = NULL; - /* failure_start() isn't quite right, but is awfully convenient. */ - failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); - --failures; /* Undo failures++ in failure_start() */ - /* Don't failure_finish() here. */ - /* Mark as skip, so doesn't count as failed test. */ - failed_lines[skipping_line].skip = 1; - ++skips; -} - -/* - * - * ASSERTIONS - * - */ - -/* Generic assert() just displays the failed condition. */ -int -assertion_assert(const char *file, int line, int value, - const char *condition, void *extra) -{ - assertion_count(file, line); - if (!value) { - failure_start(file, line, "Assertion failed: %s", condition); - failure_finish(extra); - } - return (value); -} - -/* chdir() and report any errors */ -int -assertion_chdir(const char *file, int line, const char *pathname) -{ - assertion_count(file, line); - if (chdir(pathname) == 0) - return (1); - failure_start(file, line, "chdir(\"%s\")", pathname); - failure_finish(NULL); - return (0); - -} - -/* Verify two integers are equal. */ -int -assertion_equal_int(const char *file, int line, - long long v1, const char *e1, long long v2, const char *e2, void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); - failure_finish(extra); - return (0); -} - -/* - * Utility to convert a single UTF-8 sequence. - */ -static int -_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) -{ - static const char utf8_count[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ - }; - int ch; - int cnt; - uint32_t wc; - - *pwc = 0; - - /* Sanity check. */ - if (n == 0) - return (0); - /* - * 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. */ - cnt = utf8_count[ch]; - - /* Invalid sequence or there are not plenty bytes. */ - if (n < (size_t)cnt) - return (-1); - - /* Make a Unicode code point from a single UTF-8 sequence. */ - switch (cnt) { - case 1: /* 1 byte sequence. */ - *pwc = ch & 0x7f; - return (cnt); - case 2: /* 2 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); - return (cnt); - case 3: /* 3 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - if ((s[2] & 0xc0) != 0x80) return (-1); - wc = ((ch & 0x0f) << 12) - | ((s[1] & 0x3f) << 6) - | (s[2] & 0x3f); - if (wc < 0x800) - return (-1);/* Overlong sequence. */ - break; - case 4: /* 4 bytes sequence. */ - 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); - wc = ((ch & 0x07) << 18) - | ((s[1] & 0x3f) << 12) - | ((s[2] & 0x3f) << 6) - | (s[3] & 0x3f); - if (wc < 0x10000) - return (-1);/* Overlong sequence. */ - break; - default: - return (-1); - } - - /* The code point larger than 0x10FFFF is not legal - * Unicode values. */ - if (wc > 0x10FFFF) - return (-1); - /* Correctly gets a Unicode, returns used bytes. */ - *pwc = wc; - return (cnt); -} - -static void strdump(const char *e, const char *p, int ewidth, int utf8) -{ - const char *q = p; - - logprintf(" %*s = ", ewidth, e); - if (p == NULL) { - logprintf("NULL\n"); - return; - } - logprintf("\""); - while (*p != '\0') { - unsigned int c = 0xff & *p++; - switch (c) { - case '\a': logprintf("\\a"); break; - case '\b': logprintf("\\b"); break; - case '\n': logprintf("\\n"); break; - case '\r': logprintf("\\r"); break; - default: - if (c >= 32 && c < 127) - logprintf("%c", c); - else - logprintf("\\x%02X", c); - } - } - logprintf("\""); - logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); - - /* - * If the current string is UTF-8, dump its code points. - */ - if (utf8) { - size_t len; - uint32_t uc; - int n; - int cnt = 0; - - p = q; - len = strlen(p); - logprintf(" ["); - while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { - if (p != q) - logprintf(" "); - logprintf("%04X", uc); - p += n; - len -= n; - cnt++; - } - logprintf("]"); - logprintf(" (count %d", cnt); - if (n < 0) { - logprintf(",unknown %d bytes", len); - } - logprintf(")"); - - } - logprintf("\n"); -} - -/* Verify two strings are equal, dump them if not. */ -int -assertion_equal_string(const char *file, int line, - const char *v1, const char *e1, - const char *v2, const char *e2, - void *extra, int utf8) -{ - int l1, l2; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - l1 = (int)strlen(e1); - l2 = (int)strlen(e2); - if (l1 < l2) - l1 = l2; - strdump(e1, v1, l1, utf8); - strdump(e2, v2, l1, utf8); - failure_finish(extra); - return (0); -} - -static void -wcsdump(const char *e, const wchar_t *w) -{ - logprintf(" %s = ", e); - if (w == NULL) { - logprintf("(null)"); - return; - } - logprintf("\""); - while (*w != L'\0') { - unsigned int c = *w++; - if (c >= 32 && c < 127) - logprintf("%c", c); - else if (c < 256) - logprintf("\\x%02X", c); - else if (c < 0x10000) - logprintf("\\u%04X", c); - else - logprintf("\\U%08X", c); - } - logprintf("\"\n"); -} - -#ifndef HAVE_WCSCMP -static int -wcscmp(const wchar_t *s1, const wchar_t *s2) -{ - - while (*s1 == *s2++) { - if (*s1++ == L'\0') - return 0; - } - if (*s1 > *--s2) - return 1; - else - return -1; -} -#endif - -/* Verify that two wide strings are equal, dump them if not. */ -int -assertion_equal_wstring(const char *file, int line, - const wchar_t *v1, const char *e1, - const wchar_t *v2, const char *e2, - void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - wcsdump(e1, v1); - wcsdump(e2, v2); - failure_finish(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; - - if (p == NULL) { - logprintf("(null)\n"); - return; - } - for(i=0; i < l; i+=16) { - logprintf("%04x", (unsigned)(i + offset)); - sep = ' '; - for (j = 0; j < 16 && i + j < l; j++) { - if (ref != NULL && p[i + j] != ref[i + j]) - sep = '_'; - logprintf("%c%02x", sep, 0xff & (int)p[i+j]); - if (ref != NULL && p[i + j] == ref[i + j]) - sep = ' '; - } - for (; j < 16; j++) { - logprintf("%c ", sep); - sep = ' '; - } - logprintf("%c", sep); - for (j=0; j < 16 && i + j < l; j++) { - int c = p[i + j]; - if (c >= ' ' && c <= 126) - logprintf("%c", c); - else - logprintf("."); - } - logprintf("\n"); - } -} - -/* Verify that two blocks of memory are the same, display the first - * block of differences if they're not. */ -int -assertion_equal_mem(const char *file, int line, - const void *_v1, const char *e1, - const void *_v2, const char *e2, - size_t l, const char *ld, void *extra) -{ - const char *v1 = (const char *)_v1; - const char *v2 = (const char *)_v2; - size_t offset; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) - return (1); - if (v1 == NULL || v2 == NULL) - return (0); - - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" size %s = %d\n", ld, (int)l); - /* Dump 48 bytes (3 lines) so that the first difference is - * in the second line. */ - offset = 0; - while (l > 64 && memcmp(v1, v2, 32) == 0) { - /* Two lines agree, so step forward one line. */ - v1 += 16; - v2 += 16; - l -= 16; - offset += 16; - } - logprintf(" Dump of %s\n", e1); - hexdump(v1, v2, l < 128 ? l : 128, offset); - logprintf(" Dump of %s\n", e2); - hexdump(v2, v1, l < 128 ? l : 128, offset); - logprintf("\n"); - failure_finish(extra); - return (0); -} - -/* Verify that a block of memory is filled with the specified byte. */ -int -assertion_memory_filled_with(const char *file, int line, - const void *_v1, const char *vd, - size_t l, const char *ld, - char b, const char *bd, void *extra) -{ - const char *v1 = (const char *)_v1; - size_t c = 0; - size_t i; - (void)ld; /* UNUSED */ - - assertion_count(file, line); - - for (i = 0; i < l; ++i) { - if (v1[i] == b) { - ++c; - } - } - if (c == l) - return (1); - - failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); - logprintf(" Only %d bytes were correct\n", (int)c); - failure_finish(extra); - return (0); -} - -/* Verify that the named file exists and is empty. */ -int -assertion_empty_file(const char *filename, int line, const char *f1) -{ - char buff[1024]; - struct stat st; - ssize_t s; - FILE *f; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) - return (1); - - failure_start(filename, line, "File should be empty: %s", f1); - logprintf(" File size: %d\n", (int)st.st_size); - logprintf(" Contents:\n"); - f = fopen(f1, "rb"); - if (f == NULL) { - logprintf(" Unable to open %s\n", f1); - } else { - s = ((off_t)sizeof(buff) < st.st_size) ? - (ssize_t)sizeof(buff) : (ssize_t)st.st_size; - s = fread(buff, 1, s, f); - hexdump(buff, NULL, s, 0); - fclose(f); - } - failure_finish(NULL); - return (0); -} - -/* Verify that the named file exists and is not empty. */ -int -assertion_non_empty_file(const char *filename, int line, const char *f1) -{ - struct stat st; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) { - failure_start(filename, line, "File empty: %s", f1); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify that two files have the same contents. */ -/* TODO: hexdump the first bytes that actually differ. */ -int -assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) -{ - char buff1[1024]; - char buff2[1024]; - FILE *f1, *f2; - int n1, n2; - - assertion_count(filename, line); - - f1 = fopen(fn1, "rb"); - f2 = fopen(fn2, "rb"); - if (f1 == NULL || f2 == NULL) { - if (f1) fclose(f1); - if (f2) fclose(f2); - return (0); - } - for (;;) { - n1 = (int)fread(buff1, 1, sizeof(buff1), f1); - n2 = (int)fread(buff2, 1, sizeof(buff2), f2); - if (n1 != n2) - break; - if (n1 == 0 && n2 == 0) { - fclose(f1); - fclose(f2); - return (1); - } - if (memcmp(buff1, buff2, n1) != 0) - break; - } - fclose(f1); - fclose(f2); - failure_start(filename, line, "Files not identical"); - logprintf(" file1=\"%s\"\n", fn1); - logprintf(" file2=\"%s\"\n", fn2); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file does exist. */ -int -assertion_file_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (!_access(f, 0)) - return (1); -#else - if (!access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file doesn't exist. */ -int -assertion_file_not_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (_access(f, 0)) - return (1); -#else - if (access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should not exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Compare the contents of a file to a block of memory. */ -int -assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) -{ - char *contents; - FILE *f; - int n; - - assertion_count(filename, line); - - f = fopen(fn, "rb"); - if (f == NULL) { - failure_start(filename, line, - "File should exist: %s", fn); - failure_finish(NULL); - return (0); - } - contents = malloc(s * 2); - n = (int)fread(contents, 1, s * 2, f); - fclose(f); - if (n == s && memcmp(buff, contents, s) == 0) { - free(contents); - return (1); - } - failure_start(filename, line, "File contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) - hexdump(contents, buff, n > 512 ? 512 : n, 0); - else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s > 512 ? 512 : s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Check the contents of a text file, being tolerant of line endings. */ -int -assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) -{ - char *contents; - const char *btxt, *ftxt; - FILE *f; - int n, s; - - assertion_count(filename, line); - f = fopen(fn, "r"); - if (f == NULL) { - failure_start(filename, line, - "File doesn't exist: %s", fn); - failure_finish(NULL); - return (0); - } - s = (int)strlen(buff); - contents = malloc(s * 2 + 128); - n = (int)fread(contents, 1, s * 2 + 128 - 1, f); - if (n >= 0) - contents[n] = '\0'; - fclose(f); - /* Compare texts. */ - btxt = buff; - ftxt = (const char *)contents; - while (*btxt != '\0' && *ftxt != '\0') { - if (*btxt == *ftxt) { - ++btxt; - ++ftxt; - continue; - } - if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { - /* Pass over different new line characters. */ - ++btxt; - ftxt += 2; - continue; - } - break; - } - if (*btxt == '\0' && *ftxt == '\0') { - free(contents); - return (1); - } - failure_start(filename, line, "Contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) { - hexdump(contents, buff, n, 0); - logprintf(" expected\n", fn); - hexdump(buff, contents, s, 0); - } else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Verify that a text file contains the specified lines, regardless of order */ -/* This could be more efficient if we sorted both sets of lines, etc, but - * since this is used only for testing and only ever deals with a dozen or so - * lines at a time, this relatively crude approach is just fine. */ -int -assertion_file_contains_lines_any_order(const char *file, int line, - const char *pathname, const char *lines[]) -{ - char *buff; - size_t buff_size; - size_t expected_count, actual_count, i, j; - char **expected = NULL; - char *p, **actual = NULL; - char c; - int expected_failure = 0, actual_failure = 0; - - assertion_count(file, line); - - buff = slurpfile(&buff_size, "%s", pathname); - if (buff == NULL) { - failure_start(pathname, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - /* Make a copy of the provided lines and count up the expected - * file size. */ - for (i = 0; lines[i] != NULL; ++i) { - } - expected_count = i; - if (expected_count) { - expected = malloc(sizeof(char *) * expected_count); - if (expected == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - free(expected); - return (0); - } - for (i = 0; lines[i] != NULL; ++i) { - expected[i] = strdup(lines[i]); - } - } - - /* Break the file into lines */ - actual_count = 0; - for (c = '\0', p = buff; p < buff + buff_size; ++p) { - if (*p == '\x0d' || *p == '\x0a') - *p = '\0'; - if (c == '\0' && *p != '\0') - ++actual_count; - c = *p; - } - if (actual_count) { - actual = calloc(sizeof(char *), actual_count); - if (actual == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - free(expected); - return (0); - } - for (j = 0, p = buff; p < buff + buff_size; - p += 1 + strlen(p)) { - if (*p != '\0') { - actual[j] = p; - ++j; - } - } - } - - /* Erase matching lines from both lists */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] == NULL) - continue; - for (j = 0; j < actual_count; ++j) { - if (actual[j] == NULL) - continue; - if (strcmp(expected[i], actual[j]) == 0) { - free(expected[i]); - expected[i] = NULL; - actual[j] = NULL; - break; - } - } - } - - /* If there's anything left, it's a failure */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) - ++expected_failure; - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - ++actual_failure; - } - if (expected_failure == 0 && actual_failure == 0) { - free(buff); - free(expected); - free(actual); - return (1); - } - failure_start(file, line, "File doesn't match: %s", pathname); - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) { - logprintf(" Expected but not present: %s\n", expected[i]); - free(expected[i]); - } - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - logprintf(" Present but not expected: %s\n", actual[j]); - } - failure_finish(NULL); - free(buff); - free(expected); - free(actual); - return (0); -} - -/* Verify that a text file does not contains the specified strings */ -int -assertion_file_contains_no_invalid_strings(const char *file, int line, - const char *pathname, const char *strings[]) -{ - char *buff; - int i; - - buff = slurpfile(NULL, "%s", pathname); - if (buff == NULL) { - failure_start(file, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - for (i = 0; strings[i] != NULL; ++i) { - if (strstr(buff, strings[i]) != NULL) { - failure_start(file, line, "Invalid string in %s: %s", pathname, - strings[i]); - failure_finish(NULL); - free(buff); - return(0); - } - } - - free(buff); - return (0); -} - -/* Test that two paths point to the same file. */ -/* As a side-effect, asserts that both files exist. */ -static int -is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(path1, &bhfi1); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path1); - failure_finish(NULL); - return (0); - } - r = my_GetFileInformationByName(path2, &bhfi2); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path2); - failure_finish(NULL); - return (0); - } - return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber - && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh - && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); -#else - struct stat st1, st2; - int r; - - assertion_count(file, line); - r = lstat(path1, &st1); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path1); - failure_finish(NULL); - return (0); - } - r = lstat(path2, &st2); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path2); - failure_finish(NULL); - return (0); - } - return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); -#endif -} - -int -assertion_is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s are not hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -int -assertion_is_not_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (!is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s should not be hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -/* Verify a/b/mtime of 'pathname'. */ -/* If 'recent', verify that it's within last 10 seconds. */ -static int -assertion_file_time(const char *file, int line, - const char *pathname, long t, long nsec, char type, int recent) -{ - long long filet, filet_nsec; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define EPOC_TIME (116444736000000000ULL) - FILETIME fxtime, fbirthtime, fatime, fmtime; - ULARGE_INTEGER wintm; - HANDLE h; - fxtime.dwLowDateTime = 0; - fxtime.dwHighDateTime = 0; - - assertion_count(file, line); - /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open - * a directory file. If not, CreateFile() will fail when - * the pathname is a directory. */ - h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); - switch (type) { - case 'a': fxtime = fatime; break; - case 'b': fxtime = fbirthtime; break; - case 'm': fxtime = fmtime; break; - } - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't GetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - wintm.LowPart = fxtime.dwLowDateTime; - wintm.HighPart = fxtime.dwHighDateTime; - filet = (wintm.QuadPart - EPOC_TIME) / 10000000; - filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; - nsec = (nsec / 100) * 100; /* Round the request */ -#else - struct stat st; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - switch (type) { - case 'a': filet = st.st_atime; break; - case 'm': filet = st.st_mtime; break; - case 'b': filet = 0; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } -#if defined(__FreeBSD__) - switch (type) { - case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; - case 'b': filet = st.st_birthtime; - /* FreeBSD filesystems that don't support birthtime - * (e.g., UFS1) always return -1 here. */ - if (filet == -1) { - return (1); - } - filet_nsec = st.st_birthtimespec.tv_nsec; break; - case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } - /* FreeBSD generally only stores to microsecond res, so round. */ - filet_nsec = (filet_nsec / 1000) * 1000; - nsec = (nsec / 1000) * 1000; -#else - filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ - if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ -#if defined(__HAIKU__) - if (type == 'a') return (1); /* Haiku doesn't have atime. */ -#endif -#endif -#endif - if (recent) { - /* Check that requested time is up-to-date. */ - time_t now = time(NULL); - if (filet < now - 10 || filet > now + 1) { - failure_start(file, line, - "File %s has %ctime %lld, %lld seconds ago\n", - pathname, type, filet, now - filet); - failure_finish(NULL); - return (0); - } - } else if (filet != t || filet_nsec != nsec) { - failure_start(file, line, - "File %s has %ctime %lld.%09lld, expected %lld.%09lld", - pathname, type, filet, filet_nsec, t, nsec); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify atime of 'pathname'. */ -int -assertion_file_atime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); -} - -/* Verify atime of 'pathname' is up-to-date. */ -int -assertion_file_atime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); -} - -/* Verify birthtime of 'pathname'. */ -int -assertion_file_birthtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); -} - -/* Verify birthtime of 'pathname' is up-to-date. */ -int -assertion_file_birthtime_recent(const char *file, int line, - const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); -} - -/* Verify mode of 'pathname'. */ -int -assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) -{ - int mode; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - failure_start(file, line, "assertFileMode not yet implemented for Windows"); - (void)mode; /* UNUSED */ - (void)r; /* UNUSED */ -#else - { - struct stat st; - r = lstat(pathname, &st); - mode = (int)(st.st_mode & 0777); - } - if (r == 0 && mode == expected_mode) - return (1); - failure_start(file, line, "File %s has mode %o, expected %o", - pathname, mode, expected_mode); -#endif - failure_finish(NULL); - return (0); -} - -/* Verify mtime of 'pathname'. */ -int -assertion_file_mtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); -} - -/* Verify mtime of 'pathname' is up-to-date. */ -int -assertion_file_mtime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); -} - -/* Verify number of links to 'pathname'. */ -int -assertion_file_nlinks(const char *file, int line, - const char *pathname, int nlinks) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(pathname, &bhfi); - if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, bhfi.nNumberOfLinks, nlinks); - failure_finish(NULL); - return (0); -#else - struct stat st; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r == 0 && (int)st.st_nlink == nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, st.st_nlink, nlinks); - failure_finish(NULL); - return (0); -#endif -} - -/* Verify size of 'pathname'. */ -int -assertion_file_size(const char *file, int line, const char *pathname, long size) -{ - int64_t filesize; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - { - BY_HANDLE_FILE_INFORMATION bhfi; - r = !my_GetFileInformationByName(pathname, &bhfi); - filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; - } -#else - { - struct stat st; - r = lstat(pathname, &st); - filesize = st.st_size; - } -#endif - if (r == 0 && filesize == size) - return (1); - failure_start(file, line, "File %s has size %ld, expected %ld", - pathname, (long)filesize, (long)size); - failure_finish(NULL); - return (0); -} - -/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ -int -assertion_is_dir(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Dir should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISDIR(st.st_mode)) { - failure_start(file, line, "%s is not a dir", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "Dir %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, - * verify that too. */ -int -assertion_is_reg(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0 || !S_ISREG(st.st_mode)) { - failure_start(file, line, "File should exist: %s", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "File %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ -static int -is_symlink(const char *file, int line, - const char *pathname, const char *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)pathname; /* UNUSED */ - (void)contents; /* UNUSED */ - assertion_count(file, line); - /* Windows sort-of has real symlinks, but they're only usable - * by privileged users and are crippled even then, so there's - * really not much point in bothering with this. */ - return (0); -#else - char buff[300]; - struct stat st; - ssize_t linklen; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, - "Symlink should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISLNK(st.st_mode)) - return (0); - if (contents == NULL) - return (1); - linklen = readlink(pathname, buff, sizeof(buff)); - if (linklen < 0) { - failure_start(file, line, "Can't read symlink %s", pathname); - failure_finish(NULL); - return (0); - } - buff[linklen] = '\0'; - if (strcmp(buff, contents) != 0) - return (0); - return (1); -#endif -} - -/* Assert that path is a symlink that (optionally) contains contents. */ -int -assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) -{ - if (is_symlink(file, line, path, contents)) - return (1); - if (contents) - failure_start(file, line, "File %s is not a symlink to %s", - path, contents); - else - failure_start(file, line, "File %s is not a symlink", path); - failure_finish(NULL); - return (0); -} - - -/* Create a directory and report any errors. */ -int -assertion_make_dir(const char *file, int line, const char *dirname, int mode) -{ - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ - if (0 == _mkdir(dirname)) - return (1); -#else - if (0 == mkdir(dirname, mode)) { - if (0 == chmod(dirname, mode)) { - assertion_file_mode(file, line, dirname, mode); - return (1); - } - } -#endif - failure_start(file, line, "Could not create directory %s", dirname); - failure_finish(NULL); - return(0); -} - -/* Create a file with the specified contents and report any failures. */ -int -assertion_make_file(const char *file, int line, - const char *path, int mode, int csize, const void *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - /* TODO: Rework this to set file mode as well. */ - FILE *f; - (void)mode; /* UNUSED */ - assertion_count(file, line); - f = fopen(path, "wb"); - if (f == NULL) { - failure_start(file, line, "Could not create file %s", path); - failure_finish(NULL); - return (0); - } - if (contents != NULL) { - size_t wsize; - - if (csize < 0) - wsize = strlen(contents); - else - wsize = (size_t)csize; - if (wsize != fwrite(contents, 1, wsize, f)) { - fclose(f); - failure_start(file, line, - "Could not write file %s", path); - failure_finish(NULL); - return (0); - } - } - fclose(f); - return (1); -#else - int fd; - assertion_count(file, line); - fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); - if (fd < 0) { - failure_start(file, line, "Could not create %s", path); - failure_finish(NULL); - return (0); - } - if (0 != chmod(path, mode)) { - failure_start(file, line, "Could not chmod %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - if (contents != NULL) { - ssize_t wsize; - - if (csize < 0) - wsize = (ssize_t)strlen(contents); - else - wsize = (ssize_t)csize; - if (wsize != write(fd, contents, wsize)) { - close(fd); - failure_start(file, line, - "Could not write to %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - } - close(fd); - assertion_file_mode(file, line, path, mode); - return (1); -#endif -} - -/* Create a hardlink and report any failures. */ -int -assertion_make_hardlink(const char *file, int line, - const char *newpath, const char *linkto) -{ - int succeeded; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - succeeded = my_CreateHardLinkA(newpath, linkto); -#elif HAVE_LINK - succeeded = !link(linkto, newpath); -#else - succeeded = 0; -#endif - if (succeeded) - return (1); - failure_start(file, line, "Could not create hardlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Create a symlink and report any failures. */ -int -assertion_make_symlink(const char *file, int line, - const char *newpath, const char *linkto) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - int targetIsDir = 0; /* TODO: Fix this */ - assertion_count(file, line); - if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) - return (1); -#elif HAVE_SYMLINK - assertion_count(file, line); - if (0 == symlink(linkto, newpath)) - return (1); -#endif - failure_start(file, line, "Could not create symlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Set umask, report failures. */ -int -assertion_umask(const char *file, int line, int mask) -{ - assertion_count(file, line); - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - umask(mask); - return (1); -} - -/* Set times, report failures. */ -int -assertion_utimes(const char *file, int line, - const char *pathname, long at, long at_nsec, long mt, long mt_nsec) -{ - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ - + (((nsec)/1000)*10)) - HANDLE h; - ULARGE_INTEGER wintm; - FILETIME fatime, fmtime; - FILETIME *pat, *pmt; - - assertion_count(file, line); - h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (at > 0 || at_nsec > 0) { - wintm.QuadPart = WINTIME(at, at_nsec); - fatime.dwLowDateTime = wintm.LowPart; - fatime.dwHighDateTime = wintm.HighPart; - pat = &fatime; - } else - pat = NULL; - if (mt > 0 || mt_nsec > 0) { - wintm.QuadPart = WINTIME(mt, mt_nsec); - fmtime.dwLowDateTime = wintm.LowPart; - fmtime.dwHighDateTime = wintm.HighPart; - pmt = &fmtime; - } else - pmt = NULL; - if (pat != NULL || pmt != NULL) - r = SetFileTime(h, NULL, pat, pmt); - else - r = 1; - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't SetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#else /* defined(_WIN32) && !defined(__CYGWIN__) */ - struct stat st; - struct timeval times[2]; - -#if !defined(__FreeBSD__) - mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ -#endif - if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) - return (1); - - r = lstat(pathname, &st); - if (r < 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (mt == 0 && mt_nsec == 0) { - mt = st.st_mtime; -#if defined(__FreeBSD__) - mt_nsec = st.st_mtimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - mt_nsec = (mt_nsec / 1000) * 1000; -#endif - } - if (at == 0 && at_nsec == 0) { - at = st.st_atime; -#if defined(__FreeBSD__) - at_nsec = st.st_atimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - at_nsec = (at_nsec / 1000) * 1000; -#endif - } - - times[1].tv_sec = mt; - times[1].tv_usec = mt_nsec / 1000; - - times[0].tv_sec = at; - times[0].tv_usec = at_nsec / 1000; - -#ifdef HAVE_LUTIMES - r = lutimes(pathname, times); -#else - r = utimes(pathname, times); -#endif - if (r < 0) { - failure_start(file, line, "Can't utimes %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ -} - -/* Set nodump, report failures. */ -int -assertion_nodump(const char *file, int line, const char *pathname) -{ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - int r; - - assertion_count(file, line); - r = chflags(pathname, UF_NODUMP); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - int fd, r, flags; - - assertion_count(file, line); - fd = open(pathname, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - failure_start(file, line, "Can't open %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't get flags %s\n", pathname); - failure_finish(NULL); - return (0); - } - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } - close(fd); -#else - (void)pathname; /* UNUSED */ - assertion_count(file, line); -#endif - return (1); -} - -/* - * - * UTILITIES for use by tests. - * - */ - -/* - * Check whether platform supports symlinks. This is intended - * for tests to use in deciding whether to bother testing symlink - * support; if the platform doesn't support symlinks, there's no point - * in checking whether the program being tested can create them. - * - * Note that the first time this test is called, we actually go out to - * disk to create and verify a symlink. This is necessary because - * symlink support is actually a property of a particular filesystem - * and can thus vary between directories on a single system. After - * the first call, this returns the cached result from memory, so it's - * safe to call it as often as you wish. - */ -int -canSymlink(void) -{ - /* Remember the test result */ - static int value = 0, tested = 0; - if (tested) - return (value); - - ++tested; - assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); - /* Note: Cygwin has its own symlink() emulation that does not - * use the Win32 CreateSymbolicLink() function. */ -#if defined(_WIN32) && !defined(__CYGWIN__) - value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); -#elif HAVE_SYMLINK - value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); -#endif - return (value); -} - -/* Platform-dependent options for hiding the output of a subcommand. */ -#if defined(_WIN32) && !defined(__CYGWIN__) -static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ -#else -static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ -#endif -/* - * Can this platform run the bzip2 program? - */ -int -canBzip2(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("bzip2 -d -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the grzip program? - */ -int -canGrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("grzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the gzip program? - */ -int -canGzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("gzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lrzip program? - */ -int -canRunCommand(const char *cmd) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("%s %s", cmd, redirectArgs) == 0) - value = 1; - } - return (value); -} - -int -canLrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lrzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lz4 program? - */ -int -canLz4(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lz4 -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzip program? - */ -int -canLzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzma program? - */ -int -canLzma(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzma -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzop program? - */ -int -canLzop(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzop -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the xz program? - */ -int -canXz(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("xz -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this filesystem handle nodump flags. - */ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - struct stat sb; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - if (chflags(path, UF_NODUMP) < 0) - return (0); - if (stat(path, &sb) < 0) - return (0); - if (sb.st_flags & UF_NODUMP) - return (1); - return (0); -} - -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - int fd, r, flags; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - if (flags & EXT2_NODUMP_FL) - return (1); - return (0); -} - -#else - -int -canNodump() -{ - return (0); -} - -#endif - -/* - * Sleep as needed; useful for verifying disk timestamp changes by - * ensuring that the wall-clock time has actually changed before we - * go back to re-read something from disk. - */ -void -sleepUntilAfter(time_t t) -{ - while (t >= time(NULL)) -#if defined(_WIN32) && !defined(__CYGWIN__) - Sleep(500); -#else - sleep(1); -#endif -} - -/* - * 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); - if (verbosity > VERBOSITY_FULL) - logprintf("Cmd: %s\n", buff); - 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; - FILE *f; - int r; - - va_start(ap, fmt); - vsprintf(filename, fmt, ap); - va_end(ap); - - f = fopen(filename, "rb"); - if (f == NULL) { - /* Note: No error; non-existent file is okay here. */ - return (NULL); - } - r = fstat(fileno(f), &st); - if (r != 0) { - logprintf("Can't stat file %s\n", filename); - fclose(f); - return (NULL); - } - p = malloc((size_t)st.st_size + 1); - if (p == NULL) { - logprintf("Can't allocate %ld bytes of memory to read file %s\n", - (long int)st.st_size, filename); - fclose(f); - return (NULL); - } - bytes_read = fread(p, 1, (size_t)st.st_size, f); - if (bytes_read < st.st_size) { - logprintf("Can't read file %s\n", filename); - fclose(f); - free(p); - return (NULL); - } - p[st.st_size] = '\0'; - if (sizep != NULL) - *sizep = (size_t)st.st_size; - fclose(f); - return (p); -} - -/* - * 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. - */ -void -dumpfile(const char *filename, void *data, size_t len) -{ - ssize_t bytes_written; - FILE *f; - - f = fopen(filename, "wb"); - if (f == NULL) { - logprintf("Can't open file %s for writing\n", filename); - return; - } - bytes_written = fwrite(data, 1, len, f); - if (bytes_written < (ssize_t)len) - logprintf("Can't write file %s\n", filename); - fclose(f); -} - -/* Read a uuencoded file from the reference directory, decode, and - * write the result into the current directory. */ -#define VALID_UUDECODE(c) (c >= 32 && c <= 96) -#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. */ - out = fopen(name, "wb"); - 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) { - assert(VALID_UUDECODE(p[0])); - assert(VALID_UUDECODE(p[1])); - n = UUDECODE(*p++) << 18; - n |= UUDECODE(*p++) << 12; - fputc(n >> 16, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++) << 6; - fputc((n >> 8) & 0xFF, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++); - fputc(n & 0xFF, out); - --bytes; - } - } - } - fclose(out); - fclose(in); -} - -void -copy_reference_file(const char *name) -{ - char buff[1024]; - FILE *in, *out; - size_t rbytes; - - sprintf(buff, "%s/%s", refdir, name); - in = fopen(buff, "rb"); - failure("Couldn't open reference file %s", buff); - assert(in != NULL); - if (in == NULL) - return; - /* Now, decode the rest and write it. */ - /* Not a lot of error checking here; the input better be right. */ - out = fopen(name, "wb"); - while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { - if (fwrite(buff, 1, rbytes, out) != rbytes) { - logprintf("Error: fwrite\n"); - break; - } - } - fclose(out); - fclose(in); -} - -int -is_LargeInode(const char *file) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - r = my_GetFileInformationByName(file, &bhfi); - if (r != 0) - return (0); - return (bhfi.nFileIndexHigh & 0x0000FFFFUL); -#else - struct stat st; - int64_t ino; - - if (stat(file, &st) < 0) - return (0); - ino = (int64_t)st.st_ino; - return (ino > 0xffffffff); -#endif -} - -void -extract_reference_files(const char **names) -{ - while (names && *names) - extract_reference_file(*names++); -} - -/* - * - * TEST management - * - */ - -/* - * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has - * a line like - * DEFINE_TEST(test_function) - * for each test. - */ - -/* Use "list.h" to declare all of the test functions. */ -#undef DEFINE_TEST -#define DEFINE_TEST(name) void name(void); -#include "list.h" - -/* Use "list.h" to create a list of all tests (functions and names). */ -#undef DEFINE_TEST -#define DEFINE_TEST(n) { n, #n, 0 }, -struct test_list_t tests[] = { - #include "list.h" -}; - -/* - * Summarize repeated failures in the just-completed test. - */ -static void -test_summarize(int failed, int skips_num) -{ - unsigned int i; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: - printf(failed ? "E" : "."); - fflush(stdout); - break; - case VERBOSITY_PASSFAIL: - printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); - break; - } - - log_console = (verbosity == VERBOSITY_LIGHT_REPORT); - - for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { - if (failed_lines[i].count > 1 && !failed_lines[i].skip) - logprintf("%s:%d: Summary: Failed %d times\n", - failed_filename, i, failed_lines[i].count); - } - /* Clear the failure history for the next file. */ - failed_filename = NULL; - memset(failed_lines, 0, sizeof(failed_lines)); -} - -/* - * Actually run a single test, with appropriate setup and cleanup. - */ -static int -test_run(int i, const char *tmpdir) -{ - char workdir[1024]; - char logfilename[64]; - int failures_before = failures; - int skips_before = skips; - int oldumask; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ - break; - case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ - printf("%3d: %-64s", i, tests[i].name); - fflush(stdout); - break; - default: /* Title of test, details will follow */ - printf("%3d: %s\n", i, tests[i].name); - } - - /* Chdir to the top-level work directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, - "ERROR: Can't chdir to top work dir %s\n", tmpdir); - exit(1); - } - /* Create a log file for this test. */ - sprintf(logfilename, "%s.log", tests[i].name); - logfile = fopen(logfilename, "w"); - fprintf(logfile, "%s\n\n", tests[i].name); - /* Chdir() to a work dir for this specific test. */ - snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); - testworkdir = workdir; - if (!assertMakeDir(testworkdir, 0755) - || !assertChdir(testworkdir)) { - fprintf(stderr, - "ERROR: Can't chdir to work dir %s\n", testworkdir); - exit(1); - } - /* Explicitly reset the locale before each test. */ - setlocale(LC_ALL, "C"); - /* Record the umask before we run the test. */ - umask(oldumask = umask(0)); - /* - * Run the actual test. - */ - (*tests[i].func)(); - /* - * Clean up and report afterwards. - */ - testworkdir = NULL; - /* Restore umask */ - umask(oldumask); - /* Reset locale. */ - setlocale(LC_ALL, "C"); - /* Reset directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", - tmpdir); - exit(1); - } - /* Report per-test summaries. */ - tests[i].failures = failures - failures_before; - test_summarize(tests[i].failures, skips - skips_before); - /* Close the per-test log file. */ - fclose(logfile); - logfile = NULL; - /* If there were no failures, we can remove the work dir and logfile. */ - if (tests[i].failures == 0) { - if (!keep_temp_files && assertChdir(tmpdir)) { -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure not to leave empty directories. - * Sometimes a processing of closing files used by tests - * is not done, then rmdir will be failed and it will - * leave a empty test directory. So we should wait a few - * seconds and retry rmdir. */ - int r, t; - for (t = 0; t < 10; t++) { - if (t > 0) - Sleep(1000); - r = systemf("rmdir /S /Q %s", tests[i].name); - if (r == 0) - break; - } - systemf("del %s", logfilename); -#else - systemf("rm -rf %s", tests[i].name); - systemf("rm %s", logfilename); -#endif - } - } - /* Return appropriate status. */ - return (tests[i].failures); -} - -/* - * - * - * MAIN and support routines. - * - * - */ - -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(" -u Keep running specifies tests until one fails.\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); -} - -static char * -get_refdir(const char *d) -{ - size_t tried_size, buff_size; - char *buff, *tried, *pwd = NULL, *p = NULL; - -#ifdef PATH_MAX - buff_size = PATH_MAX; -#else - buff_size = 8192; -#endif - buff = calloc(buff_size, 1); - if (buff == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* Allocate a buffer to hold the various directories we checked. */ - tried_size = buff_size * 2; - tried = calloc(tried_size, 1); - if (tried == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* If a dir was specified, try that */ - if (d != NULL) { - pwd = NULL; - snprintf(buff, buff_size, "%s", d); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - goto failure; - } - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - - /* Look for a known file. */ - snprintf(buff, buff_size, "%s", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(LIBRARY) - snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); -#else - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); -#endif - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(PROGRAM_ALIAS) - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); -#endif - - if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, buff_size, "%s", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - } - -failure: - printf("Unable to locate known reference file %s\n", KNOWNREF); - printf(" Checked following directories:\n%s\n", tried); - printf("Use -r option to specify full path to reference directory\n"); -#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) - DebugBreak(); -#endif - exit(1); - -success: - free(p); - free(pwd); - free(tried); - - /* Copy result into a fresh buffer to reduce memory usage. */ - p = strdup(buff); - free(buff); - return p; -} - -int -main(int argc, char **argv) -{ - static const int limit = sizeof(tests) / sizeof(tests[0]); - int test_set[sizeof(tests) / sizeof(tests[0])]; - int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; - time_t now; - char *refdir_alloc = NULL; - const char *progname; - char **saved_argv; - const char *tmp, *option_arg, *p; - char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; - char tmpdir_timestamp[256]; - - (void)argc; /* UNUSED */ - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) - /* To stop to run the default invalid parameter handler. */ - _set_invalid_parameter_handler(invalid_parameter_handler); - /* Disable annoying assertion message box. */ - _CrtSetReportMode(_CRT_ASSERT, 0); -#endif - - /* - * Name of this program, used to build root of our temp directory - * tree. - */ - progname = p = argv[0]; - if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(testprogdir, progname); - while (*p != '\0') { - /* Support \ or / dir separators for Windows compat. */ - if (*p == '/' || *p == '\\') - { - progname = p + 1; - i = j; - } - ++p; - j++; - } - testprogdir[i] = '\0'; -#if defined(_WIN32) && !defined(__CYGWIN__) - if (testprogdir[0] != '/' && testprogdir[0] != '\\' && - !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || - (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && - testprogdir[1] == ':' && - (testprogdir[2] == '/' || testprogdir[2] == '\\'))) -#else - if (testprogdir[0] != '/') -#endif - { - /* Fixup path for relative directories. */ - if ((testprogdir = (char *)realloc(testprogdir, - strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - memmove(testprogdir + strlen(pwd) + 1, testprogdir, - strlen(testprogdir) + 1); - memcpy(testprogdir, pwd, strlen(pwd)); - testprogdir[strlen(pwd)] = '/'; - } - -#ifdef PROGRAM - /* Get the target program from environment, if available. */ - testprogfile = 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; - - /* Allow -v to be controlled through the environment. */ - if (getenv("_VERBOSITY_LEVEL") != NULL) - { - vlevel = getenv("_VERBOSITY_LEVEL"); - verbosity = atoi(vlevel); - if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) - { - /* Unsupported verbosity levels are silently ignored */ - vlevel = NULL; - verbosity = VERBOSITY_PASSFAIL; - } - } - - /* 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 - testprogfile = option_arg; -#else - fprintf(stderr, "-p option not permitted\n"); - usage(progname); -#endif - break; - case 'q': - if (!vlevel) - verbosity--; - break; - case 'r': - refdir = option_arg; - break; - case 'u': - until_failure++; - break; - case 'v': - if (!vlevel) - verbosity++; - break; - default: - fprintf(stderr, "Unrecognized option '%c'\n", - option); - usage(progname); - } - } - } - - /* - * Sanity-check that our options make sense. - */ -#ifdef PROGRAM - if (testprogfile == NULL) - { - if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + - strlen(PROGRAM) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(tmp2, testprogdir); - strcat(tmp2, "/"); - strcat(tmp2, PROGRAM); - testprogfile = tmp2; - } - - { - char *testprg; -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Command.com sometimes rejects '/' separators. */ - testprg = strdup(testprogfile); - for (i = 0; testprg[i] != '\0'; i++) { - if (testprg[i] == '/') - testprg[i] = '\\'; - } - testprogfile = testprg; -#endif - /* Quote the name that gets put into shell command lines. */ - testprg = malloc(strlen(testprogfile) + 3); - strcpy(testprg, "\""); - strcat(testprg, testprogfile); - strcat(testprg, "\""); - testprog = testprg; - } -#endif - -#if !defined(_WIN32) && defined(SIGPIPE) - { /* Ignore SIGPIPE signals */ - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGPIPE, &sa, NULL); - } -#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++) { - 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 (assertMakeDir(tmpdir,0755)) - break; - if (i >= 999) { - 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." - */ - refdir = refdir_alloc = get_refdir(refdir); - - /* - * Banner with basic information. - */ - printf("\n"); - printf("If tests fail or crash, details will be in:\n"); - printf(" %s\n", tmpdir); - printf("\n"); - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - 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); - } else { - printf("Running "); - fflush(stdout); - } - - /* - * Run some or all of the individual tests. - */ - saved_argv = argv; - do { - argv = saved_argv; - do { - int test_num; - - test_num = get_test_set(test_set, limit, *argv, tests); - if (test_num < 0) { - printf("*** INVALID Test %s\n", *argv); - free(refdir_alloc); - free(testprogdir); - usage(progname); - return (1); - } - for (i = 0; i < test_num; i++) { - tests_run++; - if (test_run(test_set[i], tmpdir)) { - tests_failed++; - if (until_failure) - goto finish; - } - } - if (*argv != NULL) - argv++; - } while (*argv != NULL); - } while (until_failure); - -finish: - /* Must be freed after all tests run */ - free(tmp2); - free(testprogdir); - free(pwd); - - /* - * Report summary statistics. - */ - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - printf("\n"); - printf("Totals:\n"); - printf(" Tests run: %8d\n", tests_run); - printf(" Tests failed: %8d\n", tests_failed); - printf(" Assertions checked:%8d\n", assertions); - printf(" Assertions failed: %8d\n", failures); - printf(" Skips reported: %8d\n", skips); - } - if (failures) { - printf("\n"); - printf("Failing tests:\n"); - for (i = 0; i < limit; ++i) { - if (tests[i].failures) - printf(" %d: %s (%d failures)\n", i, - tests[i].name, tests[i].failures); - } - printf("\n"); - printf("Details for failing tests: %s\n", tmpdir); - printf("\n"); - } else { - if (verbosity == VERBOSITY_SUMMARY_ONLY) - printf("\n"); - printf("%d tests passed, no failures\n", tests_run); - } - - free(refdir_alloc); - - /* If the final tmpdir is empty, we can remove it. */ - /* This should be the usual case when all tests succeed. */ - assertChdir(".."); - rmdir(tmpdir); - - return (tests_failed ? 1 : 0); -} Property changes on: vendor/libarchive/dist/cat/test/main.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/libarchive/dist/cat/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/cat/test/CMakeLists.txt (revision 314564) +++ vendor/libarchive/dist/cat/test/CMakeLists.txt (revision 314565) @@ -1,66 +1,67 @@ ############################################ # # How to build bsdtar_test # ############################################ IF(ENABLE_CAT AND ENABLE_TEST) SET(bsdcat_test_SOURCES ../../test_utils/test_utils.c - main.c + ../../test_utils/test_main.c test.h test_0.c test_empty_gz.c test_empty_lz4.c test_empty_xz.c test_error.c test_error_mixed.c test_expand_Z.c test_expand_bz2.c test_expand_gz.c test_expand_lz4.c test_expand_mixed.c test_expand_plain.c test_expand_xz.c test_help.c test_version.c ) # # Register target # ADD_EXECUTABLE(bsdcat_test ${bsdcat_test_SOURCES}) SET_PROPERTY(TARGET bsdcat_test PROPERTY COMPILE_DEFINITIONS LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${bsdcat_test_SOURCES}) SET_PROPERTY(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) # list.h has a line DEFINE_TEST(testname) for every # test. We can use that to define the tests for cmake by # defining a DEFINE_TEST macro and reading list.h in. MACRO (DEFINE_TEST _testname) ADD_TEST( NAME bsdcat_${_testname} COMMAND bsdcat_test -vv -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cat/test) # Experimental new test handling ADD_CUSTOM_TARGET(run_bsdcat_test COMMAND bsdcat_test -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} -vv) ADD_DEPENDENCIES(run_bsdcat_test bsdcat) ADD_DEPENDENCIES(run_all_tests run_bsdcat_test) ENDIF(ENABLE_CAT AND ENABLE_TEST) Index: vendor/libarchive/dist/cat/test/test.h =================================================================== --- vendor/libarchive/dist/cat/test/test.h (revision 314564) +++ vendor/libarchive/dist/cat/test/test.h (revision 314565) @@ -1,354 +1,40 @@ /* * 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) && !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 +#define KNOWNREF "test_expand.Z.uu" +#define ENVBASE "BSDCAT" /* Prefix for environment variables. */ +#define PROGRAM "bsdcat" /* Name of program being tested. */ +#define PROGRAM_ALIAS "cat" /* Generic alias for program */ +#undef LIBRARY /* Not testing a library. */ +#undef EXTRA_DUMP /* How to dump extra data */ +#undef EXTRA_ERRNO /* How to dump errno */ +/* How to generate extra version info. */ +#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") -#include /* Windows requires this before sys/stat.h */ -#include - -#if HAVE_DIRENT_H -#include -#endif -#ifdef HAVE_DIRECT_H -#include -#define dirent direct -#endif -#include -#include -#ifdef HAVE_IO_H -#include -#endif -#ifdef HAVE_STDINT_H -#include -#endif -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_WINDOWS_H -#include -#endif - -/* - * System-specific tweaks. We really want to minimize these - * as much as possible, since they make it harder to understand - * the mainline code. - */ - -/* Windows (including Visual Studio and MinGW but not Cygwin) */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#if !defined(__BORLANDC__) -#undef chdir -#define chdir _chdir -#define strdup _strdup -#endif -#endif - -/* Visual Studio */ -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf sprintf_s -#endif - -#if defined(__BORLANDC__) -#pragma warn -8068 /* Constant out of range in comparison. */ -#endif - -/* Haiku OS and QNX */ -#if defined(__HAIKU__) || defined(__QNXNTO__) -/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ -#include -#endif - -/* Get a real definition for __FBSDID if we can */ -#if HAVE_SYS_CDEFS_H -#include -#endif - -/* If not, define it so as to avoid dangling semicolons. */ -#ifndef __FBSDID -#define __FBSDID(a) struct _undefined_hack -#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) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) -/* chdir() and error if it fails */ -#define assertChdir(path) \ - assertion_chdir(__FILE__, __LINE__, path) -/* Assert two integers are the same. Reports value of each one if not. */ -#define assertEqualInt(v1,v2) \ - assertion_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) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) -#define assertEqualUTF8String(v1,v2) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) -/* As above, but v1 and v2 are wchar_t * */ -#define assertEqualWString(v1,v2) \ - assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) -/* As above, but raw blocks of bytes. */ -#define assertEqualMem(v1, v2, l) \ - assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) -/* Assert that memory is full of a specified byte */ -#define assertMemoryFilledWith(v1, l, b) \ - assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) -/* Assert two files are the same. */ -#define assertEqualFile(f1, f2) \ - assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) -/* Assert that a file is empty. */ -#define assertEmptyFile(pathname) \ - assertion_empty_file(__FILE__, __LINE__, (pathname)) -/* Assert that a file is not empty. */ -#define assertNonEmptyFile(pathname) \ - assertion_non_empty_file(__FILE__, __LINE__, (pathname)) -#define assertFileAtime(pathname, sec, nsec) \ - assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileAtimeRecent(pathname) \ - assertion_file_atime_recent(__FILE__, __LINE__, pathname) -#define assertFileBirthtime(pathname, sec, nsec) \ - assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileBirthtimeRecent(pathname) \ - assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) -/* Assert that a file exists; supports printf-style arguments. */ -#define assertFileExists(pathname) \ - assertion_file_exists(__FILE__, __LINE__, pathname) -/* Assert that a file exists. */ -#define assertFileNotExists(pathname) \ - assertion_file_not_exists(__FILE__, __LINE__, pathname) -/* Assert that file contents match a string. */ -#define assertFileContents(data, data_size, pathname) \ - assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) -/* Verify that a file does not contain invalid strings */ -#define assertFileContainsNoInvalidStrings(pathname, strings) \ - assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) -#define assertFileMtime(pathname, sec, nsec) \ - assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileMtimeRecent(pathname) \ - assertion_file_mtime_recent(__FILE__, __LINE__, pathname) -#define assertFileNLinks(pathname, nlinks) \ - assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) -#define assertFileSize(pathname, size) \ - assertion_file_size(__FILE__, __LINE__, pathname, size) -#define assertFileMode(pathname, mode) \ - assertion_file_mode(__FILE__, __LINE__, pathname, mode) -#define assertTextFileContents(text, pathname) \ - assertion_text_file_contents(__FILE__, __LINE__, text, pathname) -#define assertFileContainsLinesAnyOrder(pathname, lines) \ - assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) -#define assertIsDir(pathname, mode) \ - assertion_is_dir(__FILE__, __LINE__, pathname, mode) -#define assertIsHardlink(path1, path2) \ - assertion_is_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsNotHardlink(path1, path2) \ - assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsReg(pathname, mode) \ - assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) -/* Create a directory, report error if it fails. */ -#define assertMakeDir(dirname, mode) \ - assertion_make_dir(__FILE__, __LINE__, dirname, mode) -#define assertMakeFile(path, mode, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) -#define assertMakeBinFile(path, mode, csize, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) -#define assertMakeHardlink(newfile, oldfile) \ - assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) -#define assertMakeSymlink(newfile, linkto) \ - assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) -#define assertNodump(path) \ - assertion_nodump(__FILE__, __LINE__, path) -#define assertUmask(mask) \ - assertion_umask(__FILE__, __LINE__, mask) -#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ - assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) - -/* - * 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 \ - skipping_setup(__FILE__, __LINE__);test_skipping - -/* Function declarations. These are defined in test_utility.c. */ -void failure(const char *fmt, ...); -int assertion_assert(const char *, int, int, const char *, void *); -int assertion_chdir(const char *, int, const char *); -int assertion_empty_file(const char *, int, const char *); -int assertion_equal_file(const char *, int, const char *, const char *); -int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); -int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); -int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); -int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); -int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); -int assertion_file_atime(const char *, int, const char *, long, long); -int assertion_file_atime_recent(const char *, int, const char *); -int assertion_file_birthtime(const char *, int, const char *, long, long); -int assertion_file_birthtime_recent(const char *, int, const char *); -int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); -int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); -int assertion_file_contents(const char *, int, const void *, int, const char *); -int assertion_file_exists(const char *, int, const char *); -int assertion_file_mode(const char *, int, const char *, int); -int assertion_file_mtime(const char *, int, const char *, long, long); -int assertion_file_mtime_recent(const char *, int, const char *); -int assertion_file_nlinks(const char *, int, const char *, int); -int assertion_file_not_exists(const char *, int, const char *); -int assertion_file_size(const char *, int, const char *, long); -int assertion_is_dir(const char *, int, const char *, int); -int assertion_is_hardlink(const char *, int, const char *, const char *); -int assertion_is_not_hardlink(const char *, int, const char *, const char *); -int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); -int assertion_make_dir(const char *, int, const char *, int); -int assertion_make_file(const char *, int, const char *, int, int, const void *); -int assertion_make_hardlink(const char *, int, const char *newpath, const char *); -int assertion_make_symlink(const char *, int, const char *newpath, const char *); -int assertion_nodump(const char *, int, const char *); -int assertion_non_empty_file(const char *, int, const char *); -int assertion_text_file_contents(const char *, int, const char *buff, const char *f); -int assertion_umask(const char *, int, int); -int assertion_utimes(const char *, int, const char *, long, long, long, long ); - -void skipping_setup(const char *, int); -void test_skipping(const char *fmt, ...); - -/* Like sprintf, then system() */ -int systemf(const char * fmt, ...); - -/* Delay until time() returns a value after this. */ -void sleepUntilAfter(time_t); - -/* Return true if this platform can create symlinks. */ -int canSymlink(void); - -/* Return true if this platform can run the "bzip2" program. */ -int canBzip2(void); - -/* Return true if this platform can run the "grzip" program. */ -int canGrzip(void); - -/* Return true if this platform can run the "gzip" program. */ -int canGzip(void); - -/* Return true if this platform can run the specified command. */ -int canRunCommand(const char *); - -/* Return true if this platform can run the "lrzip" program. */ -int canLrzip(void); - -/* Return true if this platform can run the "lz4" program. */ -int canLz4(void); - -/* Return true if this platform can run the "lzip" program. */ -int canLzip(void); - -/* Return true if this platform can run the "lzma" program. */ -int canLzma(void); - -/* Return true if this platform can run the "lzop" program. */ -int canLzop(void); - -/* Return true if this platform can run the "xz" program. */ -int canXz(void); - -/* Return true if this filesystem can handle nodump flags. */ -int canNodump(void); - -/* Return true if the file has large i-node number(>0xffffffff). */ -int is_LargeInode(const char *); - -/* 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, ...); - -/* Dump block of bytes to a file. */ -void dumpfile(const char *filename, void *, size_t); - -/* Extracts named reference file to the current directory. */ -void extract_reference_file(const char *); -/* Copies named reference file to the current directory. */ -void copy_reference_file(const char *); - -/* Extracts a list of files to the current directory. - * List must be NULL terminated. - */ -void extract_reference_files(const char **); - -/* Subtract umask from mode */ -mode_t umasked(mode_t expected_mode); - -/* Path to working directory for current test */ -extern const char *testworkdir; - -/* - * Special interfaces for program test harness. - */ - -/* Pathname of exe to be tested. */ -extern const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -extern const char *testprog; - -#ifdef USE_DMALLOC -#include -#endif +#include "test_common.h" Index: vendor/libarchive/dist/cat/test/test_version.c =================================================================== --- vendor/libarchive/dist/cat/test/test_version.c (revision 314564) +++ vendor/libarchive/dist/cat/test/test_version.c (revision 314565) @@ -1,97 +1,34 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2017 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" /* * Test that --version option works and generates reasonable output. */ DEFINE_TEST(test_version) { - int r; - char *p, *q; - size_t s; - - - r = systemf("%s --version >version.stdout 2>version.stderr", testprog); - failure("Unable to run %s --version", testprog); - if (!assert(r == 0)) - return; - - /* --version should generate nothing to stdout. */ - assertEmptyFile("version.stderr"); - /* Verify format of version message. */ - q = p = slurpfile(&s, "version.stdout"); - /* Version message should start with name of program, then space. */ - assert(s > 6); - failure("Version must start with 'bsdcat': ``%s''", p); - if (!assertEqualMem(q, "bsdcat ", 7)) - return; - q += 7; s -= 7; - /* Version number is a series of digits and periods. */ - while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { - ++q; - --s; - } - /* Version number terminated by space. */ - failure("No space after bsdcat version: ``%s''", p); - assert(s > 1); - /* Skip a single trailing a,b,c, or d. */ - if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') - ++q; - failure("No space after bsdcat version: ``%s''", p); - assert(*q == ' '); - ++q; --s; - /* Separator. */ - failure("No `-' between bsdcat and libarchive versions: ``%s''", p); - assertEqualMem(q, "- ", 2); - q += 2; s -= 2; - /* libarchive name and version number */ - failure("Not long enough for libarchive version: ``%s''", p); - assert(s > 11); - failure("Libarchive version must start with `libarchive': ``%s''", p); - assertEqualMem(q, "libarchive ", 11); - q += 11; s -= 11; - /* Version number is a series of digits and periods. */ - while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { - ++q; - --s; - } - /* Skip a single trailing a,b,c, or d. */ - if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') - ++q; - /* Skip arbitrary third-party version numbers. */ - while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) { - ++q; - --s; - } - /* All terminated by end-of-line. */ - assert(s >= 1); - /* Skip an optional CR character (e.g., Windows) */ - failure("Version output must end with \\n or \\r\\n"); - if (*q == '\r') { ++q; --s; } - assertEqualMem(q, "\n", 1); - free(p); + assertVersion(testprog, "bsdcat"); } Index: vendor/libarchive/dist/configure.ac =================================================================== --- vendor/libarchive/dist/configure.ac (revision 314564) +++ vendor/libarchive/dist/configure.ac (revision 314565) @@ -1,921 +1,934 @@ dnl Process this file with autoconf to produce a configure script. dnl First, define all of the version numbers up front. dnl In particular, this allows the version macro to be used in AC_INIT dnl These first two version numbers are updated automatically on each release. -m4_define([LIBARCHIVE_VERSION_S],[3.2.2]) -m4_define([LIBARCHIVE_VERSION_N],[3002002]) +m4_define([LIBARCHIVE_VERSION_S],[3.3.1]) +m4_define([LIBARCHIVE_VERSION_N],[3003001]) dnl bsdtar and bsdcpio versioning tracks libarchive m4_define([BSDTAR_VERSION_S],LIBARCHIVE_VERSION_S()) m4_define([BSDCPIO_VERSION_S],LIBARCHIVE_VERSION_S()) m4_define([BSDCAT_VERSION_S],LIBARCHIVE_VERSION_S()) AC_PREREQ([2.69]) # # Now starts the "real" configure script. # AC_INIT([libarchive],[LIBARCHIVE_VERSION_S()],[libarchive-discuss@googlegroups.com]) # Make sure the srcdir contains "libarchive" directory AC_CONFIG_SRCDIR([libarchive]) # Use auxiliary subscripts from this subdirectory (cleans up root) AC_CONFIG_AUX_DIR([build/autoconf]) # M4 scripts AC_CONFIG_MACRO_DIR([build/autoconf]) # Must follow AC_CONFIG macros above... AM_INIT_AUTOMAKE() AM_MAINTAINER_MODE([enable]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) # Libtool's "interface version" can be computed from the libarchive version. # Libtool interface version bumps on any API change, so increments # whenever libarchive minor version does. ARCHIVE_MINOR=$(( (LIBARCHIVE_VERSION_N() / 1000) % 1000 )) # Libarchive 2.7 == libtool interface 9 = 2 + 7 # Libarchive 2.8 == libtool interface 10 = 2 + 8 # Libarchive 2.9 == libtool interface 11 = 2 + 8 # Libarchive 3.0 == libtool interface 12 # Libarchive 3.1 == libtool interface 13 ARCHIVE_INTERFACE=`echo $((13 + ${ARCHIVE_MINOR}))` # Libarchive revision is bumped on any source change === libtool revision ARCHIVE_REVISION=$(( LIBARCHIVE_VERSION_N() % 1000 )) # Libarchive minor is bumped on any interface addition === libtool age ARCHIVE_LIBTOOL_VERSION=$ARCHIVE_INTERFACE:$ARCHIVE_REVISION:$ARCHIVE_MINOR # Stick the version numbers into config.h AC_DEFINE([LIBARCHIVE_VERSION_STRING],"LIBARCHIVE_VERSION_S()", [Version number of libarchive]) AC_DEFINE_UNQUOTED([LIBARCHIVE_VERSION_NUMBER],"LIBARCHIVE_VERSION_N()", [Version number of libarchive as a single integer]) AC_DEFINE([BSDCPIO_VERSION_STRING],"BSDCPIO_VERSION_S()", [Version number of bsdcpio]) AC_DEFINE([BSDTAR_VERSION_STRING],"BSDTAR_VERSION_S()", [Version number of bsdtar]) AC_DEFINE([BSDCAT_VERSION_STRING],"BSDTAR_VERSION_S()", [Version number of bsdcat]) # The shell variables here must be the same as the AC_SUBST() variables # below, but the shell variable names apparently cannot be the same as # the m4 macro names above. Why? Ask autoconf. BSDCPIO_VERSION_STRING=BSDCPIO_VERSION_S() BSDTAR_VERSION_STRING=BSDTAR_VERSION_S() BSDCAT_VERSION_STRING=BSDCAT_VERSION_S() LIBARCHIVE_VERSION_STRING=LIBARCHIVE_VERSION_S() LIBARCHIVE_VERSION_NUMBER=LIBARCHIVE_VERSION_N() # Substitute the above version numbers into the various files below. # Yes, I believe this is the fourth time we define what are essentially # the same symbols. Why? Ask autoconf. AC_SUBST(ARCHIVE_LIBTOOL_VERSION) AC_SUBST(BSDCPIO_VERSION_STRING) AC_SUBST(BSDTAR_VERSION_STRING) AC_SUBST(BSDCAT_VERSION_STRING) AC_SUBST(LIBARCHIVE_VERSION_STRING) AC_SUBST(LIBARCHIVE_VERSION_NUMBER) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([build/pkgconfig/libarchive.pc]) # Check for host type AC_CANONICAL_HOST dnl Compilation on mingw and Cygwin needs special Makefile rules inc_windows_files=no inc_cygwin_files=no case "$host_os" in *mingw* ) inc_windows_files=yes ;; *cygwin* | *msys*) inc_cygwin_files=yes ;; esac AM_CONDITIONAL([INC_WINDOWS_FILES], [test $inc_windows_files = yes]) AM_CONDITIONAL([INC_CYGWIN_FILES], [test $inc_cygwin_files = yes]) dnl Defines that are required for specific platforms (e.g. -D_POSIX_SOURCE, etc) PLATFORMCPPFLAGS= case "$host_os" in *mingw* ) PLATFORMCPPFLAGS=-D__USE_MINGW_ANSI_STDIO ;; esac AC_SUBST(PLATFORMCPPFLAGS) # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AC_USE_SYSTEM_EXTENSIONS AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL AC_CHECK_TOOL([STRIP],[strip]) AC_PROG_MKDIR_P # # Options for building bsdtar. # # Default is to build bsdtar, but allow people to override that. # AC_ARG_ENABLE([bsdtar], [AS_HELP_STRING([--enable-bsdtar], [enable build of bsdtar (default)]) AS_HELP_STRING([--enable-bsdtar=static], [force static build of bsdtar]) AS_HELP_STRING([--enable-bsdtar=shared], [force dynamic build of bsdtar]) AS_HELP_STRING([--disable-bsdtar], [disable build of bsdtar])], [], [enable_bsdtar=yes]) case "$enable_bsdtar" in yes) if test "$enable_static" = "no"; then static_bsdtar=no else static_bsdtar=yes fi build_bsdtar=yes ;; dynamic|shared) if test "$enable_shared" = "no"; then AC_MSG_FAILURE([Shared linking of bsdtar requires shared libarchive]) fi build_bsdtar=yes static_bsdtar=no ;; static) build_bsdtar=yes static_bsdtar=yes ;; no) build_bsdtar=no static_bsdtar=no ;; *) AC_MSG_FAILURE([Unsupported value for --enable-bsdtar]) ;; esac AM_CONDITIONAL([BUILD_BSDTAR], [ test "$build_bsdtar" = yes ]) AM_CONDITIONAL([STATIC_BSDTAR], [ test "$static_bsdtar" = yes ]) # # Options for building bsdcat. # # Default is to build bsdcat, but allow people to override that. # AC_ARG_ENABLE([bsdcat], [AS_HELP_STRING([--enable-bsdcat], [enable build of bsdcat (default)]) AS_HELP_STRING([--enable-bsdcat=static], [force static build of bsdcat]) AS_HELP_STRING([--enable-bsdcat=shared], [force dynamic build of bsdcat]) AS_HELP_STRING([--disable-bsdcat], [disable build of bsdcat])], [], [enable_bsdcat=yes]) case "$enable_bsdcat" in yes) if test "$enable_static" = "no"; then static_bsdcat=no else static_bsdcat=yes fi build_bsdcat=yes ;; dynamic|shared) if test "$enable_shared" = "no"; then AC_MSG_FAILURE([Shared linking of bsdcat requires shared libarchive]) fi build_bsdcat=yes static_bsdcat=no ;; static) build_bsdcat=yes static_bsdcat=yes ;; no) build_bsdcat=no static_bsdcat=no ;; *) AC_MSG_FAILURE([Unsupported value for --enable-bsdcat]) ;; esac AM_CONDITIONAL([BUILD_BSDCAT], [ test "$build_bsdcat" = yes ]) AM_CONDITIONAL([STATIC_BSDCAT], [ test "$static_bsdcat" = yes ]) # # Options for building bsdcpio. # # Default is not to build bsdcpio, but that can be overridden. # AC_ARG_ENABLE([bsdcpio], [AS_HELP_STRING([--enable-bsdcpio], [enable build of bsdcpio (default)]) AS_HELP_STRING([--enable-bsdcpio=static], [static build of bsdcpio]) AS_HELP_STRING([--enable-bsdcpio=shared], [dynamic build of bsdcpio]) AS_HELP_STRING([--disable-bsdcpio], [disable build of bsdcpio])], [], [enable_bsdcpio=yes]) case "$enable_bsdcpio" in yes) if test "$enable_static" = "no"; then static_bsdcpio=no else static_bsdcpio=yes fi build_bsdcpio=yes ;; dynamic|shared) if test "$enabled_shared" = "no"; then AC_MSG_FAILURE([Shared linking of bsdcpio requires shared libarchive]) fi build_bsdcpio=yes ;; static) build_bsdcpio=yes static_bsdcpio=yes ;; no) build_bsdcpio=no static_bsdcpio=no ;; *) AC_MSG_FAILURE([Unsupported value for --enable-bsdcpio]) ;; esac AM_CONDITIONAL([BUILD_BSDCPIO], [ test "$build_bsdcpio" = yes ]) AM_CONDITIONAL([STATIC_BSDCPIO], [ test "$static_bsdcpio" = yes ]) # Set up defines needed before including any headers case $host in *mingw* | *cygwin* | *msys* ) AC_DEFINE([_WIN32_WINNT], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) AC_DEFINE([WINVER], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) AC_DEFINE([NTDDI_VERSION], 0x05020000, [Define to '0x05020000' for Windows Server 2003 APIs.]) ;; esac # Checks for header files. AC_HEADER_DIRENT AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([copyfile.h ctype.h]) AC_CHECK_HEADERS([errno.h ext2fs/ext2_fs.h fcntl.h grp.h]) AC_CACHE_CHECK([whether EXT2_IOC_GETFLAGS is usable], [ac_cv_have_decl_EXT2_IOC_GETFLAGS], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include @%:@include ], [int x = EXT2_IOC_GETFLAGS])], [AS_VAR_SET([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes])], [AS_VAR_SET([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [no])])]) AS_VAR_IF([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes], [AC_DEFINE_UNQUOTED([HAVE_WORKING_EXT2_IOC_GETFLAGS], [1], [Define to 1 if you have a working EXT2_IOC_GETFLAGS])]) AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h]) AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h linux/types.h]) + +AC_CACHE_CHECK([whether FS_IOC_GETFLAGS is usable], + [ac_cv_have_decl_FS_IOC_GETFLAGS], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include +@%:@include ], + [int x = FS_IOC_GETFLAGS])], + [AS_VAR_SET([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes])], + [AS_VAR_SET([ac_cv_have_decl_FS_IOC_GETFLAGS], [no])])]) + +AS_VAR_IF([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes], + [AC_DEFINE_UNQUOTED([HAVE_WORKING_FS_IOC_GETFLAGS], [1], + [Define to 1 if you have a working FS_IOC_GETFLAGS])]) + AC_CHECK_HEADERS([locale.h paths.h poll.h pthread.h pwd.h]) AC_CHECK_HEADERS([readpassphrase.h signal.h spawn.h]) AC_CHECK_HEADERS([stdarg.h stdint.h stdlib.h string.h]) AC_CHECK_HEADERS([sys/cdefs.h sys/extattr.h]) AC_CHECK_HEADERS([sys/ioctl.h sys/mkdev.h sys/mount.h]) AC_CHECK_HEADERS([sys/param.h sys/poll.h sys/select.h sys/statfs.h sys/statvfs.h]) AC_CHECK_HEADERS([sys/time.h sys/utime.h sys/utsname.h sys/vfs.h]) AC_CHECK_HEADERS([time.h unistd.h utime.h wchar.h wctype.h]) AC_CHECK_HEADERS([windows.h]) AC_CHECK_HEADERS([Bcrypt.h]) # check windows.h first; the other headers require it. AC_CHECK_HEADERS([wincrypt.h winioctl.h],[],[], [[#ifdef HAVE_WINDOWS_H # include #endif ]]) # Checks for libraries. AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib], [Don't build support for gzip through zlib])) if test "x$with_zlib" != "xno"; then AC_CHECK_HEADERS([zlib.h]) AC_CHECK_LIB(z,inflate) fi AC_ARG_WITH([bz2lib], AS_HELP_STRING([--without-bz2lib], [Don't build support for bzip2 through bz2lib])) if test "x$with_bz2lib" != "xno"; then AC_CHECK_HEADERS([bzlib.h]) case "$host_os" in *mingw* | *cygwin* | *msys*) dnl AC_CHECK_LIB cannot be used on the Windows port of libbz2, therefore dnl use AC_LINK_IFELSE. AC_MSG_CHECKING([for BZ2_bzDecompressInit in -lbz2]) old_LIBS="$LIBS" LIBS="-lbz2 $LIBS" AC_LINK_IFELSE( [AC_LANG_SOURCE(#include int main() { return BZ2_bzDecompressInit(NULL, 0, 0); })], [ac_cv_lib_bz2_BZ2_bzDecompressInit=yes], [ac_cv_lib_bz2_BZ2_bzDecompressInit=no]) LIBS="$old_LIBS" AC_MSG_RESULT($ac_cv_lib_bz2_BZ2_bzDecompressInit) if test "x$ac_cv_lib_bz2_BZ2_bzDecompressInit" = xyes; then AC_DEFINE([HAVE_LIBBZ2], [1], [Define to 1 if you have the `bz2' library (-lbz2).]) LIBS="-lbz2 $LIBS" fi ;; *) AC_CHECK_LIB(bz2,BZ2_bzDecompressInit) ;; esac fi AC_ARG_WITH([iconv], AS_HELP_STRING([--without-iconv], [Don't try to link against iconv])) if test "x$with_iconv" != "xno"; then AM_ICONV AC_CHECK_HEADERS([iconv.h],[],[],[#include ]) if test "x$am_cv_func_iconv" = "xyes"; then AC_CHECK_HEADERS([localcharset.h]) am_save_LIBS="$LIBS" LIBS="${LIBS} ${LIBICONV}" AC_CHECK_FUNCS([locale_charset]) LIBS="${am_save_LIBS}" if test "x$ac_cv_func_locale_charset" != "xyes"; then # If locale_charset() is not in libiconv, we have to find libcharset. AC_CHECK_LIB(charset,locale_charset) fi fi fi AC_ARG_WITH([lz4], AS_HELP_STRING([--without-lz4], [Don't build support for lz4 through liblz4])) if test "x$with_lz4" != "xno"; then AC_CHECK_HEADERS([lz4.h lz4hc.h]) AC_CHECK_LIB(lz4,LZ4_decompress_safe) fi AC_ARG_WITH([lzma], AS_HELP_STRING([--without-lzma], [Don't build support for xz through lzma])) if test "x$with_lzma" != "xno"; then AC_CHECK_HEADERS([lzma.h]) AC_CHECK_LIB(lzma,lzma_stream_decoder) # Some pre-release (but widely distributed) versions of liblzma # included a disabled version of lzma_stream_encoder_mt that # fools a naive AC_CHECK_LIB or AC_CHECK_FUNC, so we need # to do something more complex here: AC_CACHE_CHECK( [whether we have multithread support in lzma], ac_cv_lzma_has_mt, [AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ] [#if LZMA_VERSION < 50020000] [#error unsupported] [#endif]], [[lzma_stream_encoder_mt(0, 0);]])], [ac_cv_lzma_has_mt=yes], [ac_cv_lzma_has_mt=no])]) if test "x$ac_cv_lzma_has_mt" != xno; then AC_DEFINE([HAVE_LZMA_STREAM_ENCODER_MT], [1], [Define to 1 if you have the `lzma_stream_encoder_mt' function.]) fi fi AC_ARG_WITH([lzo2], AS_HELP_STRING([--with-lzo2], [Build with LZO support from liblzo2])) -if test "x$with_lzo2" == "xyes"; then +if test "x$with_lzo2" = "xyes"; then AC_CHECK_HEADERS([lzo/lzoconf.h lzo/lzo1x.h]) AC_CHECK_LIB(lzo2,lzo1x_decompress_safe) fi AC_ARG_WITH([nettle], AS_HELP_STRING([--without-nettle], [Don't build with crypto support from Nettle])) AC_ARG_WITH([openssl], AS_HELP_STRING([--without-openssl], [Don't build support for mtree and xar hashes through openssl])) case "$host_os" in *darwin* ) with_openssl=no ;; esac AC_ARG_WITH([xml2], AS_HELP_STRING([--without-xml2], [Don't build support for xar through libxml2])) AC_ARG_WITH([expat], AS_HELP_STRING([--without-expat], [Don't build support for xar through expat])) if test "x$with_xml2" != "xno"; then PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(LIBXML2_PC, [libxml-2.0], [ CPPFLAGS="${CPPFLAGS} ${LIBXML2_PC_CFLAGS}" LIBS="${LIBS} ${LIBXML2_PC_LIBS}" AC_CHECK_LIB(xml2,xmlInitParser,[true],AC_MSG_FAILURE(Missing xml2 library)) ], [ AC_CHECK_LIB(xml2,xmlInitParser) ]) AC_CHECK_HEADERS([libxml/xmlreader.h libxml/xmlwriter.h]) fi if test "x$ac_cv_header_libxml_xmlreader_h" != "xyes"; then if test "x$with_expat" != "xno"; then AC_CHECK_HEADERS([expat.h]) AC_CHECK_LIB(expat,XML_ParserCreate) fi fi AC_ARG_ENABLE([posix-regex-lib], [AS_HELP_STRING([--enable-posix-regex-lib], [choose what library to use for POSIX regular expression support (default: auto)]) AS_HELP_STRING([--enable-posix-regex-lib=libc], [use libc POSIX regular expression support]) AS_HELP_STRING([--enable-posix-regex-lib=libregex], [use libregex POSIX regular expression support]) AS_HELP_STRING([--enable-posix-regex-lib=libpcreposix], [use libpcreposix POSIX regular expression support]) AS_HELP_STRING([--disable-posix-regex-lib], [don't enable POSIX regular expression support])], [], [enable_posix_regex_lib=auto]) posix_regex_lib_found= if test "$enable_posix_regex_lib" = "auto" || test "$enable_posix_regex_lib" = "libc" || test "$enable_posix_regex_lib" = "libregex"; then AC_CHECK_HEADERS([regex.h]) if test "x$ac_cv_header_regex_h" != "xno"; then AC_CHECK_FUNC(regcomp) if test "x$ac_cv_func_regcomp" = xyes; then posix_regex_lib_found=1 else AC_CHECK_LIB(regex,regcomp) if test "x$ac_cv_lib_regex_regcomp" = xyes; then posix_regex_lib_found=1 fi fi fi fi if test -z $posix_regex_lib_found && (test "$enable_posix_regex_lib" = "auto" || test "$enable_posix_regex_lib" = "libpcreposix"); then AC_CHECK_HEADERS([pcreposix.h]) AC_CHECK_LIB(pcreposix,regcomp) if test "x$ac_cv_lib_pcreposix_regcomp" != xyes; then AC_MSG_NOTICE(trying libpcreposix check again with libpcre) unset ac_cv_lib_pcreposix_regcomp AC_CHECK_LIB(pcre,pcre_exec) AC_CHECK_LIB(pcreposix,regcomp) if test "x$ac_cv_lib_pcre_pcre_exec" = xyes && test "x$ac_cv_lib_pcreposix_regcomp" = xyes; then AC_MSG_CHECKING(if PCRE_STATIC needs to be defined) AC_LINK_IFELSE( [AC_LANG_SOURCE(#include int main() { return regcomp(NULL, NULL, 0); })], [without_pcre_static=yes], [without_pcre_static=no]) AC_LINK_IFELSE( [AC_LANG_SOURCE(#define PCRE_STATIC #include int main() { return regcomp(NULL, NULL, 0); })], [with_pcre_static=yes], [with_pcre_static=no]) if test "x$without_pcre_static" != xyes && test "x$with_pcre_static" = xyes; then AC_MSG_RESULT(yes) AC_DEFINE([PCRE_STATIC], [1], [Define to 1 if PCRE_STATIC needs to be defined.]) elif test "x$without_pcre_static" = xyes || test "x$with_pcre_static" = xyes; then AC_MSG_RESULT(no) fi posix_regex_lib_found=1 fi else posix_regex_lib_found=1 fi fi # TODO: Give the user the option of using a pre-existing system # libarchive. This will define HAVE_LIBARCHIVE which will cause # bsdtar_platform.h to use #include <...> for the libarchive headers. # Need to include Makefile.am magic to link against system # -larchive in that case. #AC_CHECK_LIB(archive,archive_version) # Checks for supported compiler flags AX_APPEND_COMPILE_FLAGS([-Wall -Wformat -Wformat-security]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST # la_TYPE_UID_T defaults to "int", which is incorrect for MinGW # and MSVC. Use a customized version. la_TYPE_UID_T AC_TYPE_MODE_T # AC_TYPE_OFF_T defaults to "long", which limits us to 4GB files on # most systems... default to "long long" instead. AC_CHECK_TYPE(off_t, [long long]) AC_TYPE_SIZE_T AC_CHECK_TYPE(id_t, [unsigned long]) AC_CHECK_TYPE(uintptr_t, [unsigned int]) # Check for tm_gmtoff in struct tm AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.__tm_gmtoff],,, [ #include ]) # Check for f_namemax in struct statfs AC_CHECK_MEMBERS([struct statfs.f_namemax],,, [ #include #include ]) # Check for f_iosize in struct statvfs AC_CHECK_MEMBERS([struct statvfs.f_iosize],,, [ #include ]) # Check for birthtime in struct stat AC_CHECK_MEMBERS([struct stat.st_birthtime]) # Check for high-resolution timestamps in struct stat AC_CHECK_MEMBERS([struct stat.st_birthtimespec.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtimespec.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtime_n]) # AIX AC_CHECK_MEMBERS([struct stat.st_umtime]) # Tru64 AC_CHECK_MEMBERS([struct stat.st_mtime_usec]) # Hurd # Check for block size support in struct stat AC_CHECK_MEMBERS([struct stat.st_blksize]) # Check for st_flags in struct stat (BSD fflags) AC_CHECK_MEMBERS([struct stat.st_flags]) # If you have uintmax_t, we assume printf supports %ju # If you have unsigned long long, we assume printf supports %llu # TODO: Check for %ju and %llu support directly. AC_CHECK_TYPES([uintmax_t, unsigned long long]) # We use C99-style integer types # Declare them if the local platform doesn't already do so. AC_TYPE_INTMAX_T AC_TYPE_UINTMAX_T AC_TYPE_INT64_T AC_TYPE_UINT64_T AC_TYPE_INT32_T AC_TYPE_UINT32_T AC_TYPE_INT16_T AC_TYPE_UINT16_T AC_TYPE_UINT8_T AC_CHECK_DECLS([SIZE_MAX, INT32_MAX, INT32_MIN]) AC_CHECK_DECLS([INT64_MAX, INT64_MIN, UINT64_MAX, UINT32_MAX]) AC_CHECK_DECLS([INTMAX_MAX, INTMAX_MIN, UINTMAX_MAX]) AC_CHECK_DECL([SSIZE_MAX], [AC_DEFINE(HAVE_DECL_SSIZE_MAX, 1, [Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you don't.])], [], [#include ]) AC_CHECK_DECL([EFTYPE], [AC_DEFINE(HAVE_EFTYPE, 1, [A possible errno value for invalid file format errors])], [], [#include ]) AC_CHECK_DECL([EILSEQ], [AC_DEFINE(HAVE_EILSEQ, 1, [A possible errno value for invalid file format errors])], [], [#include ]) AC_CHECK_TYPE([wchar_t], [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]wchar_t), 1, [Define to 1 if the system has the type `wchar_t'.])dnl AC_CHECK_SIZEOF([wchar_t])], []) AC_HEADER_TIME # Checks for library functions. AC_PROG_GCC_TRADITIONAL AC_HEADER_MAJOR AC_FUNC_FSEEKO AC_FUNC_MEMCMP AC_FUNC_LSTAT AC_FUNC_STAT AC_FUNC_STRERROR_R AC_FUNC_STRFTIME AC_FUNC_VPRINTF # check for: # CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES) # To avoid necessity for including windows.h or special forward declaration # workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *' AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *]) AC_CHECK_FUNCS([arc4random_buf chflags chown chroot ctime_r dirfd]) AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fork]) AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate]) AC_CHECK_FUNCS([futimens futimes futimesat]) AC_CHECK_FUNCS([geteuid getpid getgrgid_r getgrnam_r]) AC_CHECK_FUNCS([getpwnam_r getpwuid_r getvfsbyname gmtime_r]) AC_CHECK_FUNCS([lchflags lchmod lchown link localtime_r lstat lutimes]) AC_CHECK_FUNCS([mbrtowc memmove memset]) AC_CHECK_FUNCS([mkdir mkfifo mknod mkstemp]) AC_CHECK_FUNCS([nl_langinfo openat pipe poll posix_spawnp readlink readlinkat]) AC_CHECK_FUNCS([readpassphrase]) AC_CHECK_FUNCS([select setenv setlocale sigaction statfs statvfs]) AC_CHECK_FUNCS([strchr strdup strerror strncpy_s strrchr symlink timegm]) AC_CHECK_FUNCS([tzset unsetenv utime utimensat utimes vfork]) AC_CHECK_FUNCS([wcrtomb wcscmp wcscpy wcslen wctomb wmemcmp wmemcpy wmemmove]) AC_CHECK_FUNCS([_ctime64_s _fseeki64]) AC_CHECK_FUNCS([_get_timezone _localtime64_s _mkgmtime64]) # detects cygwin-1.7, as opposed to older versions AC_CHECK_FUNCS([cygwin_conv_path]) # DragonFly uses vfsconf, FreeBSD xvfsconf. AC_CHECK_TYPES(struct vfsconf,,, [#if HAVE_SYS_TYPES_H #include #endif #include ]) AC_CHECK_TYPES(struct xvfsconf,,, [#if HAVE_SYS_TYPES_H #include #endif #include ]) # There are several variants of readdir_r around; we only # accept the POSIX-compliant version. AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[DIR *dir; struct dirent e, *r; return(readdir_r(dir, &e, &r));]])], [AC_DEFINE(HAVE_READDIR_R,1,[Define to 1 if you have a POSIX compatible readdir_r])] ) # FreeBSD's nl_langinfo supports an option to specify whether the # current locale uses month/day or day/month ordering. It makes the # output a little prettier... AC_CHECK_DECL([D_MD_ORDER], [AC_DEFINE(HAVE_D_MD_ORDER, 1, [Define to 1 if nl_langinfo supports D_MD_ORDER])], [], [#if HAVE_LANGINFO_H #include #endif ]) # Check for dirent.d_namlen field explicitly # (This is a bit more straightforward than, if not quite as portable as, # the recipe given by the autoconf maintainers.) AC_CHECK_MEMBER(struct dirent.d_namlen,,, [#if HAVE_DIRENT_H #include #endif ]) # Check for Extended Attributes support AC_ARG_ENABLE([xattr], AS_HELP_STRING([--disable-xattr], [Disable Extended Attributes support (default: check)])) if test "x$enable_xattr" != "xno"; then AC_CHECK_HEADERS([attr/xattr.h]) AC_CHECK_HEADERS([sys/xattr.h sys/ea.h]) AC_SEARCH_LIBS([setxattr], [attr]) AC_CHECK_FUNCS([extattr_get_file extattr_list_file]) AC_CHECK_FUNCS([extattr_set_fd extattr_set_file]) AC_CHECK_FUNCS([fgetxattr flistxattr fsetxattr getxattr]) AC_CHECK_FUNCS([lgetxattr listxattr llistxattr lsetxattr]) AC_CHECK_FUNCS([fgetea flistea fsetea getea]) AC_CHECK_FUNCS([lgetea listea llistea lsetea]) AC_CHECK_DECLS([EXTATTR_NAMESPACE_USER], [], [], [#include #include ]) fi # Check for ACL support # # The ACL support in libarchive is written against the POSIX1e draft, # which was never officially approved and varies quite a bit across # platforms. Worse, some systems have completely non-POSIX acl functions, # which makes the following checks rather more complex than I would like. # AC_ARG_ENABLE([acl], AS_HELP_STRING([--disable-acl], [Disable ACL support (default: check)])) if test "x$enable_acl" != "xno"; then AC_CHECK_HEADERS([acl/libacl.h]) AC_CHECK_HEADERS([sys/acl.h]) AC_CHECK_LIB([acl],[acl_get_file]) AC_CHECK_FUNCS([acl_create_entry acl_get_fd_np]) AC_CHECK_FUNCS([acl_init acl_set_fd acl_set_fd_np acl_set_file]) AC_CHECK_TYPES(acl_permset_t,,, [#if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_ACL_H #include #endif ]) # The "acl_get_perm()" function was omitted from the POSIX draft. # (It's a pretty obvious oversight; otherwise, there's no way to # test for specific permissions in a permset.) Linux uses the obvious # name, FreeBSD adds _np to mark it as "non-Posix extension." # Test for both as a double-check that we really have POSIX-style ACL # support. AC_CHECK_FUNCS(acl_get_perm_np acl_get_perm acl_get_link acl_get_link_np,,, [#if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_ACL_H #include #endif ]) # Check for acl_is_trivial_np on FreeBSD AC_CHECK_FUNCS(acl_is_trivial_np,,, [#if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_ACL_H #include #endif ]) # Check for ACL_TYPE_NFS4 AC_CHECK_DECL([ACL_TYPE_NFS4], [AC_DEFINE(HAVE_ACL_TYPE_NFS4, 1, [True for FreeBSD with NFSv4 ACL support])], [], [#include ]) # MacOS has an acl.h that isn't POSIX. It can be detected by # checking for ACL_USER AC_CHECK_DECL([ACL_USER], [AC_DEFINE(HAVE_ACL_USER, 1, [True for systems with POSIX ACL support])], [], [#include ]) # MacOS has ACL_TYPE_EXTENDED instead AC_CHECK_DECL([ACL_TYPE_EXTENDED], [AC_DEFINE(HAVE_ACL_TYPE_EXTENDED, 1, [True for MacOS ACL support])], [], [#include #include ]) # Solaris and derivates ACLs AC_CHECK_LIB([sec], [acl_get]) AC_CHECK_TYPES([aclent_t], [], [], [[#include ]]) AC_CHECK_TYPES([ace_t], [], [], [[#include ]]) AC_CHECK_FUNCS(acl_get facl_get acl_set facl_set) fi # Additional requirements AC_SYS_LARGEFILE dnl NOTE: Crypto checks must run last. AC_DEFUN([CRYPTO_CHECK], [ if test "$found_$1" != yes; then saved_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I. -I$srcdir -I$srcdir/libarchive" touch "check_crypto_md.h" AC_MSG_CHECKING([support for ARCHIVE_CRYPTO_$1_$2]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #define ARCHIVE_$1_COMPILE_TEST #define ARCHIVE_CRYPTO_$1_$2 #define PLATFORM_CONFIG_H "check_crypto_md.h" $(cat "$srcdir/libarchive/archive_digest.c") int main(int argc, char **argv) { archive_$3_ctx ctx; archive_$3_init(&ctx); archive_$3_update(&ctx, *argv, argc); archive_$3_final(&ctx, NULL); return 0; } ])], [ AC_MSG_RESULT([yes]) found_$1=yes found_$2=yes AC_DEFINE(ARCHIVE_CRYPTO_$1_$2, 1, [ $1 via ARCHIVE_CRYPTO_$1_$2 supported.]) ], [ AC_MSG_RESULT([no])]) CPPFLAGS="$saved_CPPFLAGS" rm "check_crypto_md.h" fi ]) AC_DEFUN([CRYPTO_CHECK_WIN], [ if test "$found_$1" != yes; then AC_MSG_CHECKING([support for ARCHIVE_CRYPTO_$1_WIN]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #define ARCHIVE_$1_COMPILE_TEST #include #include int main(int argc, char **argv) { (void)argc; (void)argv; return ($2); } ])], [ AC_MSG_RESULT([yes]) found_$1=yes found_WIN=yes AC_DEFINE(ARCHIVE_CRYPTO_$1_WIN, 1, [ $1 via ARCHIVE_CRYPTO_$1_WIN supported.]) ], [ AC_MSG_RESULT([no])]) fi ]) case "$host_os" in *mingw* | *cygwin* | *msys*) ;; *) CRYPTO_CHECK(MD5, LIBC, md5) CRYPTO_CHECK(MD5, LIBSYSTEM, md5) CRYPTO_CHECK(RMD160, LIBC, rmd160) CRYPTO_CHECK(SHA1, LIBC, sha1) CRYPTO_CHECK(SHA1, LIBSYSTEM, sha1) CRYPTO_CHECK(SHA256, LIBC, sha256) CRYPTO_CHECK(SHA256, LIBC2, sha256) CRYPTO_CHECK(SHA256, LIBC3, sha256) CRYPTO_CHECK(SHA256, LIBSYSTEM, sha256) CRYPTO_CHECK(SHA384, LIBC, sha384) CRYPTO_CHECK(SHA384, LIBC2, sha384) CRYPTO_CHECK(SHA384, LIBC3, sha384) CRYPTO_CHECK(SHA384, LIBSYSTEM, sha384) CRYPTO_CHECK(SHA512, LIBC, sha512) CRYPTO_CHECK(SHA512, LIBC2, sha512) CRYPTO_CHECK(SHA512, LIBC3, sha512) CRYPTO_CHECK(SHA512, LIBSYSTEM, sha512) ;; esac if test "x$with_nettle" != "xno"; then AC_CHECK_HEADERS([nettle/md5.h nettle/ripemd160.h nettle/sha.h]) AC_CHECK_HEADERS([nettle/pbkdf2.h nettle/aes.h nettle/hmac.h]) saved_LIBS=$LIBS AC_CHECK_LIB(nettle,nettle_sha1_init) CRYPTO_CHECK(MD5, NETTLE, md5) CRYPTO_CHECK(RMD160, NETTLE, rmd160) CRYPTO_CHECK(SHA1, NETTLE, sha1) CRYPTO_CHECK(SHA256, NETTLE, sha256) CRYPTO_CHECK(SHA384, NETTLE, sha384) CRYPTO_CHECK(SHA512, NETTLE, sha512) if test "x$found_NETTLE" != "xyes"; then LIBS=$saved_LIBS fi fi if test "x$with_openssl" != "xno"; then AC_CHECK_HEADERS([openssl/evp.h]) saved_LIBS=$LIBS case "$host_os" in *mingw* | *cygwin* | *msys*) case "$host_cpu" in x86_64) AC_CHECK_LIB(eay64,OPENSSL_config) if test "x$ac_cv_lib_eay64_main" != "xyes"; then AC_CHECK_LIB(eay32,OPENSSL_config) fi ;; *) AC_CHECK_LIB(eay32,OPENSSL_config) ;; esac ;; *) AC_CHECK_LIB(crypto,OPENSSL_config) ;; esac CRYPTO_CHECK(MD5, OPENSSL, md5) CRYPTO_CHECK(RMD160, OPENSSL, rmd160) CRYPTO_CHECK(SHA1, OPENSSL, sha1) CRYPTO_CHECK(SHA256, OPENSSL, sha256) CRYPTO_CHECK(SHA384, OPENSSL, sha384) CRYPTO_CHECK(SHA512, OPENSSL, sha512) if test "x$found_OPENSSL" != "xyes"; then LIBS=$saved_LIBS else AC_CHECK_FUNCS([PKCS5_PBKDF2_HMAC_SHA1]) fi AC_CHECK_LIB(crypto,EVP_CIPHER_CTX_init) fi # Probe libmd AFTER OpenSSL/libcrypto. # The two are incompatible and OpenSSL is more complete. AC_CHECK_HEADERS([md5.h ripemd.h sha.h sha256.h sha512.h]) saved_LIBS=$LIBS AC_CHECK_LIB(md,MD5Init) CRYPTO_CHECK(MD5, LIBMD, md5) CRYPTO_CHECK(RMD160, LIBMD, rmd160) CRYPTO_CHECK(SHA1, LIBMD, sha1) CRYPTO_CHECK(SHA256, LIBMD, sha256) CRYPTO_CHECK(SHA512, LIBMD, sha512) if test "x$found_LIBMD" != "xyes"; then LIBS=$saved_LIBS fi case "$host_os" in *mingw* | *cygwin* | *msys*) CRYPTO_CHECK_WIN(MD5, CALG_MD5) CRYPTO_CHECK_WIN(SHA1, CALG_SHA1) CRYPTO_CHECK_WIN(SHA256, CALG_SHA_256) CRYPTO_CHECK_WIN(SHA384, CALG_SHA_384) CRYPTO_CHECK_WIN(SHA512, CALG_SHA_512) ;; esac # Ensure test directories are present if building out-of-tree AC_CONFIG_COMMANDS([mkdirs], [mkdir -p libarchive/test tar/test cat/test cpio/test]) AC_OUTPUT Index: vendor/libarchive/dist/cpio/test/main.c =================================================================== --- vendor/libarchive/dist/cpio/test/main.c (revision 314564) +++ vendor/libarchive/dist/cpio/test/main.c (nonexistent) @@ -1,3073 +0,0 @@ -/* - * 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" -#include "test_utils.h" -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_ICONV_H -#include -#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 -#include -#include -#ifdef HAVE_SIGNAL_H -#include -#endif -#include -#include - -/* - * This same file is used pretty much verbatim for all test harnesses. - * - * The next few lines are the only differences. - * TODO: Move this into a separate configuration header, have all test - * suites share one copy of this file. - */ -__FBSDID("$FreeBSD: src/usr.bin/cpio/test/main.c,v 1.3 2008/08/24 04:58:22 kientzle Exp $"); -#define KNOWNREF "test_option_f.cpio.uu" -#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */ -#define PROGRAM "bsdcpio" /* Name of program being tested. */ -#define PROGRAM_ALIAS "cpio" /* Generic alias for program */ -#undef LIBRARY /* Not testing a library. */ -#undef EXTRA_DUMP /* How to dump extra data */ -#undef EXTRA_ERRNO /* How to dump errno */ -/* How to generate extra version info. */ -#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") - -/* - * - * Windows support routines - * - * Note: Configuration is a tricky issue. Using HAVE_* feature macros - * in the test harness is dangerous because they cover up - * configuration errors. The classic example of this is omitting a - * configure check. If libarchive and libarchive_test both look for - * the same feature macro, such errors are hard to detect. Platform - * macros (e.g., _WIN32 or __GNUC__) are a little better, but can - * easily lead to very messy code. It's best to limit yourself - * to only the most generic programming techniques in the test harness - * and thus avoid conditionals altogether. Where that's not possible, - * try to minimize conditionals by grouping platform-specific tests in - * one place (e.g., test_acl_freebsd) or by adding new assert() - * functions (e.g., assertMakeHardlink()) to cover up platform - * differences. Platform-specific coding in libarchive_test is often - * a symptom that some capability is missing from libarchive itself. - */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#include -#include -#include -#ifndef F_OK -#define F_OK (0) -#endif -#ifndef S_ISDIR -#define S_ISDIR(m) ((m) & _S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(m) ((m) & _S_IFREG) -#endif -#if !defined(__BORLANDC__) -#define access _access -#undef chdir -#define chdir _chdir -#endif -#ifndef fileno -#define fileno _fileno -#endif -/*#define fstat _fstat64*/ -#if !defined(__BORLANDC__) -#define getcwd _getcwd -#endif -#define lstat stat -/*#define lstat _stat64*/ -/*#define stat _stat64*/ -#define rmdir _rmdir -#if !defined(__BORLANDC__) -#define strdup _strdup -#define umask _umask -#endif -#define int64_t __int64 -#endif - -#if defined(HAVE__CrtSetReportMode) -# include -#endif - -mode_t umasked(mode_t expected_mode) -{ - mode_t mode = umask(0); - umask(mode); - return expected_mode & ~mode; -} - -/* Path to working directory for current test */ -const char *testworkdir; -#ifdef PROGRAM -/* Pathname of exe to be tested. */ -const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -const char *testprog; -#endif - -#if defined(_WIN32) && !defined(__CYGWIN__) -static void *GetFunctionKernel32(const char *); -static int my_CreateSymbolicLinkA(const char *, const char *, int); -static int my_CreateHardLinkA(const char *, const char *); -static int my_GetFileInformationByName(const char *, - BY_HANDLE_FILE_INFORMATION *); - -static void * -GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary("kernel32.dll"); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} - -static int -my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateSymbolicLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, flags); -} - -static int -my_CreateHardLinkA(const char *linkname, const char *target) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateHardLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, NULL); -} - -static int -my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) -{ - HANDLE h; - int r; - - memset(bhfi, 0, sizeof(*bhfi)); - h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) - return (0); - r = GetFileInformationByHandle(h, bhfi); - CloseHandle(h); - return (r); -} -#endif - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) -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 - -/* - * - * OPTIONS FLAGS - * - */ - -/* Enable core dump on failure. */ -static int dump_on_failure = 0; -/* Default is to remove temp dirs and log data for successful tests. */ -static int keep_temp_files = 0; -/* Default is to run the specified tests once and report errors. */ -static int until_failure = 0; -/* Default is to just report pass/fail for each test. */ -static int verbosity = 0; -#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ -#define VERBOSITY_PASSFAIL 0 /* Default */ -#define VERBOSITY_LIGHT_REPORT 1 /* -v */ -#define VERBOSITY_FULL 2 /* -vv */ -/* A few places generate even more output for verbosity > VERBOSITY_FULL, - * mostly for debugging the test harness itself. */ -/* Cumulative count of assertion failures. */ -static int failures = 0; -/* Cumulative count of reported skips. */ -static int skips = 0; -/* Cumulative count of assertions checked. */ -static int assertions = 0; - -/* Directory where uuencoded reference files can be found. */ -static const char *refdir; - -/* - * Report log information selectively to console and/or disk log. - */ -static int log_console = 0; -static FILE *logfile; -static void -vlogprintf(const char *fmt, va_list ap) -{ -#ifdef va_copy - va_list lfap; - va_copy(lfap, ap); -#endif - if (log_console) - vfprintf(stdout, fmt, ap); - if (logfile != NULL) -#ifdef va_copy - vfprintf(logfile, fmt, lfap); - va_end(lfap); -#else - vfprintf(logfile, fmt, ap); -#endif -} - -static void -logprintf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vlogprintf(fmt, ap); - va_end(ap); -} - -/* Set up a message to display only if next assertion fails. */ -static char msgbuff[4096]; -static const char *msg, *nextmsg; -void -failure(const char *fmt, ...) -{ - va_list ap; - if (fmt == NULL) { - nextmsg = NULL; - } else { - va_start(ap, fmt); - vsprintf(msgbuff, fmt, ap); - va_end(ap); - nextmsg = msgbuff; - } -} - -/* - * Copy arguments into file-local variables. - * This was added to permit vararg assert() functions without needing - * variadic wrapper macros. Turns out that the vararg capability is almost - * never used, so almost all of the vararg assertions can be simplified - * by removing the vararg capability and reworking the wrapper macro to - * pass __FILE__, __LINE__ directly into the function instead of using - * this hook. I suspect this machinery is used so rarely that we - * would be better off just removing it entirely. That would simplify - * the code here noticeably. - */ -static const char *skipping_filename; -static int skipping_line; -void skipping_setup(const char *filename, int line) -{ - skipping_filename = filename; - skipping_line = line; -} - -/* Called at the beginning of each assert() function. */ -static void -assertion_count(const char *file, int line) -{ - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - ++assertions; - /* Proper handling of "failure()" message. */ - msg = nextmsg; - nextmsg = NULL; - /* Uncomment to print file:line after every assertion. - * Verbose, but occasionally useful in tracking down crashes. */ - /* printf("Checked %s:%d\n", file, line); */ -} - -/* - * For each test source file, we remember how many times each - * assertion was reported. Cleared before each new test, - * used by test_summarize(). - */ -static struct line { - int count; - int skip; -} failed_lines[10000]; -const char *failed_filename; - -/* Count this failure, setup up log destination and handle initial report. */ -static void -failure_start(const char *filename, int line, const char *fmt, ...) -{ - va_list ap; - - /* Record another failure for this line. */ - ++failures; - failed_filename = filename; - failed_lines[line].count++; - - /* Determine whether to log header to console. */ - switch (verbosity) { - case VERBOSITY_LIGHT_REPORT: - log_console = (failed_lines[line].count < 2); - break; - default: - log_console = (verbosity >= VERBOSITY_FULL); - } - - /* Log file:line header for this failure */ - va_start(ap, fmt); -#if _MSC_VER - logprintf("%s(%d): ", filename, line); -#else - logprintf("%s:%d: ", filename, line); -#endif - vlogprintf(fmt, ap); - va_end(ap); - logprintf("\n"); - - if (msg != NULL && msg[0] != '\0') { - logprintf(" Description: %s\n", msg); - msg = NULL; - } - - /* Determine whether to log details to console. */ - if (verbosity == VERBOSITY_LIGHT_REPORT) - log_console = 0; -} - -/* Complete reporting of failed tests. */ -/* - * The 'extra' hook here is used by libarchive to include libarchive - * error messages with assertion failures. It could also be used - * to add strerror() output, for example. Just define the EXTRA_DUMP() - * macro appropriately. - */ -static void -failure_finish(void *extra) -{ - (void)extra; /* UNUSED (maybe) */ -#ifdef EXTRA_DUMP - if (extra != NULL) { - logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); - logprintf(" detail: %s\n", EXTRA_DUMP(extra)); - } -#endif - - if (dump_on_failure) { - fprintf(stderr, - " *** forcing core dump so failure can be debugged ***\n"); - abort(); - } -} - -/* Inform user that we're skipping some checks. */ -void -test_skipping(const char *fmt, ...) -{ - char buff[1024]; - va_list ap; - - va_start(ap, fmt); - vsprintf(buff, fmt, ap); - va_end(ap); - /* Use failure() message if set. */ - msg = nextmsg; - nextmsg = NULL; - /* failure_start() isn't quite right, but is awfully convenient. */ - failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); - --failures; /* Undo failures++ in failure_start() */ - /* Don't failure_finish() here. */ - /* Mark as skip, so doesn't count as failed test. */ - failed_lines[skipping_line].skip = 1; - ++skips; -} - -/* - * - * ASSERTIONS - * - */ - -/* Generic assert() just displays the failed condition. */ -int -assertion_assert(const char *file, int line, int value, - const char *condition, void *extra) -{ - assertion_count(file, line); - if (!value) { - failure_start(file, line, "Assertion failed: %s", condition); - failure_finish(extra); - } - return (value); -} - -/* chdir() and report any errors */ -int -assertion_chdir(const char *file, int line, const char *pathname) -{ - assertion_count(file, line); - if (chdir(pathname) == 0) - return (1); - failure_start(file, line, "chdir(\"%s\")", pathname); - failure_finish(NULL); - return (0); - -} - -/* Verify two integers are equal. */ -int -assertion_equal_int(const char *file, int line, - long long v1, const char *e1, long long v2, const char *e2, void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); - failure_finish(extra); - return (0); -} - -/* - * Utility to convert a single UTF-8 sequence. - */ -static int -_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) -{ - static const char utf8_count[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ - }; - int ch; - int cnt; - uint32_t wc; - - *pwc = 0; - - /* Sanity check. */ - if (n == 0) - return (0); - /* - * 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. */ - cnt = utf8_count[ch]; - - /* Invalid sequence or there are not plenty bytes. */ - if (n < (size_t)cnt) - return (-1); - - /* Make a Unicode code point from a single UTF-8 sequence. */ - switch (cnt) { - case 1: /* 1 byte sequence. */ - *pwc = ch & 0x7f; - return (cnt); - case 2: /* 2 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); - return (cnt); - case 3: /* 3 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - if ((s[2] & 0xc0) != 0x80) return (-1); - wc = ((ch & 0x0f) << 12) - | ((s[1] & 0x3f) << 6) - | (s[2] & 0x3f); - if (wc < 0x800) - return (-1);/* Overlong sequence. */ - break; - case 4: /* 4 bytes sequence. */ - 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); - wc = ((ch & 0x07) << 18) - | ((s[1] & 0x3f) << 12) - | ((s[2] & 0x3f) << 6) - | (s[3] & 0x3f); - if (wc < 0x10000) - return (-1);/* Overlong sequence. */ - break; - default: - return (-1); - } - - /* The code point larger than 0x10FFFF is not legal - * Unicode values. */ - if (wc > 0x10FFFF) - return (-1); - /* Correctly gets a Unicode, returns used bytes. */ - *pwc = wc; - return (cnt); -} - -static void strdump(const char *e, const char *p, int ewidth, int utf8) -{ - const char *q = p; - - logprintf(" %*s = ", ewidth, e); - if (p == NULL) { - logprintf("NULL\n"); - return; - } - logprintf("\""); - while (*p != '\0') { - unsigned int c = 0xff & *p++; - switch (c) { - case '\a': logprintf("\\a"); break; - case '\b': logprintf("\\b"); break; - case '\n': logprintf("\\n"); break; - case '\r': logprintf("\\r"); break; - default: - if (c >= 32 && c < 127) - logprintf("%c", c); - else - logprintf("\\x%02X", c); - } - } - logprintf("\""); - logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); - - /* - * If the current string is UTF-8, dump its code points. - */ - if (utf8) { - size_t len; - uint32_t uc; - int n; - int cnt = 0; - - p = q; - len = strlen(p); - logprintf(" ["); - while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { - if (p != q) - logprintf(" "); - logprintf("%04X", uc); - p += n; - len -= n; - cnt++; - } - logprintf("]"); - logprintf(" (count %d", cnt); - if (n < 0) { - logprintf(",unknown %d bytes", len); - } - logprintf(")"); - - } - logprintf("\n"); -} - -/* Verify two strings are equal, dump them if not. */ -int -assertion_equal_string(const char *file, int line, - const char *v1, const char *e1, - const char *v2, const char *e2, - void *extra, int utf8) -{ - int l1, l2; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - l1 = (int)strlen(e1); - l2 = (int)strlen(e2); - if (l1 < l2) - l1 = l2; - strdump(e1, v1, l1, utf8); - strdump(e2, v2, l1, utf8); - failure_finish(extra); - return (0); -} - -static void -wcsdump(const char *e, const wchar_t *w) -{ - logprintf(" %s = ", e); - if (w == NULL) { - logprintf("(null)"); - return; - } - logprintf("\""); - while (*w != L'\0') { - unsigned int c = *w++; - if (c >= 32 && c < 127) - logprintf("%c", c); - else if (c < 256) - logprintf("\\x%02X", c); - else if (c < 0x10000) - logprintf("\\u%04X", c); - else - logprintf("\\U%08X", c); - } - logprintf("\"\n"); -} - -#ifndef HAVE_WCSCMP -static int -wcscmp(const wchar_t *s1, const wchar_t *s2) -{ - - while (*s1 == *s2++) { - if (*s1++ == L'\0') - return 0; - } - if (*s1 > *--s2) - return 1; - else - return -1; -} -#endif - -/* Verify that two wide strings are equal, dump them if not. */ -int -assertion_equal_wstring(const char *file, int line, - const wchar_t *v1, const char *e1, - const wchar_t *v2, const char *e2, - void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - wcsdump(e1, v1); - wcsdump(e2, v2); - failure_finish(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; - - if (p == NULL) { - logprintf("(null)\n"); - return; - } - for(i=0; i < l; i+=16) { - logprintf("%04x", (unsigned)(i + offset)); - sep = ' '; - for (j = 0; j < 16 && i + j < l; j++) { - if (ref != NULL && p[i + j] != ref[i + j]) - sep = '_'; - logprintf("%c%02x", sep, 0xff & (int)p[i+j]); - if (ref != NULL && p[i + j] == ref[i + j]) - sep = ' '; - } - for (; j < 16; j++) { - logprintf("%c ", sep); - sep = ' '; - } - logprintf("%c", sep); - for (j=0; j < 16 && i + j < l; j++) { - int c = p[i + j]; - if (c >= ' ' && c <= 126) - logprintf("%c", c); - else - logprintf("."); - } - logprintf("\n"); - } -} - -/* Verify that two blocks of memory are the same, display the first - * block of differences if they're not. */ -int -assertion_equal_mem(const char *file, int line, - const void *_v1, const char *e1, - const void *_v2, const char *e2, - size_t l, const char *ld, void *extra) -{ - const char *v1 = (const char *)_v1; - const char *v2 = (const char *)_v2; - size_t offset; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) - return (1); - if (v1 == NULL || v2 == NULL) - return (0); - - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" size %s = %d\n", ld, (int)l); - /* Dump 48 bytes (3 lines) so that the first difference is - * in the second line. */ - offset = 0; - while (l > 64 && memcmp(v1, v2, 32) == 0) { - /* Two lines agree, so step forward one line. */ - v1 += 16; - v2 += 16; - l -= 16; - offset += 16; - } - logprintf(" Dump of %s\n", e1); - hexdump(v1, v2, l < 128 ? l : 128, offset); - logprintf(" Dump of %s\n", e2); - hexdump(v2, v1, l < 128 ? l : 128, offset); - logprintf("\n"); - failure_finish(extra); - return (0); -} - -/* Verify that a block of memory is filled with the specified byte. */ -int -assertion_memory_filled_with(const char *file, int line, - const void *_v1, const char *vd, - size_t l, const char *ld, - char b, const char *bd, void *extra) -{ - const char *v1 = (const char *)_v1; - size_t c = 0; - size_t i; - (void)ld; /* UNUSED */ - - assertion_count(file, line); - - for (i = 0; i < l; ++i) { - if (v1[i] == b) { - ++c; - } - } - if (c == l) - return (1); - - failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); - logprintf(" Only %d bytes were correct\n", (int)c); - failure_finish(extra); - return (0); -} - -/* Verify that the named file exists and is empty. */ -int -assertion_empty_file(const char *filename, int line, const char *f1) -{ - char buff[1024]; - struct stat st; - ssize_t s; - FILE *f; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) - return (1); - - failure_start(filename, line, "File should be empty: %s", f1); - logprintf(" File size: %d\n", (int)st.st_size); - logprintf(" Contents:\n"); - f = fopen(f1, "rb"); - if (f == NULL) { - logprintf(" Unable to open %s\n", f1); - } else { - s = ((off_t)sizeof(buff) < st.st_size) ? - (ssize_t)sizeof(buff) : (ssize_t)st.st_size; - s = fread(buff, 1, s, f); - hexdump(buff, NULL, s, 0); - fclose(f); - } - failure_finish(NULL); - return (0); -} - -/* Verify that the named file exists and is not empty. */ -int -assertion_non_empty_file(const char *filename, int line, const char *f1) -{ - struct stat st; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) { - failure_start(filename, line, "File empty: %s", f1); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify that two files have the same contents. */ -/* TODO: hexdump the first bytes that actually differ. */ -int -assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) -{ - char buff1[1024]; - char buff2[1024]; - FILE *f1, *f2; - int n1, n2; - - assertion_count(filename, line); - - f1 = fopen(fn1, "rb"); - f2 = fopen(fn2, "rb"); - if (f1 == NULL || f2 == NULL) { - if (f1) fclose(f1); - if (f2) fclose(f2); - return (0); - } - for (;;) { - n1 = (int)fread(buff1, 1, sizeof(buff1), f1); - n2 = (int)fread(buff2, 1, sizeof(buff2), f2); - if (n1 != n2) - break; - if (n1 == 0 && n2 == 0) { - fclose(f1); - fclose(f2); - return (1); - } - if (memcmp(buff1, buff2, n1) != 0) - break; - } - fclose(f1); - fclose(f2); - failure_start(filename, line, "Files not identical"); - logprintf(" file1=\"%s\"\n", fn1); - logprintf(" file2=\"%s\"\n", fn2); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file does exist. */ -int -assertion_file_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (!_access(f, 0)) - return (1); -#else - if (!access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file doesn't exist. */ -int -assertion_file_not_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (_access(f, 0)) - return (1); -#else - if (access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should not exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Compare the contents of a file to a block of memory. */ -int -assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) -{ - char *contents; - FILE *f; - int n; - - assertion_count(filename, line); - - f = fopen(fn, "rb"); - if (f == NULL) { - failure_start(filename, line, - "File should exist: %s", fn); - failure_finish(NULL); - return (0); - } - contents = malloc(s * 2); - n = (int)fread(contents, 1, s * 2, f); - fclose(f); - if (n == s && memcmp(buff, contents, s) == 0) { - free(contents); - return (1); - } - failure_start(filename, line, "File contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) - hexdump(contents, buff, n > 512 ? 512 : n, 0); - else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s > 512 ? 512 : s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Check the contents of a text file, being tolerant of line endings. */ -int -assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) -{ - char *contents; - const char *btxt, *ftxt; - FILE *f; - int n, s; - - assertion_count(filename, line); - f = fopen(fn, "r"); - if (f == NULL) { - failure_start(filename, line, - "File doesn't exist: %s", fn); - failure_finish(NULL); - return (0); - } - s = (int)strlen(buff); - contents = malloc(s * 2 + 128); - n = (int)fread(contents, 1, s * 2 + 128 - 1, f); - if (n >= 0) - contents[n] = '\0'; - fclose(f); - /* Compare texts. */ - btxt = buff; - ftxt = (const char *)contents; - while (*btxt != '\0' && *ftxt != '\0') { - if (*btxt == *ftxt) { - ++btxt; - ++ftxt; - continue; - } - if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { - /* Pass over different new line characters. */ - ++btxt; - ftxt += 2; - continue; - } - break; - } - if (*btxt == '\0' && *ftxt == '\0') { - free(contents); - return (1); - } - failure_start(filename, line, "Contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) { - hexdump(contents, buff, n, 0); - logprintf(" expected\n", fn); - hexdump(buff, contents, s, 0); - } else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Verify that a text file contains the specified lines, regardless of order */ -/* This could be more efficient if we sorted both sets of lines, etc, but - * since this is used only for testing and only ever deals with a dozen or so - * lines at a time, this relatively crude approach is just fine. */ -int -assertion_file_contains_lines_any_order(const char *file, int line, - const char *pathname, const char *lines[]) -{ - char *buff; - size_t buff_size; - size_t expected_count, actual_count, i, j; - char **expected = NULL; - char *p, **actual = NULL; - char c; - int expected_failure = 0, actual_failure = 0; - - assertion_count(file, line); - - buff = slurpfile(&buff_size, "%s", pathname); - if (buff == NULL) { - failure_start(pathname, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - /* Make a copy of the provided lines and count up the expected - * file size. */ - for (i = 0; lines[i] != NULL; ++i) { - } - expected_count = i; - if (expected_count) { - expected = malloc(sizeof(char *) * expected_count); - if (expected == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - free(expected); - return (0); - } - for (i = 0; lines[i] != NULL; ++i) { - expected[i] = strdup(lines[i]); - } - } - - /* Break the file into lines */ - actual_count = 0; - for (c = '\0', p = buff; p < buff + buff_size; ++p) { - if (*p == '\x0d' || *p == '\x0a') - *p = '\0'; - if (c == '\0' && *p != '\0') - ++actual_count; - c = *p; - } - if (actual_count) { - actual = calloc(sizeof(char *), actual_count); - if (actual == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - free(expected); - return (0); - } - for (j = 0, p = buff; p < buff + buff_size; - p += 1 + strlen(p)) { - if (*p != '\0') { - actual[j] = p; - ++j; - } - } - } - - /* Erase matching lines from both lists */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] == NULL) - continue; - for (j = 0; j < actual_count; ++j) { - if (actual[j] == NULL) - continue; - if (strcmp(expected[i], actual[j]) == 0) { - free(expected[i]); - expected[i] = NULL; - actual[j] = NULL; - break; - } - } - } - - /* If there's anything left, it's a failure */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) - ++expected_failure; - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - ++actual_failure; - } - if (expected_failure == 0 && actual_failure == 0) { - free(buff); - free(expected); - free(actual); - return (1); - } - failure_start(file, line, "File doesn't match: %s", pathname); - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) { - logprintf(" Expected but not present: %s\n", expected[i]); - free(expected[i]); - } - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - logprintf(" Present but not expected: %s\n", actual[j]); - } - failure_finish(NULL); - free(buff); - free(expected); - free(actual); - return (0); -} - -/* Verify that a text file does not contains the specified strings */ -int -assertion_file_contains_no_invalid_strings(const char *file, int line, - const char *pathname, const char *strings[]) -{ - char *buff; - int i; - - buff = slurpfile(NULL, "%s", pathname); - if (buff == NULL) { - failure_start(file, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - for (i = 0; strings[i] != NULL; ++i) { - if (strstr(buff, strings[i]) != NULL) { - failure_start(file, line, "Invalid string in %s: %s", pathname, - strings[i]); - failure_finish(NULL); - free(buff); - return(0); - } - } - - free(buff); - return (0); -} - -/* Test that two paths point to the same file. */ -/* As a side-effect, asserts that both files exist. */ -static int -is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(path1, &bhfi1); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path1); - failure_finish(NULL); - return (0); - } - r = my_GetFileInformationByName(path2, &bhfi2); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path2); - failure_finish(NULL); - return (0); - } - return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber - && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh - && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); -#else - struct stat st1, st2; - int r; - - assertion_count(file, line); - r = lstat(path1, &st1); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path1); - failure_finish(NULL); - return (0); - } - r = lstat(path2, &st2); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path2); - failure_finish(NULL); - return (0); - } - return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); -#endif -} - -int -assertion_is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s are not hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -int -assertion_is_not_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (!is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s should not be hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -/* Verify a/b/mtime of 'pathname'. */ -/* If 'recent', verify that it's within last 10 seconds. */ -static int -assertion_file_time(const char *file, int line, - const char *pathname, long t, long nsec, char type, int recent) -{ - long long filet, filet_nsec; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define EPOC_TIME (116444736000000000ULL) - FILETIME fxtime, fbirthtime, fatime, fmtime; - ULARGE_INTEGER wintm; - HANDLE h; - fxtime.dwLowDateTime = 0; - fxtime.dwHighDateTime = 0; - - assertion_count(file, line); - /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open - * a directory file. If not, CreateFile() will fail when - * the pathname is a directory. */ - h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); - switch (type) { - case 'a': fxtime = fatime; break; - case 'b': fxtime = fbirthtime; break; - case 'm': fxtime = fmtime; break; - } - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't GetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - wintm.LowPart = fxtime.dwLowDateTime; - wintm.HighPart = fxtime.dwHighDateTime; - filet = (wintm.QuadPart - EPOC_TIME) / 10000000; - filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; - nsec = (nsec / 100) * 100; /* Round the request */ -#else - struct stat st; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - switch (type) { - case 'a': filet = st.st_atime; break; - case 'm': filet = st.st_mtime; break; - case 'b': filet = 0; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } -#if defined(__FreeBSD__) - switch (type) { - case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; - case 'b': filet = st.st_birthtime; - /* FreeBSD filesystems that don't support birthtime - * (e.g., UFS1) always return -1 here. */ - if (filet == -1) { - return (1); - } - filet_nsec = st.st_birthtimespec.tv_nsec; break; - case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } - /* FreeBSD generally only stores to microsecond res, so round. */ - filet_nsec = (filet_nsec / 1000) * 1000; - nsec = (nsec / 1000) * 1000; -#else - filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ - if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ -#if defined(__HAIKU__) - if (type == 'a') return (1); /* Haiku doesn't have atime. */ -#endif -#endif -#endif - if (recent) { - /* Check that requested time is up-to-date. */ - time_t now = time(NULL); - if (filet < now - 10 || filet > now + 1) { - failure_start(file, line, - "File %s has %ctime %lld, %lld seconds ago\n", - pathname, type, filet, now - filet); - failure_finish(NULL); - return (0); - } - } else if (filet != t || filet_nsec != nsec) { - failure_start(file, line, - "File %s has %ctime %lld.%09lld, expected %lld.%09lld", - pathname, type, filet, filet_nsec, t, nsec); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify atime of 'pathname'. */ -int -assertion_file_atime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); -} - -/* Verify atime of 'pathname' is up-to-date. */ -int -assertion_file_atime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); -} - -/* Verify birthtime of 'pathname'. */ -int -assertion_file_birthtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); -} - -/* Verify birthtime of 'pathname' is up-to-date. */ -int -assertion_file_birthtime_recent(const char *file, int line, - const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); -} - -/* Verify mode of 'pathname'. */ -int -assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) -{ - int mode; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - failure_start(file, line, "assertFileMode not yet implemented for Windows"); - (void)mode; /* UNUSED */ - (void)r; /* UNUSED */ -#else - { - struct stat st; - r = lstat(pathname, &st); - mode = (int)(st.st_mode & 0777); - } - if (r == 0 && mode == expected_mode) - return (1); - failure_start(file, line, "File %s has mode %o, expected %o", - pathname, mode, expected_mode); -#endif - failure_finish(NULL); - return (0); -} - -/* Verify mtime of 'pathname'. */ -int -assertion_file_mtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); -} - -/* Verify mtime of 'pathname' is up-to-date. */ -int -assertion_file_mtime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); -} - -/* Verify number of links to 'pathname'. */ -int -assertion_file_nlinks(const char *file, int line, - const char *pathname, int nlinks) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(pathname, &bhfi); - if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, bhfi.nNumberOfLinks, nlinks); - failure_finish(NULL); - return (0); -#else - struct stat st; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r == 0 && (int)st.st_nlink == nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, st.st_nlink, nlinks); - failure_finish(NULL); - return (0); -#endif -} - -/* Verify size of 'pathname'. */ -int -assertion_file_size(const char *file, int line, const char *pathname, long size) -{ - int64_t filesize; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - { - BY_HANDLE_FILE_INFORMATION bhfi; - r = !my_GetFileInformationByName(pathname, &bhfi); - filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; - } -#else - { - struct stat st; - r = lstat(pathname, &st); - filesize = st.st_size; - } -#endif - if (r == 0 && filesize == size) - return (1); - failure_start(file, line, "File %s has size %ld, expected %ld", - pathname, (long)filesize, (long)size); - failure_finish(NULL); - return (0); -} - -/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ -int -assertion_is_dir(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Dir should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISDIR(st.st_mode)) { - failure_start(file, line, "%s is not a dir", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "Dir %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, - * verify that too. */ -int -assertion_is_reg(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0 || !S_ISREG(st.st_mode)) { - failure_start(file, line, "File should exist: %s", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "File %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ -static int -is_symlink(const char *file, int line, - const char *pathname, const char *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)pathname; /* UNUSED */ - (void)contents; /* UNUSED */ - assertion_count(file, line); - /* Windows sort-of has real symlinks, but they're only usable - * by privileged users and are crippled even then, so there's - * really not much point in bothering with this. */ - return (0); -#else - char buff[300]; - struct stat st; - ssize_t linklen; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, - "Symlink should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISLNK(st.st_mode)) - return (0); - if (contents == NULL) - return (1); - linklen = readlink(pathname, buff, sizeof(buff)); - if (linklen < 0) { - failure_start(file, line, "Can't read symlink %s", pathname); - failure_finish(NULL); - return (0); - } - buff[linklen] = '\0'; - if (strcmp(buff, contents) != 0) - return (0); - return (1); -#endif -} - -/* Assert that path is a symlink that (optionally) contains contents. */ -int -assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) -{ - if (is_symlink(file, line, path, contents)) - return (1); - if (contents) - failure_start(file, line, "File %s is not a symlink to %s", - path, contents); - else - failure_start(file, line, "File %s is not a symlink", path); - failure_finish(NULL); - return (0); -} - - -/* Create a directory and report any errors. */ -int -assertion_make_dir(const char *file, int line, const char *dirname, int mode) -{ - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ - if (0 == _mkdir(dirname)) - return (1); -#else - if (0 == mkdir(dirname, mode)) { - if (0 == chmod(dirname, mode)) { - assertion_file_mode(file, line, dirname, mode); - return (1); - } - } -#endif - failure_start(file, line, "Could not create directory %s", dirname); - failure_finish(NULL); - return(0); -} - -/* Create a file with the specified contents and report any failures. */ -int -assertion_make_file(const char *file, int line, - const char *path, int mode, int csize, const void *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - /* TODO: Rework this to set file mode as well. */ - FILE *f; - (void)mode; /* UNUSED */ - assertion_count(file, line); - f = fopen(path, "wb"); - if (f == NULL) { - failure_start(file, line, "Could not create file %s", path); - failure_finish(NULL); - return (0); - } - if (contents != NULL) { - size_t wsize; - - if (csize < 0) - wsize = strlen(contents); - else - wsize = (size_t)csize; - if (wsize != fwrite(contents, 1, wsize, f)) { - fclose(f); - failure_start(file, line, - "Could not write file %s", path); - failure_finish(NULL); - return (0); - } - } - fclose(f); - return (1); -#else - int fd; - assertion_count(file, line); - fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); - if (fd < 0) { - failure_start(file, line, "Could not create %s", path); - failure_finish(NULL); - return (0); - } - if (0 != chmod(path, mode)) { - failure_start(file, line, "Could not chmod %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - if (contents != NULL) { - ssize_t wsize; - - if (csize < 0) - wsize = (ssize_t)strlen(contents); - else - wsize = (ssize_t)csize; - if (wsize != write(fd, contents, wsize)) { - close(fd); - failure_start(file, line, - "Could not write to %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - } - close(fd); - assertion_file_mode(file, line, path, mode); - return (1); -#endif -} - -/* Create a hardlink and report any failures. */ -int -assertion_make_hardlink(const char *file, int line, - const char *newpath, const char *linkto) -{ - int succeeded; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - succeeded = my_CreateHardLinkA(newpath, linkto); -#elif HAVE_LINK - succeeded = !link(linkto, newpath); -#else - succeeded = 0; -#endif - if (succeeded) - return (1); - failure_start(file, line, "Could not create hardlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Create a symlink and report any failures. */ -int -assertion_make_symlink(const char *file, int line, - const char *newpath, const char *linkto) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - int targetIsDir = 0; /* TODO: Fix this */ - assertion_count(file, line); - if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) - return (1); -#elif HAVE_SYMLINK - assertion_count(file, line); - if (0 == symlink(linkto, newpath)) - return (1); -#endif - failure_start(file, line, "Could not create symlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Set umask, report failures. */ -int -assertion_umask(const char *file, int line, int mask) -{ - assertion_count(file, line); - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - umask(mask); - return (1); -} - -/* Set times, report failures. */ -int -assertion_utimes(const char *file, int line, - const char *pathname, long at, long at_nsec, long mt, long mt_nsec) -{ - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ - + (((nsec)/1000)*10)) - HANDLE h; - ULARGE_INTEGER wintm; - FILETIME fatime, fmtime; - FILETIME *pat, *pmt; - - assertion_count(file, line); - h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (at > 0 || at_nsec > 0) { - wintm.QuadPart = WINTIME(at, at_nsec); - fatime.dwLowDateTime = wintm.LowPart; - fatime.dwHighDateTime = wintm.HighPart; - pat = &fatime; - } else - pat = NULL; - if (mt > 0 || mt_nsec > 0) { - wintm.QuadPart = WINTIME(mt, mt_nsec); - fmtime.dwLowDateTime = wintm.LowPart; - fmtime.dwHighDateTime = wintm.HighPart; - pmt = &fmtime; - } else - pmt = NULL; - if (pat != NULL || pmt != NULL) - r = SetFileTime(h, NULL, pat, pmt); - else - r = 1; - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't SetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#else /* defined(_WIN32) && !defined(__CYGWIN__) */ - struct stat st; - struct timeval times[2]; - -#if !defined(__FreeBSD__) - mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ -#endif - if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) - return (1); - - r = lstat(pathname, &st); - if (r < 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (mt == 0 && mt_nsec == 0) { - mt = st.st_mtime; -#if defined(__FreeBSD__) - mt_nsec = st.st_mtimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - mt_nsec = (mt_nsec / 1000) * 1000; -#endif - } - if (at == 0 && at_nsec == 0) { - at = st.st_atime; -#if defined(__FreeBSD__) - at_nsec = st.st_atimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - at_nsec = (at_nsec / 1000) * 1000; -#endif - } - - times[1].tv_sec = mt; - times[1].tv_usec = mt_nsec / 1000; - - times[0].tv_sec = at; - times[0].tv_usec = at_nsec / 1000; - -#ifdef HAVE_LUTIMES - r = lutimes(pathname, times); -#else - r = utimes(pathname, times); -#endif - if (r < 0) { - failure_start(file, line, "Can't utimes %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ -} - -/* Set nodump, report failures. */ -int -assertion_nodump(const char *file, int line, const char *pathname) -{ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - int r; - - assertion_count(file, line); - r = chflags(pathname, UF_NODUMP); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - int fd, r, flags; - - assertion_count(file, line); - fd = open(pathname, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - failure_start(file, line, "Can't open %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't get flags %s\n", pathname); - failure_finish(NULL); - return (0); - } - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } - close(fd); -#else - (void)pathname; /* UNUSED */ - assertion_count(file, line); -#endif - return (1); -} - -/* - * - * UTILITIES for use by tests. - * - */ - -/* - * Check whether platform supports symlinks. This is intended - * for tests to use in deciding whether to bother testing symlink - * support; if the platform doesn't support symlinks, there's no point - * in checking whether the program being tested can create them. - * - * Note that the first time this test is called, we actually go out to - * disk to create and verify a symlink. This is necessary because - * symlink support is actually a property of a particular filesystem - * and can thus vary between directories on a single system. After - * the first call, this returns the cached result from memory, so it's - * safe to call it as often as you wish. - */ -int -canSymlink(void) -{ - /* Remember the test result */ - static int value = 0, tested = 0; - if (tested) - return (value); - - ++tested; - assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); - /* Note: Cygwin has its own symlink() emulation that does not - * use the Win32 CreateSymbolicLink() function. */ -#if defined(_WIN32) && !defined(__CYGWIN__) - value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); -#elif HAVE_SYMLINK - value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); -#endif - return (value); -} - -/* Platform-dependent options for hiding the output of a subcommand. */ -#if defined(_WIN32) && !defined(__CYGWIN__) -static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ -#else -static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ -#endif -/* - * Can this platform run the bzip2 program? - */ -int -canBzip2(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("bzip2 -d -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the grzip program? - */ -int -canGrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("grzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the gzip program? - */ -int -canGzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("gzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lrzip program? - */ -int -canRunCommand(const char *cmd) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("%s %s", cmd, redirectArgs) == 0) - value = 1; - } - return (value); -} - -int -canLrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lrzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lz4 program? - */ -int -canLz4(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lz4 -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzip program? - */ -int -canLzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzma program? - */ -int -canLzma(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzma -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzop program? - */ -int -canLzop(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzop -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the xz program? - */ -int -canXz(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("xz -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this filesystem handle nodump flags. - */ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - struct stat sb; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - if (chflags(path, UF_NODUMP) < 0) - return (0); - if (stat(path, &sb) < 0) - return (0); - if (sb.st_flags & UF_NODUMP) - return (1); - return (0); -} - -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - int fd, r, flags; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - if (flags & EXT2_NODUMP_FL) - return (1); - return (0); -} - -#else - -int -canNodump() -{ - return (0); -} - -#endif - -/* - * Sleep as needed; useful for verifying disk timestamp changes by - * ensuring that the wall-clock time has actually changed before we - * go back to re-read something from disk. - */ -void -sleepUntilAfter(time_t t) -{ - while (t >= time(NULL)) -#if defined(_WIN32) && !defined(__CYGWIN__) - Sleep(500); -#else - sleep(1); -#endif -} - -/* - * 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); - if (verbosity > VERBOSITY_FULL) - logprintf("Cmd: %s\n", buff); - 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; - FILE *f; - int r; - - va_start(ap, fmt); - vsprintf(filename, fmt, ap); - va_end(ap); - - f = fopen(filename, "rb"); - if (f == NULL) { - /* Note: No error; non-existent file is okay here. */ - return (NULL); - } - r = fstat(fileno(f), &st); - if (r != 0) { - logprintf("Can't stat file %s\n", filename); - fclose(f); - return (NULL); - } - p = malloc((size_t)st.st_size + 1); - if (p == NULL) { - logprintf("Can't allocate %ld bytes of memory to read file %s\n", - (long int)st.st_size, filename); - fclose(f); - return (NULL); - } - bytes_read = fread(p, 1, (size_t)st.st_size, f); - if (bytes_read < st.st_size) { - logprintf("Can't read file %s\n", filename); - fclose(f); - free(p); - return (NULL); - } - p[st.st_size] = '\0'; - if (sizep != NULL) - *sizep = (size_t)st.st_size; - fclose(f); - return (p); -} - -/* - * 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. - */ -void -dumpfile(const char *filename, void *data, size_t len) -{ - ssize_t bytes_written; - FILE *f; - - f = fopen(filename, "wb"); - if (f == NULL) { - logprintf("Can't open file %s for writing\n", filename); - return; - } - bytes_written = fwrite(data, 1, len, f); - if (bytes_written < (ssize_t)len) - logprintf("Can't write file %s\n", filename); - fclose(f); -} - -/* Read a uuencoded file from the reference directory, decode, and - * write the result into the current directory. */ -#define VALID_UUDECODE(c) (c >= 32 && c <= 96) -#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. */ - out = fopen(name, "wb"); - 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) { - assert(VALID_UUDECODE(p[0])); - assert(VALID_UUDECODE(p[1])); - n = UUDECODE(*p++) << 18; - n |= UUDECODE(*p++) << 12; - fputc(n >> 16, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++) << 6; - fputc((n >> 8) & 0xFF, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++); - fputc(n & 0xFF, out); - --bytes; - } - } - } - fclose(out); - fclose(in); -} - -void -copy_reference_file(const char *name) -{ - char buff[1024]; - FILE *in, *out; - size_t rbytes; - - sprintf(buff, "%s/%s", refdir, name); - in = fopen(buff, "rb"); - failure("Couldn't open reference file %s", buff); - assert(in != NULL); - if (in == NULL) - return; - /* Now, decode the rest and write it. */ - /* Not a lot of error checking here; the input better be right. */ - out = fopen(name, "wb"); - while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { - if (fwrite(buff, 1, rbytes, out) != rbytes) { - logprintf("Error: fwrite\n"); - break; - } - } - fclose(out); - fclose(in); -} - -int -is_LargeInode(const char *file) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - r = my_GetFileInformationByName(file, &bhfi); - if (r != 0) - return (0); - return (bhfi.nFileIndexHigh & 0x0000FFFFUL); -#else - struct stat st; - int64_t ino; - - if (stat(file, &st) < 0) - return (0); - ino = (int64_t)st.st_ino; - return (ino > 0xffffffff); -#endif -} - -void -extract_reference_files(const char **names) -{ - while (names && *names) - extract_reference_file(*names++); -} - -/* - * - * TEST management - * - */ - -/* - * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has - * a line like - * DEFINE_TEST(test_function) - * for each test. - */ - -/* Use "list.h" to declare all of the test functions. */ -#undef DEFINE_TEST -#define DEFINE_TEST(name) void name(void); -#include "list.h" - -/* Use "list.h" to create a list of all tests (functions and names). */ -#undef DEFINE_TEST -#define DEFINE_TEST(n) { n, #n, 0 }, -struct test_list_t tests[] = { - #include "list.h" -}; - -/* - * Summarize repeated failures in the just-completed test. - */ -static void -test_summarize(int failed, int skips_num) -{ - unsigned int i; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: - printf(failed ? "E" : "."); - fflush(stdout); - break; - case VERBOSITY_PASSFAIL: - printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); - break; - } - - log_console = (verbosity == VERBOSITY_LIGHT_REPORT); - - for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { - if (failed_lines[i].count > 1 && !failed_lines[i].skip) - logprintf("%s:%d: Summary: Failed %d times\n", - failed_filename, i, failed_lines[i].count); - } - /* Clear the failure history for the next file. */ - failed_filename = NULL; - memset(failed_lines, 0, sizeof(failed_lines)); -} - -/* - * Actually run a single test, with appropriate setup and cleanup. - */ -static int -test_run(int i, const char *tmpdir) -{ - char workdir[1024]; - char logfilename[64]; - int failures_before = failures; - int skips_before = skips; - int oldumask; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ - break; - case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ - printf("%3d: %-64s", i, tests[i].name); - fflush(stdout); - break; - default: /* Title of test, details will follow */ - printf("%3d: %s\n", i, tests[i].name); - } - - /* Chdir to the top-level work directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, - "ERROR: Can't chdir to top work dir %s\n", tmpdir); - exit(1); - } - /* Create a log file for this test. */ - sprintf(logfilename, "%s.log", tests[i].name); - logfile = fopen(logfilename, "w"); - fprintf(logfile, "%s\n\n", tests[i].name); - /* Chdir() to a work dir for this specific test. */ - snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); - testworkdir = workdir; - if (!assertMakeDir(testworkdir, 0755) - || !assertChdir(testworkdir)) { - fprintf(stderr, - "ERROR: Can't chdir to work dir %s\n", testworkdir); - exit(1); - } - /* Explicitly reset the locale before each test. */ - setlocale(LC_ALL, "C"); - /* Record the umask before we run the test. */ - umask(oldumask = umask(0)); - /* - * Run the actual test. - */ - (*tests[i].func)(); - /* - * Clean up and report afterwards. - */ - testworkdir = NULL; - /* Restore umask */ - umask(oldumask); - /* Reset locale. */ - setlocale(LC_ALL, "C"); - /* Reset directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", - tmpdir); - exit(1); - } - /* Report per-test summaries. */ - tests[i].failures = failures - failures_before; - test_summarize(tests[i].failures, skips - skips_before); - /* Close the per-test log file. */ - fclose(logfile); - logfile = NULL; - /* If there were no failures, we can remove the work dir and logfile. */ - if (tests[i].failures == 0) { - if (!keep_temp_files && assertChdir(tmpdir)) { -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure not to leave empty directories. - * Sometimes a processing of closing files used by tests - * is not done, then rmdir will be failed and it will - * leave a empty test directory. So we should wait a few - * seconds and retry rmdir. */ - int r, t; - for (t = 0; t < 10; t++) { - if (t > 0) - Sleep(1000); - r = systemf("rmdir /S /Q %s", tests[i].name); - if (r == 0) - break; - } - systemf("del %s", logfilename); -#else - systemf("rm -rf %s", tests[i].name); - systemf("rm %s", logfilename); -#endif - } - } - /* Return appropriate status. */ - return (tests[i].failures); -} - -/* - * - * - * MAIN and support routines. - * - * - */ - -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(" -u Keep running specifies tests until one fails.\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); -} - -static char * -get_refdir(const char *d) -{ - size_t tried_size, buff_size; - char *buff, *tried, *pwd = NULL, *p = NULL; - -#ifdef PATH_MAX - buff_size = PATH_MAX; -#else - buff_size = 8192; -#endif - buff = calloc(buff_size, 1); - if (buff == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* Allocate a buffer to hold the various directories we checked. */ - tried_size = buff_size * 2; - tried = calloc(tried_size, 1); - if (tried == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* If a dir was specified, try that */ - if (d != NULL) { - pwd = NULL; - snprintf(buff, buff_size, "%s", d); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - goto failure; - } - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - - /* Look for a known file. */ - snprintf(buff, buff_size, "%s", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(LIBRARY) - snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); -#else - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); -#endif - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(PROGRAM_ALIAS) - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); -#endif - - if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, buff_size, "%s", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - } - -failure: - printf("Unable to locate known reference file %s\n", KNOWNREF); - printf(" Checked following directories:\n%s\n", tried); - printf("Use -r option to specify full path to reference directory\n"); -#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) - DebugBreak(); -#endif - exit(1); - -success: - free(p); - free(pwd); - free(tried); - - /* Copy result into a fresh buffer to reduce memory usage. */ - p = strdup(buff); - free(buff); - return p; -} - -int -main(int argc, char **argv) -{ - static const int limit = sizeof(tests) / sizeof(tests[0]); - int test_set[sizeof(tests) / sizeof(tests[0])]; - int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; - time_t now; - char *refdir_alloc = NULL; - const char *progname; - char **saved_argv; - const char *tmp, *option_arg, *p; - char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; - char tmpdir_timestamp[256]; - - (void)argc; /* UNUSED */ - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) - /* To stop to run the default invalid parameter handler. */ - _set_invalid_parameter_handler(invalid_parameter_handler); - /* Disable annoying assertion message box. */ - _CrtSetReportMode(_CRT_ASSERT, 0); -#endif - - /* - * Name of this program, used to build root of our temp directory - * tree. - */ - progname = p = argv[0]; - if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(testprogdir, progname); - while (*p != '\0') { - /* Support \ or / dir separators for Windows compat. */ - if (*p == '/' || *p == '\\') - { - progname = p + 1; - i = j; - } - ++p; - j++; - } - testprogdir[i] = '\0'; -#if defined(_WIN32) && !defined(__CYGWIN__) - if (testprogdir[0] != '/' && testprogdir[0] != '\\' && - !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || - (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && - testprogdir[1] == ':' && - (testprogdir[2] == '/' || testprogdir[2] == '\\'))) -#else - if (testprogdir[0] != '/') -#endif - { - /* Fixup path for relative directories. */ - if ((testprogdir = (char *)realloc(testprogdir, - strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - memmove(testprogdir + strlen(pwd) + 1, testprogdir, - strlen(testprogdir) + 1); - memcpy(testprogdir, pwd, strlen(pwd)); - testprogdir[strlen(pwd)] = '/'; - } - -#ifdef PROGRAM - /* Get the target program from environment, if available. */ - testprogfile = 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; - - /* Allow -v to be controlled through the environment. */ - if (getenv("_VERBOSITY_LEVEL") != NULL) - { - vlevel = getenv("_VERBOSITY_LEVEL"); - verbosity = atoi(vlevel); - if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) - { - /* Unsupported verbosity levels are silently ignored */ - vlevel = NULL; - verbosity = VERBOSITY_PASSFAIL; - } - } - - /* 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 - testprogfile = option_arg; -#else - fprintf(stderr, "-p option not permitted\n"); - usage(progname); -#endif - break; - case 'q': - if (!vlevel) - verbosity--; - break; - case 'r': - refdir = option_arg; - break; - case 'u': - until_failure++; - break; - case 'v': - if (!vlevel) - verbosity++; - break; - default: - fprintf(stderr, "Unrecognized option '%c'\n", - option); - usage(progname); - } - } - } - - /* - * Sanity-check that our options make sense. - */ -#ifdef PROGRAM - if (testprogfile == NULL) - { - if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + - strlen(PROGRAM) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(tmp2, testprogdir); - strcat(tmp2, "/"); - strcat(tmp2, PROGRAM); - testprogfile = tmp2; - } - - { - char *testprg; -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Command.com sometimes rejects '/' separators. */ - testprg = strdup(testprogfile); - for (i = 0; testprg[i] != '\0'; i++) { - if (testprg[i] == '/') - testprg[i] = '\\'; - } - testprogfile = testprg; -#endif - /* Quote the name that gets put into shell command lines. */ - testprg = malloc(strlen(testprogfile) + 3); - strcpy(testprg, "\""); - strcat(testprg, testprogfile); - strcat(testprg, "\""); - testprog = testprg; - } -#endif - -#if !defined(_WIN32) && defined(SIGPIPE) - { /* Ignore SIGPIPE signals */ - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGPIPE, &sa, NULL); - } -#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++) { - 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 (assertMakeDir(tmpdir,0755)) - break; - if (i >= 999) { - 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." - */ - refdir = refdir_alloc = get_refdir(refdir); - - /* - * Banner with basic information. - */ - printf("\n"); - printf("If tests fail or crash, details will be in:\n"); - printf(" %s\n", tmpdir); - printf("\n"); - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - 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); - } else { - printf("Running "); - fflush(stdout); - } - - /* - * Run some or all of the individual tests. - */ - saved_argv = argv; - do { - argv = saved_argv; - do { - int test_num; - - test_num = get_test_set(test_set, limit, *argv, tests); - if (test_num < 0) { - printf("*** INVALID Test %s\n", *argv); - free(refdir_alloc); - free(testprogdir); - usage(progname); - return (1); - } - for (i = 0; i < test_num; i++) { - tests_run++; - if (test_run(test_set[i], tmpdir)) { - tests_failed++; - if (until_failure) - goto finish; - } - } - if (*argv != NULL) - argv++; - } while (*argv != NULL); - } while (until_failure); - -finish: - /* Must be freed after all tests run */ - free(tmp2); - free(testprogdir); - free(pwd); - - /* - * Report summary statistics. - */ - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - printf("\n"); - printf("Totals:\n"); - printf(" Tests run: %8d\n", tests_run); - printf(" Tests failed: %8d\n", tests_failed); - printf(" Assertions checked:%8d\n", assertions); - printf(" Assertions failed: %8d\n", failures); - printf(" Skips reported: %8d\n", skips); - } - if (failures) { - printf("\n"); - printf("Failing tests:\n"); - for (i = 0; i < limit; ++i) { - if (tests[i].failures) - printf(" %d: %s (%d failures)\n", i, - tests[i].name, tests[i].failures); - } - printf("\n"); - printf("Details for failing tests: %s\n", tmpdir); - printf("\n"); - } else { - if (verbosity == VERBOSITY_SUMMARY_ONLY) - printf("\n"); - printf("%d tests passed, no failures\n", tests_run); - } - - free(refdir_alloc); - - /* If the final tmpdir is empty, we can remove it. */ - /* This should be the usual case when all tests succeed. */ - assertChdir(".."); - rmdir(tmpdir); - - return (tests_failed ? 1 : 0); -} Property changes on: vendor/libarchive/dist/cpio/test/main.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: vendor/libarchive/dist/cpio/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/cpio/test/CMakeLists.txt (revision 314564) +++ vendor/libarchive/dist/cpio/test/CMakeLists.txt (revision 314565) @@ -1,99 +1,100 @@ ############################################ # # How to build bsdcpio_test # ############################################ IF(ENABLE_CPIO AND ENABLE_TEST) SET(bsdcpio_test_SOURCES ../cmdline.c ../../libarchive_fe/err.c ../../test_utils/test_utils.c - main.c + ../../test_utils/test_main.c test.h test_0.c test_basic.c test_cmdline.c test_extract_cpio_Z test_extract_cpio_bz2 test_extract_cpio_grz test_extract_cpio_gz test_extract_cpio_lrz test_extract_cpio_lz test_extract_cpio_lz4 test_extract_cpio_lzma test_extract_cpio_lzo test_extract_cpio_xz test_format_newc.c test_gcpio_compat.c test_missing_file.c test_option_0.c test_option_B_upper.c test_option_C_upper.c test_option_J_upper.c test_option_L_upper.c test_option_Z_upper.c test_option_a.c test_option_b64encode.c test_option_c.c test_option_d.c test_option_f.c test_option_grzip.c test_option_help.c test_option_l.c test_option_lrzip.c test_option_lz4.c test_option_lzma.c test_option_lzop.c test_option_m.c test_option_passphrase.c test_option_t.c test_option_u.c test_option_uuencode.c test_option_version.c test_option_xz.c test_option_y.c test_option_z.c test_owner_parse.c test_passthrough_dotdot.c test_passthrough_reverse.c ) # # Register target # ADD_EXECUTABLE(bsdcpio_test ${bsdcpio_test_SOURCES}) SET_PROPERTY(TARGET bsdcpio_test PROPERTY COMPILE_DEFINITIONS LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${bsdcpio_test_SOURCES}) SET_PROPERTY(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) # list.h has a line DEFINE_TEST(testname) for every # test. We can use that to define the tests for cmake by # defining a DEFINE_TEST macro and reading list.h in. MACRO (DEFINE_TEST _testname) ADD_TEST( NAME bsdcpio_${_testname} COMMAND bsdcpio_test -vv -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cpio/test) # Experimental new test handling ADD_CUSTOM_TARGET(run_bsdcpio_test COMMAND bsdcpio_test -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} -vv) ADD_DEPENDENCIES(run_bsdcpio_test bsdcpio) ADD_DEPENDENCIES(run_all_tests run_bsdcpio_test) ENDIF(ENABLE_CPIO AND ENABLE_TEST) Index: vendor/libarchive/dist/cpio/test/test.h =================================================================== --- vendor/libarchive/dist/cpio/test/test.h (revision 314564) +++ vendor/libarchive/dist/cpio/test/test.h (revision 314565) @@ -1,354 +1,40 @@ /* - * Copyright (c) 2003-2006 Tim Kientzle + * Copyright (c) 2003-2017 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: src/usr.bin/cpio/test/test.h,v 1.2 2008/06/21 02:17:18 kientzle Exp $ + * $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) && !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 +#define KNOWNREF "test_option_f.cpio.uu" +#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */ +#define PROGRAM "bsdcpio" /* Name of program being tested. */ +#define PROGRAM_ALIAS "cpio" /* Generic alias for program */ +#undef LIBRARY /* Not testing a library. */ +#undef EXTRA_DUMP /* How to dump extra data */ +#undef EXTRA_ERRNO /* How to dump errno */ +/* How to generate extra version info. */ +#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") -#include /* Windows requires this before sys/stat.h */ -#include - -#if HAVE_DIRENT_H -#include -#endif -#ifdef HAVE_DIRECT_H -#include -#define dirent direct -#endif -#include -#include -#ifdef HAVE_IO_H -#include -#endif -#ifdef HAVE_STDINT_H -#include -#endif -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_WINDOWS_H -#include -#endif - -/* - * System-specific tweaks. We really want to minimize these - * as much as possible, since they make it harder to understand - * the mainline code. - */ - -/* Windows (including Visual Studio and MinGW but not Cygwin) */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#if !defined(__BORLANDC__) -#undef chdir -#define chdir _chdir -#define strdup _strdup -#endif -#endif - -/* Visual Studio */ -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf sprintf_s -#endif - -#if defined(__BORLANDC__) -#pragma warn -8068 /* Constant out of range in comparison. */ -#endif - -/* Haiku OS and QNX */ -#if defined(__HAIKU__) || defined(__QNXNTO__) -/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ -#include -#endif - -/* Get a real definition for __FBSDID if we can */ -#if HAVE_SYS_CDEFS_H -#include -#endif - -/* If not, define it so as to avoid dangling semicolons. */ -#ifndef __FBSDID -#define __FBSDID(a) struct _undefined_hack -#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) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) -/* chdir() and error if it fails */ -#define assertChdir(path) \ - assertion_chdir(__FILE__, __LINE__, path) -/* Assert two integers are the same. Reports value of each one if not. */ -#define assertEqualInt(v1,v2) \ - assertion_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) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) -#define assertEqualUTF8String(v1,v2) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) -/* As above, but v1 and v2 are wchar_t * */ -#define assertEqualWString(v1,v2) \ - assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) -/* As above, but raw blocks of bytes. */ -#define assertEqualMem(v1, v2, l) \ - assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) -/* Assert that memory is full of a specified byte */ -#define assertMemoryFilledWith(v1, l, b) \ - assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) -/* Assert two files are the same. */ -#define assertEqualFile(f1, f2) \ - assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) -/* Assert that a file is empty. */ -#define assertEmptyFile(pathname) \ - assertion_empty_file(__FILE__, __LINE__, (pathname)) -/* Assert that a file is not empty. */ -#define assertNonEmptyFile(pathname) \ - assertion_non_empty_file(__FILE__, __LINE__, (pathname)) -#define assertFileAtime(pathname, sec, nsec) \ - assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileAtimeRecent(pathname) \ - assertion_file_atime_recent(__FILE__, __LINE__, pathname) -#define assertFileBirthtime(pathname, sec, nsec) \ - assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileBirthtimeRecent(pathname) \ - assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) -/* Assert that a file exists; supports printf-style arguments. */ -#define assertFileExists(pathname) \ - assertion_file_exists(__FILE__, __LINE__, pathname) -/* Assert that a file exists. */ -#define assertFileNotExists(pathname) \ - assertion_file_not_exists(__FILE__, __LINE__, pathname) -/* Assert that file contents match a string. */ -#define assertFileContents(data, data_size, pathname) \ - assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) -/* Verify that a file does not contain invalid strings */ -#define assertFileContainsNoInvalidStrings(pathname, strings) \ - assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) -#define assertFileMtime(pathname, sec, nsec) \ - assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileMtimeRecent(pathname) \ - assertion_file_mtime_recent(__FILE__, __LINE__, pathname) -#define assertFileNLinks(pathname, nlinks) \ - assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) -#define assertFileSize(pathname, size) \ - assertion_file_size(__FILE__, __LINE__, pathname, size) -#define assertFileMode(pathname, mode) \ - assertion_file_mode(__FILE__, __LINE__, pathname, mode) -#define assertTextFileContents(text, pathname) \ - assertion_text_file_contents(__FILE__, __LINE__, text, pathname) -#define assertFileContainsLinesAnyOrder(pathname, lines) \ - assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) -#define assertIsDir(pathname, mode) \ - assertion_is_dir(__FILE__, __LINE__, pathname, mode) -#define assertIsHardlink(path1, path2) \ - assertion_is_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsNotHardlink(path1, path2) \ - assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsReg(pathname, mode) \ - assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) -/* Create a directory, report error if it fails. */ -#define assertMakeDir(dirname, mode) \ - assertion_make_dir(__FILE__, __LINE__, dirname, mode) -#define assertMakeFile(path, mode, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) -#define assertMakeBinFile(path, mode, csize, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) -#define assertMakeHardlink(newfile, oldfile) \ - assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) -#define assertMakeSymlink(newfile, linkto) \ - assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) -#define assertNodump(path) \ - assertion_nodump(__FILE__, __LINE__, path) -#define assertUmask(mask) \ - assertion_umask(__FILE__, __LINE__, mask) -#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ - assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) - -/* - * 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 \ - skipping_setup(__FILE__, __LINE__);test_skipping - -/* Function declarations. These are defined in test_utility.c. */ -void failure(const char *fmt, ...); -int assertion_assert(const char *, int, int, const char *, void *); -int assertion_chdir(const char *, int, const char *); -int assertion_empty_file(const char *, int, const char *); -int assertion_equal_file(const char *, int, const char *, const char *); -int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); -int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); -int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); -int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); -int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); -int assertion_file_atime(const char *, int, const char *, long, long); -int assertion_file_atime_recent(const char *, int, const char *); -int assertion_file_birthtime(const char *, int, const char *, long, long); -int assertion_file_birthtime_recent(const char *, int, const char *); -int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); -int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); -int assertion_file_contents(const char *, int, const void *, int, const char *); -int assertion_file_exists(const char *, int, const char *); -int assertion_file_mode(const char *, int, const char *, int); -int assertion_file_mtime(const char *, int, const char *, long, long); -int assertion_file_mtime_recent(const char *, int, const char *); -int assertion_file_nlinks(const char *, int, const char *, int); -int assertion_file_not_exists(const char *, int, const char *); -int assertion_file_size(const char *, int, const char *, long); -int assertion_is_dir(const char *, int, const char *, int); -int assertion_is_hardlink(const char *, int, const char *, const char *); -int assertion_is_not_hardlink(const char *, int, const char *, const char *); -int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); -int assertion_make_dir(const char *, int, const char *, int); -int assertion_make_file(const char *, int, const char *, int, int, const void *); -int assertion_make_hardlink(const char *, int, const char *newpath, const char *); -int assertion_make_symlink(const char *, int, const char *newpath, const char *); -int assertion_nodump(const char *, int, const char *); -int assertion_non_empty_file(const char *, int, const char *); -int assertion_text_file_contents(const char *, int, const char *buff, const char *f); -int assertion_umask(const char *, int, int); -int assertion_utimes(const char *, int, const char *, long, long, long, long ); - -void skipping_setup(const char *, int); -void test_skipping(const char *fmt, ...); - -/* Like sprintf, then system() */ -int systemf(const char * fmt, ...); - -/* Delay until time() returns a value after this. */ -void sleepUntilAfter(time_t); - -/* Return true if this platform can create symlinks. */ -int canSymlink(void); - -/* Return true if this platform can run the "bzip2" program. */ -int canBzip2(void); - -/* Return true if this platform can run the "grzip" program. */ -int canGrzip(void); - -/* Return true if this platform can run the "gzip" program. */ -int canGzip(void); - -/* Return true if this platform can run the specified command. */ -int canRunCommand(const char *); - -/* Return true if this platform can run the "lrzip" program. */ -int canLrzip(void); - -/* Return true if this platform can run the "lz4" program. */ -int canLz4(void); - -/* Return true if this platform can run the "lzip" program. */ -int canLzip(void); - -/* Return true if this platform can run the "lzma" program. */ -int canLzma(void); - -/* Return true if this platform can run the "lzop" program. */ -int canLzop(void); - -/* Return true if this platform can run the "xz" program. */ -int canXz(void); - -/* Return true if this filesystem can handle nodump flags. */ -int canNodump(void); - -/* Return true if the file has large i-node number(>0xffffffff). */ -int is_LargeInode(const char *); - -/* 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, ...); - -/* Dump block of bytes to a file. */ -void dumpfile(const char *filename, void *, size_t); - -/* Extracts named reference file to the current directory. */ -void extract_reference_file(const char *); -/* Copies named reference file to the current directory. */ -void copy_reference_file(const char *); - -/* Extracts a list of files to the current directory. - * List must be NULL terminated. - */ -void extract_reference_files(const char **); - -/* Subtract umask from mode */ -mode_t umasked(mode_t expected_mode); - -/* Path to working directory for current test */ -extern const char *testworkdir; - -/* - * Special interfaces for program test harness. - */ - -/* Pathname of exe to be tested. */ -extern const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -extern const char *testprog; - -#ifdef USE_DMALLOC -#include -#endif +#include "test_common.h" Index: vendor/libarchive/dist/cpio/test/test_option_version.c =================================================================== --- vendor/libarchive/dist/cpio/test/test_option_version.c (revision 314564) +++ vendor/libarchive/dist/cpio/test/test_option_version.c (revision 314565) @@ -1,114 +1,30 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2017 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$"); -/* - * Test that --version option works and generates reasonable output. - */ - -static void -verify(const char *p, size_t s) -{ - const char *q = p; - - /* Version message should start with name of program, then space. */ - failure("version message too short:", p); - if (!assert(s > 6)) - return; - failure("Version message should begin with 'bsdcpio': %s", p); - if (!assertEqualMem(q, "bsdcpio ", 8)) - /* If we're not testing bsdcpio, don't keep going. */ - return; - q += 8; s -= 8; - /* Version number is a series of digits and periods. */ - while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { - ++q; - --s; - } - /* Version number terminated by space. */ - failure("Version: %s", p); - assert(s > 1); - /* Skip a single trailing a,b,c, or d. */ - if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') - ++q; - failure("Version: %s", p); - assert(*q == ' '); - ++q; --s; - /* Separator. */ - failure("Version: %s", p); - assertEqualMem(q, "- ", 2); - q += 2; s -= 2; - /* libarchive name and version number */ - assert(s > 11); - failure("Version: %s", p); - assertEqualMem(q, "libarchive ", 11); - q += 11; s -= 11; - /* Version number is a series of digits and periods. */ - while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { - ++q; - --s; - } - /* Skip a single trailing a,b,c, or d. */ - if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') - ++q; - /* Skip arbitrary third-party version numbers. */ - while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) { - ++q; - --s; - } - /* All terminated by end-of-line: \r, \r\n, or \n */ - assert(s >= 1); - failure("Version: %s", p); - if (*q == '\x0d') { - if (q[1] != '\0') - assertEqualMem(q, "\x0d\x0a", 2); - } else - assertEqualMem(q, "\x0a", 1); -} - - DEFINE_TEST(test_option_version) { - int r; - char *p; - size_t s; - - r = systemf("%s --version >version.stdout 2>version.stderr", testprog); - if (r != 0) - r = systemf("%s -W version >version.stdout 2>version.stderr", - testprog); - failure("Unable to run either %s --version or %s -W version", - testprog, testprog); - if (!assert(r == 0)) - return; - - /* --version should generate nothing to stderr. */ - assertEmptyFile("version.stderr"); - /* Verify format of version message. */ - p = slurpfile(&s, "version.stdout"); - verify(p, s); - free(p); + assertVersion(testprog, "bsdcpio"); } Index: vendor/libarchive/dist/libarchive/archive.h =================================================================== --- vendor/libarchive/dist/libarchive/archive.h (revision 314564) +++ vendor/libarchive/dist/libarchive/archive.h (revision 314565) @@ -1,1183 +1,1187 @@ /*- * Copyright (c) 2003-2010 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: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $ */ #ifndef ARCHIVE_H_INCLUDED #define ARCHIVE_H_INCLUDED /* * 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_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3002002 +#define ARCHIVE_VERSION_NUMBER 3003001 #include #include /* for wchar_t */ #include /* For FILE * */ #include /* For time_t */ /* * 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. */ #if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 # include #elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) # include #endif /* Get appropriate definitions of 64-bit integer */ #if !defined(__LA_INT64_T_DEFINED) /* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_INT64_T la_int64_t # endif #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; # else # include /* ssize_t */ # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; # else typedef int64_t la_int64_t; # endif # endif #endif /* The la_ssize_t should match the type used in 'struct stat' */ #if !defined(__LA_SSIZE_T_DEFINED) /* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_SSIZE_T la_ssize_t # endif #define __LA_SSIZE_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) typedef ssize_t la_ssize_t; # elif defined(_WIN64) typedef __int64 la_ssize_t; # else typedef long la_ssize_t; # endif # else # include /* ssize_t */ typedef ssize_t la_ssize_t; # endif #endif /* Large file support for Android */ #ifdef __ANDROID__ #include "android_lf.h" #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(__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 # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries or non-Windows needs no special declaration. */ # define __LA_DECL #endif #if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__) #define __LA_PRINTF(fmtarg, firstvararg) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) #else #define __LA_PRINTF(fmtarg, firstvararg) /* nothing */ #endif #if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 # define __LA_DEPRECATED __attribute__((deprecated)) #else # define __LA_DEPRECATED #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. */ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.2.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.3.1" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); /* * Detailed textual name/version of the library and its dependencies. * This has the form: * "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..." * the list of libraries described here will vary depending on how * libarchive was compiled. */ __LA_DECL const char * archive_version_details(void); /* * Returns NULL if libarchive was compiled without the associated library. * Otherwise, returns the version number that libarchive was compiled * against. */ __LA_DECL const char * archive_zlib_version(void); __LA_DECL const char * archive_liblzma_version(void); __LA_DECL const char * archive_bzlib_version(void); __LA_DECL const char * archive_liblz4_version(void); /* 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. * This may skip fewer bytes than requested; it may even skip zero bytes. * If you do skip fewer bytes than requested, libarchive will invoke your * read callback and discard data as necessary to make up the full skip. */ typedef la_int64_t archive_skip_callback(struct archive *, void *_client_data, la_int64_t request); /* Seeks to specified location in the file and returns the position. * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h. * Return ARCHIVE_FATAL if the seek fails for any reason. */ typedef la_int64_t archive_seek_callback(struct archive *, void *_client_data, la_int64_t offset, int whence); /* 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); typedef int archive_open_callback(struct archive *, void *_client_data); typedef int archive_close_callback(struct archive *, void *_client_data); /* Switches from one client data object to the next/prev client data object. * This is useful for reading from different data blocks such as a set of files * that make up one large file. */ typedef int archive_switch_callback(struct archive *, void *_client_data1, void *_client_data2); /* * Returns a passphrase used for encryption or decryption, NULL on nothing * to do and give it up. */ typedef const char *archive_passphrase_callback(struct archive *, void *_client_data); /* * Codes to identify various stream filters. */ #define ARCHIVE_FILTER_NONE 0 #define ARCHIVE_FILTER_GZIP 1 #define ARCHIVE_FILTER_BZIP2 2 #define ARCHIVE_FILTER_COMPRESS 3 #define ARCHIVE_FILTER_PROGRAM 4 #define ARCHIVE_FILTER_LZMA 5 #define ARCHIVE_FILTER_XZ 6 #define ARCHIVE_FILTER_UU 7 #define ARCHIVE_FILTER_RPM 8 #define ARCHIVE_FILTER_LZIP 9 #define ARCHIVE_FILTER_LRZIP 10 #define ARCHIVE_FILTER_LZOP 11 #define ARCHIVE_FILTER_GRZIP 12 #define ARCHIVE_FILTER_LZ4 13 #if ARCHIVE_VERSION_NUMBER < 4000000 #define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE #define ARCHIVE_COMPRESSION_GZIP ARCHIVE_FILTER_GZIP #define ARCHIVE_COMPRESSION_BZIP2 ARCHIVE_FILTER_BZIP2 #define ARCHIVE_COMPRESSION_COMPRESS ARCHIVE_FILTER_COMPRESS #define ARCHIVE_COMPRESSION_PROGRAM ARCHIVE_FILTER_PROGRAM #define ARCHIVE_COMPRESSION_LZMA ARCHIVE_FILTER_LZMA #define ARCHIVE_COMPRESSION_XZ ARCHIVE_FILTER_XZ #define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU #define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM #define ARCHIVE_COMPRESSION_LZIP ARCHIVE_FILTER_LZIP #define ARCHIVE_COMPRESSION_LRZIP ARCHIVE_FILTER_LRZIP #endif /* * 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 formats. (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_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6) #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 #define ARCHIVE_FORMAT_RAW 0x90000 #define ARCHIVE_FORMAT_XAR 0xA0000 #define ARCHIVE_FORMAT_LHA 0xB0000 #define ARCHIVE_FORMAT_CAB 0xC0000 #define ARCHIVE_FORMAT_RAR 0xD0000 #define ARCHIVE_FORMAT_7ZIP 0xE0000 #define ARCHIVE_FORMAT_WARC 0xF0000 /* * Codes returned by archive_read_format_capabilities(). * * This list can be extended with values between 0 and 0xffff. * The original purpose of this list was to let different archive * format readers expose their general capabilities in terms of * encryption. */ #define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */ #define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0) /* reader can detect encrypted data */ #define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1) /* reader can detect encryptable metadata (pathname, mtime, etc.) */ /* * Codes returned by archive_read_has_encrypted_entries(). * * In case the archive does not support encryption detection at all * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader * for some other reason (e.g. not enough bytes read) cannot say if * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW * is returned. */ #define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2 #define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1 /*- * 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_free 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. */ #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_read_support_compression_all(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_bzip2(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_compress(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_gzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_lzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_lzma(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_none(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program(struct archive *, const char *command) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program_signature (struct archive *, const char *, const void * /* match */, size_t) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_rpm(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_uu(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_xz(struct archive *) __LA_DEPRECATED; #endif __LA_DECL int archive_read_support_filter_all(struct archive *); __LA_DECL int archive_read_support_filter_bzip2(struct archive *); __LA_DECL int archive_read_support_filter_compress(struct archive *); __LA_DECL int archive_read_support_filter_gzip(struct archive *); __LA_DECL int archive_read_support_filter_grzip(struct archive *); __LA_DECL int archive_read_support_filter_lrzip(struct archive *); __LA_DECL int archive_read_support_filter_lz4(struct archive *); __LA_DECL int archive_read_support_filter_lzip(struct archive *); __LA_DECL int archive_read_support_filter_lzma(struct archive *); __LA_DECL int archive_read_support_filter_lzop(struct archive *); __LA_DECL int archive_read_support_filter_none(struct archive *); __LA_DECL int archive_read_support_filter_program(struct archive *, const char *command); __LA_DECL int archive_read_support_filter_program_signature (struct archive *, const char * /* cmd */, const void * /* match */, size_t); __LA_DECL int archive_read_support_filter_rpm(struct archive *); __LA_DECL int archive_read_support_filter_uu(struct archive *); __LA_DECL int archive_read_support_filter_xz(struct archive *); __LA_DECL int archive_read_support_format_7zip(struct archive *); __LA_DECL int archive_read_support_format_all(struct archive *); __LA_DECL int archive_read_support_format_ar(struct archive *); __LA_DECL int archive_read_support_format_by_code(struct archive *, int); __LA_DECL int archive_read_support_format_cab(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_lha(struct archive *); __LA_DECL int archive_read_support_format_mtree(struct archive *); __LA_DECL int archive_read_support_format_rar(struct archive *); __LA_DECL int archive_read_support_format_raw(struct archive *); __LA_DECL int archive_read_support_format_tar(struct archive *); __LA_DECL int archive_read_support_format_warc(struct archive *); __LA_DECL int archive_read_support_format_xar(struct archive *); /* archive_read_support_format_zip() enables both streamable and seekable * zip readers. */ __LA_DECL int archive_read_support_format_zip(struct archive *); /* Reads Zip archives as stream from beginning to end. Doesn't * correctly handle SFX ZIP files or ZIP archives that have been modified * in-place. */ __LA_DECL int archive_read_support_format_zip_streamable(struct archive *); /* Reads starting from central directory; requires seekable input. */ __LA_DECL int archive_read_support_format_zip_seekable(struct archive *); /* Functions to manually set the format and filters to be used. This is * useful to bypass the bidding process when the format and filters to use * is known in advance. */ __LA_DECL int archive_read_set_format(struct archive *, int); __LA_DECL int archive_read_append_filter(struct archive *, int); __LA_DECL int archive_read_append_filter_program(struct archive *, const char *); __LA_DECL int archive_read_append_filter_program_signature (struct archive *, const char *, const void * /* match */, size_t); /* Set various callbacks. */ __LA_DECL int archive_read_set_open_callback(struct archive *, archive_open_callback *); __LA_DECL int archive_read_set_read_callback(struct archive *, archive_read_callback *); __LA_DECL int archive_read_set_seek_callback(struct archive *, archive_seek_callback *); __LA_DECL int archive_read_set_skip_callback(struct archive *, archive_skip_callback *); __LA_DECL int archive_read_set_close_callback(struct archive *, archive_close_callback *); /* Callback used to switch between one data object to the next */ __LA_DECL int archive_read_set_switch_callback(struct archive *, archive_switch_callback *); /* This sets the first data object. */ __LA_DECL int archive_read_set_callback_data(struct archive *, void *); /* This sets data object at specified index */ __LA_DECL int archive_read_set_callback_data2(struct archive *, void *, unsigned int); /* This adds a data object at the specified index. */ __LA_DECL int archive_read_add_callback_data(struct archive *, void *, unsigned int); /* This appends a data object to the end of list */ __LA_DECL int archive_read_append_callback_data(struct archive *, void *); /* This prepends a data object to the beginning of list */ __LA_DECL int archive_read_prepend_callback_data(struct archive *, void *); /* Opening freezes the callbacks. */ __LA_DECL int archive_read_open1(struct archive *); /* Convenience wrappers around the above. */ __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); /* Use this for reading multivolume files by filenames. * NOTE: Must be NULL terminated. Sorting is NOT done. */ __LA_DECL int archive_read_open_filenames(struct archive *, const char **_filenames, size_t _block_size); __LA_DECL int archive_read_open_filename_w(struct archive *, const wchar_t *_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) __LA_DEPRECATED; /* Read an archive that's stored in memory. */ __LA_DECL int archive_read_open_memory(struct archive *, const 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, const 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 **); /* Parses and returns next entry header using the archive_entry passed in */ __LA_DECL int archive_read_next_header2(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 *); /* * Returns 1 if the archive contains at least one encrypted entry. * If the archive format not support encryption at all * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. * If for any other reason (e.g. not enough data read so far) * we cannot say whether there are encrypted entries, then * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned. * In general, this function will return values below zero when the * reader is uncertain or totally incapable of encryption support. * When this function returns 0 you can be sure that the reader * supports encryption detection but no encrypted entries have * been found yet. * * NOTE: If the metadata/header of an archive is also encrypted, you * cannot rely on the number of encrypted entries. That is why this * function does not return the number of encrypted entries but# * just shows that there are some. */ __LA_DECL int archive_read_has_encrypted_entries(struct archive *); /* * Returns a bitmask of capabilities that are supported by the archive format reader. * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned. */ __LA_DECL int archive_read_format_capabilities(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); /* Seek within the body of an entry. Similar to lseek(2). */ __LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int); /* * 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. */ __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, la_int64_t *offset); /*- * Some convenience functions that are built on archive_read_data: * 'skip': skips entire entry * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ __LA_DECL int archive_read_data_skip(struct archive *); __LA_DECL int archive_read_data_into_fd(struct archive *, int fd); /* * Set read options. */ /* Apply option to the format only. */ __LA_DECL int archive_read_set_format_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to the filter only. */ __LA_DECL int archive_read_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to both the format and the filter. */ __LA_DECL int archive_read_set_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_read_set_options(struct archive *_a, const char *opts); /* * Add a decryption passphrase. */ __LA_DECL int archive_read_add_passphrase(struct archive *, const char *); __LA_DECL int archive_read_set_passphrase_callback(struct archive *, void *client_data, archive_passphrase_callback *); /*- * 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) /* Default: Do not restore Mac extended metadata. */ /* This has no effect except on Mac OS. */ #define ARCHIVE_EXTRACT_MAC_METADATA (0x2000) /* Default: Use HFS+ compression if it was compressed. */ /* This has no effect except on Mac OS v10.6 or later. */ #define ARCHIVE_EXTRACT_NO_HFS_COMPRESSION (0x4000) /* Default: Do not use HFS+ compression if it was not compressed. */ /* This has no effect except on Mac OS v10.6 or later. */ #define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000) /* Default: Do not reject entries with absolute paths */ #define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000) /* Default: Do not clear no-change flags when unlinking object */ #define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000) __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 *, la_int64_t, la_int64_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_free will call archive_read_close for you. */ __LA_DECL int archive_read_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_read_free() for backwards compatibility. */ __LA_DECL int archive_read_finish(struct archive *) __LA_DEPRECATED; #endif /*- * To create an archive: * 1) Ask archive_write_new for an 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_free 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 *, la_int64_t, la_int64_t); #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_write_set_compression_bzip2(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_compress(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_gzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_lzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_lzma(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_none(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_xz(struct archive *) __LA_DEPRECATED; #endif /* A convenience function to set the filter based on the code. */ __LA_DECL int archive_write_add_filter(struct archive *, int filter_code); __LA_DECL int archive_write_add_filter_by_name(struct archive *, const char *name); __LA_DECL int archive_write_add_filter_b64encode(struct archive *); __LA_DECL int archive_write_add_filter_bzip2(struct archive *); __LA_DECL int archive_write_add_filter_compress(struct archive *); __LA_DECL int archive_write_add_filter_grzip(struct archive *); __LA_DECL int archive_write_add_filter_gzip(struct archive *); __LA_DECL int archive_write_add_filter_lrzip(struct archive *); __LA_DECL int archive_write_add_filter_lz4(struct archive *); __LA_DECL int archive_write_add_filter_lzip(struct archive *); __LA_DECL int archive_write_add_filter_lzma(struct archive *); __LA_DECL int archive_write_add_filter_lzop(struct archive *); __LA_DECL int archive_write_add_filter_none(struct archive *); __LA_DECL int archive_write_add_filter_program(struct archive *, const char *cmd); __LA_DECL int archive_write_add_filter_uuencode(struct archive *); __LA_DECL int archive_write_add_filter_xz(struct archive *); /* 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_7zip(struct archive *); __LA_DECL int archive_write_set_format_ar_bsd(struct archive *); __LA_DECL int archive_write_set_format_ar_svr4(struct archive *); __LA_DECL int archive_write_set_format_cpio(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_gnutar(struct archive *); __LA_DECL int archive_write_set_format_iso9660(struct archive *); __LA_DECL int archive_write_set_format_mtree(struct archive *); __LA_DECL int archive_write_set_format_mtree_classic(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_raw(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_set_format_v7tar(struct archive *); __LA_DECL int archive_write_set_format_warc(struct archive *); __LA_DECL int archive_write_set_format_xar(struct archive *); __LA_DECL int archive_write_set_format_zip(struct archive *); __LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename); __LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext); __LA_DECL int archive_write_zip_set_compression_deflate(struct archive *); __LA_DECL int archive_write_zip_set_compression_store(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); __LA_DECL int archive_write_open_filename_w(struct archive *, const wchar_t *_file); /* A deprecated synonym for archive_write_open_filename() */ __LA_DECL int archive_write_open_file(struct archive *, const char *_file) __LA_DEPRECATED; __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 *); __LA_DECL la_ssize_t archive_write_data(struct archive *, const void *, size_t); /* This interface is currently only available for archive_write_disk handles. */ __LA_DECL la_ssize_t archive_write_data_block(struct archive *, const void *, size_t, la_int64_t); __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); /* Marks the archive as FATAL so that a subsequent free() operation * won't try to close() cleanly. Provides a fast abort capability * when the client discovers that things have gone wrong. */ __LA_DECL int archive_write_fail(struct archive *); /* This can fail if the archive wasn't already closed, in which case * archive_write_free() will implicitly call archive_write_close(). */ __LA_DECL int archive_write_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_write_free() for backwards compatibility. */ __LA_DECL int archive_write_finish(struct archive *) __LA_DEPRECATED; #endif /* * Set write options. */ /* Apply option to the format only. */ __LA_DECL int archive_write_set_format_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to the filter only. */ __LA_DECL int archive_write_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to both the format and the filter. */ __LA_DECL int archive_write_set_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_write_set_options(struct archive *_a, const char *opts); /* * Set a encryption passphrase. */ __LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p); __LA_DECL int archive_write_set_passphrase_callback(struct archive *, void *client_data, archive_passphrase_callback *); /*- * ARCHIVE_WRITE_DISK API * * 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 probably * want to set the options. * 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_free 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 *, la_int64_t, la_int64_t); /* Set flags to control how the next item gets created. * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */ __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_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, void * /* private_data */, la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t); __LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t); /* * 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_int64_t); __LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_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_int64_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_int64_t), void (* /* cleanup_fn */)(void *)); /* Start traversal. */ __LA_DECL int archive_read_disk_open(struct archive *, const char *); __LA_DECL int archive_read_disk_open_w(struct archive *, const wchar_t *); /* * Request that current entry be visited. If you invoke it on every * directory, you'll get a physical traversal. This is ignored if the * current entry isn't a directory or a link to a directory. So, if * you invoke this on every returned path, you'll get a full logical * traversal. */ __LA_DECL int archive_read_disk_descend(struct archive *); __LA_DECL int archive_read_disk_can_descend(struct archive *); __LA_DECL int archive_read_disk_current_filesystem(struct archive *); __LA_DECL int archive_read_disk_current_filesystem_is_synthetic(struct archive *); __LA_DECL int archive_read_disk_current_filesystem_is_remote(struct archive *); /* Request that the access time of the entry visited by traversal be restored. */ __LA_DECL int archive_read_disk_set_atime_restored(struct archive *); /* * Set behavior. The "flags" argument selects optional behavior. */ /* Request that the access time of the entry visited by traversal be restored. * This is the same as archive_read_disk_set_atime_restored. */ #define ARCHIVE_READDISK_RESTORE_ATIME (0x0001) /* Default: Do not skip an entry which has nodump flags. */ #define ARCHIVE_READDISK_HONOR_NODUMP (0x0002) /* Default: Skip a mac resource fork file whose prefix is "._" because of * using copyfile. */ #define ARCHIVE_READDISK_MAC_COPYFILE (0x0004) /* Default: Traverse mount points. */ #define ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS (0x0008) /* Default: Xattrs are read from disk. */ #define ARCHIVE_READDISK_NO_XATTR (0x0010) +/* Default: ACLs are read from disk. */ +#define ARCHIVE_READDISK_NO_ACL (0x0020) +/* Default: File flags are read from disk. */ +#define ARCHIVE_READDISK_NO_FFLAGS (0x0040) __LA_DECL int archive_read_disk_set_behavior(struct archive *, int flags); /* * Set archive_match object that will be used in archive_read_disk to * know whether an entry should be skipped. The callback function * _excluded_func will be invoked when an entry is skipped by the result * of archive_match. */ __LA_DECL int archive_read_disk_set_matching(struct archive *, struct archive *_matching, void (*_excluded_func) (struct archive *, void *, struct archive_entry *), void *_client_data); __LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data); /* Simplified cleanup interface; * This calls archive_read_free() or archive_write_free() as needed. */ __LA_DECL int archive_free(struct archive *); /* * Accessor functions to read/set various information in * the struct archive object: */ /* Number of filters in the current filter pipeline. */ /* Filter #0 is the one closest to the format, -1 is a synonym for the * last filter, which is always the pseudo-filter that wraps the * client callbacks. */ __LA_DECL int archive_filter_count(struct archive *); __LA_DECL la_int64_t archive_filter_bytes(struct archive *, int); __LA_DECL int archive_filter_code(struct archive *, int); __LA_DECL const char * archive_filter_name(struct archive *, int); #if ARCHIVE_VERSION_NUMBER < 4000000 /* These don't properly handle multiple filters, so are deprecated and * will eventually be removed. */ /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */ __LA_DECL la_int64_t archive_position_compressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */ __LA_DECL la_int64_t archive_position_uncompressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */ __LA_DECL const char *archive_compression_name(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */ __LA_DECL int archive_compression(struct archive *) __LA_DEPRECATED; #endif __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_PRINTF(3, 4); __LA_DECL void archive_copy_error(struct archive *dest, struct archive *src); __LA_DECL int archive_file_count(struct archive *); /* * ARCHIVE_MATCH API */ __LA_DECL struct archive *archive_match_new(void); __LA_DECL int archive_match_free(struct archive *); /* * Test if archive_entry is excluded. * This is a convenience function. This is the same as calling all * archive_match_path_excluded, archive_match_time_excluded * and archive_match_owner_excluded. */ __LA_DECL int archive_match_excluded(struct archive *, struct archive_entry *); /* * Test if pathname is excluded. The conditions are set by following functions. */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, const wchar_t *); /* Add exclusion pathname pattern from file. */ __LA_DECL int archive_match_exclude_pattern_from_file(struct archive *, const char *, int _nullSeparator); __LA_DECL int archive_match_exclude_pattern_from_file_w(struct archive *, const wchar_t *, int _nullSeparator); /* Add inclusion pathname pattern. */ __LA_DECL int archive_match_include_pattern(struct archive *, const char *); __LA_DECL int archive_match_include_pattern_w(struct archive *, const wchar_t *); /* Add inclusion pathname pattern from file. */ __LA_DECL int archive_match_include_pattern_from_file(struct archive *, const char *, int _nullSeparator); __LA_DECL int archive_match_include_pattern_from_file_w(struct archive *, const wchar_t *, int _nullSeparator); /* * How to get statistic information for inclusion patterns. */ /* Return the amount number of unmatched inclusion patterns. */ __LA_DECL int archive_match_path_unmatched_inclusions(struct archive *); /* Return the pattern of unmatched inclusion with ARCHIVE_OK. * Return ARCHIVE_EOF if there is no inclusion pattern. */ __LA_DECL int archive_match_path_unmatched_inclusions_next( struct archive *, const char **); __LA_DECL int archive_match_path_unmatched_inclusions_next_w( struct archive *, const wchar_t **); /* * Test if a file is excluded by its time stamp. * The conditions are set by following functions. */ __LA_DECL int archive_match_time_excluded(struct archive *, struct archive_entry *); /* * Flags to tell a matching type of time stamps. These are used for * following functions. */ /* Time flag: mtime to be tested. */ #define ARCHIVE_MATCH_MTIME (0x0100) /* Time flag: ctime to be tested. */ #define ARCHIVE_MATCH_CTIME (0x0200) /* Comparison flag: Match the time if it is newer than. */ #define ARCHIVE_MATCH_NEWER (0x0001) /* Comparison flag: Match the time if it is older than. */ #define ARCHIVE_MATCH_OLDER (0x0002) /* Comparison flag: Match the time if it is equal to. */ #define ARCHIVE_MATCH_EQUAL (0x0010) /* Set inclusion time. */ __LA_DECL int archive_match_include_time(struct archive *, int _flag, time_t _sec, long _nsec); /* Set inclusion time by a date string. */ __LA_DECL int archive_match_include_date(struct archive *, int _flag, const char *_datestr); __LA_DECL int archive_match_include_date_w(struct archive *, int _flag, const wchar_t *_datestr); /* Set inclusion time by a particular file. */ __LA_DECL int archive_match_include_file_time(struct archive *, int _flag, const char *_pathname); __LA_DECL int archive_match_include_file_time_w(struct archive *, int _flag, const wchar_t *_pathname); /* Add exclusion entry. */ __LA_DECL int archive_match_exclude_entry(struct archive *, int _flag, struct archive_entry *); /* * Test if a file is excluded by its uid ,gid, uname or gname. * The conditions are set by following functions. */ __LA_DECL int archive_match_owner_excluded(struct archive *, struct archive_entry *); /* Add inclusion uid, gid, uname and gname. */ __LA_DECL int archive_match_include_uid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_gid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_uname(struct archive *, const char *); __LA_DECL int archive_match_include_uname_w(struct archive *, const wchar_t *); __LA_DECL int archive_match_include_gname(struct archive *, const char *); __LA_DECL int archive_match_include_gname_w(struct archive *, const wchar_t *); /* Utility functions */ /* Convenience function to sort a NULL terminated list of strings */ __LA_DECL int archive_utility_string_sort(char **); #ifdef __cplusplus } #endif /* These are meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_H_INCLUDED */ Index: vendor/libarchive/dist/libarchive/archive_digest_private.h =================================================================== --- vendor/libarchive/dist/libarchive/archive_digest_private.h (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_digest_private.h (revision 314565) @@ -1,376 +1,377 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Andres Mejia * 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. */ #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif #ifndef ARCHIVE_CRYPTO_PRIVATE_H_INCLUDED #define ARCHIVE_CRYPTO_PRIVATE_H_INCLUDED /* * Crypto support in various Operating Systems: * * NetBSD: * - MD5 and SHA1 in libc: without _ after algorithm name * - SHA2 in libc: with _ after algorithm name * * OpenBSD: * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name * * DragonFly and FreeBSD: * - MD5 libmd: without _ after algorithm name * - SHA1, SHA256 and SHA512 in libmd: with _ after algorithm name * * Mac OS X (10.4 and later): * - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name * * OpenSSL: * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name * * Windows: * - MD5, SHA1 and SHA2 in archive_crypto.c using Windows crypto API */ /* libc crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) #include #endif #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC3) #include #endif /* libmd crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||\ defined(ARCHIVE_CRYPTO_RMD160_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) #define ARCHIVE_CRYPTO_LIBMD 1 #endif #if defined(ARCHIVE_CRYPTO_MD5_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_SHA256_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_SHA512_LIBMD) #include #endif /* libSystem crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) #include #endif /* Nettle crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_NETTLE) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_NETTLE) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) #include #endif /* OpenSSL crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) #define ARCHIVE_CRYPTO_OPENSSL 1 #include "archive_openssl_evp_private.h" #endif /* Windows crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) +#include #include typedef struct { int valid; HCRYPTPROV cryptProv; HCRYPTHASH hash; } Digest_CTX; #endif /* typedefs */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) typedef MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_LIBMD) typedef MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) typedef CC_MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE) typedef struct md5_ctx archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL) typedef EVP_MD_CTX *archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_WIN) typedef Digest_CTX archive_md5_ctx; #else typedef unsigned char archive_md5_ctx; #endif #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) typedef RMD160_CTX archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD) typedef RIPEMD160_CTX archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE) typedef struct ripemd160_ctx archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) typedef EVP_MD_CTX *archive_rmd160_ctx; #else typedef unsigned char archive_rmd160_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) typedef SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD) typedef SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) typedef CC_SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE) typedef struct sha1_ctx archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) typedef EVP_MD_CTX *archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_WIN) typedef Digest_CTX archive_sha1_ctx; #else typedef unsigned char archive_sha1_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2) typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3) typedef SHA2_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD) typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) typedef CC_SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE) typedef struct sha256_ctx archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) typedef EVP_MD_CTX *archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_WIN) typedef Digest_CTX archive_sha256_ctx; #else typedef unsigned char archive_sha256_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA384_LIBC) typedef SHA384_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2) typedef SHA384_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3) typedef SHA2_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) typedef CC_SHA512_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE) typedef struct sha384_ctx archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) typedef EVP_MD_CTX *archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_WIN) typedef Digest_CTX archive_sha384_ctx; #else typedef unsigned char archive_sha384_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA512_LIBC) typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2) typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3) typedef SHA2_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD) typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) typedef CC_SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) typedef struct sha512_ctx archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) typedef EVP_MD_CTX *archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_WIN) typedef Digest_CTX archive_sha512_ctx; #else typedef unsigned char archive_sha512_ctx; #endif /* defines */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\ defined(ARCHIVE_CRYPTO_MD5_LIBMD) || \ defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\ defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_MD5_WIN) #define ARCHIVE_HAS_MD5 #endif #define archive_md5_init(ctx)\ __archive_digest.md5init(ctx) #define archive_md5_final(ctx, md)\ __archive_digest.md5final(ctx, md) #define archive_md5_update(ctx, buf, n)\ __archive_digest.md5update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\ defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) #define ARCHIVE_HAS_RMD160 #endif #define archive_rmd160_init(ctx)\ __archive_digest.rmd160init(ctx) #define archive_rmd160_final(ctx, md)\ __archive_digest.rmd160final(ctx, md) #define archive_rmd160_update(ctx, buf, n)\ __archive_digest.rmd160update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA1_WIN) #define ARCHIVE_HAS_SHA1 #endif #define archive_sha1_init(ctx)\ __archive_digest.sha1init(ctx) #define archive_sha1_final(ctx, md)\ __archive_digest.sha1final(ctx, md) #define archive_sha1_update(ctx, buf, n)\ __archive_digest.sha1update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA256_WIN) #define ARCHIVE_HAS_SHA256 #endif #define archive_sha256_init(ctx)\ __archive_digest.sha256init(ctx) #define archive_sha256_final(ctx, md)\ __archive_digest.sha256final(ctx, md) #define archive_sha256_update(ctx, buf, n)\ __archive_digest.sha256update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) #define ARCHIVE_HAS_SHA384 #endif #define archive_sha384_init(ctx)\ __archive_digest.sha384init(ctx) #define archive_sha384_final(ctx, md)\ __archive_digest.sha384final(ctx, md) #define archive_sha384_update(ctx, buf, n)\ __archive_digest.sha384update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) #define ARCHIVE_HAS_SHA512 #endif #define archive_sha512_init(ctx)\ __archive_digest.sha512init(ctx) #define archive_sha512_final(ctx, md)\ __archive_digest.sha512final(ctx, md) #define archive_sha512_update(ctx, buf, n)\ __archive_digest.sha512update(ctx, buf, n) /* Minimal interface to digest functionality for internal use in libarchive */ struct archive_digest { /* Message Digest */ int (*md5init)(archive_md5_ctx *ctx); int (*md5update)(archive_md5_ctx *, const void *, size_t); int (*md5final)(archive_md5_ctx *, void *); int (*rmd160init)(archive_rmd160_ctx *); int (*rmd160update)(archive_rmd160_ctx *, const void *, size_t); int (*rmd160final)(archive_rmd160_ctx *, void *); int (*sha1init)(archive_sha1_ctx *); int (*sha1update)(archive_sha1_ctx *, const void *, size_t); int (*sha1final)(archive_sha1_ctx *, void *); int (*sha256init)(archive_sha256_ctx *); int (*sha256update)(archive_sha256_ctx *, const void *, size_t); int (*sha256final)(archive_sha256_ctx *, void *); int (*sha384init)(archive_sha384_ctx *); int (*sha384update)(archive_sha384_ctx *, const void *, size_t); int (*sha384final)(archive_sha384_ctx *, void *); int (*sha512init)(archive_sha512_ctx *); int (*sha512update)(archive_sha512_ctx *, const void *, size_t); int (*sha512final)(archive_sha512_ctx *, void *); }; extern const struct archive_digest __archive_digest; #endif Index: vendor/libarchive/dist/libarchive/archive_entry.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_entry.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_entry.c (revision 314565) @@ -1,1967 +1,2035 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2016 Martin Matuska * 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: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #if MAJOR_IN_MKDEV #include #define HAVE_MAJOR #elif MAJOR_IN_SYSMACROS #include #define HAVE_MAJOR #endif #ifdef HAVE_ERRNO_H #include #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_acl_private.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_entry_private.h" #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) #endif /* 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__) && !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 /* * This adjustment is needed to support the following idiom for adding * 1000ns to the stored time: * archive_entry_set_atime(archive_entry_atime(), * archive_entry_atime_nsec() + 1000) * The additional if() here compensates for ambiguity in the C standard, * which permits two possible interpretations of a % b when a is negative. */ #define FIX_NS(t,ns) \ do { \ t += ns / 1000000000; \ ns %= 1000000000; \ if (ns < 0) { --t; ns += 1000000000; } \ } while (0) 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); #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 /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); archive_mstring_clean(&entry->ae_fflags_text); archive_mstring_clean(&entry->ae_gname); archive_mstring_clean(&entry->ae_hardlink); archive_mstring_clean(&entry->ae_pathname); archive_mstring_clean(&entry->ae_sourcepath); archive_mstring_clean(&entry->ae_symlink); archive_mstring_clean(&entry->ae_uname); archive_entry_copy_mac_metadata(entry, NULL, 0); archive_acl_clear(&entry->acl); archive_entry_xattr_clear(entry); archive_entry_sparse_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_xattr *xp; struct ae_sparse *sp; size_t s; const void *p; /* Allocate new structure and copy over all of the fields. */ /* TODO: Should we copy the archive over? Or require a new archive * as an argument? */ entry2 = archive_entry_new2(entry->archive); if (entry2 == NULL) return (NULL); entry2->ae_stat = entry->ae_stat; entry2->ae_fflags_set = entry->ae_fflags_set; entry2->ae_fflags_clear = entry->ae_fflags_clear; /* TODO: XXX If clone can have a different archive, what do we do here if * character sets are different? XXX */ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname); archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink); archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname); archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy encryption status */ entry2->encryption = entry->encryption; /* Copy ACL data over. */ archive_acl_copy(&entry2->acl, &entry->acl); /* Copy Mac OS metadata. */ p = archive_entry_mac_metadata(entry, &s); archive_entry_copy_mac_metadata(entry2, p, s); /* 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; } /* Copy sparse data over. */ sp = entry->sparse_head; while (sp != NULL) { archive_entry_sparse_add_entry(entry2, sp->offset, sp->length); sp = sp->next; } return (entry2); } void archive_entry_free(struct archive_entry *entry) { archive_entry_clear(entry); free(entry); } struct archive_entry * archive_entry_new(void) { return archive_entry_new2(NULL); } struct archive_entry * archive_entry_new2(struct archive *a) { struct archive_entry *entry; entry = (struct archive_entry *)calloc(1, sizeof(*entry)); if (entry == NULL) return (NULL); entry->archive = a; 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); } int archive_entry_dev_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_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->acl.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; if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) { if (f != NULL) return (f); } else if (errno == ENOMEM) __archive_errx(1, "No memory"); 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); archive_mstring_copy_mbs(&entry->ae_fflags_text, p); free(p); if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) return (f); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int64_t archive_entry_gid(struct archive_entry *entry) { return (entry->ae_stat.aest_gid); } const char * archive_entry_gname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_gname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_gname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc)); } const char * archive_entry_hardlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_hardlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_hardlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_HARDLINK) == 0) { *p = NULL; *len = 0; return (0); } return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc)); } int64_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } int archive_entry_ino_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_INO); } int64_t archive_entry_ino64(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->acl.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) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_pathname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_pathname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc)); } mode_t archive_entry_perm(struct archive_entry *entry) { return (~AE_IFMT & entry->acl.mode); } 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) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_sourcepath_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); return (NULL); } const char * archive_entry_symlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_symlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_symlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_SYMLINK) == 0) { *p = NULL; *len = 0; return (0); } return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc)); } int64_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_uid); } const char * archive_entry_uname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_uname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_uname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc)); } int archive_entry_is_data_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA); } int archive_entry_is_metadata_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA); } int archive_entry_is_encrypted(struct archive_entry *entry) { return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA)); } /* * Functions to set archive_entry properties. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->acl.mode &= ~AE_IFMT; entry->acl.mode |= AE_IFMT & type; } void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { archive_mstring_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) { archive_mstring_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) { archive_mstring_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, int64_t g) { entry->stat_valid = 0; entry->ae_stat.aest_gid = g; } void archive_entry_set_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_gname, name); } void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_gname, name); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_gname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_gname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc)); } void archive_entry_set_ino(struct archive_entry *entry, int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_ino64(struct archive_entry *entry, int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_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_set_hardlink_utf8(struct archive_entry *entry, const char *target) { archive_mstring_copy_utf8(&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) { archive_mstring_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) { archive_mstring_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } int archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target) { if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_hardlink_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); if (target != NULL && r == 0) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; return (r); } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, 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 t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = t; 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) { FIX_NS(t, 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_set |= AE_SET_DEV; 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_set |= AE_SET_DEV; 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_set |= AE_SET_DEV; 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) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } void archive_entry_set_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_utf8(&entry->ae_symlink, target); else archive_mstring_copy_utf8(&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) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_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) archive_mstring_copy_wcs(&entry->ae_symlink, target); else archive_mstring_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, target); else r = archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target); if (r == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_link_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, target, len, sc); else r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); return (r); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->acl.mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = t; 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) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_pathname, name); } void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_pathname, name); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_pathname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_pathname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname, name, len, sc)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; entry->acl.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) { archive_mstring_copy_mbs(&entry->ae_sourcepath, path); } void archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path) { archive_mstring_copy_wcs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_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_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_utf8(&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) { archive_mstring_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) { archive_mstring_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } int archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname) { if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, linkname) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_symlink_l(struct archive_entry *entry, const char *linkname, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, linkname, len, sc); if (linkname != NULL && r == 0) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; return (r); } void archive_entry_set_uid(struct archive_entry *entry, int64_t u) { entry->stat_valid = 0; entry->ae_stat.aest_uid = u; } void archive_entry_set_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_uname, name); } void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_uname, name); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_uname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } void archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_DATA; } else { entry->encryption &= ~AE_ENCRYPTION_DATA; } } void archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_METADATA; } else { entry->encryption &= ~AE_ENCRYPTION_METADATA; } } int _archive_entry_copy_uname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_uname, name, len, sc)); } const void * archive_entry_mac_metadata(struct archive_entry *entry, size_t *s) { *s = entry->mac_metadata_size; return entry->mac_metadata; } void archive_entry_copy_mac_metadata(struct archive_entry *entry, const void *p, size_t s) { free(entry->mac_metadata); if (p == NULL || s == 0) { entry->mac_metadata = NULL; entry->mac_metadata_size = 0; } else { entry->mac_metadata_size = s; entry->mac_metadata = malloc(s); if (entry->mac_metadata == NULL) abort(); memcpy(entry->mac_metadata, p, s); } } /* * 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. */ struct archive_acl * archive_entry_acl(struct archive_entry *entry) { return &entry->acl; } void archive_entry_acl_clear(struct archive_entry *entry) { archive_acl_clear(&entry->acl); } /* * Add a single ACL entry to the internal list of ACL data. */ int archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name); } /* * As above, but with a wide-character name. */ int archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { return archive_acl_add_entry_w_len(&entry->acl, type, permset, tag, id, name, wcslen(name)); } /* * Return a bitmask of ACL types in an archive entry ACL list */ int archive_entry_acl_types(struct archive_entry *entry) { return (archive_acl_types(&entry->acl)); } /* * Return a count of entries matching "want_type". */ int archive_entry_acl_count(struct archive_entry *entry, int want_type) { return archive_acl_count(&entry->acl, want_type); } /* * 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) { return archive_acl_reset(&entry->acl, want_type); } /* * 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) { int r; r = archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name); if (r == ARCHIVE_FATAL && errno == ENOMEM) __archive_errx(1, "No memory"); return (r); } /* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ wchar_t * archive_entry_acl_to_text_w(struct archive_entry *entry, ssize_t *len, int flags) { return (archive_acl_to_text_w(&entry->acl, len, flags, entry->archive)); } char * archive_entry_acl_to_text(struct archive_entry *entry, ssize_t *len, int flags) { return (archive_acl_to_text_l(&entry->acl, len, flags, NULL)); } char * _archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len, int flags, struct archive_string_conv *sc) { return (archive_acl_to_text_l(&entry->acl, len, flags, sc)); } /* * ACL text parser. */ int archive_entry_acl_from_text_w(struct archive_entry *entry, const wchar_t *wtext, int type) { return (archive_acl_from_text_w(&entry->acl, wtext, type)); } int archive_entry_acl_from_text(struct archive_entry *entry, const char *text, int type) { return (archive_acl_from_text_l(&entry->acl, text, type, NULL)); } int _archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text, int type, struct archive_string_conv *sc) { return (archive_acl_from_text_l(&entry->acl, text, type, sc)); } /* Deprecated */ static int archive_entry_acl_text_compat(int *flags) { if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0) return (1); /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) *flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID; /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) *flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; *flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA; return (0); } /* Deprecated */ const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { if (entry->acl.acl_text_w != NULL) { free(entry->acl.acl_text_w); entry->acl.acl_text_w = NULL; } if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl, NULL, flags, entry->archive); return (entry->acl.acl_text_w); } /* Deprecated */ const char * archive_entry_acl_text(struct archive_entry *entry, int flags) { if (entry->acl.acl_text != NULL) { free(entry->acl.acl_text); entry->acl.acl_text = NULL; } if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL, flags, NULL); return (entry->acl.acl_text); } /* Deprecated */ int _archive_entry_acl_text_l(struct archive_entry *entry, int flags, const char **acl_text, size_t *len, struct archive_string_conv *sc) { if (entry->acl.acl_text != NULL) { free(entry->acl.acl_text); entry->acl.acl_text = NULL; } if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, (ssize_t *)len, flags, sc); *acl_text = entry->acl.acl_text; return (0); } /* * 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' */ +#if defined(FS_APPEND_FL) /* 'a' */ + { "nosappnd", L"nosappnd", FS_APPEND_FL, 0 }, + { "nosappend", L"nosappend", FS_APPEND_FL, 0 }, +#elif defined(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' */ +#if defined(FS_IMMUTABLE_FL) /* 'i' */ + { "noschg", L"noschg", FS_IMMUTABLE_FL, 0 }, + { "noschange", L"noschange", FS_IMMUTABLE_FL, 0 }, + { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0 }, +#elif defined(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' */ +#if defined(FS_NODUMP_FL) /* 'd' */ + { "nodump", L"nodump", 0, FS_NODUMP_FL}, +#elif defined(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 UF_COMPRESSED { "nocompressed",L"nocompressed", UF_COMPRESSED, 0 }, #endif -#ifdef EXT2_UNRM_FL +#if defined(FS_UNRM_FL) + { "nouunlink", L"nouunlink", FS_UNRM_FL, 0}, +#elif defined(EXT2_UNRM_FL) { "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0}, #endif -#ifdef EXT2_BTREE_FL +#if defined(FS_BTREE_FL) + { "nobtree", L"nobtree", FS_BTREE_FL, 0 }, +#elif defined(EXT2_BTREE_FL) { "nobtree", L"nobtree", EXT2_BTREE_FL, 0 }, #endif -#ifdef EXT2_ECOMPR_FL +#if defined(FS_ECOMPR_FL) + { "nocomperr", L"nocomperr", FS_ECOMPR_FL, 0 }, +#elif defined(EXT2_ECOMPR_FL) { "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 }, #endif -#ifdef EXT2_COMPR_FL /* 'c' */ +#if defined(FS_COMPR_FL) /* 'c' */ + { "nocompress", L"nocompress", FS_COMPR_FL, 0 }, +#elif defined(EXT2_COMPR_FL) /* 'c' */ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, #endif -#ifdef EXT2_NOATIME_FL /* 'A' */ +#if defined(FS_NOATIME_FL) /* 'A' */ + { "noatime", L"noatime", 0, FS_NOATIME_FL}, +#elif defined(EXT2_NOATIME_FL) /* 'A' */ { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif -#ifdef EXT2_DIRTY_FL +#if defined(FS_DIRTY_FL) + { "nocompdirty",L"nocompdirty", FS_DIRTY_FL, 0}, +#elif defined(EXT2_DIRTY_FL) { "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0}, #endif -#ifdef EXT2_COMPRBLK_FL -#ifdef EXT2_NOCOMPR_FL +#if defined(FS_COMPRBLK_FL) +#if defined(FS_NOCOMPR_FL) + { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, FS_NOCOMPR_FL}, +#else + { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, 0}, +#endif +#elif defined(EXT2_COMPRBLK_FL) +#if defined(EXT2_NOCOMPR_FL) { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL}, #else { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0}, #endif #endif -#ifdef EXT2_DIRSYNC_FL +#if defined(FS_DIRSYNC_FL) + { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, +#elif defined(EXT2_DIRSYNC_FL) { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, #endif -#ifdef EXT2_INDEX_FL +#if defined(FS_INDEX_FL) + { "nohashidx", L"nohashidx", FS_INDEX_FL, 0}, +#elif defined(EXT2_INDEX_FL) { "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0}, #endif -#ifdef EXT2_IMAGIC_FL +#if defined(FS_IMAGIC_FL) + { "noimagic", L"noimagic", FS_IMAGIC_FL, 0}, +#elif defined(EXT2_IMAGIC_FL) { "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0}, #endif -#ifdef EXT3_JOURNAL_DATA_FL +#if defined(FS_JOURNAL_DATA_FL) + { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, +#elif defined(EXT3_JOURNAL_DATA_FL) { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, #endif -#ifdef EXT2_SECRM_FL +#if defined(FS_SECRM_FL) + { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, +#elif defined(EXT2_SECRM_FL) { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, #endif -#ifdef EXT2_SYNC_FL +#if defined(FS_SYNC_FL) + { "nosync", L"nosync", FS_SYNC_FL, 0}, +#elif defined(EXT2_SYNC_FL) { "nosync", L"nosync", EXT2_SYNC_FL, 0}, #endif -#ifdef EXT2_NOTAIL_FL +#if defined(FS_NOTAIL_FL) + { "notail", L"notail", 0, FS_NOTAIL_FL}, +#elif defined(EXT2_NOTAIL_FL) { "notail", L"notail", 0, EXT2_NOTAIL_FL}, #endif -#ifdef EXT2_TOPDIR_FL +#if defined(FS_TOPDIR_FL) + { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, +#elif defined(EXT2_TOPDIR_FL) { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, #endif -#ifdef EXT2_RESERVED_FL +#ifdef FS_ENCRYPT_FL + { "noencrypt", L"noencrypt", FS_ENCRYPT_FL, 0}, +#endif +#ifdef FS_HUGE_FILE_FL + { "nohugefile", L"nohugefile", FS_HUGE_FILE_FL, 0}, +#endif +#ifdef FS_EXTENT_FL + { "noextent", L"noextent", FS_EXTENT_FL, 0}, +#endif +#ifdef FS_EA_INODE_FL + { "noeainode", L"noeainode", FS_EA_INODE_FL, 0}, +#endif +#ifdef FS_EOFBLOCKS_FL + { "noeofblocks",L"noeofblocks", FS_EOFBLOCKS_FL, 0}, +#endif +#ifdef FS_NOCOW_FL + { "nocow", L"nocow", FS_NOCOW_FL, 0}, +#endif +#ifdef FS_INLINE_DATA_FL + { "noinlinedata",L"noinlinedata", FS_INLINE_DATA_FL, 0}, +#endif +#ifdef FS_PROJINHERIT_FL + { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, +#endif +#if defined(FS_RESERVED_FL) + { "noreserved", L"noreserved", FS_RESERVED_FL, 0}, +#elif defined(EXT2_RESERVED_FL) { "noreserved", L"noreserved", EXT2_RESERVED_FL, 0}, #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') { size_t length; /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; length = end - start; for (flag = flags; flag->name != NULL; flag++) { size_t flag_length = strlen(flag->name); if (length == flag_length && memcmp(start, flag->name, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && memcmp(start, flag->name + 2, length) == 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') { size_t length; /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; length = end - start; for (flag = flags; flag->wname != NULL; flag++) { size_t flag_length = wcslen(flag->wname); if (length == flag_length && wmemcmp(start, flag->wname, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && wmemcmp(start, flag->wname + 2, length) == 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 Index: vendor/libarchive/dist/libarchive/archive_entry.h =================================================================== --- vendor/libarchive/dist/libarchive/archive_entry.h (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_entry.h (revision 314565) @@ -1,681 +1,702 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * Copyright (c) 2016 Martin Matuska * 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: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $ */ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3002002 +#define ARCHIVE_VERSION_NUMBER 3003001 /* * 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 #if defined(_WIN32) && !defined(__CYGWIN__) #include #endif /* Get a suitable 64-bit integer type. */ #if !defined(__LA_INT64_T_DEFINED) # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_INT64_T la_int64_t # endif #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; # else #include # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; # else typedef int64_t la_int64_t; # endif # endif #endif +/* The la_ssize_t should match the type used in 'struct stat' */ +#if !defined(__LA_SSIZE_T_DEFINED) +/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ +# if ARCHIVE_VERSION_NUMBER < 4000000 +#define __LA_SSIZE_T la_ssize_t +# endif +#define __LA_SSIZE_T_DEFINED +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) +# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) +typedef ssize_t la_ssize_t; +# elif defined(_WIN64) +typedef __int64 la_ssize_t; +# else +typedef long la_ssize_t; +# endif +# else +# include /* ssize_t */ +typedef ssize_t la_ssize_t; +# endif +#endif + /* Get a suitable definition for mode_t */ #if ARCHIVE_VERSION_NUMBER >= 3999000 /* Switch to plain 'int' for libarchive 4.0. It's less broken than 'mode_t' */ # define __LA_MODE_T int #elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__) # define __LA_MODE_T unsigned short #else # define __LA_MODE_T mode_t #endif /* Large file support for Android */ #ifdef __ANDROID__ #include "android_lf.h" #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(__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 # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries on all platforms and shared libraries on non-Windows. */ # define __LA_DECL #endif #if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 # define __LA_DEPRECATED __attribute__((deprecated)) #else # define __LA_DEPRECATED #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; 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. */ /* * In libarchive 4.0, we can drop the casts here. * They're needed to work around Borland C's broken mode_t. */ #define AE_IFMT ((__LA_MODE_T)0170000) #define AE_IFREG ((__LA_MODE_T)0100000) #define AE_IFLNK ((__LA_MODE_T)0120000) #define AE_IFSOCK ((__LA_MODE_T)0140000) #define AE_IFCHR ((__LA_MODE_T)0020000) #define AE_IFBLK ((__LA_MODE_T)0060000) #define AE_IFDIR ((__LA_MODE_T)0040000) #define AE_IFIFO ((__LA_MODE_T)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); /* * This form of archive_entry_new2() will pull character-set * conversion information from the specified archive handle. The * older archive_entry_new(void) form is equivalent to calling * archive_entry_new2(NULL) and will result in the use of an internal * default character-set conversion. */ __LA_DECL struct archive_entry *archive_entry_new2(struct archive *); /* * 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 int archive_entry_dev_is_set(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_int64_t archive_entry_gid(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); __LA_DECL const char *archive_entry_gname_utf8(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 char *archive_entry_hardlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *); __LA_DECL int archive_entry_ino_is_set(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 char *archive_entry_pathname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_perm(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 const wchar_t *archive_entry_sourcepath_w(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 char *archive_entry_symlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); __LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); __LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *); __LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *); __LA_DECL int archive_entry_is_encrypted(struct archive_entry *); /* * Set fields in an archive_entry. * * Note: Before libarchive 2.4, there were 'set' and 'copy' versions * of the string setters. 'copy' copied the actual string, 'set' just * stored the pointer. In libarchive 2.4 and later, strings are * always copied. */ __LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_atime(struct archive_entry *); #if defined(_WIN32) && !defined(__CYGWIN__) __LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *); #endif __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_int64_t); __LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_gname_utf8(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_set_hardlink_utf8(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 int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_link_utf8(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_set_pathname_utf8(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_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_symlink_utf8(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 int archive_entry_update_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_uid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_uname_utf8(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 *); __LA_DECL void archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted); __LA_DECL void archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted); /* * 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 and other LFS systems, provide both stat32 and * stat64 versions of these functions and all of the macro glue so * that archive_entry_stat is magically defined to * archive_entry_stat32 or archive_entry_stat64 as appropriate. */ __LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); /* * Storage for Mac OS-specific AppleDouble metadata information. * Apple-format tar files store a separate binary blob containing * encoded metadata with ACL, extended attributes, etc. * This provides a place to store that blob. */ __LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *); __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t); /* * ACL routines. This used to simply store and return text-format ACL * strings, but that proved insufficient for a number of reasons: * = clients need control over uname/uid and gname/gid mappings * = there are many different ACL text formats * = would like to be able to read/convert archives containing ACLs * on platforms that lack ACL libraries * * This last point, in particular, forces me to implement a reasonably * complete set of ACL support routines. */ /* * Permission bits. */ #define ARCHIVE_ENTRY_ACL_EXECUTE 0x00000001 #define ARCHIVE_ENTRY_ACL_WRITE 0x00000002 #define ARCHIVE_ENTRY_ACL_READ 0x00000004 #define ARCHIVE_ENTRY_ACL_READ_DATA 0x00000008 #define ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 0x00000008 #define ARCHIVE_ENTRY_ACL_WRITE_DATA 0x00000010 #define ARCHIVE_ENTRY_ACL_ADD_FILE 0x00000010 #define ARCHIVE_ENTRY_ACL_APPEND_DATA 0x00000020 #define ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY 0x00000020 #define ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS 0x00000040 #define ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS 0x00000080 #define ARCHIVE_ENTRY_ACL_DELETE_CHILD 0x00000100 #define ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES 0x00000200 #define ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES 0x00000400 #define ARCHIVE_ENTRY_ACL_DELETE 0x00000800 #define ARCHIVE_ENTRY_ACL_READ_ACL 0x00001000 #define ARCHIVE_ENTRY_ACL_WRITE_ACL 0x00002000 #define ARCHIVE_ENTRY_ACL_WRITE_OWNER 0x00004000 #define ARCHIVE_ENTRY_ACL_SYNCHRONIZE 0x00008000 #define ARCHIVE_ENTRY_ACL_PERMS_POSIX1E \ (ARCHIVE_ENTRY_ACL_EXECUTE \ | ARCHIVE_ENTRY_ACL_WRITE \ | ARCHIVE_ENTRY_ACL_READ) #define ARCHIVE_ENTRY_ACL_PERMS_NFS4 \ (ARCHIVE_ENTRY_ACL_EXECUTE \ | ARCHIVE_ENTRY_ACL_READ_DATA \ | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY \ | ARCHIVE_ENTRY_ACL_WRITE_DATA \ | ARCHIVE_ENTRY_ACL_ADD_FILE \ | ARCHIVE_ENTRY_ACL_APPEND_DATA \ | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY \ | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS \ | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS \ | ARCHIVE_ENTRY_ACL_DELETE_CHILD \ | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES \ | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES \ | ARCHIVE_ENTRY_ACL_DELETE \ | ARCHIVE_ENTRY_ACL_READ_ACL \ | ARCHIVE_ENTRY_ACL_WRITE_ACL \ | ARCHIVE_ENTRY_ACL_WRITE_OWNER \ | ARCHIVE_ENTRY_ACL_SYNCHRONIZE) /* * Inheritance values (NFS4 ACLs only); included in permset. */ #define ARCHIVE_ENTRY_ACL_ENTRY_INHERITED 0x01000000 #define ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT 0x02000000 #define ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT 0x04000000 #define ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT 0x08000000 #define ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY 0x10000000 #define ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS 0x20000000 #define ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS 0x40000000 #define ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4 \ (ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT \ | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT \ | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT \ | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY \ | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS \ | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS \ | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) /* We need to be able to specify combinations of these. */ #define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 0x00000100 /* POSIX.1e only */ #define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 0x00000200 /* POSIX.1e only */ #define ARCHIVE_ENTRY_ACL_TYPE_ALLOW 0x00000400 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_DENY 0x00000800 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_AUDIT 0x00001000 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_ALARM 0x00002000 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_POSIX1E (ARCHIVE_ENTRY_ACL_TYPE_ACCESS \ | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) #define ARCHIVE_ENTRY_ACL_TYPE_NFS4 (ARCHIVE_ENTRY_ACL_TYPE_ALLOW \ | ARCHIVE_ENTRY_ACL_TYPE_DENY \ | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \ | ARCHIVE_ENTRY_ACL_TYPE_ALARM) /* 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 (POSIX.1e only) */ #define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public (POSIX.1e only) */ #define ARCHIVE_ENTRY_ACL_EVERYONE 10107 /* Everyone (NFS4 only) */ /* * 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 int archive_entry_acl_add_entry(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const char * /* name */); __LA_DECL int 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 * certain types of 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: * * Flags only for archive entries with POSIX.1e ACL: * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries. * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries. * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each * default ACL entry. * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and * "mask" entries. * * Flags only for archive entries with NFSv4 ACL: * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for * unset permissions and flags in NFSv4 ACL permission and flag fields * * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL: * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in * each ACL entry. * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma * instead of newline. */ #define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 0x00000001 #define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 0x00000002 #define ARCHIVE_ENTRY_ACL_STYLE_SOLARIS 0x00000004 #define ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 0x00000008 #define ARCHIVE_ENTRY_ACL_STYLE_COMPACT 0x00000010 __LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *, - ssize_t * /* len */, int /* flags */); + la_ssize_t * /* len */, int /* flags */); __LA_DECL char *archive_entry_acl_to_text(struct archive_entry *, - ssize_t * /* len */, int /* flags */); + la_ssize_t * /* len */, int /* flags */); __LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *, const wchar_t * /* wtext */, int /* type */); __LA_DECL int archive_entry_acl_from_text(struct archive_entry *, const char * /* text */, int /* type */); /* Deprecated constants */ #define OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024 #define OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 /* Deprecated functions */ __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int /* flags */) __LA_DEPRECATED; __LA_DECL const char *archive_entry_acl_text(struct archive_entry *, int /* flags */) __LA_DEPRECATED; /* Return bitmask of ACL types in an archive entry */ __LA_DECL int archive_entry_acl_types(struct archive_entry *); /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); /* Return an opaque ACL object. */ /* There's not yet anything clients can actually do with this... */ struct archive_acl; __LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *); /* * 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 *); /* * sparse */ __LA_DECL void archive_entry_sparse_clear(struct archive_entry *); __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *, la_int64_t /* offset */, la_int64_t /* length */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_sparse_count(struct archive_entry *); __LA_DECL int archive_entry_sparse_reset(struct archive_entry *); __LA_DECL int archive_entry_sparse_next(struct archive_entry *, la_int64_t * /* offset */, la_int64_t * /* length */); /* * 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_linkresolver_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 noticeably 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 **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); #ifdef __cplusplus } #endif /* This is meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ Index: vendor/libarchive/dist/libarchive/archive_hmac.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_hmac.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_hmac.c (revision 314565) @@ -1,250 +1,254 @@ /*- * Copyright (c) 2014 Michihiro NAKAJIMA * 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" #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_hmac_private.h" /* * On systems that do not support any recognized crypto libraries, * the archive_hmac.c file is expected to define no usable symbols. * * But some compilers and linkers choke on empty object files, so * define a public symbol that will always exist. This could * be removed someday if this file gains another always-present * symbol definition. */ int __libarchive_hmac_build_hack(void) { return 0; } #ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len); return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { CCHmacUpdate(ctx, data, data_len); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { CCHmacFinal(ctx, out); *out_len = 20; } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); } #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#ifndef BCRYPT_HASH_REUSABLE_FLAG +# define BCRYPT_HASH_REUSABLE_FLAG 0x00000020 +#endif + static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; PBYTE hash; ULONG result; NTSTATUS status; ctx->hAlg = NULL; status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (!BCRYPT_SUCCESS(status)) return -1; status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len, sizeof(hash_len), &result, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len); if (hash == NULL) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } status = BCryptCreateHash(hAlg, &hHash, NULL, 0, (PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); HeapFree(GetProcessHeap(), 0, hash); return -1; } ctx->hAlg = hAlg; ctx->hHash = hHash; ctx->hash_len = hash_len; ctx->hash = hash; return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0); if (ctx->hash_len == *out_len) memcpy(out, ctx->hash, *out_len); } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { if (ctx->hAlg != NULL) { BCryptCloseAlgorithmProvider(ctx->hAlg, 0); HeapFree(GetProcessHeap(), 0, ctx->hash); ctx->hAlg = NULL; } } #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { hmac_sha1_set_key(ctx, key_len, key); return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { hmac_sha1_update(ctx, data_len, data); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { hmac_sha1_digest(ctx, (unsigned)*out_len, out); } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); } #elif defined(HAVE_LIBCRYPTO) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { *ctx = HMAC_CTX_new(); if (*ctx == NULL) return -1; HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL); return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { HMAC_Update(*ctx, data, data_len); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { unsigned int len = (unsigned int)*out_len; HMAC_Final(*ctx, out, &len); *out_len = len; } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { HMAC_CTX_free(*ctx); *ctx = NULL; } #else /* Stub */ static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { (void)ctx;/* UNUSED */ (void)key;/* UNUSED */ (void)key_len;/* UNUSED */ return -1; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { (void)ctx;/* UNUSED */ (void)data;/* UNUSED */ (void)data_len;/* UNUSED */ } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { (void)ctx;/* UNUSED */ (void)out;/* UNUSED */ (void)out_len;/* UNUSED */ } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { (void)ctx;/* UNUSED */ } #endif const struct archive_hmac __archive_hmac = { &__hmac_sha1_init, &__hmac_sha1_update, &__hmac_sha1_final, &__hmac_sha1_cleanup, }; Index: vendor/libarchive/dist/libarchive/archive_read_disk_entry_from_file.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_disk_entry_from_file.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_read_disk_entry_from_file.c (revision 314565) @@ -1,2001 +1,2014 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * Copyright (c) 2016 Martin Matuska * 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: head/lib/libarchive/archive_read_disk_entry_from_file.c 201084 2009-12-28 02:14:09Z kientzle $"); /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H /* Mac OSX requires sys/types.h before sys/acl.h. */ #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_DARWIN_ACL #include #include #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #if defined(HAVE_SYS_XATTR_H) #include #elif defined(HAVE_ATTR_XATTR_H) #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_FIEMAP_H #include #endif #ifdef HAVE_LINUX_FS_H #include #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_PATHS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* * Linux and FreeBSD plug this obvious hole in POSIX.1e in * different ways. */ #if HAVE_ACL_GET_PERM #define ACL_GET_PERM acl_get_perm #elif HAVE_ACL_GET_PERM_NP #define ACL_GET_PERM acl_get_perm_np #endif /* NFSv4 platform ACL type */ #if HAVE_SUN_ACL #define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACE_T #elif HAVE_DARWIN_ACL #define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACL_TYPE_EXTENDED #elif HAVE_ACL_TYPE_NFS4 #define ARCHIVE_PLATFORM_ACL_TYPE_NFS4 ACL_TYPE_NFS4 #endif static int setup_acls(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_mac_metadata(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_xattrs(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_sparse(struct archive_read_disk *, struct archive_entry *, int *fd); #if defined(HAVE_LINUX_FIEMAP_H) static int setup_sparse_fiemap(struct archive_read_disk *, struct archive_entry *, int *fd); #endif int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ - if (st->st_flags != 0) + if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif -#if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) +#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ + (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ - if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { + if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && + (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; - r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); + r = ioctl(fd, +#if defined(FS_IOC_GETFLAGS) + FS_IOC_GETFLAGS, +#else + EXT2_IOC_GETFLAGS, +#endif + &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size + 1; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ - r = setup_acls(a, entry, &fd); - if (!a->suppress_xattr) { + r = 0; + if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0) + r = setup_acls(a, entry, &fd); + if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) { r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; } - if (a->enable_copyfile) { + if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); } #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) /* * The Mac OS "copyfile()" API copies the extended metadata for a * file into a separate file in AppleDouble format (see RFC 1740). * * Mac OS tar and cpio implementations store this extended * metadata as a separate entry just before the regular entry * with a "._" prefix added to the filename. * * Note that this is currently done unconditionally; the tar program has * an option to discard this information before the archive is written. * * TODO: If there's a failure, report it and return ARCHIVE_WARN. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int tempfd = -1; int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; struct stat copyfile_stat; int ret = ARCHIVE_OK; void *buff = NULL; int have_attrs; const char *name, *tempdir; struct archive_string tempfile; (void)fd; /* UNUSED */ name = archive_entry_sourcepath(entry); if (name == NULL) name = archive_entry_pathname(entry); + else if (a->tree != NULL && a->tree_enter_working_dir(a->tree) != 0) { + archive_set_error(&a->archive, errno, + "Can't change dir to read extended attributes"); + return (ARCHIVE_FAILED); + } if (name == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't open file to read extended attributes: No name"); return (ARCHIVE_WARN); } - if (a->tree != NULL) { - if (a->tree_enter_working_dir(a->tree) != 0) { - archive_set_error(&a->archive, errno, - "Couldn't change dir"); - return (ARCHIVE_FAILED); - } - } - /* Short-circuit if there's nothing to do. */ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); if (have_attrs == -1) { archive_set_error(&a->archive, errno, "Could not check extended attributes"); return (ARCHIVE_WARN); } if (have_attrs == 0) return (ARCHIVE_OK); tempdir = NULL; if (issetugid() == 0) tempdir = getenv("TMPDIR"); if (tempdir == NULL) tempdir = _PATH_TMP; archive_string_init(&tempfile); archive_strcpy(&tempfile, tempdir); archive_strcat(&tempfile, "tar.md.XXXXXX"); tempfd = mkstemp(tempfile.s); if (tempfd < 0) { archive_set_error(&a->archive, errno, "Could not open extended attribute file"); ret = ARCHIVE_WARN; goto cleanup; } __archive_ensure_cloexec_flag(tempfd); /* XXX I wish copyfile() could pack directly to a memory * buffer; that would avoid the temp file here. For that * matter, it would be nice if fcopyfile() actually worked, * that would reduce the many open/close races here. */ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { archive_set_error(&a->archive, errno, "Could not pack extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } buff = malloc(copyfile_stat.st_size); if (buff == NULL) { archive_set_error(&a->archive, errno, "Could not allocate memory for extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { archive_set_error(&a->archive, errno, "Could not read extended attributes into memory"); ret = ARCHIVE_WARN; goto cleanup; } archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); cleanup: if (tempfd >= 0) { close(tempfd); unlink(tempfile.s); } archive_string_free(&tempfile); free(buff); return (ret); } #else /* * Stub implementation for non-Mac systems. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if HAVE_DARWIN_ACL static int translate_guid(struct archive *, acl_entry_t, int *, int *, const char **); static void add_trivial_nfs4_acl(struct archive_entry *); #endif #if HAVE_SUN_ACL static int sun_acl_is_trivial(acl_t *, mode_t, int *trivialp); #endif #if HAVE_POSIX_ACL || HAVE_NFS4_ACL static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, #if HAVE_SUN_ACL acl_t *acl, #else acl_t acl, #endif int archive_entry_acl_type); static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *accpath; #if HAVE_SUN_ACL acl_t *acl; #else acl_t acl; #endif int r; - accpath = archive_entry_sourcepath(entry); - if (accpath == NULL) - accpath = archive_entry_pathname(entry); + accpath = NULL; - if (*fd < 0 && a->tree != NULL) { - if (a->follow_symlinks || - archive_entry_filetype(entry) != AE_IFLNK) +#if HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_ACL_GET_FD_NP + if (*fd < 0) +#else + /* For default ACLs on Linux we need reachable accpath */ + if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) +#endif + { + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL || (a->tree != NULL && + a->tree_enter_working_dir(a->tree) != 0)) + accpath = archive_entry_pathname(entry); + if (accpath == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Couldn't determine file path to read ACLs"); + return (ARCHIVE_WARN); + } + if (a->tree != NULL && +#if !HAVE_SUN_ACL && !HAVE_DARWIN_ACL && !HAVE_ACL_GET_FD_NP + *fd < 0 && +#endif + (a->follow_symlinks || + archive_entry_filetype(entry) != AE_IFLNK)) { *fd = a->open_on_current_dir(a->tree, accpath, O_RDONLY | O_NONBLOCK); - if (*fd < 0) { - if (a->tree_enter_working_dir(a->tree) != 0) { - archive_set_error(&a->archive, errno, - "Couldn't access %s", accpath); - return (ARCHIVE_FAILED); - } } } archive_entry_acl_clear(entry); acl = NULL; #if HAVE_NFS4_ACL /* Try NFSv4 ACL first. */ if (*fd >= 0) #if HAVE_SUN_ACL /* Solaris reads both POSIX.1e and NFSv4 ACL here */ facl_get(*fd, 0, &acl); #elif HAVE_ACL_GET_FD_NP acl = acl_get_fd_np(*fd, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); #else acl = acl_get_fd(*fd); #endif #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else #if HAVE_SUN_ACL /* Solaris reads both POSIX.1e and NFSv4 ACLs here */ acl_get(accpath, 0, &acl); #else acl = acl_get_file(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); #endif #if HAVE_ACL_IS_TRIVIAL_NP || HAVE_SUN_ACL /* Ignore "trivial" ACLs that just mirror the file mode. */ if (acl != NULL) { #if HAVE_SUN_ACL if (sun_acl_is_trivial(acl, archive_entry_mode(entry), &r) == 0 && r == 1) #elif HAVE_ACL_IS_TRIVIAL_NP if (acl_is_trivial_np(acl, &r) == 0 && r == 1) #endif { acl_free(acl); acl = NULL; /* * Simultaneous NFSv4 and POSIX.1e ACLs for the same * entry are not allowed, so we should return here */ return (ARCHIVE_OK); } } #endif /* HAVE_ACL_IS_TRIVIAL_NP || HAVE_SUN_ACL */ if (acl != NULL) { r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); acl_free(acl); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, errno, -#if HAVE_SUN_ACL - "Couldn't translate ACLs: %s", accpath); -#else - "Couldn't translate NFSv4 ACLs: %s", accpath); + "Couldn't translate " +#if !HAVE_SUN_ACL + "NFSv4 " #endif + "ACLs"); } #if HAVE_DARWIN_ACL /* * Because Mac OS doesn't support owner@, group@ and everyone@ * ACLs we need to add NFSv4 ACLs mirroring the file mode to * the archive entry. Otherwise extraction on non-Mac platforms * would lead to an invalid file mode. */ - if (archive_entry_acl_count(entry, - ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) + if ((archive_entry_acl_types(entry) & + ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) add_trivial_nfs4_acl(entry); #endif return (r); } #endif /* HAVE_NFS4_ACL */ #if HAVE_POSIX_ACL /* This code path is skipped on MacOS and Solaris */ /* Retrieve access ACL from file. */ if (*fd >= 0) acl = acl_get_fd(*fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); #if HAVE_ACL_IS_TRIVIAL_NP /* Ignore "trivial" ACLs that just mirror the file mode. */ if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) { if (r) { acl_free(acl); acl = NULL; } } #endif if (acl != NULL) { r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); acl = NULL; if (r != ARCHIVE_OK) { archive_set_error(&a->archive, errno, - "Couldn't translate access ACLs: %s", accpath); + "Couldn't translate access ACLs"); return (r); } } /* Only directories can have default ACLs. */ if (S_ISDIR(archive_entry_mode(entry))) { #if HAVE_ACL_GET_FD_NP if (*fd >= 0) acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT); else #endif acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, errno, - "Couldn't translate default ACLs: %s", - accpath); + "Couldn't translate default ACLs"); return (r); } } } #endif /* HAVE_POSIX_ACL */ return (ARCHIVE_OK); } /* * Translate system ACL permissions into libarchive internal structure */ static const struct { const int archive_perm; const int platform_perm; } acl_perm_map[] = { #if HAVE_SUN_ACL /* Solaris NFSv4 ACL permissions */ {ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE}, {ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA}, {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY}, {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA}, {ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE}, {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA}, {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY}, {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS}, {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS}, {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD}, {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE}, {ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER}, {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE} #elif HAVE_DARWIN_ACL /* MacOS ACL permissions */ {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES}, {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES}, {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY}, {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY}, {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER}, {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} #else /* POSIX.1e ACL permissions */ {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, #if HAVE_ACL_TYPE_NFS4 /* FreeBSD NFSv4 ACL permissions */ {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} #endif #endif /* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */ }; #if HAVE_NFS4_ACL /* * Translate system NFSv4 inheritance flags into libarchive internal structure */ static const struct { const int archive_inherit; const int platform_inherit; } acl_inherit_map[] = { #if HAVE_SUN_ACL /* Solaris ACL inheritance flags */ {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE}, {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE}, {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE}, {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG}, {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE} #elif HAVE_DARWIN_ACL /* MacOS NFSv4 inheritance flags */ {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}, {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT} #else /* FreeBSD NFSv4 ACL inheritance flags */ {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} #endif /* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */ }; #endif /* HAVE_NFS4_ACL */ #if HAVE_DARWIN_ACL static int translate_guid(struct archive *a, acl_entry_t acl_entry, int *ae_id, int *ae_tag, const char **ae_name) { void *q; uid_t ugid; int r, idtype; struct passwd *pwd; struct group *grp; q = acl_get_qualifier(acl_entry); if (q == NULL) return (1); r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype); if (r != 0) { acl_free(q); return (1); } if (idtype == ID_TYPE_UID) { *ae_tag = ARCHIVE_ENTRY_ACL_USER; pwd = getpwuuid(q); if (pwd == NULL) { *ae_id = ugid; *ae_name = NULL; } else { *ae_id = pwd->pw_uid; *ae_name = archive_read_disk_uname(a, *ae_id); } } else if (idtype == ID_TYPE_GID) { *ae_tag = ARCHIVE_ENTRY_ACL_GROUP; grp = getgruuid(q); if (grp == NULL) { *ae_id = ugid; *ae_name = NULL; } else { *ae_id = grp->gr_gid; *ae_name = archive_read_disk_gname(a, *ae_id); } } else r = 1; acl_free(q); return (r); } /* * Add trivial NFSv4 ACL entries from mode */ static void add_trivial_nfs4_acl(struct archive_entry *entry) { mode_t mode; int i; const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA; const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA; const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE; const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE; const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER; struct { const int type; const int tag; int permset; } tacl_entry[] = { {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0}, {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0}, {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0}, {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset}, {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset}, {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset} }; mode = archive_entry_mode(entry); /* Permissions for everyone@ */ if (mode & 0004) tacl_entry[5].permset |= rperm; if (mode & 0002) tacl_entry[5].permset |= wperm; if (mode & 0001) tacl_entry[5].permset |= eperm; /* Permissions for group@ */ if (mode & 0040) tacl_entry[4].permset |= rperm; else if (mode & 0004) tacl_entry[2].permset |= rperm; if (mode & 0020) tacl_entry[4].permset |= wperm; else if (mode & 0002) tacl_entry[2].permset |= wperm; if (mode & 0010) tacl_entry[4].permset |= eperm; else if (mode & 0001) tacl_entry[2].permset |= eperm; /* Permissions for owner@ */ if (mode & 0400) { tacl_entry[3].permset |= rperm; if (!(mode & 0040) && (mode & 0004)) tacl_entry[0].permset |= rperm; } else if ((mode & 0040) || (mode & 0004)) tacl_entry[1].permset |= rperm; if (mode & 0200) { tacl_entry[3].permset |= wperm; if (!(mode & 0020) && (mode & 0002)) tacl_entry[0].permset |= wperm; } else if ((mode & 0020) || (mode & 0002)) tacl_entry[1].permset |= wperm; if (mode & 0100) { tacl_entry[3].permset |= eperm; if (!(mode & 0010) && (mode & 0001)) tacl_entry[0].permset |= eperm; } else if ((mode & 0010) || (mode & 0001)) tacl_entry[1].permset |= eperm; for (i = 0; i < 6; i++) { if (tacl_entry[i].permset != 0) { archive_entry_acl_add_entry(entry, tacl_entry[i].type, tacl_entry[i].permset, tacl_entry[i].tag, -1, NULL); } } return; } #elif HAVE_SUN_ACL /* * Check if acl is trivial * This is a FreeBSD acl_is_trivial_np() implementation for Solaris */ static int sun_acl_is_trivial(acl_t *acl, mode_t mode, int *trivialp) { int i, p; const uint32_t rperm = ACE_READ_DATA; const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA; const uint32_t eperm = ACE_EXECUTE; const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE; const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES | ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER; ace_t *ace; ace_t tace[6]; if (acl == NULL || trivialp == NULL) return (-1); *trivialp = 0; /* ACL_IS_TRIVIAL flag must be set for both POSIX.1e and NFSv4 ACLs */ if ((acl->acl_flags & ACL_IS_TRIVIAL) == 0) return (0); /* * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries, * including mask. */ if (acl->acl_type == ACLENT_T) { if (acl->acl_cnt == 4) *trivialp = 1; return (0); } if (acl->acl_type != ACE_T || acl->acl_entry_size != sizeof(ace_t)) return (-1); /* * Continue with checking NFSv4 ACLs * * Create list of trivial ace's to be compared */ /* owner@ allow pre */ tace[0].a_flags = ACE_OWNER; tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; tace[0].a_access_mask = 0; /* owner@ deny */ tace[1].a_flags = ACE_OWNER; tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE; tace[1].a_access_mask = 0; /* group@ deny */ tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP; tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE; tace[2].a_access_mask = 0; /* owner@ allow */ tace[3].a_flags = ACE_OWNER; tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; tace[3].a_access_mask = ownset; /* group@ allow */ tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP; tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; tace[4].a_access_mask = pubset; /* everyone@ allow */ tace[5].a_flags = ACE_EVERYONE; tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; tace[5].a_access_mask = pubset; /* Permissions for everyone@ */ if (mode & 0004) tace[5].a_access_mask |= rperm; if (mode & 0002) tace[5].a_access_mask |= wperm; if (mode & 0001) tace[5].a_access_mask |= eperm; /* Permissions for group@ */ if (mode & 0040) tace[4].a_access_mask |= rperm; else if (mode & 0004) tace[2].a_access_mask |= rperm; if (mode & 0020) tace[4].a_access_mask |= wperm; else if (mode & 0002) tace[2].a_access_mask |= wperm; if (mode & 0010) tace[4].a_access_mask |= eperm; else if (mode & 0001) tace[2].a_access_mask |= eperm; /* Permissions for owner@ */ if (mode & 0400) { tace[3].a_access_mask |= rperm; if (!(mode & 0040) && (mode & 0004)) tace[0].a_access_mask |= rperm; } else if ((mode & 0040) || (mode & 0004)) tace[1].a_access_mask |= rperm; if (mode & 0200) { tace[3].a_access_mask |= wperm; if (!(mode & 0020) && (mode & 0002)) tace[0].a_access_mask |= wperm; } else if ((mode & 0020) || (mode & 0002)) tace[1].a_access_mask |= wperm; if (mode & 0100) { tace[3].a_access_mask |= eperm; if (!(mode & 0010) && (mode & 0001)) tace[0].a_access_mask |= eperm; } else if ((mode & 0010) || (mode & 0001)) tace[1].a_access_mask |= eperm; /* Check if the acl count matches */ p = 3; for (i = 0; i < 3; i++) { if (tace[i].a_access_mask != 0) p++; } if (acl->acl_cnt != p) return (0); p = 0; for (i = 0; i < 6; i++) { if (tace[i].a_access_mask != 0) { ace = &((ace_t *)acl->acl_aclp)[p]; /* * Illumos added ACE_DELETE_CHILD to write perms for * directories. We have to check against that, too. */ if (ace->a_flags != tace[i].a_flags || ace->a_type != tace[i].a_type || (ace->a_access_mask != tace[i].a_access_mask && ((acl->acl_flags & ACL_IS_DIR) == 0 || (tace[i].a_access_mask & wperm) == 0 || ace->a_access_mask != (tace[i].a_access_mask | ACE_DELETE_CHILD)))) return (0); p++; } } *trivialp = 1; return (0); } #endif /* HAVE_SUN_ACL */ #if HAVE_SUN_ACL /* * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL */ static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t *acl, int default_entry_acl_type) { int e, i; int ae_id, ae_tag, ae_perm; int entry_acl_type; const char *ae_name; aclent_t *aclent; ace_t *ace; (void)default_entry_acl_type; if (acl->acl_cnt <= 0) return (ARCHIVE_OK); for (e = 0; e < acl->acl_cnt; e++) { ae_name = NULL; ae_tag = 0; ae_perm = 0; if (acl->acl_type == ACE_T) { ace = &((ace_t *)acl->acl_aclp)[e]; ae_id = ace->a_who; switch(ace->a_type) { case ACE_ACCESS_ALLOWED_ACE_TYPE: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; break; case ACE_ACCESS_DENIED_ACE_TYPE: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; break; case ACE_SYSTEM_AUDIT_ACE_TYPE: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; break; case ACE_SYSTEM_ALARM_ACE_TYPE: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; break; default: /* Unknown entry type, skip */ continue; } if ((ace->a_flags & ACE_OWNER) != 0) ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; else if ((ace->a_flags & ACE_GROUP) != 0) ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; else if ((ace->a_flags & ACE_EVERYONE) != 0) ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) { ae_tag = ARCHIVE_ENTRY_ACL_GROUP; ae_name = archive_read_disk_gname(&a->archive, ae_id); } else { ae_tag = ARCHIVE_ENTRY_ACL_USER; ae_name = archive_read_disk_uname(&a->archive, ae_id); } for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { if ((ace->a_flags & acl_inherit_map[i].platform_inherit) != 0) ae_perm |= acl_inherit_map[i].archive_inherit; } for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { if ((ace->a_access_mask & acl_perm_map[i].platform_perm) != 0) ae_perm |= acl_perm_map[i].archive_perm; } } else { aclent = &((aclent_t *)acl->acl_aclp)[e]; if ((aclent->a_type & ACL_DEFAULT) != 0) entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; else entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; ae_id = aclent->a_id; switch(aclent->a_type) { case DEF_USER: case USER: ae_name = archive_read_disk_uname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_USER; break; case DEF_GROUP: case GROUP: ae_name = archive_read_disk_gname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_GROUP; break; case DEF_CLASS_OBJ: case CLASS_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_MASK; break; case DEF_USER_OBJ: case USER_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; break; case DEF_GROUP_OBJ: case GROUP_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case DEF_OTHER_OBJ: case OTHER_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; break; default: /* Unknown tag type, skip */ continue; } if ((aclent->a_perm & 1) != 0) ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; if ((aclent->a_perm & 2) != 0) ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; if ((aclent->a_perm & 4) != 0) ae_perm |= ARCHIVE_ENTRY_ACL_READ; } /* default_entry_acl_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4 */ archive_entry_acl_add_entry(entry, entry_acl_type, ae_perm, ae_tag, ae_id, ae_name); } return (ARCHIVE_OK); } #else /* !HAVE_SUN_ACL */ /* * Translate POSIX.1e (Linux), FreeBSD (both POSIX.1e and NFSv4) and * MacOS (NFSv4 only) ACLs into libarchive internal structure */ static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int default_entry_acl_type) { acl_tag_t acl_tag; #if HAVE_ACL_TYPE_NFS4 acl_entry_type_t acl_type; int brand; #endif #if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL acl_flagset_t acl_flagset; #endif acl_entry_t acl_entry; acl_permset_t acl_permset; int i, entry_acl_type; int r, s, ae_id, ae_tag, ae_perm; #if !HAVE_DARWIN_ACL void *q; #endif const char *ae_name; #if HAVE_ACL_TYPE_NFS4 // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 // Make sure the "brand" on this ACL is consistent // with the default_entry_acl_type bits provided. if (acl_get_brand_np(acl, &brand) != 0) { archive_set_error(&a->archive, errno, "Failed to read ACL brand"); return (ARCHIVE_WARN); } switch (brand) { case ACL_BRAND_POSIX: switch (default_entry_acl_type) { case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid ACL entry type for POSIX.1e ACL"); return (ARCHIVE_WARN); } break; case ACL_BRAND_NFS4: if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid ACL entry type for NFSv4 ACL"); return (ARCHIVE_WARN); } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown ACL brand"); return (ARCHIVE_WARN); } #endif s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get first ACL entry"); return (ARCHIVE_WARN); } #if HAVE_DARWIN_ACL while (s == 0) #else /* FreeBSD, Linux */ while (s == 1) #endif { ae_id = -1; ae_name = NULL; ae_perm = 0; if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { archive_set_error(&a->archive, errno, "Failed to get ACL tag type"); return (ARCHIVE_WARN); } switch (acl_tag) { #if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ case ACL_USER: q = acl_get_qualifier(acl_entry); if (q != NULL) { ae_id = (int)*(uid_t *)q; acl_free(q); ae_name = archive_read_disk_uname(&a->archive, ae_id); } ae_tag = ARCHIVE_ENTRY_ACL_USER; break; case ACL_GROUP: q = acl_get_qualifier(acl_entry); if (q != NULL) { ae_id = (int)*(gid_t *)q; acl_free(q); ae_name = archive_read_disk_gname(&a->archive, ae_id); } ae_tag = ARCHIVE_ENTRY_ACL_GROUP; break; case ACL_MASK: ae_tag = ARCHIVE_ENTRY_ACL_MASK; break; case ACL_USER_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; break; case ACL_GROUP_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case ACL_OTHER: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; break; #if HAVE_ACL_TYPE_NFS4 case ACL_EVERYONE: ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; break; #endif #else /* HAVE_DARWIN_ACL */ case ACL_EXTENDED_ALLOW: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; r = translate_guid(&a->archive, acl_entry, &ae_id, &ae_tag, &ae_name); break; case ACL_EXTENDED_DENY: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; r = translate_guid(&a->archive, acl_entry, &ae_id, &ae_tag, &ae_name); break; #endif /* HAVE_DARWIN_ACL */ default: /* Skip types that libarchive can't support. */ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); continue; } #if HAVE_DARWIN_ACL /* Skip if translate_guid() above failed */ if (r != 0) { s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); continue; } #endif #if !HAVE_DARWIN_ACL // XXX acl_type maps to allow/deny/audit/YYYY bits entry_acl_type = default_entry_acl_type; #endif #if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { #if HAVE_ACL_TYPE_NFS4 /* * acl_get_entry_type_np() fails with non-NFSv4 ACLs */ if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { archive_set_error(&a->archive, errno, "Failed " "to get ACL type from a NFSv4 ACL entry"); return (ARCHIVE_WARN); } switch (acl_type) { case ACL_ENTRY_TYPE_ALLOW: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; break; case ACL_ENTRY_TYPE_DENY: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; break; case ACL_ENTRY_TYPE_AUDIT: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; break; case ACL_ENTRY_TYPE_ALARM: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; break; default: archive_set_error(&a->archive, errno, "Invalid NFSv4 ACL entry type"); return (ARCHIVE_WARN); } #endif /* HAVE_ACL_TYPE_NFS4 */ /* * Libarchive stores "flag" (NFSv4 inheritance bits) * in the ae_perm bitmap. * * acl_get_flagset_np() fails with non-NFSv4 ACLs */ if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { archive_set_error(&a->archive, errno, "Failed to get flagset from a NFSv4 ACL entry"); return (ARCHIVE_WARN); } for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { r = acl_get_flag_np(acl_flagset, acl_inherit_map[i].platform_inherit); if (r == -1) { archive_set_error(&a->archive, errno, "Failed to check flag in a NFSv4 " "ACL flagset"); return (ARCHIVE_WARN); } else if (r) ae_perm |= acl_inherit_map[i].archive_inherit; } } #endif /* HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL */ if (acl_get_permset(acl_entry, &acl_permset) != 0) { archive_set_error(&a->archive, errno, "Failed to get ACL permission set"); return (ARCHIVE_WARN); } for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { /* * acl_get_perm() is spelled differently on different * platforms; see above. */ r = ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm); if (r == -1) { archive_set_error(&a->archive, errno, "Failed to check permission in an ACL permission set"); return (ARCHIVE_WARN); } else if (r) ae_perm |= acl_perm_map[i].archive_perm; } archive_entry_acl_add_entry(entry, entry_acl_type, ae_perm, ae_tag, ae_id, ae_name); s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); #if !HAVE_DARWIN_ACL if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get next ACL entry"); return (ARCHIVE_WARN); } #endif } return (ARCHIVE_OK); } #endif /* !HAVE_SUN_ACL */ #else /* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */ static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif /* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */ #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \ HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \ (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA) /* * Linux and AIX extended attribute support. * * TODO: By using a stack-allocated buffer for the first * call to getxattr(), we might be able to avoid the second * call entirely. We only need the second call if the * stack-allocated buffer is too small. But a modest buffer * of 1024 bytes or so will often be big enough. Same applies * to listxattr(). */ static int setup_xattr(struct archive_read_disk *a, - struct archive_entry *entry, const char *name, int fd) + struct archive_entry *entry, const char *name, int fd, const char *accpath) { ssize_t size; void *value = NULL; - const char *accpath; - accpath = archive_entry_sourcepath(entry); - if (accpath == NULL) - accpath = archive_entry_pathname(entry); - #if HAVE_FGETXATTR if (fd >= 0) size = fgetxattr(fd, name, NULL, 0); else if (!a->follow_symlinks) size = lgetxattr(accpath, name, NULL, 0); else size = getxattr(accpath, name, NULL, 0); #elif HAVE_FGETEA if (fd >= 0) size = fgetea(fd, name, NULL, 0); else if (!a->follow_symlinks) size = lgetea(accpath, name, NULL, 0); else size = getea(accpath, name, NULL, 0); #endif if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } #if HAVE_FGETXATTR if (fd >= 0) size = fgetxattr(fd, name, value, size); else if (!a->follow_symlinks) size = lgetxattr(accpath, name, value, size); else size = getxattr(accpath, name, value, size); #elif HAVE_FGETEA if (fd >= 0) size = fgetea(fd, name, value, size); else if (!a->follow_symlinks) size = lgetea(accpath, name, value, size); else size = getea(accpath, name, value, size); #endif if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, name, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char *list, *p; const char *path; ssize_t list_size; - path = archive_entry_sourcepath(entry); - if (path == NULL) - path = archive_entry_pathname(entry); + path = NULL; - if (*fd < 0 && a->tree != NULL) { - if (a->follow_symlinks || - archive_entry_filetype(entry) != AE_IFLNK) - *fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); - if (*fd < 0) { - if (a->tree_enter_working_dir(a->tree) != 0) { - archive_set_error(&a->archive, errno, - "Couldn't access %s", path); - return (ARCHIVE_FAILED); - } + if (*fd < 0) { + path = archive_entry_sourcepath(entry); + if (path == NULL || (a->tree != NULL && + a->tree_enter_working_dir(a->tree) != 0)) + path = archive_entry_pathname(entry); + if (path == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Couldn't determine file path to read " + "extended attributes"); + return (ARCHIVE_WARN); } + if (a->tree != NULL && (a->follow_symlinks || + archive_entry_filetype(entry) != AE_IFLNK)) { + *fd = a->open_on_current_dir(a->tree, + path, O_RDONLY | O_NONBLOCK); + } } #if HAVE_FLISTXATTR if (*fd >= 0) list_size = flistxattr(*fd, NULL, 0); else if (!a->follow_symlinks) list_size = llistxattr(path, NULL, 0); else list_size = listxattr(path, NULL, 0); #elif HAVE_FLISTEA if (*fd >= 0) list_size = flistea(*fd, NULL, 0); else if (!a->follow_symlinks) list_size = llistea(path, NULL, 0); else list_size = listea(path, NULL, 0); #endif if (list_size == -1) { if (errno == ENOTSUP || errno == ENOSYS) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } #if HAVE_FLISTXATTR if (*fd >= 0) list_size = flistxattr(*fd, list, list_size); else if (!a->follow_symlinks) list_size = llistxattr(path, list, list_size); else list_size = listxattr(path, list, list_size); #elif HAVE_FLISTEA if (*fd >= 0) list_size = flistea(*fd, list, list_size); else if (!a->follow_symlinks) list_size = llistea(path, list, list_size); else list_size = listea(path, list, list_size); #endif if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } for (p = list; (p - list) < list_size; p += strlen(p) + 1) { if (strncmp(p, "system.", 7) == 0 || strncmp(p, "xfsroot.", 8) == 0) continue; - setup_xattr(a, entry, p, *fd); + setup_xattr(a, entry, p, *fd, path); } free(list); return (ARCHIVE_OK); } #elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \ HAVE_DECL_EXTATTR_NAMESPACE_USER /* * FreeBSD extattr interface. */ /* TODO: Implement this. Follow the Linux model above, but * with FreeBSD-specific system calls, of course. Be careful * to not include the system extattrs that hold ACLs; we handle * those separately. */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, - int namespace, const char *name, const char *fullname, int fd); + int namespace, const char *name, const char *fullname, int fd, + const char *path); static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, - int namespace, const char *name, const char *fullname, int fd) + int namespace, const char *name, const char *fullname, int fd, + const char *accpath) { ssize_t size; void *value = NULL; - const char *accpath; - accpath = archive_entry_sourcepath(entry); - if (accpath == NULL) - accpath = archive_entry_pathname(entry); - if (fd >= 0) size = extattr_get_fd(fd, namespace, name, NULL, 0); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, NULL, 0); else size = extattr_get_file(accpath, namespace, name, NULL, 0); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (fd >= 0) size = extattr_get_fd(fd, namespace, name, value, size); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, value, size); else size = extattr_get_file(accpath, namespace, name, value, size); if (size == -1) { free(value); archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, fullname, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[512]; char *list, *p; ssize_t list_size; const char *path; int namespace = EXTATTR_NAMESPACE_USER; - path = archive_entry_sourcepath(entry); - if (path == NULL) - path = archive_entry_pathname(entry); + path = NULL; - if (*fd < 0 && a->tree != NULL) { - if (a->follow_symlinks || - archive_entry_filetype(entry) != AE_IFLNK) - *fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); - if (*fd < 0) { - if (a->tree_enter_working_dir(a->tree) != 0) { - archive_set_error(&a->archive, errno, - "Couldn't access %s", path); - return (ARCHIVE_FAILED); - } + if (*fd < 0) { + path = archive_entry_sourcepath(entry); + if (path == NULL || (a->tree != NULL && + a->tree_enter_working_dir(a->tree) != 0)) + path = archive_entry_pathname(entry); + if (path == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Couldn't determine file path to read " + "extended attributes"); + return (ARCHIVE_WARN); } + if (a->tree != NULL && (a->follow_symlinks || + archive_entry_filetype(entry) != AE_IFLNK)) { + *fd = a->open_on_current_dir(a->tree, + path, O_RDONLY | O_NONBLOCK); + } } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, NULL, 0); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, NULL, 0); else list_size = extattr_list_file(path, namespace, NULL, 0); if (list_size == -1 && errno == EOPNOTSUPP) return (ARCHIVE_OK); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, list, list_size); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, list, list_size); else list_size = extattr_list_file(path, namespace, list, list_size); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } p = list; while ((p - list) < list_size) { size_t len = 255 & (int)*p; char *name; strcpy(buff, "user."); name = buff + strlen(buff); memcpy(name, p + 1, len); name[len] = '\0'; - setup_xattr(a, entry, namespace, name, buff, *fd); + setup_xattr(a, entry, namespace, name, buff, *fd, path); p += 1 + len; } free(list); return (ARCHIVE_OK); } #else /* * Generic (stub) extended attribute support. */ static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if defined(HAVE_LINUX_FIEMAP_H) /* * Linux FIEMAP sparse interface. * * The FIEMAP ioctl returns an "extent" for each physical allocation * on disk. We need to process those to generate a more compact list * of logical file blocks. We also need to be very careful to use * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes * does not report allocations for newly-written data that hasn't * been synced to disk. * * It's important to return a minimal sparse file list because we want * to not trigger sparse file extensions if we don't have to, since * not all readers support them. */ static int setup_sparse_fiemap(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[4096]; struct fiemap *fm; struct fiemap_extent *fe; int64_t size; int count, do_fiemap, iters; int exit_sts = ARCHIVE_OK; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); if (*fd < 0) { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree != NULL) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); } /* Initialize buffer to avoid the error valgrind complains about. */ memset(buff, 0, sizeof(buff)); count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); fm = (struct fiemap *)buff; fm->fm_start = 0; fm->fm_length = ~0ULL;; fm->fm_flags = FIEMAP_FLAG_SYNC; fm->fm_extent_count = count; do_fiemap = 1; size = archive_entry_size(entry); for (iters = 0; ; ++iters) { int i, r; r = ioctl(*fd, FS_IOC_FIEMAP, fm); if (r < 0) { /* When something error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ goto exit_setup_sparse_fiemap; } if (fm->fm_mapped_extents == 0) { if (iters == 0) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } break; } fe = fm->fm_extents; for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { /* The fe_length of the last block does not * adjust itself to its size files. */ int64_t length = fe->fe_length; if (fe->fe_logical + length > (uint64_t)size) length -= fe->fe_logical + length - size; if (fe->fe_logical == 0 && length == size) { /* This is not sparse. */ do_fiemap = 0; break; } if (length > 0) archive_entry_sparse_add_entry(entry, fe->fe_logical, length); } if (fe->fe_flags & FIEMAP_EXTENT_LAST) do_fiemap = 0; } if (do_fiemap) { fe = fm->fm_extents + fm->fm_mapped_extents -1; fm->fm_start = fe->fe_logical + fe->fe_length; } else break; } exit_setup_sparse_fiemap: return (exit_sts); } #if !defined(SEEK_HOLE) || !defined(SEEK_DATA) static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { return setup_sparse_fiemap(a, entry, fd); } #endif #endif /* defined(HAVE_LINUX_FIEMAP_H) */ #if defined(SEEK_HOLE) && defined(SEEK_DATA) /* * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int64_t size; off_t initial_off; off_t off_s, off_e; int exit_sts = ARCHIVE_OK; int check_fully_sparse = 0; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); /* Does filesystem support the reporting of hole ? */ if (*fd < 0 && a->tree != NULL) { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } } if (*fd >= 0) { #ifdef _PC_MIN_HOLE_SIZE if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); #endif initial_off = lseek(*fd, 0, SEEK_CUR); if (initial_off != 0) lseek(*fd, 0, SEEK_SET); } else { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); #ifdef _PC_MIN_HOLE_SIZE if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); #endif *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); initial_off = 0; } #ifndef _PC_MIN_HOLE_SIZE /* Check if the underlying filesystem supports seek hole */ off_s = lseek(*fd, 0, SEEK_HOLE); if (off_s < 0) #if defined(HAVE_LINUX_FIEMAP_H) return setup_sparse_fiemap(a, entry, fd); #else goto exit_setup_sparse; #endif else if (off_s > 0) lseek(*fd, 0, SEEK_SET); #endif off_s = 0; size = archive_entry_size(entry); while (off_s < size) { off_s = lseek(*fd, off_s, SEEK_DATA); if (off_s == (off_t)-1) { if (errno == ENXIO) { /* no more hole */ if (archive_entry_sparse_count(entry) == 0) { /* Potentially a fully-sparse file. */ check_fully_sparse = 1; } break; } archive_set_error(&a->archive, errno, "lseek(SEEK_HOLE) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } off_e = lseek(*fd, off_s, SEEK_HOLE); if (off_e == (off_t)-1) { if (errno == ENXIO) { off_e = lseek(*fd, 0, SEEK_END); if (off_e != (off_t)-1) break;/* no more data */ } archive_set_error(&a->archive, errno, "lseek(SEEK_DATA) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } if (off_s == 0 && off_e == size) break;/* This is not sparse. */ archive_entry_sparse_add_entry(entry, off_s, off_e - off_s); off_s = off_e; } if (check_fully_sparse) { if (lseek(*fd, 0, SEEK_HOLE) == 0 && lseek(*fd, 0, SEEK_END) == size) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } } exit_setup_sparse: lseek(*fd, initial_off, SEEK_SET); return (exit_sts); } #elif !defined(HAVE_LINUX_FIEMAP_H) /* * Generic (stub) sparse support. */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ Index: vendor/libarchive/dist/libarchive/archive_read_disk_posix.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_disk_posix.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_read_disk_posix.c (revision 314565) @@ -1,2671 +1,2661 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * 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. */ /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LINUX_MAGIC_H #include #endif #ifdef HAVE_LINUX_FS_H #include #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_DIRECT_H #include #endif #ifdef HAVE_DIRENT_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 #ifdef HAVE_SYS_IOCTL_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef HAVE_FCHDIR #error fchdir function required. #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ /* * TODO: * 1) Loop checking. * 3) Arbitrary logical traversals by closing/reopening intermediate fds. */ struct restore_time { const char *name; time_t mtime; long mtime_nsec; time_t atime; long atime_nsec; mode_t filetype; int noatime; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; struct archive_string name; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to return back to the parent of a symlink. */ int symlink_parent_fd; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; int noatime; #if defined(USE_READDIR_R) size_t name_max; #endif long incr_xfer_size; long max_xfer_size; long min_xfer_size; long xfer_align; /* * Buffer used for reading file contents. */ /* Exactly allocated memory pointer. */ unsigned char *allocation_ptr; /* Pointer adjusted to the filesystem alignment . */ unsigned char *buff; size_t buff_size; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; DIR *d; #define INVALID_DIR_HANDLE NULL struct dirent *de; #if defined(USE_READDIR_R) struct dirent *dirent; size_t dirent_allocated; #endif int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* Dynamically-sized buffer for holding path */ struct archive_string path; /* Last path element */ const char *basename; /* Leading dir length */ size_t dirname_length; int depth; int openCount; int maxOpenCount; int initial_dir_fd; int working_dir_fd; struct stat lst; struct stat st; int descend; int nlink; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesystem; int entry_fd; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; unsigned char *entry_buff; size_t entry_buff_size; }; /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define onWorkingDir 64 /* We are on the working dir where we are * reading directory entry at this time. */ #define needsRestoreTimes 128 #define onInitialDir 256 /* We are on the initial dir. */ static int tree_dir_next_posix(struct tree *t); #ifdef HAVE_DIRENT_D_NAMLEN /* BSD extension; avoids need for a strlen() call. */ #define D_NAMELEN(dp) (dp)->d_namlen #else #define D_NAMELEN(dp) (strlen((dp)->d_name)) #endif /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const char *, int, int); static struct tree *tree_reopen(struct tree *, const char *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const char *, int, int64_t, int64_t, struct restore_time *); static int tree_enter_initial_dir(struct tree *); static int tree_enter_working_dir(struct tree *); static int tree_current_dir_fd(struct tree *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * * TODO: On platforms that support it, use openat()-style operations * to eliminate the chdir() operations entirely while still supporting * arbitrarily deep traversals. This makes access_path troublesome to * support, of course, which means we'll need a rich enough interface * that clients can function without it. (In particular, we'll need * tree_current_open() that returns an open file descriptor.) * */ static const char *tree_current_path(struct tree *); static const char *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const struct stat *tree_current_stat(struct tree *); static const struct stat *tree_current_lstat(struct tree *); static int tree_current_is_symblic_link_target(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const struct stat *); static int _archive_read_disk_open(struct archive *, const char *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); static int tree_dup(int); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; - a->enable_copyfile = 1; - a->traverse_mount_points = 1; + a->flags = ARCHIVE_READDISK_MAC_COPYFILE; a->open_on_current_dir = open_on_current_dir; a->tree_current_dir_fd = tree_current_dir_fd; a->tree_enter_working_dir = tree_enter_working_dir; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { -#ifndef HAVE_UTIMES - static int warning_done = 0; -#endif struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); #ifdef HAVE_UTIMES - a->restore_time = 1; + a->flags |= ARCHIVE_READDISK_RESTORE_ATIME; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); #else - if (warning_done) - /* Warning was already emitted; suppress further warnings. */ - return (ARCHIVE_OK); - + /* Display warning and unset flag */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore access time on this system"); - warning_done = 1; + a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME; return (ARCHIVE_WARN); #endif } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); + a->flags = flags; + if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { - a->restore_time = 0; if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } - if (flags & ARCHIVE_READDISK_HONOR_NODUMP) - a->honor_nodump = 1; - else - a->honor_nodump = 0; - if (flags & ARCHIVE_READDISK_MAC_COPYFILE) - a->enable_copyfile = 1; - else - a->enable_copyfile = 0; - if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) - a->traverse_mount_points = 0; - else - a->traverse_mount_points = 1; - if (flags & ARCHIVE_READDISK_NO_XATTR) - a->suppress_xattr = 1; - else - a->suppress_xattr = 0; return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } /* * Allocate memory for the reading buffer adjusted to the filesystem * alignment. */ static int setup_suitable_read_buffer(struct archive_read_disk *a) { struct tree *t = a->tree; struct filesystem *cf = t->current_filesystem; size_t asize; size_t s; if (cf->allocation_ptr == NULL) { /* If we couldn't get a filesystem alignment, * we use 4096 as default value but we won't use * O_DIRECT to open() and openat() operations. */ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align; if (cf->max_xfer_size != -1) asize = cf->max_xfer_size + xfer_align; else { long incr = cf->incr_xfer_size; /* Some platform does not set a proper value to * incr_xfer_size.*/ if (incr < 0) incr = cf->min_xfer_size; if (cf->min_xfer_size < 0) { incr = xfer_align; asize = xfer_align; } else asize = cf->min_xfer_size; /* Increase a buffer size up to 64K bytes in * a proper increment size. */ while (asize < 1024*64) asize += incr; /* Take a margin to adjust to the filesystem * alignment. */ asize += xfer_align; } cf->allocation_ptr = malloc(asize); if (cf->allocation_ptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* * Calculate proper address for the filesystem. */ s = (uintptr_t)cf->allocation_ptr; s %= xfer_align; if (s > 0) s = xfer_align - s; /* * Set a read buffer pointer in the proper alignment of * the current filesystem. */ cf->buff = cf->allocation_ptr + s; cf->buff_size = asize - xfer_align; } return (ARCHIVE_OK); } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; int r; ssize_t bytes; size_t buffbytes; int empty_sparse_region = 0; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Open the current file. */ if (t->entry_fd < 0) { int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. * * Carefully consider this to be enabled. */ #if defined(O_DIRECT) && 0/* Disabled for now */ if (t->current_filesystem->xfer_align != -1 && t->nlink == 1) flags |= O_DIRECT; #endif #if defined(O_NOATIME) /* * Linux has O_NOATIME flag; use it if we need. */ if ((t->flags & needsRestoreTimes) != 0 && t->restore_time.noatime == 0) flags |= O_NOATIME; do { #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, * if successful, set 1 to t->restore_time.noatime * not to restore an atime of the file later. * if failed by EPERM, retry it without O_NOATIME flag. */ if (flags & O_NOATIME) { if (t->entry_fd >= 0) t->restore_time.noatime = 1; else if (errno == EPERM) { flags &= ~O_NOATIME; continue; } } } while (0); #endif if (t->entry_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't open %s", tree_current_path(t)); r = ARCHIVE_FAILED; tree_enter_initial_dir(t); goto abort_read_data; } tree_enter_initial_dir(t); } /* * Allocate read buffer if not allocated. */ if (t->current_filesystem->allocation_ptr == NULL) { r = setup_suitable_read_buffer(a); if (r != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } t->entry_buff = t->current_filesystem->buff; t->entry_buff_size = t->current_filesystem->buff_size; buffbytes = t->entry_buff_size; if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; if (t->current_sparse->length == 0) empty_sparse_region = 1; /* * Skip hole. * TODO: Should we consider t->current_filesystem->xfer_align? */ if (t->current_sparse->offset > t->entry_total) { if (lseek(t->entry_fd, (off_t)t->current_sparse->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } bytes = t->current_sparse->offset - t->entry_total; t->entry_remaining_bytes -= bytes; t->entry_total += bytes; } /* * Read file contents. */ if (buffbytes > 0) { bytes = read(t->entry_fd, t->entry_buff, buffbytes); if (bytes < 0) { archive_set_error(&a->archive, errno, "Read error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } else bytes = 0; /* * Return an EOF unless we've read a leading empty sparse region, which * is used to represent fully-sparse files. */ if (bytes == 0 && !empty_sparse_region) { /* Get EOF */ t->entry_eof = 1; r = ARCHIVE_EOF; goto abort_read_data; } *buff = t->entry_buff; *size = bytes; *offset = t->entry_total; t->entry_total += bytes; t->entry_remaining_bytes -= bytes; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; t->entry_eof = 1; } t->current_sparse->offset += bytes; t->current_sparse->length -= bytes; if (t->current_sparse->length == 0 && !t->entry_eof) t->current_sparse++; return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fd >= 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; int descend, r; st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%s: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Couldn't visit directory", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); case 0: tree_enter_initial_dir(t); return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); } break; } } while (lst == NULL); #ifdef __APPLE__ - if (a->enable_copyfile) { + if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { /* If we're using copyfile(), ignore "._XXX" files. */ const char *bname = strrchr(tree_current_path(t), '/'); if (bname == NULL) bname = tree_current_path(t); else ++bname; if (bname[0] == '.' && bname[1] == '_') return (ARCHIVE_RETRY); } #endif archive_entry_copy_pathname(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; - if (!a->traverse_mount_points) { + if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) { if (t->initial_filesystem_id != t->current_filesystem_id) descend = 0; } t->descend = descend; /* * Honor nodump flag. * If the file is marked with nodump flag, do not return this entry. */ - if (a->honor_nodump) { + if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) if (st->st_flags & UF_NODUMP) return (ARCHIVE_RETRY); -#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\ - defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) +#elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \ + defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ + (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \ + defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { int stflags; t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { - r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS, + r = ioctl(t->entry_fd, +#ifdef FS_IOC_GETFLAGS + FS_IOC_GETFLAGS, +#else + EXT2_IOC_GETFLAGS, +#endif &stflags); +#ifdef FS_NODUMP_FL + if (r == 0 && (stflags & FS_NODUMP_FL) != 0) +#else if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) +#endif return (ARCHIVE_RETRY); } } #endif } archive_entry_copy_stat(entry, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, * especially, invoking a callback. */ t->restore_time.mtime = archive_entry_mtime(entry); t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry); t->restore_time.atime = archive_entry_atime(entry); t->restore_time.atime_nsec = archive_entry_atime_nsec(entry); t->restore_time.filetype = archive_entry_filetype(entry); t->restore_time.noatime = t->current_filesystem->noatime; /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } /* * Populate the archive_entry with metadata from the disk. */ archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } for (;;) { r = next_entry(a, t, entry); if (t->entry_fd >= 0) { close(t->entry_fd); t->entry_fd = -1; } if (r == ARCHIVE_RETRY) { archive_entry_clear(entry); continue; } break; } /* Return to the initial directory. */ tree_enter_initial_dir(t); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: /* Overwrite the sourcepath based on the initial directory. */ archive_entry_copy_sourcepath(entry, tree_current_path(t)); t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->nlink = archive_entry_nlink(entry); t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); t->sparse_list[i].offset = offset; t->sparse_list[i].length = length; } if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = archive_entry_size(entry); } else { t->sparse_list[i].offset = archive_entry_size(entry); t->sparse_list[i].length = 0; } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); t->stack->flags |= isDir; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); return (_archive_read_disk_open(_a, pathname)); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_string path; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); /* Make a char string from a wchar_t string. */ archive_string_init(&path); if (archive_string_append_from_wcs(&path, pathname, wcslen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a char string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open(_a, path.s); archive_string_free(&path); return (ret); } static int _archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) - a->tree = tree_reopen(a->tree, pathname, a->restore_time); + a->tree = tree_reopen(a->tree, pathname, + a->flags & ARCHIVE_READDISK_RESTORE_ATIME); else a->tree = tree_open(pathname, a->symlink_mode, - a->restore_time); + a->flags & ARCHIVE_READDISK_RESTORE_ATIME); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * This is the new filesystem which we have to generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; t->current_filesystem->allocation_ptr = NULL; t->current_filesystem->buff = NULL; /* Setup the current filesystem properties which depend on * platform specific. */ return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } #if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN) static int get_xfer_size(struct tree *t, int fd, const char *path) { t->current_filesystem->xfer_align = -1; errno = 0; if (fd >= 0) { t->current_filesystem->incr_xfer_size = fpathconf(fd, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = fpathconf(fd, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = fpathconf(fd, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = fpathconf(fd, _PC_REC_XFER_ALIGN); } else if (path != NULL) { t->current_filesystem->incr_xfer_size = pathconf(path, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = pathconf(path, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = pathconf(path, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = pathconf(path, _PC_REC_XFER_ALIGN); } /* At least we need an alignment size. */ if (t->current_filesystem->xfer_align == -1) return ((errno == EINVAL)?1:-1); else return (0); } #else static int get_xfer_size(struct tree *t, int fd, const char *path) { (void)t; /* UNUSED */ (void)fd; /* UNUSED */ (void)path; /* UNUSED */ return (1);/* Not supported */ } #endif #if defined(HAVE_STATFS) && defined(HAVE_FSTATFS) && defined(MNT_LOCAL) \ && !defined(ST_LOCAL) /* * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) /* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make * this accurate; some platforms have both and we need the one that's * used by getvfsbyname() * * Then the following would become: * #if defined(GETVFSBYNAME_ARG_TYPE) * GETVFSBYNAME_ARG_TYPE vfc; * #endif */ # if defined(HAVE_STRUCT_XVFSCONF) struct xvfsconf vfc; # else struct vfsconf vfc; # endif #endif int r, xr = 0; #if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX) long nm; #endif t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); } if (r == -1 || xr == -1) { archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_bsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; } if (sfs.f_flags & MNT_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) r = getvfsbyname(sfs.f_fstypename, &vfc); if (r == -1) { archive_set_error(&a->archive, errno, "getvfsbyname failed"); return (ARCHIVE_FAILED); } if (vfc.vfc_flags & VFCF_SYNTHETIC) t->current_filesystem->synthetic = 1; else t->current_filesystem->synthetic = 0; #endif #if defined(MNT_NOATIME) if (sfs.f_flags & MNT_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; #else # if defined(_PC_NAME_MAX) /* Mac OS X does not have f_namemax in struct statfs. */ if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); # else nm = -1; # endif if (nm == -1) t->current_filesystem->name_max = NAME_MAX; else t->current_filesystem->name_max = nm; #endif #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL) /* * Gather current filesystem properties on NetBSD */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1; if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } if (tree_current_is_symblic_link_target(t)) { r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN * for pathconf() function. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; #if defined(HAVE_STRUCT_STATVFS_F_IOSIZE) t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; #else t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } if (sfs.f_flag & ST_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\ defined(HAVE_STATFS) && defined(HAVE_FSTATFS) /* * Note: statfs is deprecated since LSB 3.2 */ #ifndef CIFS_SUPER_MAGIC #define CIFS_SUPER_MAGIC 0xFF534D42 #endif #ifndef DEVFS_SUPER_MAGIC #define DEVFS_SUPER_MAGIC 0x1373 #endif /* * Gather current filesystem properties on Linux */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_STATVFS) struct statvfs svfs; #endif int r, vr = 0, xr = 0; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_FSTATVFS) vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */ #endif r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(tree_current_access_path(t), &svfs); #endif r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATFS #if defined(HAVE_FSTATVFS) vr = fstatvfs(tree_current_dir_fd(t), &svfs); #endif r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(".", &svfs); #endif r = statfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1 || vr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ #if defined(HAVE_STATVFS) t->current_filesystem->xfer_align = svfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = svfs.f_bsize; t->current_filesystem->incr_xfer_size = svfs.f_bsize; #else t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } switch (sfs.f_type) { case AFS_SUPER_MAGIC: case CIFS_SUPER_MAGIC: case CODA_SUPER_MAGIC: case NCP_SUPER_MAGIC:/* NetWare */ case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: t->current_filesystem->remote = 1; t->current_filesystem->synthetic = 0; break; case DEVFS_SUPER_MAGIC: case PROC_SUPER_MAGIC: case USBDEVICE_SUPER_MAGIC: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 1; break; default: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 0; break; } #if defined(ST_NOATIME) #if defined(HAVE_STATVFS) if (svfs.f_flag & ST_NOATIME) #else if (sfs.f_flag & ST_NOATIME) #endif t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namelen; #endif return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATVFS_H) &&\ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) /* * Gather current filesystem properties on other posix platform. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatvfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; } #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; #endif return (ARCHIVE_OK); } #else /* * Generic: Gather current filesystem properties. * TODO: Is this generic function really needed? */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; #if defined(_PC_NAME_MAX) && defined(USE_READDIR_R) long nm; #endif t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ t->current_filesystem->noatime = 0; (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */ t->current_filesystem->xfer_align = -1;/* Unknown */ t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = -1; t->current_filesystem->incr_xfer_size = -1; #if defined(USE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) # endif /* _PC_NAME_MAX */ /* * Some systems (HP-UX or others?) incorrectly defined * NAME_MAX macro to be a smaller value. */ # if defined(NAME_MAX) && NAME_MAX >= 255 t->current_filesystem->name_max = NAME_MAX; # else /* No way to get a trusted value of maximum filename * length. */ t->current_filesystem->name_max = PATH_MAX; # endif /* NAME_MAX */ # if defined(_PC_NAME_MAX) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #endif static int close_and_restore_time(int fd, struct tree *t, struct restore_time *rt) { #ifndef HAVE_UTIMES (void)t; /* UNUSED */ (void)rt; /* UNUSED */ return (close(fd)); #else #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) struct timespec timespecs[2]; #endif struct timeval times[2]; if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) { if (fd >= 0) return (close(fd)); else return (0); } #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) timespecs[1].tv_sec = rt->mtime; timespecs[1].tv_nsec = rt->mtime_nsec; timespecs[0].tv_sec = rt->atime; timespecs[0].tv_nsec = rt->atime_nsec; /* futimens() is defined in POSIX.1-2008. */ if (futimens(fd, timespecs) == 0) return (close(fd)); #endif times[1].tv_sec = rt->mtime; times[1].tv_usec = rt->mtime_nsec / 1000; times[0].tv_sec = rt->atime; times[0].tv_usec = rt->atime_nsec / 1000; #if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (futimes(fd, times) == 0) return (close(fd)); #endif close(fd); #if defined(HAVE_FUTIMESAT) if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0) return (0); #endif #ifdef HAVE_LUTIMES if (lutimes(rt->name, times) != 0) #else if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0) #endif return (-1); #endif return (0); } static int open_on_current_dir(struct tree *t, const char *path, int flags) { #ifdef HAVE_OPENAT return (openat(tree_current_dir_fd(t), path, flags)); #else if (tree_enter_working_dir(t) != 0) return (-1); return (open(path, flags)); #endif } static int tree_dup(int fd) { int new_fd; #ifdef F_DUPFD_CLOEXEC static volatile int can_dupfd_cloexec = 1; if (can_dupfd_cloexec) { new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd != -1) return (new_fd); /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, * but it cannot be used. So we have to try dup(). */ /* We won't try F_DUPFD_CLOEXEC. */ can_dupfd_cloexec = 0; } #endif /* F_DUPFD_CLOEXEC */ new_fd = dup(fd); __archive_ensure_cloexec_flag(new_fd); return (new_fd); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const char *path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); te->symlink_parent_fd = -1; archive_strcpy(&te->name, path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->restore_time.name = te->name.s; if (rt != NULL) { te->restore_time.mtime = rt->mtime; te->restore_time.mtime_nsec = rt->mtime_nsec; te->restore_time.atime = rt->atime; te->restore_time.atime_nsec = rt->atime_nsec; te->restore_time.filetype = rt->filetype; te->restore_time.noatime = rt->noatime; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const char *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == '/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_string_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/') archive_strappend_char(&t->path, '/'); t->basename = t->path.s + archive_strlen(&t->path); archive_strncat(&t->path, name, name_length); t->restore_time.name = t->basename; } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const char *path, int symlink_mode, int restore_time) { struct tree *t; if ((t = calloc(1, sizeof(*t))) == NULL) return (NULL); archive_string_init(&t->path); archive_string_ensure(&t->path, 31); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { - t->flags = (restore_time)?needsRestoreTimes:0; + t->flags = (restore_time != 0)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; t->tree_errno = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_DIR_HANDLE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&t->path); t->entry_fd = -1; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* First item is set up a lot like a symlink traversal. */ tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } static int tree_descent(struct tree *t) { int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; } else { t->depth++; /* If it is a link, set up fd for the ascent. */ if (t->stack->flags & isDirLink) { t->stack->symlink_parent_fd = t->working_dir_fd; t->openCount++; if (t->openCount > t->maxOpenCount) t->maxOpenCount = t->openCount; } else close(t->working_dir_fd); /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; } return (r); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; int new_fd, r = 0, prev_dir_fd; te = t->stack; prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; else { new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(new_fd); } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; } else { /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; /* Current directory has been changed, we should * close an fd of previous working directory. */ close_and_restore_time(prev_dir_fd, t, &te->restore_time); if (te->flags & isDirLink) { t->openCount--; te->symlink_parent_fd = -1; } t->depth--; } return (r); } /* * Return to the initial directory where tree_open() was performed. */ static int tree_enter_initial_dir(struct tree *t) { int r = 0; if ((t->flags & onInitialDir) == 0) { r = fchdir(t->initial_dir_fd); if (r == 0) { t->flags &= ~onWorkingDir; t->flags |= onInitialDir; } } return (r); } /* * Restore working directory of directory traversals. */ static int tree_enter_working_dir(struct tree *t) { int r = 0; /* * Change the current directory if really needed. * Sometimes this is unneeded when we did not do * descent. */ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) { r = fchdir(t->working_dir_fd); if (r == 0) { t->flags &= ~onInitialDir; t->flags |= onWorkingDir; } } return (r); } static int tree_current_dir_fd(struct tree *t) { return (t->working_dir_fd); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; while (t->basename[0] == '/') t->basename++; archive_string_free(&te->name); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_DIR_HANDLE) { r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); /* t->dirname_length = t->path_length; */ /* tree_pop(t); */ t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_posix(struct tree *t) { int r; const char *name; size_t namelen; if (t->d == NULL) { #if defined(USE_READDIR_R) size_t dirent_size; #endif #if defined(HAVE_FDOPENDIR) t->d = fdopendir(tree_dup(t->working_dir_fd)); #else /* HAVE_FDOPENDIR */ if (tree_enter_working_dir(t) == 0) { t->d = opendir("."); #if HAVE_DIRFD || defined(dirfd) __archive_ensure_cloexec_flag(dirfd(t->d)); #endif } #endif /* HAVE_FDOPENDIR */ if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } #if defined(USE_READDIR_R) dirent_size = offsetof(struct dirent, d_name) + t->filesystem_table[t->current->filesystem_id].name_max + 1; if (t->dirent == NULL || t->dirent_allocated < dirent_size) { free(t->dirent); t->dirent = malloc(dirent_size); if (t->dirent == NULL) { closedir(t->d); t->d = INVALID_DIR_HANDLE; (void)tree_ascend(t); tree_pop(t); t->tree_errno = ENOMEM; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->dirent_allocated = dirent_size; } #endif /* USE_READDIR_R */ } for (;;) { errno = 0; #if defined(USE_READDIR_R) r = readdir_r(t->d, t->dirent, &t->de); #ifdef _AIX /* Note: According to the man page, return value 9 indicates * that the readdir_r was not successful and the error code * is set to the global errno variable. And then if the end * of directory entries was reached, the return value is 9 * and the third parameter is set to NULL and errno is * unchanged. */ if (r == 9) r = errno; #endif /* _AIX */ if (r != 0 || t->de == NULL) { #else t->de = readdir(t->d); if (t->de == NULL) { r = errno; #endif closedir(t->d); t->d = INVALID_DIR_HANDLE; if (r != 0) { t->tree_errno = r; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } else return (0); } name = t->de->d_name; namelen = D_NAMELEN(t->de); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == '.' && name[1] == '\0') continue; if (name[0] == '.' && name[1] == '.' && name[2] == '\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } /* * Get the stat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->st, 0) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->lst, AT_SYMLINK_NOFOLLOW) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (lstat(tree_current_access_path(t), &t->lst) != 0) #endif return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { const struct stat *st; /* * If we already have lstat() info, then try some * cheap tests to determine if this is a dir. */ if (t->flags & hasLstat) { /* If lstat() says it's a dir, it must be a dir. */ st = tree_current_lstat(t); if (st == NULL) return 0; if (S_ISDIR(st->st_mode)) return 1; /* Not a dir; might be a link to a dir. */ /* If it's not a link, then it's not a link to a dir. */ if (!S_ISLNK(st->st_mode)) return 0; /* * It's a link, but we don't know what it's a link to, * so we'll have to use stat(). */ } st = tree_current_stat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { const struct stat *st; /* * If stat() says it isn't a dir, then it's not a dir. * If stat() data is cached, this check is free, so do it first. */ if (t->flags & hasStat) { st = tree_current_stat(t); if (st == NULL) return (0); if (!S_ISDIR(st->st_mode)) return (0); } /* * Either stat() said it was a dir (in which case, we have * to determine whether it's really a link to a dir) or * stat() info wasn't available. So we use lstat(), which * hopefully is already cached. */ st = tree_current_lstat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const struct stat *st) { struct tree_entry *te; for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == (int64_t)st->st_dev && te->ino == (int64_t)st->st_ino) return (1); } return (0); } /* * Test whether the current file is symbolic link target and * on the other filesystem. */ static int tree_current_is_symblic_link_target(struct tree *t) { static const struct stat *lst, *st; lst = tree_current_lstat(t); st = tree_current_stat(t); return (st != NULL && lst != NULL && (int64_t)st->st_dev == t->current_filesystem->dev && st->st_dev != lst->st_dev); } /* * Return the access path for the entry just returned from tree_next(). */ static const char * tree_current_access_path(struct tree *t) { return (t->basename); } /* * Return the full path for the entry just returned from tree_next(). */ static const char * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } /* Close the handle of readdir(). */ if (t->d != INVALID_DIR_HANDLE) { closedir(t->d); t->d = INVALID_DIR_HANDLE; } /* Release anything remaining in the stack. */ while (t->stack != NULL) { if (t->stack->flags & isDirLink) close(t->stack->symlink_parent_fd); tree_pop(t); } if (t->working_dir_fd >= 0) { close(t->working_dir_fd); t->working_dir_fd = -1; } if (t->initial_dir_fd >= 0) { close(t->initial_dir_fd); t->initial_dir_fd = -1; } } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_string_free(&t->path); #if defined(USE_READDIR_R) free(t->dirent); #endif free(t->sparse_list); for (i = 0; i < t->max_filesystem_id; i++) free(t->filesystem_table[i].allocation_ptr); free(t->filesystem_table); free(t); } #endif Index: vendor/libarchive/dist/libarchive/archive_read_disk_private.h =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_disk_private.h (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_read_disk_private.h (revision 314565) @@ -1,97 +1,89 @@ /*- * 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 * 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. * * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $ */ #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif #ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED #define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED struct tree; struct archive_entry; struct archive_read_disk { struct archive archive; /* Reused by archive_read_next_header() */ struct archive_entry *entry; /* * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid, * following an old BSD convention. 'L' follows all symlinks, * 'P' follows none, 'H' follows symlinks only for the first * item. */ char symlink_mode; /* * Since symlink interaction changes, we need to track whether * we're following symlinks for the current item. 'L' mode above * sets this true, 'P' sets it false, 'H' changes it as we traverse. */ char follow_symlinks; /* Either 'L' or 'P'. */ /* Directory traversals. */ struct tree *tree; int (*open_on_current_dir)(struct tree*, const char *, int); int (*tree_current_dir_fd)(struct tree*); int (*tree_enter_working_dir)(struct tree*); - /* Set 1 if users request to restore atime . */ - int restore_time; - /* Set 1 if users request to honor nodump flag . */ - int honor_nodump; - /* Set 1 if users request to enable mac copyfile. */ - int enable_copyfile; - /* Set 1 if users request to traverse mount points. */ - int traverse_mount_points; - /* Set 1 if users want to suppress xattr information. */ - int suppress_xattr; + /* Bitfield with ARCHIVE_READDISK_* tunables */ + int flags; const char * (*lookup_gname)(void *private, int64_t gid); void (*cleanup_gname)(void *private); void *lookup_gname_data; const char * (*lookup_uname)(void *private, int64_t uid); void (*cleanup_uname)(void *private); void *lookup_uname_data; int (*metadata_filter_func)(struct archive *, void *, struct archive_entry *); void *metadata_filter_data; /* ARCHIVE_MATCH object. */ struct archive *matching; /* Callback function, this will be invoked when ARCHIVE_MATCH * archive_match_*_excluded_ae return true. */ void (*excluded_cb_func)(struct archive *, void *, struct archive_entry *); void *excluded_cb_data; }; #endif Index: vendor/libarchive/dist/libarchive/archive_read_disk_windows.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_disk_windows.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_read_disk_windows.c (revision 314565) @@ -1,2308 +1,2298 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * 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$"); #if defined(_WIN32) && !defined(__CYGWIN__) #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef IO_REPARSE_TAG_SYMLINK /* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */ #define IO_REPARSE_TAG_SYMLINK 0xA000000CL #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ struct restore_time { const wchar_t *full_path; FILETIME lastWriteTime; FILETIME lastAccessTime; mode_t filetype; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; size_t full_path_dir_length; struct archive_wstring name; struct archive_wstring full_path; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; DWORD bytesPerSector; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * On Windows, "first visit" is handled as a pattern to be handed to * _findfirst(). This is consistent with Windows conventions that * file patterns are handled within the application. On Posix, * "first visit" is just returned to the client. */ #define MAX_OVERLAPPED 8 #define BUFFER_SIZE (1024 * 8) #define DIRECT_IO 0/* Disabled */ #define ASYNC_IO 1/* Enabled */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; HANDLE d; WIN32_FIND_DATAW _findData; WIN32_FIND_DATAW *findData; int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* A full path with "\\?\" prefix. */ struct archive_wstring full_path; size_t full_path_dir_length; /* Dynamically-sized buffer for holding path */ struct archive_wstring path; /* Last path element */ const wchar_t *basename; /* Leading dir length */ size_t dirname_length; int depth; BY_HANDLE_FILE_INFORMATION lst; BY_HANDLE_FILE_INFORMATION st; int descend; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesystem; HANDLE entry_fh; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; int ol_idx_doing; int ol_idx_done; int ol_num_doing; int ol_num_done; int64_t ol_remaining_bytes; int64_t ol_total; struct la_overlapped { OVERLAPPED ol; struct archive * _a; unsigned char *buff; size_t buff_size; int64_t offset; size_t bytes_expected; size_t bytes_transferred; } ol[MAX_OVERLAPPED]; int direct_io; int async_io; }; #define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber) /* Treat FileIndex as i-node. We should remove a sequence number * which is high-16-bits of nFileIndexHigh. */ #define bhfi_ino(bhfi) \ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \ + (bhfi)->nFileIndexLow) /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define needsRestoreTimes 128 static int tree_dir_next_windows(struct tree *t, const wchar_t *pattern); /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const wchar_t *, int, int); static struct tree *tree_reopen(struct tree *, const wchar_t *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const wchar_t *, const wchar_t *, int, int64_t, int64_t, struct restore_time *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * */ static const wchar_t *tree_current_path(struct tree *); static const wchar_t *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const BY_HANDLE_FILE_INFORMATION *tree_current_stat(struct tree *); static const BY_HANDLE_FILE_INFORMATION *tree_current_lstat(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_link(struct tree *); /* Instead of archive_entry_copy_stat for BY_HANDLE_FILE_INFORMATION */ static void tree_archive_entry_copy_bhfi(struct archive_entry *, struct tree *, const BY_HANDLE_FILE_INFORMATION *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const BY_HANDLE_FILE_INFORMATION *); static int _archive_read_disk_open_w(struct archive *, const wchar_t *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(HANDLE, struct tree *, struct restore_time *); static int setup_sparse_from_disk(struct archive_read_disk *, struct archive_entry *, HANDLE); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; - a->enable_copyfile = 1; - a->traverse_mount_points = 1; + a->flags = ARCHIVE_READDISK_MAC_COPYFILE; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); - a->restore_time = 1; + a->flags |= ARCHIVE_READDISK_RESTORE_ATIME; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); + a->flags = flags; + if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { - a->restore_time = 0; if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } - if (flags & ARCHIVE_READDISK_HONOR_NODUMP) - a->honor_nodump = 1; - else - a->honor_nodump = 0; - if (flags & ARCHIVE_READDISK_MAC_COPYFILE) - a->enable_copyfile = 1; - else - a->enable_copyfile = 0; - if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) - a->traverse_mount_points = 0; - else - a->traverse_mount_points = 1; return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } static int64_t align_num_per_sector(struct tree *t, int64_t size) { int64_t surplus; size += t->current_filesystem->bytesPerSector -1; surplus = size % t->current_filesystem->bytesPerSector; size -= surplus; return (size); } static int start_next_async_read(struct archive_read_disk *a, struct tree *t) { struct la_overlapped *olp; DWORD buffbytes, rbytes; if (t->ol_remaining_bytes == 0) return (ARCHIVE_EOF); olp = &(t->ol[t->ol_idx_doing]); t->ol_idx_doing = (t->ol_idx_doing + 1) % MAX_OVERLAPPED; /* Allocate read buffer. */ if (olp->buff == NULL) { void *p; size_t s = (size_t)align_num_per_sector(t, BUFFER_SIZE); p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } olp->buff = p; olp->buff_size = s; olp->_a = &a->archive; olp->ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if (olp->ol.hEvent == NULL) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "CreateEvent failed"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } else ResetEvent(olp->ol.hEvent); buffbytes = (DWORD)olp->buff_size; if (buffbytes > t->current_sparse->length) buffbytes = (DWORD)t->current_sparse->length; /* Skip hole. */ if (t->current_sparse->offset > t->ol_total) { t->ol_remaining_bytes -= t->current_sparse->offset - t->ol_total; } olp->offset = t->current_sparse->offset; olp->ol.Offset = (DWORD)(olp->offset & 0xffffffff); olp->ol.OffsetHigh = (DWORD)(olp->offset >> 32); if (t->ol_remaining_bytes > buffbytes) { olp->bytes_expected = buffbytes; t->ol_remaining_bytes -= buffbytes; } else { olp->bytes_expected = (size_t)t->ol_remaining_bytes; t->ol_remaining_bytes = 0; } olp->bytes_transferred = 0; t->current_sparse->offset += buffbytes; t->current_sparse->length -= buffbytes; t->ol_total = t->current_sparse->offset; if (t->current_sparse->length == 0 && t->ol_remaining_bytes > 0) t->current_sparse++; if (!ReadFile(t->entry_fh, olp->buff, buffbytes, &rbytes, &(olp->ol))) { DWORD lasterr; lasterr = GetLastError(); if (lasterr == ERROR_HANDLE_EOF) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Reading file truncated"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } else if (lasterr != ERROR_IO_PENDING) { if (lasterr == ERROR_NO_DATA) errno = EAGAIN; else if (lasterr == ERROR_ACCESS_DENIED) errno = EBADF; else la_dosmaperr(lasterr); archive_set_error(&a->archive, errno, "Read error"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } else olp->bytes_transferred = rbytes; t->ol_num_doing++; return (t->ol_remaining_bytes == 0)? ARCHIVE_EOF: ARCHIVE_OK; } static void cancel_async(struct tree *t) { if (t->ol_num_doing != t->ol_num_done) { CancelIo(t->entry_fh); t->ol_num_doing = t->ol_num_done = 0; } } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; struct la_overlapped *olp; DWORD bytes_transferred; int r = ARCHIVE_FATAL; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Make a request to read the file in asynchronous. */ if (t->ol_num_doing == 0) { do { r = start_next_async_read(a, t); if (r == ARCHIVE_FATAL) goto abort_read_data; if (!t->async_io) break; } while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED); } else { if (start_next_async_read(a, t) == ARCHIVE_FATAL) goto abort_read_data; } olp = &(t->ol[t->ol_idx_done]); t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED; if (olp->bytes_transferred) bytes_transferred = (DWORD)olp->bytes_transferred; else if (!GetOverlappedResult(t->entry_fh, &(olp->ol), &bytes_transferred, TRUE)) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "GetOverlappedResult failed"); a->archive.state = ARCHIVE_STATE_FATAL; r = ARCHIVE_FATAL; goto abort_read_data; } t->ol_num_done++; if (bytes_transferred == 0 || olp->bytes_expected != bytes_transferred) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Reading file truncated"); a->archive.state = ARCHIVE_STATE_FATAL; r = ARCHIVE_FATAL; goto abort_read_data; } *buff = olp->buff; *size = bytes_transferred; *offset = olp->offset; if (olp->offset > t->entry_total) t->entry_remaining_bytes -= olp->offset - t->entry_total; t->entry_total = olp->offset + *size; t->entry_remaining_bytes -= *size; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; t->entry_eof = 1; } return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fh != INVALID_HANDLE_VALUE) { cancel_async(t); /* Close the current file descriptor */ close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const BY_HANDLE_FILE_INFORMATION *st; const BY_HANDLE_FILE_INFORMATION *lst; const char*name; int descend, r; st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%ls: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, t->tree_errno, "%ls: Couldn't visit directory", tree_current_path(t)); return (ARCHIVE_FAILED); case 0: return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { archive_set_error(&a->archive, t->tree_errno, "%ls: Cannot stat", tree_current_path(t)); return (ARCHIVE_FAILED); } break; } } while (lst == NULL); archive_entry_copy_pathname_w(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, bhfi_dev(st)) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; - if (!a->traverse_mount_points) { + if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) { if (t->initial_filesystem_id != t->current_filesystem_id) return (ARCHIVE_RETRY); } t->descend = descend; tree_archive_entry_copy_bhfi(entry, t, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, * especially, invoking a callback. */ t->restore_time.lastWriteTime = st->ftLastWriteTime; t->restore_time.lastAccessTime = st->ftLastAccessTime; t->restore_time.filetype = archive_entry_filetype(entry); /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } archive_entry_copy_sourcepath_w(entry, tree_current_access_path(t)); r = ARCHIVE_OK; if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) > 0) { DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; if (t->async_io) flags |= FILE_FLAG_OVERLAPPED; if (t->direct_io) flags |= FILE_FLAG_NO_BUFFERING; else flags |= FILE_FLAG_SEQUENTIAL_SCAN; t->entry_fh = CreateFileW(tree_current_access_path(t), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL); if (t->entry_fh == INVALID_HANDLE_VALUE) { archive_set_error(&a->archive, errno, "Couldn't open %ls", tree_current_path(a->tree)); return (ARCHIVE_FAILED); } /* Find sparse data from the disk. */ if (archive_entry_hardlink(entry) == NULL && (st->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0) r = setup_sparse_from_disk(a, entry, t->entry_fh); } return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fh != INVALID_HANDLE_VALUE) { cancel_async(t); close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; } while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY) archive_entry_clear(entry); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } t->ol_idx_doing = t->ol_idx_done = 0; t->ol_num_doing = t->ol_num_done = 0; t->ol_remaining_bytes = t->entry_remaining_bytes; t->ol_total = 0; a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t aligned, length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } /* * Get sparse list and make sure those offsets and lengths are * aligned by a sector size. */ for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); aligned = align_num_per_sector(t, offset); if (aligned != offset) { aligned -= t->current_filesystem->bytesPerSector; length += offset - aligned; } t->sparse_list[i].offset = aligned; aligned = align_num_per_sector(t, length); t->sparse_list[i].length = aligned; } aligned = align_num_per_sector(t, archive_entry_size(entry)); if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = aligned; } else { int j, last = i; t->sparse_list[i].offset = aligned; t->sparse_list[i].length = 0; for (i = 0; i < last; i++) { if ((t->sparse_list[i].offset + t->sparse_list[i].length) <= t->sparse_list[i+1].offset) continue; /* * Now sparse_list[i+1] is overlapped by sparse_list[i]. * Merge those two. */ length = t->sparse_list[i+1].offset - t->sparse_list[i].offset; t->sparse_list[i+1].offset = t->sparse_list[i].offset; t->sparse_list[i+1].length += length; /* Remove sparse_list[i]. */ for (j = i; j < last; j++) { t->sparse_list[j].offset = t->sparse_list[j+1].offset; t->sparse_list[j].length = t->sparse_list[j+1].length; } last--; } } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->full_path.s, t->current_filesystem_id, bhfi_dev(&(t->lst)), bhfi_ino(&(t->lst)), &t->restore_time); t->stack->flags |= isDir; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->full_path.s, t->current_filesystem_id, bhfi_dev(&(t->st)), bhfi_ino(&(t->st)), &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_wstring wpath; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); /* Make a wchar_t string from a char string. */ archive_string_init(&wpath); if (archive_wstring_append_from_mbs(&wpath, pathname, strlen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a wchar_t string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open_w(_a, wpath.s); archive_wstring_free(&wpath); return (ret); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); return (_archive_read_disk_open_w(_a, pathname)); } static int _archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) - a->tree = tree_reopen(a->tree, pathname, a->restore_time); + a->tree = tree_reopen(a->tree, pathname, + a->flags & ARCHIVE_READDISK_RESTORE_ATIME); else - a->tree = tree_open(pathname, a->symlink_mode, a->restore_time); + a->tree = tree_open(pathname, a->symlink_mode, + a->flags & ARCHIVE_READDISK_RESTORE_ATIME); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate directory traversal data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * There is a new filesystem, we generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = (int)s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } /* * If symlink is broken, statfs or statvfs will fail. * Use its directory path instead. */ static wchar_t * safe_path_for_statfs(struct tree *t) { const wchar_t *path; wchar_t *cp, *p = NULL; path = tree_current_access_path(t); if (tree_current_stat(t) == NULL) { p = _wcsdup(path); cp = wcsrchr(p, '/'); if (cp != NULL && wcslen(cp) >= 2) { cp[1] = '.'; cp[2] = '\0'; path = p; } } else p = _wcsdup(path); return (p); } /* * Get conditions of synthetic and remote on Windows */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; wchar_t vol[256]; wchar_t *path; t->current_filesystem->synthetic = -1;/* Not supported */ path = safe_path_for_statfs(t); if (!GetVolumePathNameW(path, vol, sizeof(vol)/sizeof(vol[0]))) { free(path); t->current_filesystem->remote = -1; t->current_filesystem->bytesPerSector = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "GetVolumePathName failed: %d", (int)GetLastError()); return (ARCHIVE_FAILED); } free(path); switch (GetDriveTypeW(vol)) { case DRIVE_UNKNOWN: case DRIVE_NO_ROOT_DIR: t->current_filesystem->remote = -1; break; case DRIVE_REMOTE: t->current_filesystem->remote = 1; break; default: t->current_filesystem->remote = 0; break; } if (!GetDiskFreeSpaceW(vol, NULL, &(t->current_filesystem->bytesPerSector), NULL, NULL)) { t->current_filesystem->bytesPerSector = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "GetDiskFreeSpace failed: %d", (int)GetLastError()); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) { HANDLE handle; int r = 0; if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype) return (0); /* Close a file descriptor. * It will not be used for SetFileTime() because it has been opened * by a read only mode. */ if (h != INVALID_HANDLE_VALUE) CloseHandle(h); if ((t->flags & needsRestoreTimes) == 0) return (r); handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { errno = EINVAL; return (-1); } if (SetFileTime(handle, NULL, &rt->lastAccessTime, &rt->lastWriteTime) == 0) { errno = EINVAL; r = -1; } else r = 0; CloseHandle(handle); return (r); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); archive_wstrcpy(&te->name, path); archive_string_init(&te->full_path); archive_wstrcpy(&te->full_path, full_path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->full_path_dir_length = t->full_path_dir_length; te->restore_time.full_path = te->full_path.s; if (rt != NULL) { te->restore_time.lastWriteTime = rt->lastWriteTime; te->restore_time.lastAccessTime = rt->lastAccessTime; te->restore_time.filetype = rt->filetype; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const wchar_t *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = L'\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == L'/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_wstring_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != L'/') archive_wstrappend_wchar(&t->path, L'/'); t->basename = t->path.s + archive_strlen(&t->path); archive_wstrncat(&t->path, name, name_length); t->restore_time.full_path = t->basename; if (t->full_path_dir_length > 0) { t->full_path.s[t->full_path_dir_length] = L'\0'; t->full_path.length = t->full_path_dir_length; size_needed = name_length + t->full_path_dir_length + 2; archive_wstring_ensure(&t->full_path, size_needed); /* Add a separating '\' if it's needed. */ if (t->full_path.s[archive_strlen(&t->full_path)-1] != L'\\') archive_wstrappend_wchar(&t->full_path, L'\\'); archive_wstrncat(&t->full_path, name, name_length); t->restore_time.full_path = t->full_path.s; } } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const wchar_t *path, int symlink_mode, int restore_time) { struct tree *t; t = calloc(1, sizeof(*t)); archive_string_init(&(t->full_path)); archive_string_init(&t->path); archive_wstring_ensure(&t->path, 15); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const wchar_t *path, int restore_time) { struct archive_wstring ws; wchar_t *pathname, *p, *base; - t->flags = (restore_time)?needsRestoreTimes:0; + t->flags = (restore_time != 0)?needsRestoreTimes:0; t->visit_type = 0; t->tree_errno = 0; t->full_path_dir_length = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_HANDLE_VALUE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&(t->full_path)); archive_string_empty(&t->path); t->entry_fh = INVALID_HANDLE_VALUE; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* Get wchar_t strings from char strings. */ archive_string_init(&ws); archive_wstrcpy(&ws, path); pathname = ws.s; /* Get a full-path-name. */ p = __la_win_permissive_name_w(pathname); if (p == NULL) goto failed; archive_wstrcpy(&(t->full_path), p); free(p); /* Convert path separators from '\' to '/' */ for (p = pathname; *p != L'\0'; ++p) { if (*p == L'\\') *p = L'/'; } base = pathname; /* First item is set up a lot like a symlink traversal. */ /* printf("Looking for wildcard in %s\n", path); */ if ((base[0] == L'/' && base[1] == L'/' && base[2] == L'?' && base[3] == L'/' && (wcschr(base+4, L'*') || wcschr(base+4, L'?'))) || (!(base[0] == L'/' && base[1] == L'/' && base[2] == L'?' && base[3] == L'/') && (wcschr(base, L'*') || wcschr(base, L'?')))) { // It has a wildcard in it... // Separate the last element. p = wcsrchr(base, L'/'); if (p != NULL) { *p = L'\0'; tree_append(t, base, p - base); t->dirname_length = archive_strlen(&t->path); base = p + 1; } p = wcsrchr(t->full_path.s, L'\\'); if (p != NULL) { *p = L'\0'; t->full_path.length = wcslen(t->full_path.s); t->full_path_dir_length = archive_strlen(&t->full_path); } } tree_push(t, base, t->full_path.s, 0, 0, 0, NULL); archive_wstring_free(&ws); t->stack->flags = needsFirstVisit; /* * Debug flag for Direct IO(No buffering) or Async IO. * Those dependent on environment variable switches * will be removed until next release. */ { const char *e; if ((e = getenv("LIBARCHIVE_DIRECT_IO")) != NULL) { if (e[0] == '0') t->direct_io = 0; else t->direct_io = 1; fprintf(stderr, "LIBARCHIVE_DIRECT_IO=%s\n", (t->direct_io)?"Enabled":"Disabled"); } else t->direct_io = DIRECT_IO; if ((e = getenv("LIBARCHIVE_ASYNC_IO")) != NULL) { if (e[0] == '0') t->async_io = 0; else t->async_io = 1; fprintf(stderr, "LIBARCHIVE_ASYNC_IO=%s\n", (t->async_io)?"Enabled":"Disabled"); } else t->async_io = ASYNC_IO; } return (t); failed: archive_wstring_free(&ws); tree_free(t); return (NULL); } static int tree_descent(struct tree *t) { t->dirname_length = archive_strlen(&t->path); t->full_path_dir_length = archive_strlen(&t->full_path); t->depth++; return (0); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; te = t->stack; t->depth--; close_and_restore_time(INVALID_HANDLE_VALUE, t, &te->restore_time); return (0); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->full_path.s[t->full_path_dir_length] = L'\0'; t->full_path.length = t->full_path_dir_length; t->path.s[t->dirname_length] = L'\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; t->full_path_dir_length = te->full_path_dir_length; while (t->basename[0] == L'/') t->basename++; archive_wstring_free(&te->name); archive_wstring_free(&te->full_path); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_HANDLE_VALUE) { r = tree_dir_next_windows(t, NULL); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { wchar_t *d = t->stack->name.s; t->stack->flags &= ~needsFirstVisit; if (!(d[0] == L'/' && d[1] == L'/' && d[2] == L'?' && d[3] == L'/') && (wcschr(d, L'*') || wcschr(d, L'?'))) { r = tree_dir_next_windows(t, d); if (r == 0) continue; return (r); } else { HANDLE h = FindFirstFileW(d, &t->_findData); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->findData = &t->_findData; FindClose(h); } /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); //t->dirname_length = t->path_length; //tree_pop(t); t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_windows(t, L"*"); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_windows(struct tree *t, const wchar_t *pattern) { const wchar_t *name; size_t namelen; int r; for (;;) { if (pattern != NULL) { struct archive_wstring pt; archive_string_init(&pt); archive_wstring_ensure(&pt, archive_strlen(&(t->full_path)) + 2 + wcslen(pattern)); archive_wstring_copy(&pt, &(t->full_path)); archive_wstrappend_wchar(&pt, L'\\'); archive_wstrcat(&pt, pattern); t->d = FindFirstFileW(pt.s, &t->_findData); archive_wstring_free(&pt); if (t->d == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } t->findData = &t->_findData; pattern = NULL; } else if (!FindNextFileW(t->d, &t->_findData)) { FindClose(t->d); t->d = INVALID_HANDLE_VALUE; t->findData = NULL; return (0); } name = t->findData->cFileName; namelen = wcslen(name); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == L'.' && name[1] == L'\0') continue; if (name[0] == L'.' && name[1] == L'.' && name[2] == L'\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) static void fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns) { ULARGE_INTEGER utc; utc.HighPart = filetime->dwHighDateTime; utc.LowPart = filetime->dwLowDateTime; if (utc.QuadPart >= EPOC_TIME) { utc.QuadPart -= EPOC_TIME; /* milli seconds base */ *t = (time_t)(utc.QuadPart / 10000000); /* nano seconds base */ *ns = (long)(utc.QuadPart % 10000000) * 100; } else { *t = 0; *ns = 0; } } static void entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path, const WIN32_FIND_DATAW *findData, const BY_HANDLE_FILE_INFORMATION *bhfi) { time_t secs; long nsecs; mode_t mode; fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs); archive_entry_set_atime(entry, secs, nsecs); fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs); archive_entry_set_mtime(entry, secs, nsecs); fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs); archive_entry_set_birthtime(entry, secs, nsecs); archive_entry_set_ctime(entry, secs, nsecs); archive_entry_set_dev(entry, bhfi_dev(bhfi)); archive_entry_set_ino64(entry, bhfi_ino(bhfi)); if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) archive_entry_set_nlink(entry, bhfi->nNumberOfLinks + 1); else archive_entry_set_nlink(entry, bhfi->nNumberOfLinks); archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32) + bhfi->nFileSizeLow); archive_entry_set_uid(entry, 0); archive_entry_set_gid(entry, 0); archive_entry_set_rdev(entry, 0); mode = S_IRUSR | S_IRGRP | S_IROTH; if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) mode |= S_IWUSR | S_IWGRP | S_IWOTH; if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && findData != NULL && findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) mode |= S_IFLNK; else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else { const wchar_t *p; mode |= S_IFREG; p = wcsrchr(path, L'.'); if (p != NULL && wcslen(p) == 4) { switch (p[1]) { case L'B': case L'b': if ((p[2] == L'A' || p[2] == L'a' ) && (p[3] == L'T' || p[3] == L't' )) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; case L'C': case L'c': if (((p[2] == L'M' || p[2] == L'm' ) && (p[3] == L'D' || p[3] == L'd' ))) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; case L'E': case L'e': if ((p[2] == L'X' || p[2] == L'x' ) && (p[3] == L'E' || p[3] == L'e' )) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; default: break; } } } archive_entry_set_mode(entry, mode); } static void tree_archive_entry_copy_bhfi(struct archive_entry *entry, struct tree *t, const BY_HANDLE_FILE_INFORMATION *bhfi) { entry_copy_bhfi(entry, tree_current_path(t), t->findData, bhfi); } static int tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st, int sim_lstat) { HANDLE h; int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; if (sim_lstat && tree_current_is_physical_link(t)) flag |= FILE_FLAG_OPEN_REPARSE_POINT; h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; return (0); } r = GetFileInformationByHandle(h, st); CloseHandle(h); return (r); } /* * Get the stat() data for the entry just returned from tree_next(). */ static const BY_HANDLE_FILE_INFORMATION * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { if (!tree_current_file_information(t, &t->st, 0)) return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const BY_HANDLE_FILE_INFORMATION * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { if (!tree_current_file_information(t, &t->lst, 1)) return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { if (t->findData) return (t->findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); return (0); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { if (tree_current_is_physical_link(t)) return (0); return (tree_current_is_dir(t)); } /* * Test whether current entry is a symbolic link. */ static int tree_current_is_physical_link(struct tree *t) { if (t->findData) return ((t->findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (t->findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK)); return (0); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const BY_HANDLE_FILE_INFORMATION *st) { struct tree_entry *te; int64_t dev = bhfi_dev(st); int64_t ino = bhfi_ino(st); for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == dev && te->ino == ino) return (1); } return (0); } /* * Return the access path for the entry just returned from tree_next(). */ static const wchar_t * tree_current_access_path(struct tree *t) { return (t->full_path.s); } /* * Return the full path for the entry just returned from tree_next(). */ static const wchar_t * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fh != INVALID_HANDLE_VALUE) { cancel_async(t); close_and_restore_time(t->entry_fh, t, &t->restore_time); t->entry_fh = INVALID_HANDLE_VALUE; } /* Close the handle of FindFirstFileW */ if (t->d != INVALID_HANDLE_VALUE) { FindClose(t->d); t->d = INVALID_HANDLE_VALUE; t->findData = NULL; } /* Release anything remaining in the stack. */ while (t->stack != NULL) tree_pop(t); } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_wstring_free(&t->path); archive_wstring_free(&t->full_path); free(t->sparse_list); free(t->filesystem_table); for (i = 0; i < MAX_OVERLAPPED; i++) { if (t->ol[i].buff) VirtualFree(t->ol[i].buff, 0, MEM_RELEASE); CloseHandle(t->ol[i].ol.hEvent); } free(t); } /* * Populate the archive_entry with metadata from the disk. */ int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const wchar_t *path; const wchar_t *wname; const char *name; HANDLE h; BY_HANDLE_FILE_INFORMATION bhfi; DWORD fileAttributes = 0; int r; archive_clear_error(_a); wname = archive_entry_sourcepath_w(entry); if (wname == NULL) wname = archive_entry_pathname_w(entry); if (wname == NULL) { archive_set_error(&a->archive, EINVAL, "Can't get a wide character version of the path"); return (ARCHIVE_FAILED); } path = __la_win_permissive_name_w(wname); if (st == NULL) { /* * Get metadata through GetFileInformationByHandle(). */ if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); r = GetFileInformationByHandle(h, &bhfi); if (r == 0) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't GetFileInformationByHandle"); return (ARCHIVE_FAILED); } entry_copy_bhfi(entry, path, NULL, &bhfi); } else { WIN32_FIND_DATAW findData; DWORD flag, desiredAccess; h = FindFirstFileW(path, &findData); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't FindFirstFileW"); return (ARCHIVE_FAILED); } FindClose(h); flag = FILE_FLAG_BACKUP_SEMANTICS; if (!a->follow_symlinks && (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { flag |= FILE_FLAG_OPEN_REPARSE_POINT; desiredAccess = 0; } else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { desiredAccess = 0; } else desiredAccess = GENERIC_READ; h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't CreateFileW"); return (ARCHIVE_FAILED); } r = GetFileInformationByHandle(h, &bhfi); if (r == 0) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't GetFileInformationByHandle"); CloseHandle(h); return (ARCHIVE_FAILED); } entry_copy_bhfi(entry, path, &findData, &bhfi); } fileAttributes = bhfi.dwFileAttributes; } else { archive_entry_copy_stat(entry, st); h = INVALID_HANDLE_VALUE; } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Can this file be sparse file ? */ if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) { if (h != INVALID_HANDLE_VALUE && fd < 0) CloseHandle(h); return (ARCHIVE_OK); } if (h == INVALID_HANDLE_VALUE) { if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); } else { h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't CreateFileW"); return (ARCHIVE_FAILED); } } r = GetFileInformationByHandle(h, &bhfi); if (r == 0) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Can't GetFileInformationByHandle"); if (h != INVALID_HANDLE_VALUE && fd < 0) CloseHandle(h); return (ARCHIVE_FAILED); } fileAttributes = bhfi.dwFileAttributes; } /* Sparse file must be set a mark, FILE_ATTRIBUTE_SPARSE_FILE */ if ((fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { if (fd < 0) CloseHandle(h); return (ARCHIVE_OK); } r = setup_sparse_from_disk(a, entry, h); if (fd < 0) CloseHandle(h); return (r); } /* * Windows sparse interface. */ #if defined(__MINGW32__) && !defined(FSCTL_QUERY_ALLOCATED_RANGES) #define FSCTL_QUERY_ALLOCATED_RANGES 0x940CF typedef struct { LARGE_INTEGER FileOffset; LARGE_INTEGER Length; } FILE_ALLOCATED_RANGE_BUFFER; #endif static int setup_sparse_from_disk(struct archive_read_disk *a, struct archive_entry *entry, HANDLE handle) { FILE_ALLOCATED_RANGE_BUFFER range, *outranges = NULL; size_t outranges_size; int64_t entry_size = archive_entry_size(entry); int exit_sts = ARCHIVE_OK; range.FileOffset.QuadPart = 0; range.Length.QuadPart = entry_size; outranges_size = 2048; outranges = (FILE_ALLOCATED_RANGE_BUFFER *)malloc(outranges_size); if (outranges == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); exit_sts = ARCHIVE_FATAL; goto exit_setup_sparse; } for (;;) { DWORD retbytes; BOOL ret; for (;;) { ret = DeviceIoControl(handle, FSCTL_QUERY_ALLOCATED_RANGES, &range, sizeof(range), outranges, (DWORD)outranges_size, &retbytes, NULL); if (ret == 0 && GetLastError() == ERROR_MORE_DATA) { free(outranges); outranges_size *= 2; outranges = (FILE_ALLOCATED_RANGE_BUFFER *) malloc(outranges_size); if (outranges == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); exit_sts = ARCHIVE_FATAL; goto exit_setup_sparse; } continue; } else break; } if (ret != 0) { if (retbytes > 0) { DWORD i, n; n = retbytes / sizeof(outranges[0]); if (n == 1 && outranges[0].FileOffset.QuadPart == 0 && outranges[0].Length.QuadPart == entry_size) break;/* This is not sparse. */ for (i = 0; i < n; i++) archive_entry_sparse_add_entry(entry, outranges[i].FileOffset.QuadPart, outranges[i].Length.QuadPart); range.FileOffset.QuadPart = outranges[n-1].FileOffset.QuadPart + outranges[n-1].Length.QuadPart; range.Length.QuadPart = entry_size - range.FileOffset.QuadPart; if (range.Length.QuadPart > 0) continue; } else { /* The remaining data is hole. */ archive_entry_sparse_add_entry(entry, range.FileOffset.QuadPart, range.Length.QuadPart); } break; } else { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "DeviceIoControl Failed: %lu", GetLastError()); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } } exit_setup_sparse: free(outranges); return (exit_sts); } #endif Index: vendor/libarchive/dist/libarchive/archive_read_support_format_tar.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_tar.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_tar.c (revision 314565) @@ -1,2876 +1,2879 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * Copyright (c) 2016 Martin Matuska * 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: head/lib/libarchive/archive_read_support_format_tar.c 201161 2009-12-29 05:44:39Z kientzle $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_acl_private.h" /* For ACL parsing routines. */ #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #define tar_min(a,b) ((a) < (b) ? (a) : (b)) /* * Layout of POSIX 'ustar' tar header. */ struct archive_entry_header_ustar { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char checksum[8]; char typeflag[1]; char linkname[100]; /* "old format" header ends here */ char magic[6]; /* For POSIX: "ustar\0" */ char version[2]; /* For POSIX: "00" */ char uname[32]; char gname[32]; char rdevmajor[8]; char rdevminor[8]; char prefix[155]; }; /* * Structure of GNU tar header */ struct gnu_sparse { char offset[12]; char numbytes[12]; }; struct archive_entry_header_gnutar { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char checksum[8]; char typeflag[1]; char linkname[100]; char magic[8]; /* "ustar \0" (note blank/blank/null at end) */ char uname[32]; char gname[32]; char rdevmajor[8]; char rdevminor[8]; char atime[12]; char ctime[12]; char offset[12]; char longnames[4]; char unused[1]; struct gnu_sparse sparse[4]; char isextended[1]; char realsize[12]; /* * Old GNU format doesn't use POSIX 'prefix' field; they use * the 'L' (longname) entry instead. */ }; /* * Data specific to this format. */ struct sparse_block { struct sparse_block *next; int64_t offset; int64_t remaining; int hole; }; struct tar { struct archive_string acl_text; struct archive_string entry_pathname; /* For "GNU.sparse.name" and other similar path extensions. */ struct archive_string entry_pathname_override; struct archive_string entry_linkpath; struct archive_string entry_uname; struct archive_string entry_gname; struct archive_string longlink; struct archive_string longname; struct archive_string pax_header; struct archive_string pax_global; struct archive_string line; int pax_hdrcharset_binary; int header_recursion_depth; int64_t entry_bytes_remaining; int64_t entry_offset; int64_t entry_padding; int64_t entry_bytes_unconsumed; int64_t realsize; int sparse_allowed; struct sparse_block *sparse_list; struct sparse_block *sparse_last; int64_t sparse_offset; int64_t sparse_numbytes; int sparse_gnu_major; int sparse_gnu_minor; char sparse_gnu_pending; struct archive_string localname; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv; struct archive_string_conv *sconv_acl; struct archive_string_conv *sconv_default; int init_default_conversion; int compat_2x; int process_mac_extensions; int read_concatenated_archives; }; static int archive_block_is_null(const char *p); static char *base64_decode(const char *, size_t, size_t *); static int gnu_add_sparse_entry(struct archive_read *, struct tar *, int64_t offset, int64_t remaining); static void gnu_clear_sparse_list(struct tar *); static int gnu_sparse_old_read(struct archive_read *, struct tar *, const struct archive_entry_header_gnutar *header, size_t *); static int gnu_sparse_old_parse(struct archive_read *, struct tar *, const struct gnu_sparse *sparse, int length); static int gnu_sparse_01_parse(struct archive_read *, struct tar *, const char *); static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *, size_t *); static int header_Solaris_ACL(struct archive_read *, struct tar *, struct archive_entry *, const void *, size_t *); static int header_common(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_old_tar(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_pax_extensions(struct archive_read *, struct tar *, struct archive_entry *, const void *, size_t *); static int header_pax_global(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_longlink(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_longname(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int read_mac_metadata_blob(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_volume(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_ustar(struct archive_read *, struct tar *, struct archive_entry *, const void *h); static int header_gnutar(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int archive_read_format_tar_bid(struct archive_read *, int); static int archive_read_format_tar_options(struct archive_read *, const char *, const char *); static int archive_read_format_tar_cleanup(struct archive_read *); static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static int archive_read_format_tar_skip(struct archive_read *a); static int archive_read_format_tar_read_header(struct archive_read *, struct archive_entry *); static int checksum(struct archive_read *, const void *); static int pax_attribute(struct archive_read *, struct tar *, struct archive_entry *, const char *key, const char *value, size_t value_length); static int pax_attribute_acl(struct archive_read *, struct tar *, struct archive_entry *, const char *, int); static int pax_attribute_xattr(struct archive_entry *, const char *, const char *); static int pax_header(struct archive_read *, struct tar *, struct archive_entry *, struct archive_string *); static void pax_time(const char *, int64_t *sec, long *nanos); static ssize_t readline(struct archive_read *, struct tar *, const char **, ssize_t limit, size_t *); static int read_body_to_string(struct archive_read *, struct tar *, struct archive_string *, const void *h, size_t *); static int solaris_sparse_parse(struct archive_read *, struct tar *, struct archive_entry *, const char *); static int64_t tar_atol(const char *, size_t); static int64_t tar_atol10(const char *, size_t); static int64_t tar_atol256(const char *, size_t); static int64_t tar_atol8(const char *, size_t); static int tar_read_header(struct archive_read *, struct tar *, struct archive_entry *, size_t *); static int tohex(int c); static char *url_decode(const char *); static void tar_flush_unconsumed(struct archive_read *, size_t *); int archive_read_support_format_gnutar(struct archive *a) { archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar"); return (archive_read_support_format_tar(a)); } int archive_read_support_format_tar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct tar *tar; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_tar"); tar = (struct tar *)calloc(1, sizeof(*tar)); #ifdef HAVE_COPYFILE_H /* Set this by default on Mac OS. */ tar->process_mac_extensions = 1; #endif if (tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } r = __archive_read_register_format(a, tar, "tar", archive_read_format_tar_bid, archive_read_format_tar_options, archive_read_format_tar_read_header, archive_read_format_tar_read_data, archive_read_format_tar_skip, NULL, archive_read_format_tar_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(tar); return (ARCHIVE_OK); } static int archive_read_format_tar_cleanup(struct archive_read *a) { struct tar *tar; tar = (struct tar *)(a->format->data); gnu_clear_sparse_list(tar); archive_string_free(&tar->acl_text); archive_string_free(&tar->entry_pathname); archive_string_free(&tar->entry_pathname_override); archive_string_free(&tar->entry_linkpath); archive_string_free(&tar->entry_uname); archive_string_free(&tar->entry_gname); archive_string_free(&tar->line); archive_string_free(&tar->pax_global); archive_string_free(&tar->pax_header); archive_string_free(&tar->longname); archive_string_free(&tar->longlink); archive_string_free(&tar->localname); free(tar); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * Validate number field * * This has to be pretty lenient in order to accommodate the enormous * variety of tar writers in the world: * = POSIX (IEEE Std 1003.1-1988) ustar requires octal values with leading * zeros and allows fields to be terminated with space or null characters * = Many writers use different termination (in particular, libarchive * omits terminator bytes to squeeze one or two more digits) * = Many writers pad with space and omit leading zeros * = GNU tar and star write base-256 values if numbers are too * big to be represented in octal * * Examples of specific tar headers that we should support: * = Perl Archive::Tar terminates uid, gid, devminor and devmajor with two * null bytes, pads size with spaces and other numeric fields with zeroes * = plexus-archiver prior to 2.6.3 (before switching to commons-compress) * may have uid and gid fields filled with spaces without any octal digits * at all and pads all numeric fields with spaces * * This should tolerate all variants in use. It will reject a field * where the writer just left garbage after a trailing NUL. */ static int validate_number_field(const char* p_field, size_t i_size) { unsigned char marker = (unsigned char)p_field[0]; if (marker == 128 || marker == 255 || marker == 0) { /* Base-256 marker, there's nothing we can check. */ return 1; } else { /* Must be octal */ size_t i = 0; /* Skip any leading spaces */ while (i < i_size && p_field[i] == ' ') { ++i; } /* Skip octal digits. */ while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') { ++i; } /* Any remaining characters must be space or NUL padding. */ while (i < i_size) { if (p_field[i] != ' ' && p_field[i] != 0) { return 0; } ++i; } return 1; } } static int archive_read_format_tar_bid(struct archive_read *a, int best_bid) { int bid; const char *h; const struct archive_entry_header_ustar *header; (void)best_bid; /* UNUSED */ bid = 0; /* Now let's look at the actual header and see if it matches. */ h = __archive_read_ahead(a, 512, NULL); if (h == NULL) return (-1); /* If it's an end-of-archive mark, we can handle it. */ if (h[0] == 0 && archive_block_is_null(h)) { /* * Usually, I bid the number of bits verified, but * in this case, 4096 seems excessive so I picked 10 as * an arbitrary but reasonable-seeming value. */ return (10); } /* If it's not an end-of-archive mark, it must have a valid checksum.*/ if (!checksum(a, h)) return (0); bid += 48; /* Checksum is usually 6 octal digits. */ header = (const struct archive_entry_header_ustar *)h; /* Recognize POSIX formats. */ if ((memcmp(header->magic, "ustar\0", 6) == 0) && (memcmp(header->version, "00", 2) == 0)) bid += 56; /* Recognize GNU tar format. */ if ((memcmp(header->magic, "ustar ", 6) == 0) && (memcmp(header->version, " \0", 2) == 0)) bid += 56; /* Type flag must be null, digit or A-Z, a-z. */ if (header->typeflag[0] != 0 && !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') && !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') && !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') ) return (0); bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ /* * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields. */ if (bid > 0 && ( validate_number_field(header->mode, sizeof(header->mode)) == 0 || validate_number_field(header->uid, sizeof(header->uid)) == 0 || validate_number_field(header->gid, sizeof(header->gid)) == 0 || validate_number_field(header->mtime, sizeof(header->mtime)) == 0 || validate_number_field(header->size, sizeof(header->size)) == 0 || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0 || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) { bid = 0; } return (bid); } static int archive_read_format_tar_options(struct archive_read *a, const char *key, const char *val) { struct tar *tar; int ret = ARCHIVE_FAILED; tar = (struct tar *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle UTF-8 filenames as libarchive 2.x */ tar->compat_2x = (val != NULL && val[0] != 0); tar->init_default_conversion = tar->compat_2x; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "tar: hdrcharset option needs a character-set name"); else { tar->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (tar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "mac-ext") == 0) { tar->process_mac_extensions = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } else if (strcmp(key, "read_concatenated_archives") == 0) { tar->read_concatenated_archives = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } /* utility function- this exists to centralize the logic of tracking * how much unconsumed data we have floating around, and to consume * anything outstanding since we're going to do read_aheads */ static void tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed) { if (*unconsumed) { /* void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL); * this block of code is to poison claimed unconsumed space, ensuring * things break if it is in use still. * currently it WILL break things, so enable it only for debugging this issue if (data) { memset(data, 0xff, *unconsumed); } */ __archive_read_consume(a, *unconsumed); *unconsumed = 0; } } /* * The function invoked by archive_read_next_header(). This * just sets up a few things and then calls the internal * tar_read_header() function below. */ static int archive_read_format_tar_read_header(struct archive_read *a, struct archive_entry *entry) { /* * When converting tar archives to cpio archives, it is * essential that each distinct file have a distinct inode * number. To simplify this, we keep a static count here to * assign fake dev/inode numbers to each tar entry. Note that * pax format archives may overwrite this with something more * useful. * * Ideally, we would track every file read from the archive so * that we could assign the same dev/ino pair to hardlinks, * but the memory required to store a complete lookup table is * probably not worthwhile just to support the relatively * obscure tar->cpio conversion case. */ static int default_inode; static int default_dev; struct tar *tar; const char *p; const wchar_t *wp; int r; size_t l, unconsumed = 0; /* Assign default device/inode values. */ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */ /* Limit generated st_ino number to 16 bits. */ if (default_inode >= 0xffff) { ++default_dev; default_inode = 0; } tar = (struct tar *)(a->format->data); tar->entry_offset = 0; gnu_clear_sparse_list(tar); tar->realsize = -1; /* Mark this as "unset" */ /* Setup default string conversion. */ tar->sconv = tar->opt_sconv; if (tar->sconv == NULL) { if (!tar->init_default_conversion) { tar->sconv_default = archive_string_default_conversion_for_read(&(a->archive)); tar->init_default_conversion = 1; } tar->sconv = tar->sconv_default; } r = tar_read_header(a, tar, entry, &unconsumed); tar_flush_unconsumed(a, &unconsumed); /* * "non-sparse" files are really just sparse files with * a single block. */ if (tar->sparse_list == NULL) { if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { struct sparse_block *sb; for (sb = tar->sparse_list; sb != NULL; sb = sb->next) { if (!sb->hole) archive_entry_sparse_add_entry(entry, sb->offset, sb->remaining); } } if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) { /* * "Regular" entry with trailing '/' is really * directory: This is needed for certain old tar * variants and even for some broken newer ones. */ if ((wp = archive_entry_pathname_w(entry)) != NULL) { l = wcslen(wp); if (l > 0 && wp[l - 1] == L'/') { archive_entry_set_filetype(entry, AE_IFDIR); } } else if ((p = archive_entry_pathname(entry)) != NULL) { l = strlen(p); if (l > 0 && p[l - 1] == '/') { archive_entry_set_filetype(entry, AE_IFDIR); } } } return (r); } static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct tar *tar; struct sparse_block *p; tar = (struct tar *)(a->format->data); for (;;) { /* Remove exhausted entries from sparse list. */ while (tar->sparse_list != NULL && tar->sparse_list->remaining == 0) { p = tar->sparse_list; tar->sparse_list = p->next; free(p); } if (tar->entry_bytes_unconsumed) { __archive_read_consume(a, tar->entry_bytes_unconsumed); tar->entry_bytes_unconsumed = 0; } /* If we're at end of file, return EOF. */ if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) { if (__archive_read_consume(a, tar->entry_padding) < 0) return (ARCHIVE_FATAL); tar->entry_padding = 0; *buff = NULL; *size = 0; *offset = tar->realsize; return (ARCHIVE_EOF); } *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated tar archive"); return (ARCHIVE_FATAL); } if (bytes_read > tar->entry_bytes_remaining) bytes_read = (ssize_t)tar->entry_bytes_remaining; /* Don't read more than is available in the * current sparse block. */ if (tar->sparse_list->remaining < bytes_read) bytes_read = (ssize_t)tar->sparse_list->remaining; *size = bytes_read; *offset = tar->sparse_list->offset; tar->sparse_list->remaining -= bytes_read; tar->sparse_list->offset += bytes_read; tar->entry_bytes_remaining -= bytes_read; tar->entry_bytes_unconsumed = bytes_read; if (!tar->sparse_list->hole) return (ARCHIVE_OK); /* Current is hole data and skip this. */ } } static int archive_read_format_tar_skip(struct archive_read *a) { int64_t bytes_skipped; int64_t request; struct sparse_block *p; struct tar* tar; tar = (struct tar *)(a->format->data); /* Do not consume the hole of a sparse file. */ request = 0; for (p = tar->sparse_list; p != NULL; p = p->next) { if (!p->hole) { if (p->remaining >= INT64_MAX - request) { return ARCHIVE_FATAL; } request += p->remaining; } } if (request > tar->entry_bytes_remaining) request = tar->entry_bytes_remaining; request += tar->entry_padding + tar->entry_bytes_unconsumed; bytes_skipped = __archive_read_consume(a, request); if (bytes_skipped < 0) return (ARCHIVE_FATAL); tar->entry_bytes_remaining = 0; tar->entry_bytes_unconsumed = 0; tar->entry_padding = 0; /* Free the sparse list. */ gnu_clear_sparse_list(tar); return (ARCHIVE_OK); } /* * This function recursively interprets all of the headers associated * with a single entry. */ static int tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, size_t *unconsumed) { ssize_t bytes; int err; const char *h; const struct archive_entry_header_ustar *header; const struct archive_entry_header_gnutar *gnuheader; /* Loop until we find a workable header record. */ for (;;) { tar_flush_unconsumed(a, unconsumed); /* Read 512-byte header record */ h = __archive_read_ahead(a, 512, &bytes); if (bytes < 0) return ((int)bytes); if (bytes == 0) { /* EOF at a block boundary. */ /* Some writers do omit the block of nulls. */ return (ARCHIVE_EOF); } if (bytes < 512) { /* Short block at EOF; this is bad. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive"); return (ARCHIVE_FATAL); } *unconsumed = 512; /* Header is workable if it's not an end-of-archive mark. */ if (h[0] != 0 || !archive_block_is_null(h)) break; /* Ensure format is set for archives with only null blocks. */ if (a->archive.archive_format_name == NULL) { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar"; } if (!tar->read_concatenated_archives) { /* Try to consume a second all-null record, as well. */ tar_flush_unconsumed(a, unconsumed); h = __archive_read_ahead(a, 512, NULL); if (h != NULL && h[0] == 0 && archive_block_is_null(h)) __archive_read_consume(a, 512); archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* * We're reading concatenated archives, ignore this block and * loop to get the next. */ } /* * Note: If the checksum fails and we return ARCHIVE_RETRY, * then the client is likely to just retry. This is a very * crude way to search for the next valid header! * * TODO: Improve this by implementing a real header scan. */ if (!checksum(a, h)) { tar_flush_unconsumed(a, unconsumed); archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); return (ARCHIVE_RETRY); /* Retryable: Invalid header */ } if (++tar->header_recursion_depth > 32) { tar_flush_unconsumed(a, unconsumed); archive_set_error(&a->archive, EINVAL, "Too many special headers"); return (ARCHIVE_WARN); } /* Determine the format variant. */ header = (const struct archive_entry_header_ustar *)h; switch(header->typeflag[0]) { case 'A': /* Solaris tar ACL */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "Solaris tar"; err = header_Solaris_ACL(a, tar, entry, h, unconsumed); break; case 'g': /* POSIX-standard 'g' header. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_global(a, tar, entry, h, unconsumed); if (err == ARCHIVE_EOF) return (err); break; case 'K': /* Long link name (GNU tar, others) */ err = header_longlink(a, tar, entry, h, unconsumed); break; case 'L': /* Long filename (GNU tar, others) */ err = header_longname(a, tar, entry, h, unconsumed); break; case 'V': /* GNU volume header */ err = header_volume(a, tar, entry, h, unconsumed); break; case 'X': /* Used by SUN tar; same as 'x'. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format (Sun variant)"; err = header_pax_extensions(a, tar, entry, h, unconsumed); break; case 'x': /* POSIX-standard 'x' header. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_extensions(a, tar, entry, h, unconsumed); break; default: gnuheader = (const struct archive_entry_header_gnutar *)h; if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; a->archive.archive_format_name = "GNU tar format"; err = header_gnutar(a, tar, entry, h, unconsumed); } else if (memcmp(header->magic, "ustar", 5) == 0) { if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; a->archive.archive_format_name = "POSIX ustar format"; } err = header_ustar(a, tar, entry, h); } else { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar (non-POSIX)"; err = header_old_tar(a, tar, entry, h); } } if (err == ARCHIVE_FATAL) return (err); tar_flush_unconsumed(a, unconsumed); h = NULL; header = NULL; --tar->header_recursion_depth; /* Yuck. Apple's design here ends up storing long pathname * extensions for both the AppleDouble extension entry and the * regular entry. */ if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) && tar->header_recursion_depth == 0 && tar->process_mac_extensions) { int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed); if (err2 < err) err = err2; } /* We return warnings or success as-is. Anything else is fatal. */ if (err == ARCHIVE_WARN || err == ARCHIVE_OK) { if (tar->sparse_gnu_pending) { if (tar->sparse_gnu_major == 1 && tar->sparse_gnu_minor == 0) { ssize_t bytes_read; tar->sparse_gnu_pending = 0; /* Read initial sparse map. */ bytes_read = gnu_sparse_10_read(a, tar, unconsumed); - tar->entry_bytes_remaining -= bytes_read; if (bytes_read < 0) return ((int)bytes_read); + tar->entry_bytes_remaining -= bytes_read; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unrecognized GNU sparse file format"); return (ARCHIVE_WARN); } tar->sparse_gnu_pending = 0; } return (err); } if (err == ARCHIVE_EOF) /* EOF when recursively reading a header is bad. */ archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); return (ARCHIVE_FATAL); } /* * Return true if block checksum is correct. */ static int checksum(struct archive_read *a, const void *h) { const unsigned char *bytes; const struct archive_entry_header_ustar *header; int check, sum; size_t i; (void)a; /* UNUSED */ bytes = (const unsigned char *)h; header = (const struct archive_entry_header_ustar *)h; /* Checksum field must hold an octal number */ for (i = 0; i < sizeof(header->checksum); ++i) { char c = header->checksum[i]; if (c != ' ' && c != '\0' && (c < '0' || c > '7')) return 0; } /* * Test the checksum. Note that POSIX specifies _unsigned_ * bytes for this calculation. */ sum = (int)tar_atol(header->checksum, sizeof(header->checksum)); check = 0; for (i = 0; i < 148; i++) check += (unsigned char)bytes[i]; for (; i < 156; i++) check += 32; for (; i < 512; i++) check += (unsigned char)bytes[i]; if (sum == check) return (1); /* * Repeat test with _signed_ bytes, just in case this archive * was created by an old BSD, Solaris, or HP-UX tar with a * broken checksum calculation. */ check = 0; for (i = 0; i < 148; i++) check += (signed char)bytes[i]; for (; i < 156; i++) check += 32; for (; i < 512; i++) check += (signed char)bytes[i]; if (sum == check) return (1); return (0); } /* * Return true if this block contains only nulls. */ static int archive_block_is_null(const char *p) { unsigned i; for (i = 0; i < 512; i++) if (*p++) return (0); return (1); } /* * Interpret 'A' Solaris ACL header */ static int header_Solaris_ACL(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { const struct archive_entry_header_ustar *header; size_t size; int err, acl_type; int64_t type; char *acl, *p; /* * read_body_to_string adds a NUL terminator, but we need a little * more to make sure that we don't overrun acl_text later. */ header = (const struct archive_entry_header_ustar *)h; size = (size_t)tar_atol(header->size, sizeof(header->size)); err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed); if (err != ARCHIVE_OK) return (err); /* Recursively read next header */ err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* TODO: Examine the first characters to see if this * is an AIX ACL descriptor. We'll likely never support * them, but it would be polite to recognize and warn when * we do see them. */ /* Leading octal number indicates ACL type and number of entries. */ p = acl = tar->acl_text.s; type = 0; while (*p != '\0' && p < acl + size) { if (*p < '0' || *p > '7') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (invalid digit)"); return(ARCHIVE_WARN); } type <<= 3; type += *p - '0'; if (type > 077777777) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (count too large)"); return (ARCHIVE_WARN); } p++; } switch ((int)type & ~0777777) { case 01000000: /* POSIX.1e ACL */ acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; break; case 03000000: /* NFSv4 ACL */ acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (unsupported type %o)", (int)type); return (ARCHIVE_WARN); } p++; if (p >= acl + size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (body overflow)"); return(ARCHIVE_WARN); } /* ACL text is null-terminated; find the end. */ size -= (p - acl); acl = p; while (*p != '\0' && p < acl + size) p++; if (tar->sconv_acl == NULL) { tar->sconv_acl = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (tar->sconv_acl == NULL) return (ARCHIVE_FATAL); } archive_strncpy(&(tar->localname), acl, p - acl); err = archive_acl_from_text_l(archive_entry_acl(entry), tar->localname.s, acl_type, tar->sconv_acl); if (err != ARCHIVE_OK) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for ACL"); } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (unparsable)"); } return (err); } /* * Interpret 'K' long linkname header. */ static int header_longlink(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* Set symlink if symlink already set, else hardlink. */ archive_entry_copy_link(entry, tar->longlink.s); return (ARCHIVE_OK); } static int set_conversion_failed_error(struct archive_read *a, struct archive_string_conv *sconv, const char *name) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for %s", name); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "%s can't be converted from %s to current locale.", name, archive_string_conversion_charset_name(sconv)); return (ARCHIVE_WARN); } /* * Interpret 'L' long filename header. */ static int header_longname(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed); if (err != ARCHIVE_OK) return (err); /* Read and parse "real" header, then override name. */ err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); if (archive_entry_copy_pathname_l(entry, tar->longname.s, archive_strlen(&(tar->longname)), tar->sconv) != 0) err = set_conversion_failed_error(a, tar->sconv, "Pathname"); return (err); } /* * Interpret 'V' GNU tar volume header. */ static int header_volume(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { (void)h; /* Just skip this and read the next header. */ return (tar_read_header(a, tar, entry, unconsumed)); } /* * Read body of an archive entry into an archive_string object. */ static int read_body_to_string(struct archive_read *a, struct tar *tar, struct archive_string *as, const void *h, size_t *unconsumed) { int64_t size; const struct archive_entry_header_ustar *header; const void *src; (void)tar; /* UNUSED */ header = (const struct archive_entry_header_ustar *)h; size = tar_atol(header->size, sizeof(header->size)); if ((size > 1048576) || (size < 0)) { archive_set_error(&a->archive, EINVAL, "Special header too large"); return (ARCHIVE_FATAL); } /* Fail if we can't make our buffer big enough. */ if (archive_string_ensure(as, (size_t)size+1) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } tar_flush_unconsumed(a, unconsumed); /* Read the body into the string. */ *unconsumed = (size_t)((size + 511) & ~ 511); src = __archive_read_ahead(a, *unconsumed, NULL); if (src == NULL) { *unconsumed = 0; return (ARCHIVE_FATAL); } memcpy(as->s, src, (size_t)size); as->s[size] = '\0'; as->length = (size_t)size; return (ARCHIVE_OK); } /* * Parse out common header elements. * * This would be the same as header_old_tar, except that the * filename is handled slightly differently for old and POSIX * entries (POSIX entries support a 'prefix'). This factoring * allows header_old_tar and header_ustar * to handle filenames differently, while still putting most of the * common parsing into one place. */ static int header_common(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; char tartype; int err = ARCHIVE_OK; header = (const struct archive_entry_header_ustar *)h; if (header->linkname[0]) archive_strncpy(&(tar->entry_linkpath), header->linkname, sizeof(header->linkname)); else archive_string_empty(&(tar->entry_linkpath)); /* Parse out the numeric fields (all are octal) */ archive_entry_set_mode(entry, (mode_t)tar_atol(header->mode, sizeof(header->mode))); archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid))); archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size)); if (tar->entry_bytes_remaining < 0) { tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Tar entry has negative size"); return (ARCHIVE_FATAL); } if (tar->entry_bytes_remaining == INT64_MAX) { /* Note: tar_atol returns INT64_MAX on overflow */ tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Tar entry size overflow"); return (ARCHIVE_FATAL); } tar->realsize = tar->entry_bytes_remaining; archive_entry_set_size(entry, tar->entry_bytes_remaining); archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); /* Handle the tar type flag appropriately. */ tartype = header->typeflag[0]; switch (tartype) { case '1': /* Hard link */ if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Linkname"); if (err == ARCHIVE_FATAL) return (err); } /* * The following may seem odd, but: Technically, tar * does not store the file type for a "hard link" * entry, only the fact that it is a hard link. So, I * leave the type zero normally. But, pax interchange * format allows hard links to have data, which * implies that the underlying entry is a regular * file. */ if (archive_entry_size(entry) > 0) archive_entry_set_filetype(entry, AE_IFREG); /* * A tricky point: Traditionally, tar readers have * ignored the size field when reading hardlink * entries, and some writers put non-zero sizes even * though the body is empty. POSIX blessed this * convention in the 1988 standard, but broke with * this tradition in 2001 by permitting hardlink * entries to store valid bodies in pax interchange * format, but not in ustar format. Since there is no * hard and fast way to distinguish pax interchange * from earlier archives (the 'x' and 'g' entries are * optional, after all), we need a heuristic. */ if (archive_entry_size(entry) == 0) { /* If the size is already zero, we're done. */ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { /* Definitely pax extended; must obey hardlink size. */ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR) { /* Old-style or GNU tar: we must ignore the size. */ archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; } else if (archive_read_format_tar_bid(a, 50) > 50) { /* * We don't know if it's pax: If the bid * function sees a valid ustar header * immediately following, then let's ignore * the hardlink size. */ archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; } /* * TODO: There are still two cases I'd like to handle: * = a ustar non-pax archive with a hardlink entry at * end-of-archive. (Look for block of nulls following?) * = a pax archive that has not seen any pax headers * and has an entry which is a hardlink entry storing * a body containing an uncompressed tar archive. * The first is worth addressing; I don't see any reliable * way to deal with the second possibility. */ break; case '2': /* Symlink */ archive_entry_set_filetype(entry, AE_IFLNK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Linkname"); if (err == ARCHIVE_FATAL) return (err); } break; case '3': /* Character device */ archive_entry_set_filetype(entry, AE_IFCHR); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '4': /* Block device */ archive_entry_set_filetype(entry, AE_IFBLK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '5': /* Dir */ archive_entry_set_filetype(entry, AE_IFDIR); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case '6': /* FIFO device */ archive_entry_set_filetype(entry, AE_IFIFO); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; break; case 'D': /* GNU incremental directory type */ /* * No special handling is actually required here. * It might be nice someday to preprocess the file list and * provide it to the client, though. */ archive_entry_set_filetype(entry, AE_IFDIR); break; case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/ /* * As far as I can tell, this is just like a regular file * entry, except that the contents should be _appended_ to * the indicated file at the indicated offset. This may * require some API work to fully support. */ break; case 'N': /* Old GNU "long filename" entry. */ /* The body of this entry is a script for renaming * previously-extracted entries. Ugh. It will never * be supported by libarchive. */ archive_entry_set_filetype(entry, AE_IFREG); break; case 'S': /* GNU sparse files */ /* * Sparse files are really just regular files with * sparse information in the extended area. */ /* FALLTHROUGH */ case '0': /* * Enable sparse file "read" support only for regular * files and explicit GNU sparse files. However, we * don't allow non-standard file types to be sparse. */ tar->sparse_allowed = 1; /* FALLTHROUGH */ default: /* Regular file and non-standard types */ /* * Per POSIX: non-recognized types should always be * treated as regular files. */ archive_entry_set_filetype(entry, AE_IFREG); break; } return (err); } /* * Parse out header elements for "old-style" tar archives. */ static int header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; int err = ARCHIVE_OK, err2; /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_ustar *)h; if (archive_entry_copy_pathname_l(entry, header->name, sizeof(header->name), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } /* Grab rest of common fields */ err2 = header_common(a, tar, entry, h); if (err > err2) err = err2; tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Read a Mac AppleDouble-encoded blob of file metadata, * if there is one. */ static int read_mac_metadata_blob(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int64_t size; const void *data; const char *p, *name; const wchar_t *wp, *wname; (void)h; /* UNUSED */ wname = wp = archive_entry_pathname_w(entry); if (wp != NULL) { /* Find the last path element. */ for (; *wp != L'\0'; ++wp) { if (wp[0] == '/' && wp[1] != L'\0') wname = wp + 1; } /* * If last path element starts with "._", then * this is a Mac extension. */ if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0') return ARCHIVE_OK; } else { /* Find the last path element. */ name = p = archive_entry_pathname(entry); if (p == NULL) return (ARCHIVE_FAILED); for (; *p != '\0'; ++p) { if (p[0] == '/' && p[1] != '\0') name = p + 1; } /* * If last path element starts with "._", then * this is a Mac extension. */ if (name[0] != '.' || name[1] != '_' || name[2] == '\0') return ARCHIVE_OK; } /* Read the body as a Mac OS metadata blob. */ size = archive_entry_size(entry); /* * TODO: Look beyond the body here to peek at the next header. * If it's a regular header (not an extension header) * that has the wrong name, just return the current * entry as-is, without consuming the body here. * That would reduce the risk of us mis-identifying * an ordinary file that just happened to have * a name starting with "._". * * Q: Is the above idea really possible? Even * when there are GNU or pax extension entries? */ data = __archive_read_ahead(a, (size_t)size, NULL); if (data == NULL) { *unconsumed = 0; return (ARCHIVE_FATAL); } archive_entry_copy_mac_metadata(entry, data, (size_t)size); *unconsumed = (size_t)((size + 511) & ~ 511); tar_flush_unconsumed(a, unconsumed); return (tar_read_header(a, tar, entry, unconsumed)); } /* * Parse a file header for a pax extended archive entry. */ static int header_pax_global(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed); if (err != ARCHIVE_OK) return (err); err = tar_read_header(a, tar, entry, unconsumed); return (err); } static int header_pax_extensions(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err, err2; err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed); if (err != ARCHIVE_OK) return (err); /* Parse the next header. */ err = tar_read_header(a, tar, entry, unconsumed); if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) return (err); /* * TODO: Parse global/default options into 'entry' struct here * before handling file-specific options. * * This design (parse standard header, then overwrite with pax * extended attribute data) usually works well, but isn't ideal; * it would be better to parse the pax extended attributes first * and then skip any fields in the standard header that were * defined in the pax header. */ err2 = pax_header(a, tar, entry, &tar->pax_header); err = err_combine(err, err2); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Parse a file header for a Posix "ustar" archive entry. This also * handles "pax" or "extended ustar" entries. */ static int header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; struct archive_string *as; int err = ARCHIVE_OK, r; header = (const struct archive_entry_header_ustar *)h; /* Copy name into an internal buffer to ensure null-termination. */ as = &(tar->entry_pathname); if (header->prefix[0]) { archive_strncpy(as, header->prefix, sizeof(header->prefix)); if (as->s[archive_strlen(as) - 1] != '/') archive_strappend_char(as, '/'); archive_strncat(as, header->name, sizeof(header->name)); } else { archive_strncpy(as, header->name, sizeof(header->name)); } if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } /* Handle rest of common fields. */ r = header_common(a, tar, entry, h); if (r == ARCHIVE_FATAL) return (r); if (r < err) err = r; /* Handle POSIX ustar fields. */ if (archive_entry_copy_uname_l(entry, header->uname, sizeof(header->uname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Uname"); if (err == ARCHIVE_FATAL) return (err); } if (archive_entry_copy_gname_l(entry, header->gname, sizeof(header->gname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); } /* Parse out device numbers only for char and block specials. */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { archive_entry_set_rdevmajor(entry, (dev_t) tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); archive_entry_set_rdevminor(entry, (dev_t) tar_atol(header->rdevminor, sizeof(header->rdevminor))); } tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } /* * Parse the pax extended attributes record. * * Returns non-zero if there's an error in the data. */ static int pax_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct archive_string *in_as) { size_t attr_length, l, line_length, value_length; char *p; char *key, *value; struct archive_string *as; struct archive_string_conv *sconv; int err, err2; char *attr = in_as->s; attr_length = in_as->length; tar->pax_hdrcharset_binary = 0; archive_string_empty(&(tar->entry_gname)); archive_string_empty(&(tar->entry_linkpath)); archive_string_empty(&(tar->entry_pathname)); archive_string_empty(&(tar->entry_pathname_override)); archive_string_empty(&(tar->entry_uname)); err = ARCHIVE_OK; while (attr_length > 0) { /* Parse decimal length field at start of line. */ line_length = 0; l = attr_length; p = attr; /* Record start of line. */ while (l>0) { if (*p == ' ') { p++; l--; break; } if (*p < '0' || *p > '9') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring malformed pax extended attributes"); return (ARCHIVE_WARN); } line_length *= 10; line_length += *p - '0'; if (line_length > 999999) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Rejecting pax extended attribute > 1MB"); return (ARCHIVE_WARN); } p++; l--; } /* * Parsed length must be no bigger than available data, * at least 1, and the last character of the line must * be '\n'. */ if (line_length > attr_length || line_length < 1 || attr[line_length - 1] != '\n') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring malformed pax extended attribute"); return (ARCHIVE_WARN); } /* Null-terminate the line. */ attr[line_length - 1] = '\0'; /* Find end of key and null terminate it. */ key = p; if (key[0] == '=') return (-1); while (*p && *p != '=') ++p; if (*p == '\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid pax extended attributes"); return (ARCHIVE_WARN); } *p = '\0'; value = p + 1; /* Some values may be binary data */ value_length = attr + line_length - 1 - value; /* Identify this attribute and set it in the entry. */ err2 = pax_attribute(a, tar, entry, key, value, value_length); if (err2 == ARCHIVE_FATAL) return (err2); err = err_combine(err, err2); /* Skip to next line */ attr += line_length; attr_length -= line_length; } /* * PAX format uses UTF-8 as default charset for its metadata * unless hdrcharset=BINARY is present in its header. * We apply the charset specified by the hdrcharset option only * when the hdrcharset attribute(in PAX header) is BINARY because * we respect the charset described in PAX header and BINARY also * means that metadata(filename,uname and gname) character-set * is unknown. */ if (tar->pax_hdrcharset_binary) sconv = tar->opt_sconv; else { sconv = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (sconv == NULL) return (ARCHIVE_FATAL); if (tar->compat_2x) archive_string_conversion_set_opt(sconv, SCONV_SET_OPT_UTF8_LIBARCHIVE2X); } if (archive_strlen(&(tar->entry_gname)) > 0) { if (archive_entry_copy_gname_l(entry, tar->entry_gname.s, archive_strlen(&(tar->entry_gname)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_gname(entry, tar->entry_gname.s); } } if (archive_strlen(&(tar->entry_linkpath)) > 0) { if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Linkname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_link(entry, tar->entry_linkpath.s); } } /* * Some extensions (such as the GNU sparse file extensions) * deliberately store a synthetic name under the regular 'path' * attribute and the real file name under a different attribute. * Since we're supposed to not care about the order, we * have no choice but to store all of the various filenames * we find and figure it all out afterwards. This is the * figuring out part. */ as = NULL; if (archive_strlen(&(tar->entry_pathname_override)) > 0) as = &(tar->entry_pathname_override); else if (archive_strlen(&(tar->entry_pathname)) > 0) as = &(tar->entry_pathname); if (as != NULL) { if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_pathname(entry, as->s); } } if (archive_strlen(&(tar->entry_uname)) > 0) { if (archive_entry_copy_uname_l(entry, tar->entry_uname.s, archive_strlen(&(tar->entry_uname)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Uname"); if (err == ARCHIVE_FATAL) return (err); /* Use a converted an original name. */ archive_entry_copy_uname(entry, tar->entry_uname.s); } } return (err); } static int pax_attribute_xattr(struct archive_entry *entry, const char *name, const char *value) { char *name_decoded; void *value_decoded; size_t value_len; if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0) return 3; name += 17; /* URL-decode name */ name_decoded = url_decode(name); if (name_decoded == NULL) return 2; /* Base-64 decode value */ value_decoded = base64_decode(value, strlen(value), &value_len); if (value_decoded == NULL) { free(name_decoded); return 1; } archive_entry_xattr_add_entry(entry, name_decoded, value_decoded, value_len); free(name_decoded); free(value_decoded); return 0; } static int pax_attribute_schily_xattr(struct archive_entry *entry, const char *name, const char *value, size_t value_length) { if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0) return 1; name += 13; archive_entry_xattr_add_entry(entry, name, value, value_length); return 0; } static int pax_attribute_acl(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const char *value, int type) { int r; const char* errstr; switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: errstr = "SCHILY.acl.access"; break; case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: errstr = "SCHILY.acl.default"; break; case ARCHIVE_ENTRY_ACL_TYPE_NFS4: errstr = "SCHILY.acl.ace"; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown ACL type: %d", type); return(ARCHIVE_FATAL); } if (tar->sconv_acl == NULL) { tar->sconv_acl = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (tar->sconv_acl == NULL) return (ARCHIVE_FATAL); } r = archive_acl_from_text_l(archive_entry_acl(entry), value, type, tar->sconv_acl); if (r != ARCHIVE_OK) { if (r == ARCHIVE_FATAL) { archive_set_error(&a->archive, ENOMEM, "%s %s", "Can't allocate memory for ", errstr); return (r); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr); } return (r); } /* * Parse a single key=value attribute. key/value pointers are * assumed to point into reasonably long-lived storage. * * Note that POSIX reserves all-lowercase keywords. Vendor-specific * extensions should always have keywords of the form "VENDOR.attribute" * In particular, it's quite feasible to support many different * vendor extensions here. I'm using "LIBARCHIVE" for extensions * unique to this library. * * Investigate other vendor-specific extensions and see if * any of them look useful. */ static int pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const char *key, const char *value, size_t value_length) { int64_t s; long n; int err = ARCHIVE_OK, r; if (value == NULL) value = ""; /* Disable compiler warning; do not pass * NULL pointer to strlen(). */ switch (key[0]) { case 'G': /* Reject GNU.sparse.* headers on non-regular files. */ if (strncmp(key, "GNU.sparse", 10) == 0 && !tar->sparse_allowed) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Non-regular file cannot be sparse"); return (ARCHIVE_FATAL); } /* GNU "0.0" sparse pax format. */ if (strcmp(key, "GNU.sparse.numblocks") == 0) { tar->sparse_offset = -1; tar->sparse_numbytes = -1; tar->sparse_gnu_major = 0; tar->sparse_gnu_minor = 0; } if (strcmp(key, "GNU.sparse.offset") == 0) { tar->sparse_offset = tar_atol10(value, strlen(value)); if (tar->sparse_numbytes != -1) { if (gnu_add_sparse_entry(a, tar, tar->sparse_offset, tar->sparse_numbytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); tar->sparse_offset = -1; tar->sparse_numbytes = -1; } } if (strcmp(key, "GNU.sparse.numbytes") == 0) { tar->sparse_numbytes = tar_atol10(value, strlen(value)); if (tar->sparse_numbytes != -1) { if (gnu_add_sparse_entry(a, tar, tar->sparse_offset, tar->sparse_numbytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); tar->sparse_offset = -1; tar->sparse_numbytes = -1; } } if (strcmp(key, "GNU.sparse.size") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); } /* GNU "0.1" sparse pax format. */ if (strcmp(key, "GNU.sparse.map") == 0) { tar->sparse_gnu_major = 0; tar->sparse_gnu_minor = 1; if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK) return (ARCHIVE_WARN); } /* GNU "1.0" sparse pax format */ if (strcmp(key, "GNU.sparse.major") == 0) { tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value)); tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.minor") == 0) { tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value)); tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.name") == 0) { /* * The real filename; when storing sparse * files, GNU tar puts a synthesized name into * the regular 'path' attribute in an attempt * to limit confusion. ;-) */ archive_strcpy(&(tar->entry_pathname_override), value); } if (strcmp(key, "GNU.sparse.realsize") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); } break; case 'L': /* Our extensions */ /* TODO: Handle arbitrary extended attributes... */ /* if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0) archive_entry_set_xxxxxx(entry, value); */ if (strcmp(key, "LIBARCHIVE.creationtime") == 0) { pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) pax_attribute_xattr(entry, key, value); break; case 'S': /* We support some keys used by the "star" archiver */ if (strcmp(key, "SCHILY.acl.access") == 0) { r = pax_attribute_acl(a, tar, entry, value, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); if (r == ARCHIVE_FATAL) return (r); } else if (strcmp(key, "SCHILY.acl.default") == 0) { r = pax_attribute_acl(a, tar, entry, value, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); if (r == ARCHIVE_FATAL) return (r); } else if (strcmp(key, "SCHILY.acl.ace") == 0) { r = pax_attribute_acl(a, tar, entry, value, ARCHIVE_ENTRY_ACL_TYPE_NFS4); if (r == ARCHIVE_FATAL) return (r); } else if (strcmp(key, "SCHILY.devmajor") == 0) { archive_entry_set_rdevmajor(entry, (dev_t)tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.devminor") == 0) { archive_entry_set_rdevminor(entry, (dev_t)tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.fflags") == 0) { archive_entry_copy_fflags_text(entry, value); } else if (strcmp(key, "SCHILY.dev") == 0) { archive_entry_set_dev(entry, (dev_t)tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.ino") == 0) { archive_entry_set_ino(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.nlink") == 0) { archive_entry_set_nlink(entry, (unsigned) tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.realsize") == 0) { tar->realsize = tar_atol10(value, strlen(value)); archive_entry_set_size(entry, tar->realsize); } else if (strncmp(key, "SCHILY.xattr.", 13) == 0) { pax_attribute_schily_xattr(entry, key, value, value_length); } else if (strcmp(key, "SUN.holesdata") == 0) { /* A Solaris extension for sparse. */ r = solaris_sparse_parse(a, tar, entry, value); if (r < err) { if (r == ARCHIVE_FATAL) return (r); err = r; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Parse error: SUN.holesdata"); } } break; case 'a': if (strcmp(key, "atime") == 0) { pax_time(value, &s, &n); archive_entry_set_atime(entry, s, n); } break; case 'c': if (strcmp(key, "ctime") == 0) { pax_time(value, &s, &n); archive_entry_set_ctime(entry, s, n); } else if (strcmp(key, "charset") == 0) { /* TODO: Publish charset information in entry. */ } else if (strcmp(key, "comment") == 0) { /* TODO: Publish comment in entry. */ } break; case 'g': if (strcmp(key, "gid") == 0) { archive_entry_set_gid(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "gname") == 0) { archive_strcpy(&(tar->entry_gname), value); } break; case 'h': if (strcmp(key, "hdrcharset") == 0) { if (strcmp(value, "BINARY") == 0) /* Binary mode. */ tar->pax_hdrcharset_binary = 1; else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0) tar->pax_hdrcharset_binary = 0; } break; case 'l': /* pax interchange doesn't distinguish hardlink vs. symlink. */ if (strcmp(key, "linkpath") == 0) { archive_strcpy(&(tar->entry_linkpath), value); } break; case 'm': if (strcmp(key, "mtime") == 0) { pax_time(value, &s, &n); archive_entry_set_mtime(entry, s, n); } break; case 'p': if (strcmp(key, "path") == 0) { archive_strcpy(&(tar->entry_pathname), value); } break; case 'r': /* POSIX has reserved 'realtime.*' */ break; case 's': /* POSIX has reserved 'security.*' */ /* Someday: if (strcmp(key, "security.acl") == 0) { ... } */ if (strcmp(key, "size") == 0) { /* "size" is the size of the data in the entry. */ tar->entry_bytes_remaining = tar_atol10(value, strlen(value)); /* * But, "size" is not necessarily the size of * the file on disk; if this is a sparse file, * the disk size may have already been set from * GNU.sparse.realsize or GNU.sparse.size or * an old GNU header field or SCHILY.realsize * or .... */ if (tar->realsize < 0) { archive_entry_set_size(entry, tar->entry_bytes_remaining); tar->realsize = tar->entry_bytes_remaining; } } break; case 'u': if (strcmp(key, "uid") == 0) { archive_entry_set_uid(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "uname") == 0) { archive_strcpy(&(tar->entry_uname), value); } break; } return (err); } /* * parse a decimal time value, which may include a fractional portion */ static void pax_time(const char *p, int64_t *ps, long *pn) { char digit; int64_t s; unsigned long l; int sign; int64_t limit, last_digit_limit; limit = INT64_MAX / 10; last_digit_limit = INT64_MAX % 10; s = 0; sign = 1; if (*p == '-') { sign = -1; p++; } while (*p >= '0' && *p <= '9') { digit = *p - '0'; if (s > limit || (s == limit && digit > last_digit_limit)) { s = INT64_MAX; break; } s = (s * 10) + digit; ++p; } *ps = s * sign; /* Calculate nanoseconds. */ *pn = 0; if (*p != '.') return; l = 100000000UL; do { ++p; if (*p >= '0' && *p <= '9') *pn += (*p - '0') * l; else break; } while (l /= 10); } /* * Parse GNU tar header */ static int header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { const struct archive_entry_header_gnutar *header; int64_t t; int err = ARCHIVE_OK; /* * GNU header is like POSIX ustar, except 'prefix' is * replaced with some other fields. This also means the * filename is stored as in old-style archives. */ /* Grab fields common to all tar variants. */ err = header_common(a, tar, entry, h); if (err == ARCHIVE_FATAL) return (err); /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_gnutar *)h; if (archive_entry_copy_pathname_l(entry, header->name, sizeof(header->name), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } /* Fields common to ustar and GNU */ /* XXX Can the following be factored out since it's common * to ustar and gnu tar? Is it okay to move it down into * header_common, perhaps? */ if (archive_entry_copy_uname_l(entry, header->uname, sizeof(header->uname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Uname"); if (err == ARCHIVE_FATAL) return (err); } if (archive_entry_copy_gname_l(entry, header->gname, sizeof(header->gname), tar->sconv) != 0) { err = set_conversion_failed_error(a, tar->sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); } /* Parse out device numbers only for char and block specials */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { archive_entry_set_rdevmajor(entry, (dev_t) tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); archive_entry_set_rdevminor(entry, (dev_t) tar_atol(header->rdevminor, sizeof(header->rdevminor))); } else archive_entry_set_rdev(entry, 0); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); /* Grab GNU-specific fields. */ t = tar_atol(header->atime, sizeof(header->atime)); if (t > 0) archive_entry_set_atime(entry, t, 0); t = tar_atol(header->ctime, sizeof(header->ctime)); if (t > 0) archive_entry_set_ctime(entry, t, 0); if (header->realsize[0] != 0) { tar->realsize = tar_atol(header->realsize, sizeof(header->realsize)); archive_entry_set_size(entry, tar->realsize); } if (header->sparse[0].offset[0] != 0) { if (gnu_sparse_old_read(a, tar, header, unconsumed) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (header->isextended[0] != 0) { /* XXX WTF? XXX */ } } return (err); } static int gnu_add_sparse_entry(struct archive_read *a, struct tar *tar, int64_t offset, int64_t remaining) { struct sparse_block *p; p = (struct sparse_block *)calloc(1, sizeof(*p)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (tar->sparse_last != NULL) tar->sparse_last->next = p; else tar->sparse_list = p; tar->sparse_last = p; if (remaining < 0 || offset < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data"); return (ARCHIVE_FATAL); } p->offset = offset; p->remaining = remaining; return (ARCHIVE_OK); } static void gnu_clear_sparse_list(struct tar *tar) { struct sparse_block *p; while (tar->sparse_list != NULL) { p = tar->sparse_list; tar->sparse_list = p->next; free(p); } tar->sparse_last = NULL; } /* * GNU tar old-format sparse data. * * GNU old-format sparse data is stored in a fixed-field * format. Offset/size values are 11-byte octal fields (same * format as 'size' field in ustart header). These are * stored in the header, allocating subsequent header blocks * as needed. Extending the header in this way is a pretty * severe POSIX violation; this design has earned GNU tar a * lot of criticism. */ static int gnu_sparse_old_read(struct archive_read *a, struct tar *tar, const struct archive_entry_header_gnutar *header, size_t *unconsumed) { ssize_t bytes_read; const void *data; struct extended { struct gnu_sparse sparse[21]; char isextended[1]; char padding[7]; }; const struct extended *ext; if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (header->isextended[0] == 0) return (ARCHIVE_OK); do { tar_flush_unconsumed(a, unconsumed); data = __archive_read_ahead(a, 512, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read < 512) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " "detected while reading sparse file data"); return (ARCHIVE_FATAL); } *unconsumed = 512; ext = (const struct extended *)data; if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK) return (ARCHIVE_FATAL); } while (ext->isextended[0] != 0); if (tar->sparse_list != NULL) tar->entry_offset = tar->sparse_list->offset; return (ARCHIVE_OK); } static int gnu_sparse_old_parse(struct archive_read *a, struct tar *tar, const struct gnu_sparse *sparse, int length) { while (length > 0 && sparse->offset[0] != 0) { if (gnu_add_sparse_entry(a, tar, tar_atol(sparse->offset, sizeof(sparse->offset)), tar_atol(sparse->numbytes, sizeof(sparse->numbytes))) != ARCHIVE_OK) return (ARCHIVE_FATAL); sparse++; length--; } return (ARCHIVE_OK); } /* * GNU tar sparse format 0.0 * * Beginning with GNU tar 1.15, sparse files are stored using * information in the pax extended header. The GNU tar maintainers * have gone through a number of variations in the process of working * out this scheme; fortunately, they're all numbered. * * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to * store offset/size for each block. The repeated instances of these * latter fields violate the pax specification (which frowns on * duplicate keys), so this format was quickly replaced. */ /* * GNU tar sparse format 0.1 * * This version replaced the offset/numbytes attributes with * a single "map" attribute that stored a list of integers. This * format had two problems: First, the "map" attribute could be very * long, which caused problems for some implementations. More * importantly, the sparse data was lost when extracted by archivers * that didn't recognize this extension. */ static int gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p) { const char *e; int64_t offset = -1, size = -1; for (;;) { e = p; while (*e != '\0' && *e != ',') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; } if (offset < 0) { offset = tar_atol10(p, e - p); if (offset < 0) return (ARCHIVE_WARN); } else { size = tar_atol10(p, e - p); if (size < 0) return (ARCHIVE_WARN); if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); offset = -1; } if (*e == '\0') return (ARCHIVE_OK); p = e + 1; } } /* * GNU tar sparse format 1.0 * * The idea: The offset/size data is stored as a series of base-10 * ASCII numbers prepended to the file data, so that dearchivers that * don't support this format will extract the block map along with the * data and a separate post-process can restore the sparseness. * * Unfortunately, GNU tar 1.16 had a bug that added unnecessary * padding to the body of the file when using this format. GNU tar * 1.17 corrected this bug without bumping the version number, so * it's not possible to support both variants. This code supports * the later variant at the expense of not supporting the former. * * This variant also replaced GNU.sparse.size with GNU.sparse.realsize * and introduced the GNU.sparse.major/GNU.sparse.minor attributes. */ /* * Read the next line from the input, and parse it as a decimal * integer followed by '\n'. Returns positive integer value or * negative on error. */ static int64_t gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, int64_t *remaining, size_t *unconsumed) { int64_t l, limit, last_digit_limit; const char *p; ssize_t bytes_read; int base, digit; base = 10; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; /* * Skip any lines starting with '#'; GNU tar specs * don't require this, but they should. */ do { bytes_read = readline(a, tar, &p, (ssize_t)tar_min(*remaining, 100), unconsumed); if (bytes_read <= 0) return (ARCHIVE_FATAL); *remaining -= bytes_read; } while (p[0] == '#'); l = 0; while (bytes_read > 0) { if (*p == '\n') return (l); if (*p < '0' || *p >= '0' + base) return (ARCHIVE_WARN); digit = *p - '0'; if (l > limit || (l == limit && digit > last_digit_limit)) l = INT64_MAX; /* Truncate on overflow. */ else l = (l * base) + digit; p++; bytes_read--; } /* TODO: Error message. */ return (ARCHIVE_WARN); } /* * Returns length (in bytes) of the sparse data description * that was read. */ static ssize_t gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed) { ssize_t bytes_read; int entries; int64_t offset, size, to_skip, remaining; /* Clear out the existing sparse list. */ gnu_clear_sparse_list(tar); remaining = tar->entry_bytes_remaining; /* Parse entries. */ entries = (int)gnu_sparse_10_atol(a, tar, &remaining, unconsumed); if (entries < 0) return (ARCHIVE_FATAL); /* Parse the individual entries. */ while (entries-- > 0) { /* Parse offset/size */ offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed); if (offset < 0) return (ARCHIVE_FATAL); size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed); if (size < 0) return (ARCHIVE_FATAL); /* Add a new sparse entry. */ if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Skip rest of block... */ tar_flush_unconsumed(a, unconsumed); bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining); to_skip = 0x1ff & -bytes_read; + /* Fail if tar->entry_bytes_remaing would get negative */ + if (to_skip > remaining) + return (ARCHIVE_FATAL); if (to_skip != __archive_read_consume(a, to_skip)) return (ARCHIVE_FATAL); return ((ssize_t)(bytes_read + to_skip)); } /* * Solaris pax extension for a sparse file. This is recorded with the * data and hole pairs. The way recording sparse information by Solaris' * pax simply indicates where data and sparse are, so the stored contents * consist of both data and hole. */ static int solaris_sparse_parse(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const char *p) { const char *e; int64_t start, end; int hole = 1; (void)entry; /* UNUSED */ end = 0; if (*p == ' ') p++; else return (ARCHIVE_WARN); for (;;) { e = p; while (*e != '\0' && *e != ' ') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; } start = end; end = tar_atol10(p, e - p); if (end < 0) return (ARCHIVE_WARN); if (start < end) { if (gnu_add_sparse_entry(a, tar, start, end - start) != ARCHIVE_OK) return (ARCHIVE_FATAL); tar->sparse_last->hole = hole; } if (*e == '\0') return (ARCHIVE_OK); p = e + 1; hole = hole == 0; } } /*- * Convert text->integer. * * Traditional tar formats (including POSIX) specify base-8 for * all of the standard numeric fields. This is a significant limitation * in practice: * = file size is limited to 8GB * = rdevmajor and rdevminor are limited to 21 bits * = uid/gid are limited to 21 bits * * There are two workarounds for this: * = pax extended headers, which use variable-length string fields * = GNU tar and STAR both allow either base-8 or base-256 in * most fields. The high bit is set to indicate base-256. * * On read, this implementation supports both extensions. */ static int64_t tar_atol(const char *p, size_t char_cnt) { /* * Technically, GNU tar considers a field to be in base-256 * only if the first byte is 0xff or 0x80. */ if (*p & 0x80) return (tar_atol256(p, char_cnt)); return (tar_atol8(p, char_cnt)); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t tar_atol_base_n(const char *p, size_t char_cnt, int base) { int64_t l, maxval, limit, last_digit_limit; int digit, sign; maxval = INT64_MAX; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; /* the pointer will not be dereferenced if char_cnt is zero * due to the way the && operator is evaluated. */ while (char_cnt != 0 && (*p == ' ' || *p == '\t')) { p++; char_cnt--; } sign = 1; if (char_cnt != 0 && *p == '-') { sign = -1; p++; char_cnt--; maxval = INT64_MIN; limit = -(INT64_MIN / base); last_digit_limit = INT64_MIN % base; } l = 0; if (char_cnt != 0) { digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt != 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { return maxval; /* Truncate on overflow. */ } l = (l * base) + digit; digit = *++p - '0'; char_cnt--; } } return (sign < 0) ? -l : l; } static int64_t tar_atol8(const char *p, size_t char_cnt) { return tar_atol_base_n(p, char_cnt, 8); } static int64_t tar_atol10(const char *p, size_t char_cnt) { return tar_atol_base_n(p, char_cnt, 10); } /* * Parse a base-256 integer. This is just a variable-length * twos-complement signed binary value in big-endian order, except * that the high-order bit is ignored. The values here can be up to * 12 bytes, so we need to be careful about overflowing 64-bit * (8-byte) integers. * * This code unashamedly assumes that the local machine uses 8-bit * bytes and twos-complement arithmetic. */ static int64_t tar_atol256(const char *_p, size_t char_cnt) { uint64_t l; const unsigned char *p = (const unsigned char *)_p; unsigned char c, neg; /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */ c = *p; if (c & 0x40) { neg = 0xff; c |= 0x80; l = ~ARCHIVE_LITERAL_ULL(0); } else { neg = 0; c &= 0x7f; l = 0; } /* If more than 8 bytes, check that we can ignore * high-order bits without overflow. */ while (char_cnt > sizeof(int64_t)) { --char_cnt; if (c != neg) return neg ? INT64_MIN : INT64_MAX; c = *++p; } /* c is first byte that fits; if sign mismatch, return overflow */ if ((c ^ neg) & 0x80) { return neg ? INT64_MIN : INT64_MAX; } /* Accumulate remaining bytes. */ while (--char_cnt > 0) { l = (l << 8) | c; c = *++p; } l = (l << 8) | c; /* Return signed twos-complement value. */ return (int64_t)(l); } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. This avoids copying * when possible. */ static ssize_t readline(struct archive_read *a, struct tar *tar, const char **start, ssize_t limit, size_t *unconsumed) { ssize_t bytes_read; ssize_t total_size = 0; const void *t; const char *s; void *p; tar_flush_unconsumed(a, unconsumed); t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n' in the read buffer, return pointer to that. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; if (bytes_read > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } *unconsumed = bytes_read; *start = s; return (bytes_read); } *unconsumed = bytes_read; /* Otherwise, we need to accumulate in a line buffer. */ for (;;) { if (total_size + bytes_read > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } memcpy(tar->line.s + total_size, t, bytes_read); tar_flush_unconsumed(a, unconsumed); total_size += bytes_read; /* If we found '\n', clean up and return. */ if (p != NULL) { *start = tar->line.s; return (total_size); } /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ p = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; } *unconsumed = bytes_read; } } /* * base64_decode - Base64 decode * * This accepts most variations of base-64 encoding, including: * * with or without line breaks * * with or without the final group padded with '=' or '_' characters * (The most economical Base-64 variant does not pad the last group and * omits line breaks; RFC1341 used for MIME requires both.) */ static char * base64_decode(const char *s, size_t len, size_t *out_len) { static const unsigned char digits[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' }; static unsigned char decode_table[128]; char *out, *d; const unsigned char *src = (const unsigned char *)s; /* If the decode table is not yet initialized, prepare it. */ if (decode_table[digits[1]] != 1) { unsigned i; memset(decode_table, 0xff, sizeof(decode_table)); for (i = 0; i < sizeof(digits); i++) decode_table[digits[i]] = i; } /* Allocate enough space to hold the entire output. */ /* Note that we may not use all of this... */ out = (char *)malloc(len - len / 4 + 1); if (out == NULL) { *out_len = 0; return (NULL); } d = out; while (len > 0) { /* Collect the next group of (up to) four characters. */ int v = 0; int group_size = 0; while (group_size < 4 && len > 0) { /* '=' or '_' padding indicates final group. */ if (*src == '=' || *src == '_') { len = 0; break; } /* Skip illegal characters (including line breaks) */ if (*src > 127 || *src < 32 || decode_table[*src] == 0xff) { len--; src++; continue; } v <<= 6; v |= decode_table[*src++]; len --; group_size++; } /* Align a short group properly. */ v <<= 6 * (4 - group_size); /* Unpack the group we just collected. */ switch (group_size) { case 4: d[2] = v & 0xff; /* FALLTHROUGH */ case 3: d[1] = (v >> 8) & 0xff; /* FALLTHROUGH */ case 2: d[0] = (v >> 16) & 0xff; break; case 1: /* this is invalid! */ break; } d += group_size * 3 / 4; } *out_len = d - out; return (out); } static char * url_decode(const char *in) { char *out, *d; const char *s; out = (char *)malloc(strlen(in) + 1); if (out == NULL) return (NULL); for (s = in, d = out; *s != '\0'; ) { if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') { /* Try to convert % escape */ int digit1 = tohex(s[1]); int digit2 = tohex(s[2]); if (digit1 >= 0 && digit2 >= 0) { /* Looks good, consume three chars */ s += 3; /* Convert output */ *d++ = ((digit1 << 4) | digit2); continue; } /* Else fall through and treat '%' as normal char */ } *d++ = *s++; } *d = '\0'; return (out); } static int tohex(int c) { if (c >= '0' && c <= '9') return (c - '0'); else if (c >= 'A' && c <= 'F') return (c - 'A' + 10); else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); else return (-1); } Index: vendor/libarchive/dist/libarchive/archive_read_support_format_warc.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_warc.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_warc.c (revision 314565) @@ -1,822 +1,824 @@ /*- * Copyright (c) 2014 Sebastian Freundt * 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$"); /** * WARC is standardised by ISO TC46/SC4/WG12 and currently available as * ISO 28500:2009. * For the purposes of this file we used the final draft from: * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf * * Todo: * [ ] real-world warcs can contain resources at endpoints ending in / * e.g. http://bibnum.bnf.fr/warc/ * if you're lucky their response contains a Content-Location: header * pointing to a unix-compliant filename, in the example above it's * Content-Location: http://bibnum.bnf.fr/warc/index.html * however, that's not mandated and github for example doesn't follow * this convention. * We need a set of archive options to control what to do with * entries like these, at the moment care is taken to skip them. * **/ #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" typedef enum { WT_NONE, /* warcinfo */ WT_INFO, /* metadata */ WT_META, /* resource */ WT_RSRC, /* request, unsupported */ WT_REQ, /* response, unsupported */ WT_RSP, /* revisit, unsupported */ WT_RVIS, /* conversion, unsupported */ WT_CONV, /* continuation, unsupported at the moment */ WT_CONT, /* invalid type */ LAST_WT } warc_type_t; typedef struct { size_t len; const char *str; } warc_string_t; typedef struct { size_t len; char *str; } warc_strbuf_t; struct warc_s { /* content length ahead */ size_t cntlen; /* and how much we've processed so far */ size_t cntoff; /* and how much we need to consume between calls */ size_t unconsumed; /* string pool */ warc_strbuf_t pool; /* previous version */ unsigned int pver; /* stringified format name */ struct archive_string sver; }; static int _warc_bid(struct archive_read *a, int); static int _warc_cleanup(struct archive_read *a); static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*); static int _warc_skip(struct archive_read *a); static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e); /* private routines */ static unsigned int _warc_rdver(const char buf[10], size_t bsz); static unsigned int _warc_rdtyp(const char *buf, size_t bsz); static warc_string_t _warc_rduri(const char *buf, size_t bsz); static ssize_t _warc_rdlen(const char *buf, size_t bsz); static time_t _warc_rdrtm(const char *buf, size_t bsz); static time_t _warc_rdmtm(const char *buf, size_t bsz); static const char *_warc_find_eoh(const char *buf, size_t bsz); static const char *_warc_find_eol(const char *buf, size_t bsz); int archive_read_support_format_warc(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct warc_s *w; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_warc"); if ((w = calloc(1, sizeof(*w))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } r = __archive_read_register_format( a, w, "warc", _warc_bid, NULL, _warc_rdhdr, _warc_read, _warc_skip, NULL, _warc_cleanup, NULL, NULL); if (r != ARCHIVE_OK) { free(w); return (r); } return (ARCHIVE_OK); } static int _warc_cleanup(struct archive_read *a) { struct warc_s *w = a->format->data; if (w->pool.len > 0U) { free(w->pool.str); } archive_string_free(&w->sver); free(w); a->format->data = NULL; return (ARCHIVE_OK); } static int _warc_bid(struct archive_read *a, int best_bid) { const char *hdr; ssize_t nrd; unsigned int ver; (void)best_bid; /* UNUSED */ /* check first line of file, it should be a record already */ if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) { /* no idea what to do */ return -1; } else if (nrd < 12) { /* nah, not for us, our magic cookie is at least 12 bytes */ return -1; } /* otherwise snarf the record's version number */ ver = _warc_rdver(hdr, nrd); if (ver < 1200U || ver > 10000U) { /* we only support WARC 0.12 to 1.0 */ return -1; } /* otherwise be confident */ return (64); } static int _warc_rdhdr(struct archive_read *a, struct archive_entry *entry) { #define HDR_PROBE_LEN (12U) struct warc_s *w = a->format->data; unsigned int ver; const char *buf; ssize_t nrd; const char *eoh; /* for the file name, saves some strndup()'ing */ warc_string_t fnam; /* warc record type, not that we really use it a lot */ warc_type_t ftyp; /* content-length+error monad */ ssize_t cntlen; /* record time is the WARC-Date time we reinterpret it as ctime */ time_t rtime; /* mtime is the Last-Modified time which will be the entry's mtime */ time_t mtime; start_over: /* just use read_ahead() they keep track of unconsumed * bits and bobs for us; no need to put an extra shift in * and reproduce that functionality here */ buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd); if (nrd < 0) { /* no good */ archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); return (ARCHIVE_FATAL); } else if (buf == NULL) { /* there should be room for at least WARC/bla\r\n * must be EOF therefore */ return (ARCHIVE_EOF); } /* looks good so far, try and find the end of the header now */ eoh = _warc_find_eoh(buf, nrd); if (eoh == NULL) { /* still no good, the header end might be beyond the * probe we've requested, but then again who'd cram * so much stuff into the header *and* be 28500-compliant */ archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); return (ARCHIVE_FATAL); } ver = _warc_rdver(buf, eoh - buf); /* we currently support WARC 0.12 to 1.0 */ if (ver == 0U) { archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Invalid record version"); return (ARCHIVE_FATAL); } else if (ver < 1200U || ver > 10000U) { archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Unsupported record version: %u.%u", ver / 10000, (ver % 10000) / 100); return (ARCHIVE_FATAL); } cntlen = _warc_rdlen(buf, eoh - buf); if (cntlen < 0) { /* nightmare! the specs say content-length is mandatory * so I don't feel overly bad stopping the reader here */ archive_set_error( &a->archive, EINVAL, "Bad content length"); return (ARCHIVE_FATAL); } rtime = _warc_rdrtm(buf, eoh - buf); if (rtime == (time_t)-1) { /* record time is mandatory as per WARC/1.0, * so just barf here, fast and loud */ archive_set_error( &a->archive, EINVAL, "Bad record time"); return (ARCHIVE_FATAL); } /* let the world know we're a WARC archive */ a->archive.archive_format = ARCHIVE_FORMAT_WARC; if (ver != w->pver) { /* stringify this entry's version */ archive_string_sprintf(&w->sver, "WARC/%u.%u", ver / 10000, (ver % 10000) / 100); /* remember the version */ w->pver = ver; } /* start off with the type */ ftyp = _warc_rdtyp(buf, eoh - buf); /* and let future calls know about the content */ w->cntlen = cntlen; w->cntoff = 0U; mtime = 0;/* Avoid compiling error on some platform. */ switch (ftyp) { case WT_RSRC: case WT_RSP: /* only try and read the filename in the cases that are * guaranteed to have one */ fnam = _warc_rduri(buf, eoh - buf); /* check the last character in the URI to avoid creating * directory endpoints as files, see Todo above */ if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') { /* break here for now */ fnam.len = 0U; fnam.str = NULL; break; } /* bang to our string pool, so we save a * malloc()+free() roundtrip */ if (fnam.len + 1U > w->pool.len) { w->pool.len = ((fnam.len + 64U) / 64U) * 64U; w->pool.str = realloc(w->pool.str, w->pool.len); } memcpy(w->pool.str, fnam.str, fnam.len); w->pool.str[fnam.len] = '\0'; /* let no one else know about the pool, it's a secret, shhh */ fnam.str = w->pool.str; /* snarf mtime or deduce from rtime * this is a custom header added by our writer, it's quite * hard to believe anyone else would go through with it * (apart from being part of some http responses of course) */ if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) { mtime = rtime; } break; default: fnam.len = 0U; fnam.str = NULL; break; } /* now eat some of those delicious buffer bits */ __archive_read_consume(a, eoh - buf); switch (ftyp) { case WT_RSRC: case WT_RSP: if (fnam.len > 0U) { /* populate entry object */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_copy_pathname(entry, fnam.str); archive_entry_set_size(entry, cntlen); archive_entry_set_perm(entry, 0644); /* rtime is the new ctime, mtime stays mtime */ archive_entry_set_ctime(entry, rtime, 0L); archive_entry_set_mtime(entry, mtime, 0L); break; } /* FALLTHROUGH */ default: /* consume the content and start over */ _warc_skip(a); goto start_over; } return (ARCHIVE_OK); } static int _warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off) { struct warc_s *w = a->format->data; const char *rab; ssize_t nrd; if (w->cntoff >= w->cntlen) { eof: /* it's our lucky day, no work, we can leave early */ *buf = NULL; *bsz = 0U; *off = w->cntoff + 4U/*for \r\n\r\n separator*/; w->unconsumed = 0U; return (ARCHIVE_EOF); } rab = __archive_read_ahead(a, 1U, &nrd); if (nrd < 0) { *bsz = 0U; /* big catastrophe */ return (int)nrd; } else if (nrd == 0) { goto eof; } else if ((size_t)nrd > w->cntlen - w->cntoff) { /* clamp to content-length */ nrd = w->cntlen - w->cntoff; } *off = w->cntoff; *bsz = nrd; *buf = rab; w->cntoff += nrd; w->unconsumed = (size_t)nrd; return (ARCHIVE_OK); } static int _warc_skip(struct archive_read *a) { struct warc_s *w = a->format->data; __archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/); w->cntlen = 0U; w->cntoff = 0U; return (ARCHIVE_OK); } /* private routines */ static void* deconst(const void *c) { return (char *)0x1 + (((const char *)c) - (const char *)0x1); } static char* xmemmem(const char *hay, const size_t haysize, const char *needle, const size_t needlesize) { const char *const eoh = hay + haysize; const char *const eon = needle + needlesize; const char *hp; const char *np; const char *cand; unsigned int hsum; unsigned int nsum; unsigned int eqp; /* trivial checks first * a 0-sized needle is defined to be found anywhere in haystack * then run strchr() to find a candidate in HAYSTACK (i.e. a portion * that happens to begin with *NEEDLE) */ if (needlesize == 0UL) { return deconst(hay); } else if ((hay = memchr(hay, *needle, haysize)) == NULL) { /* trivial */ return NULL; } /* First characters of haystack and needle are the same now. Both are * guaranteed to be at least one character long. Now computes the sum * of characters values of needle together with the sum of the first * needle_len characters of haystack. */ for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U; hp < eoh && np < eon; hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++); /* HP now references the (NEEDLESIZE + 1)-th character. */ if (np < eon) { /* haystack is smaller than needle, :O */ return NULL; } else if (eqp) { /* found a match */ return deconst(hay); } /* now loop through the rest of haystack, * updating the sum iteratively */ for (cand = hay; hp < eoh; hp++) { hsum ^= *cand++; hsum ^= *hp; /* Since the sum of the characters is already known to be * equal at that point, it is enough to check just NEEDLESIZE - 1 * characters for equality, * also CAND is by design < HP, so no need for range checks */ if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) { return deconst(cand); } } return NULL; } static int strtoi_lim(const char *str, const char **ep, int llim, int ulim) { int res = 0; const char *sp; /* we keep track of the number of digits via rulim */ int rulim; for (sp = str, rulim = ulim > 10 ? ulim : 10; res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9'; sp++, rulim /= 10) { res *= 10; res += *sp - '0'; } if (sp == str) { res = -1; } else if (res < llim || res > ulim) { res = -2; } *ep = (const char*)sp; return res; } static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); #elif HAVE__MKGMTIME64 return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ if (mktime(t) == (time_t)-1) return ((time_t)-1); /* Then we can compute timegm() from first principles. */ return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000 + ((t->tm_year - 69) / 4) * 86400 - ((t->tm_year - 1) / 100) * 86400 + ((t->tm_year + 299) / 400) * 86400); #endif } static time_t xstrpisotime(const char *s, char **endptr) { /** like strptime() but strictly for ISO 8601 Zulu strings */ struct tm tm; time_t res = (time_t)-1; /* make sure tm is clean */ memset(&tm, 0, sizeof(tm)); /* as a courtesy to our callers, and since this is a non-standard * routine, we skip leading whitespace */ - while (isblank((unsigned char)*s)) + while (*s == ' ' || *s == '\t') ++s; /* read year */ if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') { goto out; } /* read month */ if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') { goto out; } /* read day-of-month */ if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') { goto out; } /* read hour */ if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') { goto out; } /* read minute */ if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') { goto out; } /* read second */ if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') { goto out; } /* massage TM to fulfill some of POSIX' constraints */ tm.tm_year -= 1900; tm.tm_mon--; /* now convert our custom tm struct to a unix stamp using UTC */ res = time_from_tm(&tm); out: if (endptr != NULL) { *endptr = deconst(s); } return res; } static unsigned int _warc_rdver(const char *buf, size_t bsz) { static const char magic[] = "WARC/"; + const char *c; unsigned int ver = 0U; unsigned int end = 0U; if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) { /* buffer too small or invalid magic */ return ver; } /* looks good so far, read the version number for a laugh */ buf += sizeof(magic) - 1U; if (isdigit(buf[0U]) && (buf[1U] == '.') && isdigit(buf[2U])) { /* we support a maximum of 2 digits in the minor version */ if (isdigit(buf[3U])) end = 1U; /* set up major version */ ver = (buf[0U] - '0') * 10000U; /* set up minor version */ if (end == 1U) { ver += (buf[2U] - '0') * 1000U; ver += (buf[3U] - '0') * 100U; } else ver += (buf[2U] - '0') * 100U; /* * WARC below version 0.12 has a space-separated header * WARC 0.12 and above terminates the version with a CRLF */ + c = buf + 3U + end; if (ver >= 1200U) { - if (memcmp(buf + 3U + end, "\r\n", 2U) != 0) + if (memcmp(c, "\r\n", 2U) != 0) ver = 0U; } else if (ver < 1200U) { - if (!isblank(*(buf + 3U + end))) + if (*c != ' ' && *c != '\t') ver = 0U; } } return ver; } static unsigned int _warc_rdtyp(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Type:"; const char *val, *eol; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return WT_NONE; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { /* no end of line */ return WT_NONE; } /* overread whitespace */ - while (val < eol && isblank((unsigned char)*val)) + while (val < eol && (*val == ' ' || *val == '\t')) ++val; if (val + 8U == eol) { if (memcmp(val, "resource", 8U) == 0) return WT_RSRC; else if (memcmp(val, "response", 8U) == 0) return WT_RSP; } return WT_NONE; } static warc_string_t _warc_rduri(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Target-URI:"; const char *val, *uri, *eol, *p; warc_string_t res = {0U, NULL}; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return res; } /* overread whitespace */ val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { /* no end of line */ return res; } - while (val < eol && isblank((unsigned char)*val)) + while (val < eol && (*val == ' ' || *val == '\t')) ++val; /* overread URL designators */ if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) { /* not touching that! */ return res; } /* spaces inside uri are not allowed, CRLF should follow */ for (p = val; p < eol; p++) { if (isspace(*p)) return res; } /* there must be at least space for ftp */ if (uri < (val + 3U)) return res; /* move uri to point to after :// */ uri += 3U; /* now then, inspect the URI */ if (memcmp(val, "file", 4U) == 0) { /* perfect, nothing left to do here */ } else if (memcmp(val, "http", 4U) == 0 || memcmp(val, "ftp", 3U) == 0) { /* overread domain, and the first / */ while (uri < eol && *uri++ != '/'); } else { /* not sure what to do? best to bugger off */ return res; } res.str = uri; res.len = eol - uri; return res; } static ssize_t _warc_rdlen(const char *buf, size_t bsz) { static const char _key[] = "\r\nContent-Length:"; const char *val, *eol; char *on = NULL; long int len; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return -1; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { /* no end of line */ return -1; } /* skip leading whitespace */ - while (val < eol && isblank(*val)) + while (val < eol && (*val == ' ' || *val == '\t')) val++; /* there must be at least one digit */ if (!isdigit(*val)) return -1; len = strtol(val, &on, 10); if (on != eol) { /* line must end here */ return -1; } return (size_t)len; } static time_t _warc_rdrtm(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Date:"; const char *val, *eol; char *on = NULL; time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return (time_t)-1; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) { /* no end of line */ return -1; } /* xstrpisotime() kindly overreads whitespace for us, so use that */ res = xstrpisotime(val, &on); if (on != eol) { /* line must end here */ return -1; } return res; } static time_t _warc_rdmtm(const char *buf, size_t bsz) { static const char _key[] = "\r\nLast-Modified:"; const char *val, *eol; char *on = NULL; time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return (time_t)-1; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) { /* no end of line */ return -1; } /* xstrpisotime() kindly overreads whitespace for us, so use that */ res = xstrpisotime(val, &on); if (on != eol) { /* line must end here */ return -1; } return res; } static const char* _warc_find_eoh(const char *buf, size_t bsz) { static const char _marker[] = "\r\n\r\n"; const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U); if (hit != NULL) { hit += sizeof(_marker) - 1U; } return hit; } static const char* _warc_find_eol(const char *buf, size_t bsz) { static const char _marker[] = "\r\n"; const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U); return hit; } /* archive_read_support_format_warc.c ends here */ Index: vendor/libarchive/dist/libarchive/archive_write_disk_posix.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_disk_posix.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_write_disk_posix.c (revision 314565) @@ -1,4196 +1,4243 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * Copyright (c) 2012 Michihiro NAKAJIMA * 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$"); #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #if defined(HAVE_SYS_XATTR_H) #include #elif defined(HAVE_ATTR_XATTR_H) #include #endif #ifdef HAVE_SYS_EA_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_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LANGINFO_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 #ifdef F_GETTIMES /* Tru64 specific */ #include #endif /* * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared. * * It assumes that the input is an integer type of no more than 64 bits. * If the number is less than zero, t must be a signed type, so it fits in * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t * without loss. But it could be a large unsigned value, so we have to clip it * to INT64_MAX.* */ #define to_int64_time(t) \ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t)) #if __APPLE__ #include #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H #include #define HAVE_QUARANTINE 1 #endif #endif #ifdef HAVE_ZLIB_H #include #endif /* TODO: Support Mac OS 'quarantine' feature. This is really just a * standard tag to mark files that have been downloaded as "tainted". * On Mac OS, we should mark the extracted files as tainted if the * archive being read was tainted. Windows has a similar feature; we * should investigate ways to support this generically. */ #include "archive.h" #include "archive_acl_private.h" #include "archive_string.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* Ignore non-int O_NOFOLLOW constant. */ /* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */ #if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX) #undef O_NOFOLLOW #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; int64_t ctime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long ctime_nanos; unsigned long fflags_set; size_t mac_metadata_size; void *mac_metadata; 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_APPLEDOUBLE 0x01000000 #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 #define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA #define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; int64_t user_uid; int skip_file_set; int64_t skip_file_dev; int64_t skip_file_ino; time_t start_time; int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid); 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. */ int64_t offset; /* Last offset actually written to disk. */ int64_t fd_offset; /* Total bytes actually written to files. */ int64_t total_bytes_written; /* Maximum size of file, -1 if unknown. */ int64_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. */ int64_t uid; int64_t gid; /* * HFS+ Compression. */ /* Xattr "com.apple.decmpfs". */ uint32_t decmpfs_attr_size; unsigned char *decmpfs_header_p; /* ResourceFork set options used for fsetxattr. */ int rsrc_xattr_options; /* Xattr "com.apple.ResourceFork". */ unsigned char *resource_fork; size_t resource_fork_allocated_size; unsigned int decmpfs_block_count; uint32_t *decmpfs_block_info; /* Buffer for compressed data. */ unsigned char *compressed_buffer; size_t compressed_buffer_size; size_t compressed_buffer_remaining; /* The offset of the ResourceFork where compressed data will * be placed. */ uint32_t compressed_rsrc_position; uint32_t compressed_rsrc_position_v; /* Buffer for uncompressed data. */ char *uncompressed_buffer; size_t block_remaining_bytes; size_t file_remaining_bytes; #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; int decmpfs_compression_level; #endif }; /* * Default mode for dirs created automatically (will be modified by umask). * Note that POSIX specifies 0777 for implicitly-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 /* * Maximum uncompressed size of a decmpfs block. */ #define MAX_DECMPFS_BLOCK_SIZE (64 * 1024) /* * HFS+ compression type. */ #define CMP_XATTR 3/* Compressed data in xattr. */ #define CMP_RESOURCE_FORK 4/* Compressed data in resource fork. */ /* * HFS+ compression resource fork. */ #define RSRC_H_SIZE 260 /* Base size of Resource fork header. */ #define RSRC_F_SIZE 50 /* Size of Resource fork footer. */ /* Size to write compressed data to resource fork. */ #define COMPRESSED_W_SIZE (64 * 1024) /* decmpfs definitions. */ #define MAX_DECMPFS_XATTR_SIZE 3802 #ifndef DECMPFS_XATTR_NAME #define DECMPFS_XATTR_NAME "com.apple.decmpfs" #endif #define DECMPFS_MAGIC 0x636d7066 #define DECMPFS_COMPRESSION_MAGIC 0 #define DECMPFS_COMPRESSION_TYPE 4 #define DECMPFS_UNCOMPRESSED_SIZE 8 #define DECMPFS_HEADER_SIZE 16 #define HFS_BLOCKS(s) ((s) >> 12) static void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, int); 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); #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname_fsobj(char *, int *, struct archive_string *, int); 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 ssize_t hfs_write_data_block(struct archive_write_disk *, const char *, size_t); static int fixup_appledouble(struct archive_write_disk *, const char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); static int set_mac_metadata(struct archive_write_disk *, const char *, const void *, size_t); static int set_xattrs(struct archive_write_disk *); static int clear_nochange_fflags(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 *, int, int, const char *, time_t, long, time_t, long, time_t, long, time_t, long); static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); 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_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); static int _archive_write_disk_header(struct archive *, struct archive_entry *); static int64_t _archive_write_disk_filter_bytes(struct archive *, int); static int _archive_write_disk_finish_entry(struct archive *); static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); static int 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 occurred. 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_disk_close; av.archive_filter_bytes = _archive_write_disk_filter_bytes; av.archive_free = _archive_write_disk_free; av.archive_write_header = _archive_write_disk_header; av.archive_write_finish_entry = _archive_write_disk_finish_entry; av.archive_write_data = _archive_write_disk_data; av.archive_write_data_block = _archive_write_disk_data_block; inited = 1; } return (&av); } static int64_t _archive_write_disk_filter_bytes(struct archive *_a, int n) { struct archive_write_disk *a = (struct archive_write_disk *)_a; (void)n; /* UNUSED */ if (n == -1 || n == 0) return (a->total_bytes_written); return (-1); } 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_disk_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_disk_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->restore_pwd = -1; 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); /* * Query the umask 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. */ umask(a->user_umask = umask(0)); /* 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; } if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) { if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_ACLS; else a->todo |= TODO_ACLS; } if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) { if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_MAC_METADATA; else a->todo |= TODO_MAC_METADATA; } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) { unsigned long set, clear; archive_entry_fflags(a->entry, &set, &clear); if ((set & ~clear) & UF_COMPRESSED) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } } if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 && (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } { const char *p; /* Check if the current file name is a type of the * resource fork file. */ p = strrchr(a->name, '/'); if (p == NULL) p = a->name; else p++; if (p[0] == '.' && p[1] == '_') { /* Do not compress "._XXX" files. */ a->todo &= ~TODO_HFS_COMPRESSION; if (a->filesize > 0) a->todo |= TODO_APPLEDOUBLE; } } #endif 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) return (ret); } #if defined(HAVE_FCHDIR) && defined(PATH_MAX) /* If path exceeds PATH_MAX, shorten the path. */ edit_deep_directories(a); #endif ret = restore_entry(a); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) /* * Check if the filesystem the file is restoring on supports * HFS+ Compression. If not, cancel HFS+ Compression. */ if (a->todo | TODO_HFS_COMPRESSION) { /* * NOTE: UF_COMPRESSED is ignored even if the filesystem * supports HFS+ Compression because the file should * have at least an extended attribute "com.apple.decmpfs" * before the flag is set to indicate that the file have * been compressed. If the filesystem does not support * HFS+ Compression the system call will fail. */ if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0) a->todo &= ~TODO_HFS_COMPRESSION; } #endif /* * TODO: There are rumours that some extended attributes must * be restored before file data is written. If this is true, * then we either need to write all extended attributes both * before and after restoring the data, or find some rule for * determining which must go first and which last. Due to the * many ways people are using xattrs, this may prove to be an * intractable problem. */ #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { archive_set_error(&a->archive, errno, "chdir() failure"); ret = ARCHIVE_FATAL; } 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)); if (fe == NULL) return (ARCHIVE_FATAL); 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)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mode = a->mode; 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_ACLS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_ACLS; archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } if (a->deferred & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { memcpy(fe->mac_metadata, metadata, metadata_size); fe->mac_metadata_size = metadata_size; fe->fixup |= TODO_MAC_METADATA; } } } if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); 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; } return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, int64_t d, int64_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_set = 1; 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 = 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 && (int64_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; int64_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; } 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->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) /* * Set UF_COMPRESSED file flag. * This have to be called after hfs_write_decmpfs() because if the * file does not have "com.apple.decmpfs" xattr the flag is ignored. */ static int hfs_set_compressed_fflag(struct archive_write_disk *a) { int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags |= UF_COMPRESSED; if (fchflags(a->fd, a->st.st_flags) != 0) { archive_set_error(&a->archive, errno, "Failed to set UF_COMPRESSED file flag"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression decmpfs * * +------------------------------+ +0 * | Magic(LE 4 bytes) | * +------------------------------+ * | Type(LE 4 bytes) | * +------------------------------+ * | Uncompressed size(LE 8 bytes)| * +------------------------------+ +16 * | | * | Compressed data | * | (Placed only if Type == 3) | * | | * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE * * Type is 3: decmpfs has compressed data. * Type is 4: Resource Fork has compressed data. */ /* * Write "com.apple.decmpfs" */ static int hfs_write_decmpfs(struct archive_write_disk *a) { int r; uint32_t compression_type; r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p, a->decmpfs_attr_size, 0, 0); if (r < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr:%s", DECMPFS_XATTR_NAME); compression_type = archive_le32dec( &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]); if (compression_type == CMP_RESOURCE_FORK) fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, XATTR_SHOWCOMPRESSION); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression Resource Fork * * +-----------------------------+ * | Header(260 bytes) | * +-----------------------------+ * | Block count(LE 4 bytes) | * +-----------------------------+ --+ * +-- | Offset (LE 4 bytes) | | * | | [distance from Block count] | | Block 0 * | +-----------------------------+ | * | | Compressed size(LE 4 bytes) | | * | +-----------------------------+ --+ * | | | * | | .................. | * | | | * | +-----------------------------+ --+ * | | Offset (LE 4 bytes) | | * | +-----------------------------+ | Block (Block count -1) * | | Compressed size(LE 4 bytes) | | * +-> +-----------------------------+ --+ * | Compressed data(n bytes) | Block 0 * +-----------------------------+ * | | * | .................. | * | | * +-----------------------------+ * | Compressed data(n bytes) | Block (Block count -1) * +-----------------------------+ * | Footer(50 bytes) | * +-----------------------------+ * */ /* * Write the header of "com.apple.ResourceFork" */ static int hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff, size_t bytes, uint32_t position) { int ret; ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes, position, a->rsrc_xattr_options); if (ret < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr: %s at %u pos %u bytes", XATTR_RESOURCEFORK_NAME, (unsigned)position, (unsigned)bytes); return (ARCHIVE_WARN); } a->rsrc_xattr_options &= ~XATTR_CREATE; return (ARCHIVE_OK); } static int hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed) { int ret; ret = hfs_write_resource_fork(a, a->compressed_buffer, bytes_compressed, a->compressed_rsrc_position); if (ret == ARCHIVE_OK) a->compressed_rsrc_position += bytes_compressed; return (ret); } static int hfs_write_resource_fork_header(struct archive_write_disk *a) { unsigned char *buff; uint32_t rsrc_bytes; uint32_t rsrc_header_bytes; /* * Write resource fork header + block info. */ buff = a->resource_fork; rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE; rsrc_header_bytes = RSRC_H_SIZE + /* Header base size. */ 4 + /* Block count. */ (a->decmpfs_block_count * 8);/* Block info */ archive_be32enc(buff, 0x100); archive_be32enc(buff + 4, rsrc_bytes); archive_be32enc(buff + 8, rsrc_bytes - 256); archive_be32enc(buff + 12, 0x32); memset(buff + 16, 0, 240); archive_be32enc(buff + 256, rsrc_bytes - 260); return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0); } static size_t hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size) { static const char rsrc_footer[RSRC_F_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm', 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (buff_size < sizeof(rsrc_footer)) return (0); memcpy(buff, rsrc_footer, sizeof(rsrc_footer)); return (sizeof(rsrc_footer)); } static int hfs_reset_compressor(struct archive_write_disk *a) { int ret; if (a->stream_valid) ret = deflateReset(&a->stream); else ret = deflateInit(&a->stream, a->decmpfs_compression_level); if (ret != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize compressor"); return (ARCHIVE_FATAL); } else a->stream_valid = 1; return (ARCHIVE_OK); } static int hfs_decompress(struct archive_write_disk *a) { uint32_t *block_info; unsigned int block_count; uint32_t data_pos, data_size; ssize_t r; ssize_t bytes_written, bytes_to_write; unsigned char *b; block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); block_count = archive_le32dec(block_info++); while (block_count--) { data_pos = RSRC_H_SIZE + archive_le32dec(block_info++); data_size = archive_le32dec(block_info++); r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME, a->compressed_buffer, data_size, data_pos, 0); if (r != data_size) { archive_set_error(&a->archive, (r < 0)?errno:ARCHIVE_ERRNO_MISC, "Failed to read resource fork"); return (ARCHIVE_WARN); } if (a->compressed_buffer[0] == 0xff) { bytes_to_write = data_size -1; b = a->compressed_buffer + 1; } else { uLong dest_len = MAX_DECMPFS_BLOCK_SIZE; int zr; zr = uncompress((Bytef *)a->uncompressed_buffer, &dest_len, a->compressed_buffer, data_size); if (zr != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to decompress resource fork"); return (ARCHIVE_WARN); } bytes_to_write = dest_len; b = (unsigned char *)a->uncompressed_buffer; } do { bytes_written = write(a->fd, b, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } bytes_to_write -= bytes_written; b += bytes_written; } while (bytes_to_write > 0); } r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0); if (r == -1) { archive_set_error(&a->archive, errno, "Failed to remove resource fork"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int hfs_drive_compressor(struct archive_write_disk *a, const char *buff, size_t size) { unsigned char *buffer_compressed; size_t bytes_compressed; size_t bytes_used; int ret; ret = hfs_reset_compressor(a); if (ret != ARCHIVE_OK) return (ret); if (a->compressed_buffer == NULL) { size_t block_size; block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE + + compressBound(MAX_DECMPFS_BLOCK_SIZE); a->compressed_buffer = malloc(block_size); if (a->compressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Resource Fork"); return (ARCHIVE_FATAL); } a->compressed_buffer_size = block_size; a->compressed_buffer_remaining = block_size; } buffer_compressed = a->compressed_buffer + a->compressed_buffer_size - a->compressed_buffer_remaining; a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; a->stream.avail_in = size; a->stream.next_out = buffer_compressed; a->stream.avail_out = a->compressed_buffer_remaining; do { ret = deflate(&a->stream, Z_FINISH); switch (ret) { case Z_OK: case Z_STREAM_END: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to compress data"); return (ARCHIVE_FAILED); } } while (ret == Z_OK); bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out; /* * If the compressed size is larger than the original size, * throw away compressed data, use uncompressed data instead. */ if (bytes_compressed > size) { buffer_compressed[0] = 0xFF;/* uncompressed marker. */ memcpy(buffer_compressed + 1, buff, size); bytes_compressed = size + 1; } a->compressed_buffer_remaining -= bytes_compressed; /* * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE * and the block count in the file is only one, store compressed * data to decmpfs xattr instead of the resource fork. */ if (a->decmpfs_block_count == 1 && (a->decmpfs_attr_size + bytes_compressed) <= MAX_DECMPFS_XATTR_SIZE) { archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_XATTR); memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE, buffer_compressed, bytes_compressed); a->decmpfs_attr_size += bytes_compressed; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); return (ret); } /* Update block info. */ archive_le32enc(a->decmpfs_block_info++, a->compressed_rsrc_position_v - RSRC_H_SIZE); archive_le32enc(a->decmpfs_block_info++, bytes_compressed); a->compressed_rsrc_position_v += bytes_compressed; /* * Write the compressed data to the resource fork. */ bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining; while (bytes_used >= COMPRESSED_W_SIZE) { ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE); if (ret != ARCHIVE_OK) return (ret); bytes_used -= COMPRESSED_W_SIZE; if (bytes_used > COMPRESSED_W_SIZE) memmove(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); else memcpy(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); } a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used; /* * If the current block is the last block, write the remaining * compressed data and the resource fork footer. */ if (a->file_remaining_bytes == 0) { size_t rsrc_size; int64_t bk; /* Append the resource footer. */ rsrc_size = hfs_set_resource_fork_footer( a->compressed_buffer + bytes_used, a->compressed_buffer_remaining); ret = hfs_write_compressed_data(a, bytes_used + rsrc_size); a->compressed_buffer_remaining = a->compressed_buffer_size; /* If the compressed size is not enough smaller than * the uncompressed size. cancel HFS+ compression. * TODO: study a behavior of ditto utility and improve * the condition to fall back into no HFS+ compression. */ bk = HFS_BLOCKS(a->compressed_rsrc_position); bk += bk >> 7; if (bk > HFS_BLOCKS(a->filesize)) return hfs_decompress(a); /* * Write the resourcefork header. */ if (ret == ARCHIVE_OK) ret = hfs_write_resource_fork_header(a); /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ if (ret == ARCHIVE_OK) ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); } return (ret); } static ssize_t hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, size_t size) { const char *buffer_to_write; size_t bytes_to_write; int ret; if (a->decmpfs_block_count == (unsigned)-1) { void *new_block; size_t new_size; unsigned int block_count; if (a->decmpfs_header_p == NULL) { new_block = malloc(MAX_DECMPFS_XATTR_SIZE + sizeof(uint32_t)); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->decmpfs_header_p = new_block; } a->decmpfs_attr_size = DECMPFS_HEADER_SIZE; archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC], DECMPFS_MAGIC); archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_RESOURCE_FORK); archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE], a->filesize); /* Calculate a block count of the file. */ block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) / MAX_DECMPFS_BLOCK_SIZE; /* * Allocate buffer for resource fork. * Set up related pointers; */ new_size = RSRC_H_SIZE + /* header */ 4 + /* Block count */ (block_count * sizeof(uint32_t) * 2) + RSRC_F_SIZE; /* footer */ if (new_size > a->resource_fork_allocated_size) { new_block = realloc(a->resource_fork, new_size); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for ResourceFork"); return (ARCHIVE_FATAL); } a->resource_fork_allocated_size = new_size; a->resource_fork = new_block; } /* Allocate uncompressed buffer */ if (a->uncompressed_buffer == NULL) { new_block = malloc(MAX_DECMPFS_BLOCK_SIZE); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->uncompressed_buffer = new_block; } a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; a->file_remaining_bytes = a->filesize; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Set up a resource fork. */ a->rsrc_xattr_options = XATTR_CREATE; /* Get the position where we are going to set a bunch * of block info. */ a->decmpfs_block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); /* Set the block count to the resource fork. */ archive_le32enc(a->decmpfs_block_info++, block_count); /* Get the position where we are going to set compressed * data. */ a->compressed_rsrc_position = RSRC_H_SIZE + 4 + (block_count * 8); a->compressed_rsrc_position_v = a->compressed_rsrc_position; a->decmpfs_block_count = block_count; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); /* Do not overrun a block size. */ if (size > a->block_remaining_bytes) bytes_to_write = a->block_remaining_bytes; else bytes_to_write = size; /* Do not overrun the file size. */ if (bytes_to_write > a->file_remaining_bytes) bytes_to_write = a->file_remaining_bytes; /* For efficiency, if a copy length is full of the uncompressed * buffer size, do not copy writing data to it. */ if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE) buffer_to_write = buff; else { memcpy(a->uncompressed_buffer + MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes, buff, bytes_to_write); buffer_to_write = a->uncompressed_buffer; } a->block_remaining_bytes -= bytes_to_write; a->file_remaining_bytes -= bytes_to_write; if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) { ret = hfs_drive_compressor(a, buffer_to_write, MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes); if (ret < 0) return (ret); a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); return (bytes_to_write); } static ssize_t hfs_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 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 this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { bytes_to_write = size; /* Seek if necessary to the specified offset. */ if (a->offset < a->fd_offset) { /* Can't support backward move. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek failed"); return (ARCHIVE_FATAL); } else if (a->offset > a->fd_offset) { int64_t skip = a->offset - a->fd_offset; char nullblock[1024]; memset(nullblock, 0, sizeof(nullblock)); while (skip > 0) { if (skip > (int64_t)sizeof(nullblock)) bytes_written = hfs_write_decmpfs_block( a, nullblock, sizeof(nullblock)); else bytes_written = hfs_write_decmpfs_block( a, nullblock, skip); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } skip -= bytes_written; } a->fd_offset = a->offset; } bytes_written = hfs_write_decmpfs_block(a, buff, bytes_to_write); if (bytes_written < 0) return (bytes_written); buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #else static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { return (write_data_block(a, buff, size)); } #endif static ssize_t _archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, int64_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_data_block"); a->offset = offset; if (a->todo & TODO_HFS_COMPRESSION) r = hfs_write_data_block(a, buff, size); else r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 return (ARCHIVE_OK); #else return (size); #endif } static ssize_t _archive_write_disk_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"); if (a->todo & TODO_HFS_COMPRESSION) return (hfs_write_data_block(a, buff, size)); return (write_data_block(a, buff, size)); } static int _archive_write_disk_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. */ #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) } else if (a->todo & TODO_HFS_COMPRESSION) { char null_d[1024]; ssize_t r; if (a->file_remaining_bytes) memset(null_d, 0, sizeof(null_d)); while (a->file_remaining_bytes) { if (a->file_remaining_bytes > sizeof(null_d)) r = hfs_write_data_block( a, null_d, sizeof(null_d)); else r = hfs_write_data_block( a, null_d, a->file_remaining_bytes); if (r < 0) return ((int)r); } #endif } 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 /* * Not all platforms implement the XSI option to * extend files via ftruncate. Stat() the file again * to see what happened. */ a->pst = NULL; if ((ret = lazy_stat(a)) != ARCHIVE_OK) return (ret); /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ if (a->st.st_size < a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->filesize - 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. */ /* * This is specific to Mac OS X. * If the current file is an AppleDouble file, it should be * linked with the data fork file and remove it. */ if (a->todo & TODO_APPLEDOUBLE) { int r2 = fixup_appledouble(a, a->name); if (r2 == ARCHIVE_EOF) { /* The current file has been successfully linked * with the data fork file and removed. So there * is nothing to do on the current file. */ goto finish_metadata; } if (r2 < ret) ret = r2; } /* * 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 = archive_write_disk_uid(&a->archive, 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 = archive_write_disk_gid(&a->archive, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * Restore ownership before set_mode tries 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) { int r2 = set_ownership(a); if (r2 < ret) ret = r2; } /* * set_mode must precede ACLs on systems such as Solaris and * FreeBSD where setting the mode implicitly clears extended ACLs */ if (a->todo & TODO_MODE) { int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } /* * Security-related extended attributes (such as * security.capability on Linux) have to be restored last, * since they're implicitly removed by other file changes. */ if (a->todo & TODO_XATTR) { int r2 = set_xattrs(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 must follow most other metadata; * otherwise atime will get changed. */ if (a->todo & TODO_TIMES) { int r2 = set_times_from_entry(a); if (r2 < ret) ret = r2; } /* * Mac extended metadata includes ACLs. */ if (a->todo & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { int r2 = set_mac_metadata(a, archive_entry_pathname( a->entry), metadata, metadata_size); if (r2 < ret) ret = r2; } } /* * ACLs must be restored after timestamps because there are * ACLs that prevent attribute changes (including time). */ if (a->todo & TODO_ACLS) { int r2; #ifdef HAVE_DARWIN_ACL /* * On Mac OS, platform ACLs are stored also in mac_metadata by * the operating system. If mac_metadata is present it takes * precedence and we skip extracting libarchive NFSv4 ACLs */ const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); - if (metadata == NULL || metadata_size == 0) { + if ((a->todo & TODO_MAC_METADATA) == 0 || + metadata == NULL || metadata_size == 0) { #endif r2 = archive_write_disk_set_acls(&a->archive, a->fd, archive_entry_pathname(a->entry), archive_entry_acl(a->entry)); if (r2 < ret) ret = r2; #ifdef HAVE_DARWIN_ACL } #endif } finish_metadata: /* 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, int64_t (*lookup_gid)(void *private, const char *gname, int64_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"); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); 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, int64_t (*lookup_uid)(void *private, const char *uname, int64_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"); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } int64_t archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) { 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_gid"); if (a->lookup_gid) return (a->lookup_gid)(a->lookup_gid_data, name, id); return (id); } int64_t archive_write_disk_uid(struct archive *_a, const char *name, int64_t id) { 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_uid"); if (a->lookup_uid) return (a->lookup_uid)(a->lookup_uid_data, name, id); return (id); } /* * 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 *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); 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->start_time = time(NULL); /* Query and restore the umask. */ umask(a->user_umask = umask(0)); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ if (archive_string_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } #ifdef HAVE_ZLIB_H a->decmpfs_compression_level = 5; #endif 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. */ #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *a) { int ret; char *tail = a->name; /* 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 | O_CLOEXEC); __archive_ensure_cloexec_flag(a->restore_pwd); 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 (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); 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 == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, "Hard-link target '%s' does not exist.", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } /* * 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_SYMLINKS 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_entry_unset_size(a->entry); return (ARCHIVE_OK); } } /* If it's our archive, we're done. */ if (a->skip_file_set && a->st.st_dev == (dev_t)a->skip_file_dev && a->st.st_ino == (ino_t)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 (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); 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 (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't replace existing directory with non-directory"); 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. */ if ((&a->archive)->error == NULL) 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. */ static int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const char *linkname; mode_t final_mode, mode; int r; /* these for check_symlinks_fsobj */ char *linkname_copy; /* non-const copy of linkname */ + struct stat st; struct archive_string error_string; int error_number; /* 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) { #if !HAVE_LINK return (EPERM); #else archive_string_init(&error_string); linkname_copy = strdup(linkname); if (linkname_copy == NULL) { return (EPERM); } /* * TODO: consider using the cleaned-up path as the link * target? */ r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } free(linkname_copy); archive_string_free(&error_string); 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-authoritative 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; } else if (r == 0 && a->filesize > 0) { - a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY - | O_CLOEXEC | O_NOFOLLOW); - __archive_ensure_cloexec_flag(a->fd); - if (a->fd < 0) +#ifdef HAVE_LSTAT + r = lstat(a->name, &st); +#else + r = stat(a->name, &st); +#endif + if (r != 0) r = errno; + else if ((st.st_mode & AE_IFMT) == AE_IFREG) { + a->fd = open(a->name, O_WRONLY | O_TRUNC | + O_BINARY | O_CLOEXEC | O_NOFOLLOW); + __archive_ensure_cloexec_flag(a->fd); + if (a->fd < 0) + r = errno; + } } return (r); #endif } linkname = archive_entry_symlink(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else return (EPERM); #endif } /* * 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 & ~a->user_umask; 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 | O_CLOEXEC, mode); __archive_ensure_cloexec_flag(a->fd); 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)); break; #else /* TODO: Find a better way to warn about our inability * to restore a char device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFBLK: #ifdef HAVE_MKNOD r = mknod(a->name, mode | S_IFBLK, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ 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); break; #else /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); #endif /* HAVE_MKFIFO */ } /* 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. * * Mac OS extended metadata includes ACLs, so must be deferred on dirs. * * 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_disk_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_disk_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) { set_times(a, -1, p->mode, p->name, p->atime, p->atime_nanos, p->birthtime, p->birthtime_nanos, p->mtime, p->mtime_nanos, p->ctime, p->ctime_nanos); } if (p->fixup & TODO_MODE_BASE) chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) #ifdef HAVE_DARWIN_ACL - if (p->mac_metadata == NULL || + if ((p->fixup & TODO_MAC_METADATA) == 0 || + p->mac_metadata == NULL || p->mac_metadata_size == 0) #endif archive_write_disk_set_acls(&a->archive, -1, p->name, &p->acl); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, -1, p->name, p->mode, p->fflags_set, 0); if (p->fixup & TODO_MAC_METADATA) set_mac_metadata(a, p->name, p->mac_metadata, p->mac_metadata_size); next = p->next; archive_acl_clear(&p->acl); free(p->mac_metadata); free(p->name); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_disk_free(struct archive *_a) { struct archive_write_disk *a; int ret; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free"); a = (struct archive_write_disk *)_a; ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); if (a->entry) archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); free(a->decmpfs_header_p); free(a->resource_fork); free(a->compressed_buffer); free(a->uncompressed_buffer); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) if (a->stream_valid) { switch (deflateEnd(&a->stream)) { case Z_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; break; } } #endif 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 *)calloc(1, sizeof(struct fixup_entry)); if (fe == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a fixup"); 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); } /* Error helper for new *_fsobj functions */ static void fsobj_error(int *a_eno, struct archive_string *a_estr, int err, const char *errstr, const char *path) { if (a_eno) *a_eno = err; if (a_estr) archive_string_sprintf(a_estr, errstr, path); } /* * 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. */ /* TODO: Extend this to support symlinks on Windows Vista and later. */ /* * Checks the given path to see if any elements along it are symlinks. Returns * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. */ static int check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { #if !defined(HAVE_LSTAT) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ (void)error_number; /* UNUSED */ (void)error_string; /* UNUSED */ (void)flags; /* UNUSED */ return (ARCHIVE_OK); #else int res = ARCHIVE_OK; char *tail; char *head; int last; char c; int r; struct stat st; int restore_pwd; /* Nothing to do here if name is empty */ if(path[0] == '\0') return (ARCHIVE_OK); /* * Guard against symlink tricks. Reject any archive entry whose * destination would be altered by a symlink. * * Walk the filename in chunks separated by '/'. For each segment: * - if it doesn't exist, continue * - if it's symlink, abort or remove it * - if it's a directory and it's not the last chunk, cd into it * As we go: * head points to the current (relative) path * tail points to the temporary \0 terminating the segment we're * currently examining * c holds what used to be in *tail * last is 1 if this is the last tail */ restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(restore_pwd); if (restore_pwd < 0) return (ARCHIVE_FATAL); head = path; tail = path; last = 0; /* TODO: reintroduce a safe cache here? */ /* Skip the root directory if the path is absolute. */ if(tail == path && tail[0] == '/') ++tail; /* Keep going until we've checked the entire name. * head, tail, path all alias the same string, which is * temporarily zeroed at tail, so be careful restoring the * stashed (c=tail[0]) for error messages. * Exiting the loop with break is okay; continue is not. */ while (!last) { /* * Skip the separator we just consumed, plus any adjacent ones */ while (*tail == '/') ++tail; /* Skip the next path element. */ while (*tail != '\0' && *tail != '/') ++tail; /* is this the last path component? */ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0'); /* temporarily truncate the string here */ c = tail[0]; tail[0] = '\0'; /* Check that we haven't hit a symlink. */ r = lstat(head, &st); if (r != 0) { tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) { break; } else { /* * Treat any other error as fatal - best to be * paranoid here. * Note: This effectively disables deep * directory support when security checks are * enabled. Otherwise, very long pathnames that * trigger an error here could evade the * sandbox. * TODO: We could do better, but it would * probably require merging the symlink checks * with the deep-directory editing. */ fsobj_error(a_eno, a_estr, errno, "Could not stat %s", path); res = ARCHIVE_FAILED; break; } } else if (S_ISDIR(st.st_mode)) { if (!last) { if (chdir(head) != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir %s", path); res = (ARCHIVE_FATAL); break; } /* Our view is now from inside this dir: */ head = tail + 1; } } else if (S_ISLNK(st.st_mode)) { if (last) { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ if (unlink(head)) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not remove symlink %s", path); res = ARCHIVE_FAILED; break; } /* * 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. */ tail[0] = c; /* * FIXME: not sure how important this is to * restore */ /* if (!S_ISLNK(path)) { fsobj_error(a_eno, a_estr, 0, "Removing symlink %s", path); } */ /* Symlink gone. No more problem! */ res = ARCHIVE_OK; break; } else if (flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ if (unlink(head) != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot remove intervening " "symlink %s", path); res = ARCHIVE_FAILED; break; } tail[0] = c; } else if ((flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) { /* * We are not the last element and we want to * follow symlinks if they are a directory. * * This is needed to extract hardlinks over * symlinks. */ r = stat(head, &st); if (r != 0) { tail[0] = c; if (errno == ENOENT) { break; } else { fsobj_error(a_eno, a_estr, errno, "Could not stat %s", path); res = (ARCHIVE_FAILED); break; } } else if (S_ISDIR(st.st_mode)) { if (chdir(head) != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir %s", path); res = (ARCHIVE_FATAL); break; } /* * Our view is now from inside * this dir: */ head = tail + 1; } else { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot extract through " "symlink %s", path); res = ARCHIVE_FAILED; break; } } else { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot extract through symlink %s", path); res = ARCHIVE_FAILED; break; } } /* be sure to always maintain this */ tail[0] = c; if (tail[0] != '\0') tail++; /* Advance to the next segment. */ } /* Catches loop exits via break */ tail[0] = c; #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (restore_pwd >= 0) { r = fchdir(restore_pwd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } close(restore_pwd); restore_pwd = -1; if (r != 0) { res = (ARCHIVE_FATAL); } } #endif /* TODO: reintroduce a safe cache here? */ return res; #endif } /* * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED} */ static int check_symlinks(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); a->pst = NULL; /* to be safe */ return rc; } #if defined(__CYGWIN__) /* * 1. Convert a path separator from '\' to '/' . * We shouldn't check multibyte 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(char *path) { wchar_t wc; char *p; size_t alen, l; int mb, complete, utf8; alen = 0; mb = 0; complete = 1; utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0; for (p = path; *p != '\0'; p++) { ++alen; if (*p == '\\') { /* If previous byte is smaller than 128, * this is not second byte of multibyte characters, * so we can replace '\' with '/'. */ if (utf8 || !mb) *p = '/'; else complete = 0;/* uncompleted. */ } else if (*(unsigned char *)p > 127) mb = 1; else mb = 0; /* Rewrite the path name if its next character is unusable. */ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; } if (complete) return; /* * Convert path separator in wide-character. */ p = path; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == (size_t)-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 '..', (if _SECURE_NODOTDOT is * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS * is set) if the path is absolute. */ static int cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { char *dest, *src; char separator = '\0'; dest = src = path; if (*src == '\0') { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Invalid empty ", "pathname"); return (ARCHIVE_FAILED); } #if defined(__CYGWIN__) cleanup_pathname_win(path); #endif /* Skip leading '/'. */ if (*src == '/') { if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path is ", "absolute"); return (ARCHIVE_FAILED); } 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 (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { fsobj_error(a_eno, a_estr, 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 == path) { /* * 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); } static int cleanup_pathname(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); return rc; } /* * 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; /* 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", path); 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); if (le == NULL) return (ARCHIVE_FATAL); 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=%jd", (intmax_t)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=%jd/group=%jd for %s", (intmax_t)a->uid, (intmax_t)a->gid, a->name); return (ARCHIVE_WARN); } /* * Note: Returns 0 on success, non-zero on failure. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { /* Select the best implementation for this platform. */ #if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS) /* * utimensat() and futimens() are defined in * POSIX.1-2008. They support ns resolution and setting times * on fds and symlinks. */ struct timespec ts[2]; (void)mode; /* UNUSED */ ts[0].tv_sec = atime; ts[0].tv_nsec = atime_nsec; ts[1].tv_sec = mtime; ts[1].tv_nsec = mtime_nsec; if (fd >= 0) return futimens(fd, ts); return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW); #elif HAVE_UTIMES /* * The utimes()-family functions support µs-resolution and * setting times fds and symlinks. utimes() is documented as * LEGACY by POSIX, futimes() and lutimes() are not described * in POSIX. */ struct timeval times[2]; 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 POSIX-standard but only supports 1s resolution and * does not support fds or symlinks. */ 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 /* * We don't know how to set the time on this platform. */ (void)fd; /* UNUSED */ (void)mode; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ return (ARCHIVE_WARN); #endif } #ifdef F_SETTIMES static int set_time_tru64(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec, time_t ctime, long ctime_nsec) { struct attr_timbuf tstamp; tstamp.atime.tv_sec = atime; tstamp.mtime.tv_sec = mtime; tstamp.ctime.tv_sec = ctime; #if defined (__hpux) && defined (__ia64) tstamp.atime.tv_nsec = atime_nsec; tstamp.mtime.tv_nsec = mtime_nsec; tstamp.ctime.tv_nsec = ctime_nsec; #else tstamp.atime.tv_usec = atime_nsec / 1000; tstamp.mtime.tv_usec = mtime_nsec / 1000; tstamp.ctime.tv_usec = ctime_nsec / 1000; #endif return (fcntl(fd,F_SETTIMES,&tstamp)); } #endif /* F_SETTIMES */ static int set_times(struct archive_write_disk *a, int fd, int mode, const char *name, time_t atime, long atime_nanos, time_t birthtime, long birthtime_nanos, time_t mtime, long mtime_nanos, time_t cctime, long ctime_nanos) { /* Note: set_time doesn't use libarchive return conventions! * It uses syscall conventions. So 0 here instead of ARCHIVE_OK. */ int r1 = 0, r2 = 0; #ifdef F_SETTIMES /* * on Tru64 try own fcntl first which can restore even the * ctime, fall back to default code path below if it fails * or if we are not running as root */ if (a->user_uid == 0 && set_time_tru64(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos, cctime, ctime_nanos) == 0) { return (ARCHIVE_OK); } #else /* Tru64 */ (void)cctime; /* UNUSED */ (void)ctime_nanos; /* UNUSED */ #endif /* Tru64 */ #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME /* * 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 * birthtime == mtime, this isn't necessary, so we skip it. * If birthtime > mtime, then this won't work, so we skip it. */ if (birthtime < mtime || (birthtime == mtime && birthtime_nanos < mtime_nanos)) r1 = set_time(fd, mode, name, atime, atime_nanos, birthtime, birthtime_nanos); #else (void)birthtime; /* UNUSED */ (void)birthtime_nanos; /* UNUSED */ #endif r2 = set_time(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos); if (r1 != 0 || r2 != 0) { archive_set_error(&a->archive, errno, "Can't restore time"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int set_times_from_entry(struct archive_write_disk *a) { time_t atime, birthtime, mtime, cctime; long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec; /* Suitable defaults. */ atime = birthtime = mtime = cctime = a->start_time; atime_nsec = birthtime_nsec = mtime_nsec = ctime_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 (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } if (archive_entry_birthtime_is_set(a->entry)) { birthtime = archive_entry_birthtime(a->entry); birthtime_nsec = archive_entry_birthtime_nsec(a->entry); } if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (archive_entry_ctime_is_set(a->entry)) { cctime = archive_entry_ctime(a->entry); ctime_nsec = archive_entry_ctime_nsec(a->entry); } return set_times(a, a->fd, a->mode, a->name, atime, atime_nsec, birthtime, birthtime_nsec, mtime, mtime_nsec, cctime, ctime_nsec); } 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 = lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; 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; } } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } } 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; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } } 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) { switch (errno) { case ENOTSUP: case ENOSYS: #if ENOTSUP != EOPNOTSUPP case EOPNOTSUPP: #endif /* * if lchmod is defined but the platform * doesn't support it, silently ignore * error */ break; default: 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 +#if defined(FS_APPEND_FL) + critical_flags |= FS_APPEND_FL; +#elif defined(EXT2_APPEND_FL) critical_flags |= EXT2_APPEND_FL; #endif -#ifdef EXT2_IMMUTABLE_FL +#if defined(FS_IMMUTABLE_FL) + critical_flags |= FS_IMMUTABLE_FL; +#elif defined(EXT2_IMMUTABLE_FL) critical_flags |= EXT2_IMMUTABLE_FL; #endif +#ifdef FS_JOURNAL_DATA_FL + critical_flags |= FS_JOURNAL_DATA_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); if (le == NULL) return (ARCHIVE_FATAL); 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); } static int clear_nochange_fflags(struct archive_write_disk *a) { int nochange_flags; mode_t mode = archive_entry_mode(a->entry); /* Hopefully, the compiler will optimize this mess into a constant. */ nochange_flags = 0; #ifdef SF_IMMUTABLE nochange_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE nochange_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND nochange_flags |= SF_APPEND; #endif #ifdef UF_APPEND nochange_flags |= UF_APPEND; #endif #ifdef EXT2_APPEND_FL nochange_flags |= EXT2_APPEND_FL; #endif #ifdef EXT2_IMMUTABLE_FL nochange_flags |= EXT2_IMMUTABLE_FL; #endif return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); } #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 = 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) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) +#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \ + defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ + (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \ + defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* * 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; int newflags, oldflags; int sf_mask = 0; - if (set == 0 && clear == 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 | O_CLOEXEC); __archive_ensure_cloexec_flag(myfd); } 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 +#if defined(FS_IMMUTABLE_FL) + sf_mask |= FS_IMMUTABLE_FL; +#elif defined(EXT2_IMMUTABLE_FL) sf_mask |= EXT2_IMMUTABLE_FL; #endif -#ifdef EXT2_APPEND_FL +#if defined(FS_APPEND_FL) + sf_mask |= FS_APPEND_FL; +#elif defined(EXT2_APPEND_FL) sf_mask |= EXT2_APPEND_FL; #endif +#if defined(FS_JOURNAL_DATA_FL) + sf_mask |= FS_JOURNAL_DATA_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; /* Read the current file flags. */ - if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) < 0) + if (ioctl(myfd, +#ifdef FS_IOC_GETFLAGS + FS_IOC_GETFLAGS, +#else + EXT2_IOC_GETFLAGS, +#endif + &oldflags) < 0) goto fail; /* Try setting the flags as given. */ newflags = (oldflags & ~clear) | set; - if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) + if (ioctl(myfd, +#ifdef FS_IOC_SETFLAGS + FS_IOC_SETFLAGS, +#else + EXT2_IOC_SETFLAGS, +#endif + &newflags) >= 0) goto cleanup; if (errno != EPERM) goto fail; /* If we couldn't set all the flags, try again with a subset. */ newflags &= ~sf_mask; oldflags &= sf_mask; newflags |= oldflags; - if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) + if (ioctl(myfd, +#ifdef FS_IOC_SETFLAGS + FS_IOC_SETFLAGS, +#else + EXT2_IOC_SETFLAGS, +#endif + &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_COPYFILE_H /* Default is to simply drop Mac extended metadata. */ static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ (void)metadata; /* UNUSED */ (void)metadata_size; /* UNUSED */ return (ARCHIVE_OK); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ return (ARCHIVE_OK); } #else /* * On Mac OS, we use copyfile() to unpack the metadata and * apply it to the target file. */ #if defined(HAVE_SYS_XATTR_H) static int copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) { ssize_t xattr_size; char *xattr_names = NULL, *xattr_val = NULL; int ret = ARCHIVE_OK, xattr_i; xattr_size = flistxattr(tmpfd, NULL, 0, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_names = malloc(xattr_size); if (xattr_names == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for metadata(xattr)"); ret = ARCHIVE_FATAL; goto exit_xattr; } xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } for (xattr_i = 0; xattr_i < xattr_size; xattr_i += strlen(xattr_names + xattr_i) + 1) { char *xattr_val_saved; ssize_t s; int f; s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_val_saved = xattr_val; xattr_val = realloc(xattr_val, s); if (xattr_val == NULL) { archive_set_error(&a->archive, ENOMEM, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; free(xattr_val_saved); goto exit_xattr; } s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (f == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } } exit_xattr: free(xattr_names); free(xattr_val); return (ret); } #endif static int copy_acls(struct archive_write_disk *a, int tmpfd, int dffd) { #ifndef HAVE_SYS_ACL_H return 0; #else acl_t acl, dfacl = NULL; int acl_r, ret = ARCHIVE_OK; acl = acl_get_fd(tmpfd); if (acl == NULL) { if (errno == ENOENT) /* There are not any ACLs. */ return (ret); archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } dfacl = acl_dup(acl); acl_r = acl_set_fd(dffd, dfacl); if (acl_r == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } exit_acl: if (acl) acl_free(acl); if (dfacl) acl_free(dfacl); return (ret); #endif } static int create_tempdatafork(struct archive_write_disk *a, const char *pathname) { struct archive_string tmpdatafork; int tmpfd; archive_string_init(&tmpdatafork); archive_strcpy(&tmpdatafork, "tar.md.XXXXXX"); tmpfd = mkstemp(tmpdatafork.s); if (tmpfd < 0) { archive_set_error(&a->archive, errno, "Failed to mkstemp"); archive_string_free(&tmpdatafork); return (-1); } if (copyfile(pathname, tmpdatafork.s, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); close(tmpfd); tmpfd = -1; } unlink(tmpdatafork.s); archive_string_free(&tmpdatafork); return (tmpfd); } static int copy_metadata(struct archive_write_disk *a, const char *metadata, const char *datafork, int datafork_compressed) { int ret = ARCHIVE_OK; if (datafork_compressed) { int dffd, tmpfd; tmpfd = create_tempdatafork(a, metadata); if (tmpfd == -1) return (ARCHIVE_WARN); /* * Do not open the data fork compressed by HFS+ compression * with at least a writing mode(O_RDWR or O_WRONLY). it * makes the data fork uncompressed. */ dffd = open(datafork, 0); if (dffd == -1) { archive_set_error(&a->archive, errno, "Failed to open the data fork for metadata"); close(tmpfd); return (ARCHIVE_WARN); } #if defined(HAVE_SYS_XATTR_H) ret = copy_xattrs(a, tmpfd, dffd); if (ret == ARCHIVE_OK) #endif ret = copy_acls(a, tmpfd, dffd); close(tmpfd); close(dffd); } else { if (copyfile(metadata, datafork, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } } return (ret); } static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { struct archive_string tmp; ssize_t written; int fd; int ret = ARCHIVE_OK; /* This would be simpler if copyfile() could just accept the * metadata as a block of memory; then we could sidestep this * silly dance of writing the data to disk just so that * copyfile() can read it back in again. */ archive_string_init(&tmp); archive_strcpy(&tmp, pathname); archive_strcat(&tmp, ".XXXXXX"); fd = mkstemp(tmp.s); if (fd < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); archive_string_free(&tmp); return (ARCHIVE_WARN); } written = write(fd, metadata, metadata_size); close(fd); if ((size_t)written != metadata_size) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } else { int compressed; #if defined(UF_COMPRESSED) if ((a->todo & TODO_HFS_COMPRESSION) != 0 && (ret = lazy_stat(a)) == ARCHIVE_OK) compressed = a->st.st_flags & UF_COMPRESSED; else #endif compressed = 0; ret = copy_metadata(a, tmp.s, pathname, compressed); } unlink(tmp.s); archive_string_free(&tmp); return (ret); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { char buff[8]; struct stat st; const char *p; struct archive_string datafork; int fd = -1, ret = ARCHIVE_OK; archive_string_init(&datafork); /* Check if the current file name is a type of the resource * fork file. */ p = strrchr(pathname, '/'); if (p == NULL) p = pathname; else p++; if (p[0] != '.' || p[1] != '_') goto skip_appledouble; /* * Check if the data fork file exists. * * TODO: Check if this write disk object has handled it. */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); if (lstat(datafork.s, &st) == -1 || (st.st_mode & AE_IFMT) != AE_IFREG) goto skip_appledouble; /* * Check if the file is in the AppleDouble form. */ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd == -1) { archive_set_error(&a->archive, errno, "Failed to open a restoring file"); ret = ARCHIVE_WARN; goto skip_appledouble; } if (read(fd, buff, 8) == -1) { archive_set_error(&a->archive, errno, "Failed to read a restoring file"); close(fd); ret = ARCHIVE_WARN; goto skip_appledouble; } close(fd); /* Check AppleDouble Magic Code. */ if (archive_be32dec(buff) != 0x00051607) goto skip_appledouble; /* Check AppleDouble Version. */ if (archive_be32dec(buff+4) != 0x00020000) goto skip_appledouble; ret = copy_metadata(a, pathname, datafork.s, #if defined(UF_COMPRESSED) st.st_flags & UF_COMPRESSED); #else 0); #endif if (ret == ARCHIVE_OK) { unlink(pathname); ret = ARCHIVE_EOF; } skip_appledouble: archive_string_free(&datafork); return (ret); } #endif #if HAVE_LSETXATTR || HAVE_LSETEA /* * Restore extended attributes - Linux and AIX implementations: * AIX' ea interface is syntaxwise identical to the Linux xattr interface. */ 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 #elif HAVE_FSETEA if (a->fd >= 0) e = fsetea(a->fd, name, value, size, 0); else #endif { #if HAVE_LSETXATTR e = lsetxattr(archive_entry_pathname(entry), name, value, size, 0); #elif HAVE_LSETEA e = lsetea(archive_entry_pathname(entry), name, value, size, 0); #endif } if (e == -1) { if (errno == ENOTSUP || errno == ENOSYS) { 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 && HAVE_DECL_EXTATTR_NAMESPACE_USER /* * 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 || errno == ENOSYS) { 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 /* * 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 (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry))) return (1); /* Definitely younger. */ if (to_int64_time(st->st_mtime) > to_int64_time(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); } #endif /* !_WIN32 || __CYGWIN__ */ Index: vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c (revision 314564) +++ vendor/libarchive/dist/libarchive/archive_write_set_format_iso9660.c (revision 314565) @@ -1,8162 +1,8164 @@ /*- * Copyright (c) 2009-2012 Michihiro NAKAJIMA * 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" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_write_private.h" #if defined(_WIN32) && !defined(__CYGWIN__) #define getuid() 0 #define getgid() 0 #endif /*#define DEBUG 1*/ #ifdef DEBUG /* To compare to the ISO image file made by mkisofs. */ #define COMPAT_MKISOFS 1 #endif #define LOGICAL_BLOCK_BITS 11 #define LOGICAL_BLOCK_SIZE 2048 #define PATH_TABLE_BLOCK_SIZE 4096 #define SYSTEM_AREA_BLOCK 16 #define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 1 #define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 1 #define BOOT_RECORD_DESCRIPTOR_BLOCK 1 #define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK 1 #define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK 1 #define RRIP_ER_BLOCK 1 #define PADDING_BLOCK 150 #define FD_1_2M_SIZE (1024 * 1200) #define FD_1_44M_SIZE (1024 * 1440) #define FD_2_88M_SIZE (1024 * 2880) #define MULTI_EXTENT_SIZE (ARCHIVE_LITERAL_LL(1) << 32) /* 4Gi bytes. */ #define MAX_DEPTH 8 #define RR_CE_SIZE 28 /* SUSP "CE" extension size */ #define FILE_FLAG_EXISTENCE 0x01 #define FILE_FLAG_DIRECTORY 0x02 #define FILE_FLAG_ASSOCIATED 0x04 #define FILE_FLAG_RECORD 0x08 #define FILE_FLAG_PROTECTION 0x10 #define FILE_FLAG_MULTI_EXTENT 0x80 static const char rrip_identifier[] = "RRIP_1991A"; static const char rrip_descriptor[] = "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR " "POSIX FILE SYSTEM SEMANTICS"; static const char rrip_source[] = "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. " "SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR " "CONTACT INFORMATION."; #define RRIP_ER_ID_SIZE (sizeof(rrip_identifier)-1) #define RRIP_ER_DSC_SIZE (sizeof(rrip_descriptor)-1) #define RRIP_ER_SRC_SIZE (sizeof(rrip_source)-1) #define RRIP_ER_SIZE (8 + RRIP_ER_ID_SIZE + \ RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE) static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; #define ZF_HEADER_SIZE 16 /* zisofs header size. */ #define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */ #define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS) /* * Manage extra records. */ struct extr_rec { int location; int offset; unsigned char buf[LOGICAL_BLOCK_SIZE]; struct extr_rec *next; }; struct ctl_extr_rec { int use_extr; unsigned char *bp; struct isoent *isoent; unsigned char *ce_ptr; int cur_len; int dr_len; int limit; int extr_off; int extr_loc; }; #define DR_SAFETY RR_CE_SIZE #define DR_LIMIT (254 - DR_SAFETY) /* * The relation of struct isofile and isoent and archive_entry. * * Primary volume tree --> struct isoent * | * v * struct isofile --> archive_entry * ^ * | * Joliet volume tree --> struct isoent * * struct isoent has specific information for volume. */ struct isofile { /* Used for managing struct isofile list. */ struct isofile *allnext; struct isofile *datanext; /* Used for managing a hardlinked struct isofile list. */ struct isofile *hlnext; struct isofile *hardlink_target; struct archive_entry *entry; /* * Used for making a directory tree. */ struct archive_string parentdir; struct archive_string basename; struct archive_string basename_utf16; struct archive_string symlink; int dircnt; /* The number of elements of * its parent directory */ /* * Used for a Directory Record. */ struct content { int64_t offset_of_temp; int64_t size; int blocks; uint32_t location; /* * One extent equals one content. * If this entry has multi extent, `next' variable points * next content data. */ struct content *next; /* next content */ } content, *cur_content; int write_content; enum { NO = 0, BOOT_CATALOG, BOOT_IMAGE } boot; /* * Used for a zisofs. */ struct { unsigned char header_size; unsigned char log2_bs; uint32_t uncompressed_size; } zisofs; }; struct isoent { /* Keep `rbnode' at the first member of struct isoent. */ struct archive_rb_node rbnode; struct isofile *file; struct isoent *parent; /* A list of children.(use chnext) */ struct { struct isoent *first; struct isoent **last; int cnt; } children; struct archive_rb_tree rbtree; /* A list of sub directories.(use drnext) */ struct { struct isoent *first; struct isoent **last; int cnt; } subdirs; /* A sorted list of sub directories. */ struct isoent **children_sorted; /* Used for managing struct isoent list. */ struct isoent *chnext; struct isoent *drnext; struct isoent *ptnext; /* * Used for making a Directory Record. */ int dir_number; struct { int vd; int self; int parent; int normal; } dr_len; uint32_t dir_location; int dir_block; /* * Identifier: * on primary, ISO9660 file/directory name. * on joliet, UCS2 file/directory name. * ext_off : offset of identifier extension. * ext_len : length of identifier extension. * id_len : byte size of identifier. * on primary, this is ext_off + ext_len + version length. * on joliet, this is ext_off + ext_len. * mb_len : length of multibyte-character of identifier. * on primary, mb_len and id_len are always the same. * on joliet, mb_len and id_len are different. */ char *identifier; int ext_off; int ext_len; int id_len; int mb_len; /* * Used for making a Rockridge extension. * This is a part of Directory Records. */ struct isoent *rr_parent; struct isoent *rr_child; /* Extra Record.(which we call in this source file) * A maximum size of the Directory Record is 254. * so, if generated RRIP data of a file cannot into a Directory * Record because of its size, that surplus data relocate this * Extra Record. */ struct { struct extr_rec *first; struct extr_rec **last; struct extr_rec *current; } extr_rec_list; int virtual:1; /* If set to one, this file type is a directory. * A convenience flag to be used as * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". */ int dir:1; }; struct hardlink { struct archive_rb_node rbnode; int nlink; struct { struct isofile *first; struct isofile **last; } file_list; }; /* * ISO writer options */ struct iso_option { /* * Usage : abstract-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -abstract * * Specifies Abstract Filename. * This file shall be described in the Root Directory * and containing a abstract statement. */ unsigned int abstract_file:1; #define OPT_ABSTRACT_FILE_DEFAULT 0 /* Not specified */ #define ABSTRACT_FILE_SIZE 37 /* * Usage : application-id= * Type : string, max 128 bytes * Default: Not specified * COMPAT : mkisofs -A/-appid . * * Specifies Application Identifier. * If the first byte is set to '_'(5F), the remaining * bytes of this option shall specify an identifier * for a file containing the identification of the * application. * This file shall be described in the Root Directory. */ unsigned int application_id:1; #define OPT_APPLICATION_ID_DEFAULT 0 /* Use default identifier */ #define APPLICATION_IDENTIFIER_SIZE 128 /* * Usage : !allow-vernum * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT: mkisofs -N * * Allow filenames to use version numbers. */ unsigned int allow_vernum:1; #define OPT_ALLOW_VERNUM_DEFAULT 1 /* Enabled */ /* * Usage : biblio-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -biblio * * Specifies Bibliographic Filename. * This file shall be described in the Root Directory * and containing bibliographic records. */ unsigned int biblio_file:1; #define OPT_BIBLIO_FILE_DEFAULT 0 /* Not specified */ #define BIBLIO_FILE_SIZE 37 /* * Usage : boot= * Type : string * Default: Not specified * COMPAT : mkisofs -b/-eltorito-boot * * Specifies "El Torito" boot image file to make * a bootable CD. */ unsigned int boot:1; #define OPT_BOOT_DEFAULT 0 /* Not specified */ /* * Usage : boot-catalog= * Type : string * Default: "boot.catalog" * COMPAT : mkisofs -c/-eltorito-catalog * * Specifies a fullpath of El Torito boot catalog. */ unsigned int boot_catalog:1; #define OPT_BOOT_CATALOG_DEFAULT 0 /* Not specified */ /* * Usage : boot-info-table * Type : boolean * Default: Disabled * COMPAT : mkisofs -boot-info-table * * Modify the boot image file specified by `boot' * option; ISO writer stores boot file information * into the boot file in ISO image at offset 8 * through offset 64. */ unsigned int boot_info_table:1; #define OPT_BOOT_INFO_TABLE_DEFAULT 0 /* Disabled */ /* * Usage : boot-load-seg= * Type : hexadecimal * Default: Not specified * COMPAT : mkisofs -boot-load-seg * * Specifies a load segment for boot image. * This is used with no-emulation mode. */ unsigned int boot_load_seg:1; #define OPT_BOOT_LOAD_SEG_DEFAULT 0 /* Not specified */ /* * Usage : boot-load-size= * Type : decimal * Default: Not specified * COMPAT : mkisofs -boot-load-size * * Specifies a sector count for boot image. * This is used with no-emulation mode. */ unsigned int boot_load_size:1; #define OPT_BOOT_LOAD_SIZE_DEFAULT 0 /* Not specified */ /* * Usage : boot-type= * : 'no-emulation' : 'no emulation' image * : 'fd' : floppy disk image * : 'hard-disk' : hard disk image * Type : string * Default: Auto detect * : We check a size of boot image; * : If the size is just 1.22M/1.44M/2.88M, * : we assume boot_type is 'fd'; * : otherwise boot_type is 'no-emulation'. * COMPAT : * boot=no-emulation * mkisofs -no-emul-boot * boot=fd * This is a default on the mkisofs. * boot=hard-disk * mkisofs -hard-disk-boot * * Specifies a type of "El Torito" boot image. */ unsigned int boot_type:2; #define OPT_BOOT_TYPE_AUTO 0 /* auto detect */ #define OPT_BOOT_TYPE_NO_EMU 1 /* ``no emulation'' image */ #define OPT_BOOT_TYPE_FD 2 /* floppy disk image */ #define OPT_BOOT_TYPE_HARD_DISK 3 /* hard disk image */ #define OPT_BOOT_TYPE_DEFAULT OPT_BOOT_TYPE_AUTO /* * Usage : compression-level= * Type : decimal * Default: Not specified * COMPAT : NONE * * Specifies compression level for option zisofs=direct. */ unsigned int compression_level:1; #define OPT_COMPRESSION_LEVEL_DEFAULT 0 /* Not specified */ /* * Usage : copyright-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -copyright * * Specifies Copyright Filename. * This file shall be described in the Root Directory * and containing a copyright statement. */ unsigned int copyright_file:1; #define OPT_COPYRIGHT_FILE_DEFAULT 0 /* Not specified */ #define COPYRIGHT_FILE_SIZE 37 /* * Usage : gid= * Type : decimal * Default: Not specified * COMPAT : mkisofs -gid * * Specifies a group id to rewrite the group id of all files. */ unsigned int gid:1; #define OPT_GID_DEFAULT 0 /* Not specified */ /* * Usage : iso-level=[1234] * Type : decimal * Default: 1 * COMPAT : mkisofs -iso-level * * Specifies ISO9600 Level. * Level 1: [DEFAULT] * - limits each file size less than 4Gi bytes; * - a File Name shall not contain more than eight * d-characters or eight d1-characters; * - a File Name Extension shall not contain more than * three d-characters or three d1-characters; * - a Directory Identifier shall not contain more * than eight d-characters or eight d1-characters. * Level 2: * - limits each file size less than 4Giga bytes; * - a File Name shall not contain more than thirty * d-characters or thirty d1-characters; * - a File Name Extension shall not contain more than * thirty d-characters or thirty d1-characters; * - a Directory Identifier shall not contain more * than thirty-one d-characters or thirty-one * d1-characters. * Level 3: * - no limit of file size; use multi extent. * Level 4: * - this level 4 simulates mkisofs option * '-iso-level 4'; * - crate a enhanced volume as mkisofs doing; * - allow a File Name to have leading dot; * - allow a File Name to have all ASCII letters; * - allow a File Name to have multiple dots; * - allow more then 8 depths of directory trees; * - disable a version number to a File Name; * - disable a forced period to the tail of a File Name; * - the maximum length of files and directories is raised to 193. * if rockridge option is disabled, raised to 207. */ unsigned int iso_level:3; #define OPT_ISO_LEVEL_DEFAULT 1 /* ISO Level 1 */ /* * Usage : joliet[=long] * : !joliet * : Do not generate Joliet Volume and Records. * : joliet [DEFAULT] * : Generates Joliet Volume and Directory Records. * : [COMPAT: mkisofs -J/-joliet] * : joliet=long * : The joliet filenames are up to 103 Unicode * : characters. * : This option breaks the Joliet specification. * : [COMPAT: mkisofs -J -joliet-long] * Type : boolean/string * Default: Enabled * COMPAT : mkisofs -J / -joliet-long * * Generates Joliet Volume and Directory Records. */ unsigned int joliet:2; #define OPT_JOLIET_DISABLE 0 /* Not generate Joliet Records. */ #define OPT_JOLIET_ENABLE 1 /* Generate Joliet Records. */ #define OPT_JOLIET_LONGNAME 2 /* Use long joliet filenames.*/ #define OPT_JOLIET_DEFAULT OPT_JOLIET_ENABLE /* * Usage : !limit-depth * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT : mkisofs -D/-disable-deep-relocation * * The number of levels in hierarchy cannot exceed eight. */ unsigned int limit_depth:1; #define OPT_LIMIT_DEPTH_DEFAULT 1 /* Enabled */ /* * Usage : !limit-dirs * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT : mkisofs -no-limit-pathtables * * Limits the number of directories less than 65536 due * to the size of the Parent Directory Number of Path * Table. */ unsigned int limit_dirs:1; #define OPT_LIMIT_DIRS_DEFAULT 1 /* Enabled */ /* * Usage : !pad * Type : boolean * Default: Enabled * COMPAT : -pad/-no-pad * * Pads the end of the ISO image by null of 300Ki bytes. */ unsigned int pad:1; #define OPT_PAD_DEFAULT 1 /* Enabled */ /* * Usage : publisher= * Type : string, max 128 bytes * Default: Not specified * COMPAT : mkisofs -publisher * * Specifies Publisher Identifier. * If the first byte is set to '_'(5F), the remaining * bytes of this option shall specify an identifier * for a file containing the identification of the user. * This file shall be described in the Root Directory. */ unsigned int publisher:1; #define OPT_PUBLISHER_DEFAULT 0 /* Not specified */ #define PUBLISHER_IDENTIFIER_SIZE 128 /* * Usage : rockridge * : !rockridge * : disable to generate SUSP and RR records. * : rockridge * : the same as 'rockridge=useful'. * : rockridge=strict * : generate SUSP and RR records. * : [COMPAT: mkisofs -R] * : rockridge=useful [DEFAULT] * : generate SUSP and RR records. * : [COMPAT: mkisofs -r] * : NOTE Our rockridge=useful option does not set a zero * : to uid and gid, you should use application * : option such as --gid,--gname,--uid and --uname * : bsdtar options instead. * Type : boolean/string * Default: Enabled as rockridge=useful * COMPAT : mkisofs -r / -R * * Generates SUSP and RR records. */ unsigned int rr:2; #define OPT_RR_DISABLED 0 #define OPT_RR_STRICT 1 #define OPT_RR_USEFUL 2 #define OPT_RR_DEFAULT OPT_RR_USEFUL /* * Usage : volume-id= * Type : string, max 32 bytes * Default: Not specified * COMPAT : mkisofs -V * * Specifies Volume Identifier. */ unsigned int volume_id:1; #define OPT_VOLUME_ID_DEFAULT 0 /* Use default identifier */ #define VOLUME_IDENTIFIER_SIZE 32 /* * Usage : !zisofs [DEFAULT] * : Disable to generate RRIP 'ZF' extension. * : zisofs * : Make files zisofs file and generate RRIP 'ZF' * : extension. So you do not need mkzftree utility * : for making zisofs. * : When the file size is less than one Logical Block * : size, that file will not zisofs'ed since it does * : reduce an ISO-image size. * : * : When you specify option 'boot=', that * : 'boot-image' file won't be converted to zisofs file. * Type : boolean * Default: Disabled * * Generates RRIP 'ZF' System Use Entry. */ unsigned int zisofs:1; #define OPT_ZISOFS_DISABLED 0 #define OPT_ZISOFS_DIRECT 1 #define OPT_ZISOFS_DEFAULT OPT_ZISOFS_DISABLED }; struct iso9660 { /* The creation time of ISO image. */ time_t birth_time; /* A file stream of a temporary file, which file contents * save to until ISO image can be created. */ int temp_fd; struct isofile *cur_file; struct isoent *cur_dirent; struct archive_string cur_dirstr; uint64_t bytes_remaining; int need_multi_extent; /* Temporary string buffer for Joliet extension. */ struct archive_string utf16be; struct archive_string mbs; struct archive_string_conv *sconv_to_utf16be; struct archive_string_conv *sconv_from_utf16be; /* A list of all of struct isofile entries. */ struct { struct isofile *first; struct isofile **last; } all_file_list; /* A list of struct isofile entries which have its * contents and are not a directory, a hardlinked file * and a symlink file. */ struct { struct isofile *first; struct isofile **last; } data_file_list; /* Used for managing to find hardlinking files. */ struct archive_rb_tree hardlink_rbtree; /* Used for making the Path Table Record. */ struct vdd { /* the root of entry tree. */ struct isoent *rootent; enum vdd_type { VDD_PRIMARY, VDD_JOLIET, VDD_ENHANCED } vdd_type; struct path_table { struct isoent *first; struct isoent **last; struct isoent **sorted; int cnt; } *pathtbl; int max_depth; int path_table_block; int path_table_size; int location_type_L_path_table; int location_type_M_path_table; int total_dir_block; } primary, joliet; /* Used for making a Volume Descriptor. */ int volume_space_size; int volume_sequence_number; int total_file_block; struct archive_string volume_identifier; struct archive_string publisher_identifier; struct archive_string data_preparer_identifier; struct archive_string application_identifier; struct archive_string copyright_file_identifier; struct archive_string abstract_file_identifier; struct archive_string bibliographic_file_identifier; /* Used for making rockridge extensions. */ int location_rrip_er; /* Used for making zisofs. */ struct { int detect_magic:1; int making:1; int allzero:1; unsigned char magic_buffer[64]; int magic_cnt; #ifdef HAVE_ZLIB_H /* * Copy a compressed file to iso9660.zisofs.temp_fd * and also copy a uncompressed file(original file) to * iso9660.temp_fd . If the number of logical block * of the compressed file is less than the number of * logical block of the uncompressed file, use it and * remove the copy of the uncompressed file. * but if not, we use uncompressed file and remove * the copy of the compressed file. */ uint32_t *block_pointers; size_t block_pointers_allocated; int block_pointers_cnt; int block_pointers_idx; int64_t total_size; int64_t block_offset; z_stream stream; int stream_valid; int64_t remaining; int compression_level; #endif } zisofs; struct isoent *directories_too_deep; int dircnt_max; /* Write buffer. */ #define wb_buffmax() (LOGICAL_BLOCK_SIZE * 32) #define wb_remaining(a) (((struct iso9660 *)(a)->format_data)->wbuff_remaining) #define wb_offset(a) (((struct iso9660 *)(a)->format_data)->wbuff_offset \ + wb_buffmax() - wb_remaining(a)) unsigned char wbuff[LOGICAL_BLOCK_SIZE * 32]; size_t wbuff_remaining; enum { WB_TO_STREAM, WB_TO_TEMP } wbuff_type; int64_t wbuff_offset; int64_t wbuff_written; int64_t wbuff_tail; /* 'El Torito' boot data. */ struct { /* boot catalog file */ struct archive_string catalog_filename; struct isoent *catalog; /* boot image file */ struct archive_string boot_filename; struct isoent *boot; unsigned char platform_id; #define BOOT_PLATFORM_X86 0 #define BOOT_PLATFORM_PPC 1 #define BOOT_PLATFORM_MAC 2 struct archive_string id; unsigned char media_type; #define BOOT_MEDIA_NO_EMULATION 0 #define BOOT_MEDIA_1_2M_DISKETTE 1 #define BOOT_MEDIA_1_44M_DISKETTE 2 #define BOOT_MEDIA_2_88M_DISKETTE 3 #define BOOT_MEDIA_HARD_DISK 4 unsigned char system_type; uint16_t boot_load_seg; uint16_t boot_load_size; #define BOOT_LOAD_SIZE 4 } el_torito; struct iso_option opt; }; /* * Types of Volume Descriptor */ enum VD_type { VDT_BOOT_RECORD=0, /* Boot Record Volume Descriptor */ VDT_PRIMARY=1, /* Primary Volume Descriptor */ VDT_SUPPLEMENTARY=2, /* Supplementary Volume Descriptor */ VDT_TERMINATOR=255 /* Volume Descriptor Set Terminator */ }; /* * Types of Directory Record */ enum dir_rec_type { DIR_REC_VD, /* Stored in Volume Descriptor. */ DIR_REC_SELF, /* Stored as Current Directory. */ DIR_REC_PARENT, /* Stored as Parent Directory. */ DIR_REC_NORMAL /* Stored as Child. */ }; /* * Kinds of Volume Descriptor Character */ enum vdc { VDC_STD, VDC_LOWERCASE, VDC_UCS2, VDC_UCS2_DIRECT }; /* * IDentifier Resolver. * Used for resolving duplicated filenames. */ struct idr { struct idrent { struct archive_rb_node rbnode; /* Used in wait_list. */ struct idrent *wnext; struct idrent *avail; struct isoent *isoent; int weight; int noff; int rename_num; } *idrent_pool; struct archive_rb_tree rbtree; struct { struct idrent *first; struct idrent **last; } wait_list; int pool_size; int pool_idx; int num_size; int null_size; char char_map[0x80]; }; enum char_type { A_CHAR, D_CHAR }; static int iso9660_options(struct archive_write *, const char *, const char *); static int iso9660_write_header(struct archive_write *, struct archive_entry *); static ssize_t iso9660_write_data(struct archive_write *, const void *, size_t); static int iso9660_finish_entry(struct archive_write *); static int iso9660_close(struct archive_write *); static int iso9660_free(struct archive_write *); static void get_system_identitier(char *, size_t); static void set_str(unsigned char *, const char *, size_t, char, const char *); static inline int joliet_allowed_char(unsigned char, unsigned char); static int set_str_utf16be(struct archive_write *, unsigned char *, const char *, size_t, uint16_t, enum vdc); static int set_str_a_characters_bp(struct archive_write *, unsigned char *, int, int, const char *, enum vdc); static int set_str_d_characters_bp(struct archive_write *, unsigned char *, int, int, const char *, enum vdc); static void set_VD_bp(unsigned char *, enum VD_type, unsigned char); static inline void set_unused_field_bp(unsigned char *, int, int); static unsigned char *extra_open_record(unsigned char *, int, struct isoent *, struct ctl_extr_rec *); static void extra_close_record(struct ctl_extr_rec *, int); static unsigned char * extra_next_record(struct ctl_extr_rec *, int); static unsigned char *extra_get_record(struct isoent *, int *, int *, int *); static void extra_tell_used_size(struct ctl_extr_rec *, int); static int extra_setup_location(struct isoent *, int); static int set_directory_record_rr(unsigned char *, int, struct isoent *, struct iso9660 *, enum dir_rec_type); static int set_directory_record(unsigned char *, size_t, struct isoent *, struct iso9660 *, enum dir_rec_type, enum vdd_type); static inline int get_dir_rec_size(struct iso9660 *, struct isoent *, enum dir_rec_type, enum vdd_type); static inline unsigned char *wb_buffptr(struct archive_write *); static int wb_write_out(struct archive_write *); static int wb_consume(struct archive_write *, size_t); #ifdef HAVE_ZLIB_H static int wb_set_offset(struct archive_write *, int64_t); #endif static int write_null(struct archive_write *, size_t); static int write_VD_terminator(struct archive_write *); static int set_file_identifier(unsigned char *, int, int, enum vdc, struct archive_write *, struct vdd *, struct archive_string *, const char *, int, enum char_type); static int write_VD(struct archive_write *, struct vdd *); static int write_VD_boot_record(struct archive_write *); static int write_information_block(struct archive_write *); static int write_path_table(struct archive_write *, int, struct vdd *); static int write_directory_descriptors(struct archive_write *, struct vdd *); static int write_file_descriptors(struct archive_write *); static int write_rr_ER(struct archive_write *); static void calculate_path_table_size(struct vdd *); static void isofile_init_entry_list(struct iso9660 *); static void isofile_add_entry(struct iso9660 *, struct isofile *); static void isofile_free_all_entries(struct iso9660 *); static void isofile_init_entry_data_file_list(struct iso9660 *); static void isofile_add_data_file(struct iso9660 *, struct isofile *); static struct isofile * isofile_new(struct archive_write *, struct archive_entry *); static void isofile_free(struct isofile *); static int isofile_gen_utility_names(struct archive_write *, struct isofile *); static int isofile_register_hardlink(struct archive_write *, struct isofile *); static void isofile_connect_hardlink_files(struct iso9660 *); static void isofile_init_hardlinks(struct iso9660 *); static void isofile_free_hardlinks(struct iso9660 *); static struct isoent *isoent_new(struct isofile *); static int isoent_clone_tree(struct archive_write *, struct isoent **, struct isoent *); static void _isoent_free(struct isoent *isoent); static void isoent_free_all(struct isoent *); static struct isoent * isoent_create_virtual_dir(struct archive_write *, struct iso9660 *, const char *); static int isoent_cmp_node(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key(const struct archive_rb_node *, const void *); static int isoent_add_child_head(struct isoent *, struct isoent *); static int isoent_add_child_tail(struct isoent *, struct isoent *); static void isoent_remove_child(struct isoent *, struct isoent *); static void isoent_setup_directory_location(struct iso9660 *, int, struct vdd *); static void isoent_setup_file_location(struct iso9660 *, int); static int get_path_component(char *, size_t, const char *); static int isoent_tree(struct archive_write *, struct isoent **); static struct isoent *isoent_find_child(struct isoent *, const char *); static struct isoent *isoent_find_entry(struct isoent *, const char *); static void idr_relaxed_filenames(char *); static void idr_init(struct iso9660 *, struct vdd *, struct idr *); static void idr_cleanup(struct idr *); static int idr_ensure_poolsize(struct archive_write *, struct idr *, int); static int idr_start(struct archive_write *, struct idr *, int, int, int, int, const struct archive_rb_tree_ops *); static void idr_register(struct idr *, struct isoent *, int, int); static void idr_extend_identifier(struct idrent *, int, int); static void idr_resolve(struct idr *, void (*)(unsigned char *, int)); static void idr_set_num(unsigned char *, int); static void idr_set_num_beutf16(unsigned char *, int); static int isoent_gen_iso9660_identifier(struct archive_write *, struct isoent *, struct idr *); static int isoent_gen_joliet_identifier(struct archive_write *, struct isoent *, struct idr *); static int isoent_cmp_iso9660_identifier(const struct isoent *, const struct isoent *); static int isoent_cmp_node_iso9660(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key_iso9660(const struct archive_rb_node *, const void *); static int isoent_cmp_joliet_identifier(const struct isoent *, const struct isoent *); static int isoent_cmp_node_joliet(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key_joliet(const struct archive_rb_node *, const void *); static inline void path_table_add_entry(struct path_table *, struct isoent *); static inline struct isoent * path_table_last_entry(struct path_table *); static int isoent_make_path_table(struct archive_write *); static int isoent_find_out_boot_file(struct archive_write *, struct isoent *); static int isoent_create_boot_catalog(struct archive_write *, struct isoent *); static size_t fd_boot_image_size(int); static int make_boot_catalog(struct archive_write *); static int setup_boot_information(struct archive_write *); static int zisofs_init(struct archive_write *, struct isofile *); static void zisofs_detect_magic(struct archive_write *, const void *, size_t); static int zisofs_write_to_temp(struct archive_write *, const void *, size_t); static int zisofs_finish_entry(struct archive_write *); static int zisofs_rewind_boot_file(struct archive_write *); static int zisofs_free(struct archive_write *); int archive_write_set_format_iso9660(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct iso9660 *iso9660; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); iso9660 = calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } iso9660->birth_time = 0; iso9660->temp_fd = -1; iso9660->cur_file = NULL; iso9660->primary.max_depth = 0; iso9660->primary.vdd_type = VDD_PRIMARY; iso9660->primary.pathtbl = NULL; iso9660->joliet.rootent = NULL; iso9660->joliet.max_depth = 0; iso9660->joliet.vdd_type = VDD_JOLIET; iso9660->joliet.pathtbl = NULL; isofile_init_entry_list(iso9660); isofile_init_entry_data_file_list(iso9660); isofile_init_hardlinks(iso9660); iso9660->directories_too_deep = NULL; iso9660->dircnt_max = 1; iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_TEMP; iso9660->wbuff_offset = 0; iso9660->wbuff_written = 0; iso9660->wbuff_tail = 0; archive_string_init(&(iso9660->utf16be)); archive_string_init(&(iso9660->mbs)); /* * Init Identifiers used for PVD and SVD. */ archive_string_init(&(iso9660->volume_identifier)); archive_strcpy(&(iso9660->volume_identifier), "CDROM"); archive_string_init(&(iso9660->publisher_identifier)); archive_string_init(&(iso9660->data_preparer_identifier)); archive_string_init(&(iso9660->application_identifier)); archive_strcpy(&(iso9660->application_identifier), archive_version_string()); archive_string_init(&(iso9660->copyright_file_identifier)); archive_string_init(&(iso9660->abstract_file_identifier)); archive_string_init(&(iso9660->bibliographic_file_identifier)); /* * Init El Torito bootable CD variables. */ archive_string_init(&(iso9660->el_torito.catalog_filename)); iso9660->el_torito.catalog = NULL; /* Set default file name of boot catalog */ archive_strcpy(&(iso9660->el_torito.catalog_filename), "boot.catalog"); archive_string_init(&(iso9660->el_torito.boot_filename)); iso9660->el_torito.boot = NULL; iso9660->el_torito.platform_id = BOOT_PLATFORM_X86; archive_string_init(&(iso9660->el_torito.id)); iso9660->el_torito.boot_load_seg = 0; iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE; /* * Init zisofs variables. */ #ifdef HAVE_ZLIB_H iso9660->zisofs.block_pointers = NULL; iso9660->zisofs.block_pointers_allocated = 0; iso9660->zisofs.stream_valid = 0; iso9660->zisofs.compression_level = 9; memset(&(iso9660->zisofs.stream), 0, sizeof(iso9660->zisofs.stream)); #endif /* * Set default value of iso9660 options. */ iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT; iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT; iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT; iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT; iso9660->opt.boot = OPT_BOOT_DEFAULT; iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT; iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT; iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT; iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT; iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT; iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT; iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT; iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT; iso9660->opt.joliet = OPT_JOLIET_DEFAULT; iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT; iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT; iso9660->opt.pad = OPT_PAD_DEFAULT; iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT; iso9660->opt.rr = OPT_RR_DEFAULT; iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT; iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT; /* Create the root directory. */ iso9660->primary.rootent = isoent_create_virtual_dir(a, iso9660, ""); if (iso9660->primary.rootent == NULL) { free(iso9660); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } iso9660->primary.rootent->parent = iso9660->primary.rootent; iso9660->cur_dirent = iso9660->primary.rootent; archive_string_init(&(iso9660->cur_dirstr)); archive_string_ensure(&(iso9660->cur_dirstr), 1); iso9660->cur_dirstr.s[0] = 0; iso9660->sconv_to_utf16be = NULL; iso9660->sconv_from_utf16be = NULL; a->format_data = iso9660; a->format_name = "iso9660"; a->format_options = iso9660_options; a->format_write_header = iso9660_write_header; a->format_write_data = iso9660_write_data; a->format_finish_entry = iso9660_finish_entry; a->format_close = iso9660_close; a->format_free = iso9660_free; a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; return (ARCHIVE_OK); } static int get_str_opt(struct archive_write *a, struct archive_string *s, size_t maxsize, const char *key, const char *value) { if (strlen(value) > maxsize) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Value is longer than %zu characters " "for option ``%s''", maxsize, key); return (ARCHIVE_FATAL); } archive_strcpy(s, value); return (ARCHIVE_OK); } static int get_num_opt(struct archive_write *a, int *num, int high, int low, const char *key, const char *value) { const char *p = value; int data = 0; int neg = 0; if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(empty) for option ``%s''", key); return (ARCHIVE_FATAL); } if (*p == '-') { neg = 1; p++; } while (*p) { if (*p >= '0' && *p <= '9') data = data * 10 + *p - '0'; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value for option ``%s''", key); return (ARCHIVE_FATAL); } if (data > high) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(over %d) for " "option ``%s''", high, key); return (ARCHIVE_FATAL); } if (data < low) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(under %d) for " "option ``%s''", low, key); return (ARCHIVE_FATAL); } p++; } if (neg) data *= -1; *num = data; return (ARCHIVE_OK); } static int iso9660_options(struct archive_write *a, const char *key, const char *value) { struct iso9660 *iso9660 = a->format_data; const char *p; int r; switch (key[0]) { case 'a': if (strcmp(key, "abstract-file") == 0) { r = get_str_opt(a, &(iso9660->abstract_file_identifier), ABSTRACT_FILE_SIZE, key, value); iso9660->opt.abstract_file = r == ARCHIVE_OK; return (r); } if (strcmp(key, "application-id") == 0) { r = get_str_opt(a, &(iso9660->application_identifier), APPLICATION_IDENTIFIER_SIZE, key, value); iso9660->opt.application_id = r == ARCHIVE_OK; return (r); } if (strcmp(key, "allow-vernum") == 0) { iso9660->opt.allow_vernum = value != NULL; return (ARCHIVE_OK); } break; case 'b': if (strcmp(key, "biblio-file") == 0) { r = get_str_opt(a, &(iso9660->bibliographic_file_identifier), BIBLIO_FILE_SIZE, key, value); iso9660->opt.biblio_file = r == ARCHIVE_OK; return (r); } if (strcmp(key, "boot") == 0) { if (value == NULL) iso9660->opt.boot = 0; else { iso9660->opt.boot = 1; archive_strcpy( &(iso9660->el_torito.boot_filename), value); } return (ARCHIVE_OK); } if (strcmp(key, "boot-catalog") == 0) { r = get_str_opt(a, &(iso9660->el_torito.catalog_filename), 1024, key, value); iso9660->opt.boot_catalog = r == ARCHIVE_OK; return (r); } if (strcmp(key, "boot-info-table") == 0) { iso9660->opt.boot_info_table = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "boot-load-seg") == 0) { uint32_t seg; iso9660->opt.boot_load_seg = 0; if (value == NULL) goto invalid_value; seg = 0; p = value; if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) p += 2; while (*p) { if (seg) seg <<= 4; if (*p >= 'A' && *p <= 'F') seg += *p - 'A' + 0x0a; else if (*p >= 'a' && *p <= 'f') seg += *p - 'a' + 0x0a; else if (*p >= '0' && *p <= '9') seg += *p - '0'; else goto invalid_value; if (seg > 0xffff) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(over 0xffff) for " "option ``%s''", key); return (ARCHIVE_FATAL); } p++; } iso9660->el_torito.boot_load_seg = (uint16_t)seg; iso9660->opt.boot_load_seg = 1; return (ARCHIVE_OK); } if (strcmp(key, "boot-load-size") == 0) { int num = 0; r = get_num_opt(a, &num, 0xffff, 1, key, value); iso9660->opt.boot_load_size = r == ARCHIVE_OK; if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->el_torito.boot_load_size = (uint16_t)num; return (ARCHIVE_OK); } if (strcmp(key, "boot-type") == 0) { if (value == NULL) goto invalid_value; if (strcmp(value, "no-emulation") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU; else if (strcmp(value, "fd") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_FD; else if (strcmp(value, "hard-disk") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK; else goto invalid_value; return (ARCHIVE_OK); } break; case 'c': if (strcmp(key, "compression-level") == 0) { #ifdef HAVE_ZLIB_H if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || value[1] != '\0') goto invalid_value; iso9660->zisofs.compression_level = value[0] - '0'; iso9660->opt.compression_level = 1; return (ARCHIVE_OK); #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Option ``%s'' " "is not supported on this platform.", key); return (ARCHIVE_FATAL); #endif } if (strcmp(key, "copyright-file") == 0) { r = get_str_opt(a, &(iso9660->copyright_file_identifier), COPYRIGHT_FILE_SIZE, key, value); iso9660->opt.copyright_file = r == ARCHIVE_OK; return (r); } #ifdef DEBUG /* Specifies Volume creation date and time; * year(4),month(2),day(2),hour(2),minute(2),second(2). * e.g. "20090929033757" */ if (strcmp(key, "creation") == 0) { struct tm tm; char buf[5]; p = value; if (p == NULL || strlen(p) < 14) goto invalid_value; memset(&tm, 0, sizeof(tm)); memcpy(buf, p, 4); buf[4] = '\0'; p += 4; tm.tm_year = strtol(buf, NULL, 10) - 1900; memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_mon = strtol(buf, NULL, 10) - 1; memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_mday = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_hour = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_min = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; tm.tm_sec = strtol(buf, NULL, 10); iso9660->birth_time = mktime(&tm); return (ARCHIVE_OK); } #endif break; case 'i': if (strcmp(key, "iso-level") == 0) { if (value != NULL && value[1] == '\0' && (value[0] >= '1' && value[0] <= '4')) { iso9660->opt.iso_level = value[0]-'0'; return (ARCHIVE_OK); } goto invalid_value; } break; case 'j': if (strcmp(key, "joliet") == 0) { if (value == NULL) iso9660->opt.joliet = OPT_JOLIET_DISABLE; else if (strcmp(value, "1") == 0) iso9660->opt.joliet = OPT_JOLIET_ENABLE; else if (strcmp(value, "long") == 0) iso9660->opt.joliet = OPT_JOLIET_LONGNAME; else goto invalid_value; return (ARCHIVE_OK); } break; case 'l': if (strcmp(key, "limit-depth") == 0) { iso9660->opt.limit_depth = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "limit-dirs") == 0) { iso9660->opt.limit_dirs = value != NULL; return (ARCHIVE_OK); } break; case 'p': if (strcmp(key, "pad") == 0) { iso9660->opt.pad = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "publisher") == 0) { r = get_str_opt(a, &(iso9660->publisher_identifier), PUBLISHER_IDENTIFIER_SIZE, key, value); iso9660->opt.publisher = r == ARCHIVE_OK; return (r); } break; case 'r': if (strcmp(key, "rockridge") == 0 || strcmp(key, "Rockridge") == 0) { if (value == NULL) iso9660->opt.rr = OPT_RR_DISABLED; else if (strcmp(value, "1") == 0) iso9660->opt.rr = OPT_RR_USEFUL; else if (strcmp(value, "strict") == 0) iso9660->opt.rr = OPT_RR_STRICT; else if (strcmp(value, "useful") == 0) iso9660->opt.rr = OPT_RR_USEFUL; else goto invalid_value; return (ARCHIVE_OK); } break; case 'v': if (strcmp(key, "volume-id") == 0) { r = get_str_opt(a, &(iso9660->volume_identifier), VOLUME_IDENTIFIER_SIZE, key, value); iso9660->opt.volume_id = r == ARCHIVE_OK; return (r); } break; case 'z': if (strcmp(key, "zisofs") == 0) { if (value == NULL) iso9660->opt.zisofs = OPT_ZISOFS_DISABLED; else { #ifdef HAVE_ZLIB_H iso9660->opt.zisofs = OPT_ZISOFS_DIRECT; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "``zisofs'' " "is not supported on this platform."); return (ARCHIVE_FATAL); #endif } return (ARCHIVE_OK); } break; } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); invalid_value: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value for option ``%s''", key); return (ARCHIVE_FAILED); } static int iso9660_write_header(struct archive_write *a, struct archive_entry *entry) { struct iso9660 *iso9660; struct isofile *file; struct isoent *isoent; int r, ret = ARCHIVE_OK; iso9660 = a->format_data; iso9660->cur_file = NULL; iso9660->bytes_remaining = 0; iso9660->need_multi_extent = 0; if (archive_entry_filetype(entry) == AE_IFLNK && iso9660->opt.rr == OPT_RR_DISABLED) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore symlink file."); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) >= MULTI_EXTENT_SIZE) { if (iso9660->opt.iso_level < 3) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore over %lld bytes file. " "This file too large.", MULTI_EXTENT_SIZE); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } iso9660->need_multi_extent = 1; } file = isofile_new(a, entry); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } r = isofile_gen_utility_names(a, file); if (r < ARCHIVE_WARN) { isofile_free(file); return (r); } else if (r < ret) ret = r; /* * Ignore a path which looks like the top of directory name * since we have already made the root directory of an ISO image. */ if (archive_strlen(&(file->parentdir)) == 0 && archive_strlen(&(file->basename)) == 0) { isofile_free(file); return (r); } isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } if (isoent->file->dircnt > iso9660->dircnt_max) iso9660->dircnt_max = isoent->file->dircnt; /* Add the current file into tree */ r = isoent_tree(a, &isoent); if (r != ARCHIVE_OK) return (r); /* If there is the same file in tree and * the current file is older than the file in tree. * So we don't need the current file data anymore. */ if (isoent->file != file) return (ARCHIVE_OK); /* Non regular files contents are unneeded to be saved to * temporary files. */ if (archive_entry_filetype(file->entry) != AE_IFREG) return (ret); /* * Set the current file to cur_file to read its contents. */ iso9660->cur_file = file; if (archive_entry_nlink(file->entry) > 1) { r = isofile_register_hardlink(a, file); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* * Prepare to save the contents of the file. */ if (iso9660->temp_fd < 0) { iso9660->temp_fd = __archive_mktemp(NULL); if (iso9660->temp_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } } /* Save an offset of current file in temporary file. */ file->content.offset_of_temp = wb_offset(a); file->cur_content = &(file->content); r = zisofs_init(a, file); if (r < ret) ret = r; iso9660->bytes_remaining = archive_entry_size(file->entry); return (ret); } static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; ssize_t written; const unsigned char *b; b = (const unsigned char *)buff; while (s) { written = write(iso9660->temp_fd, b, s); if (written < 0) { archive_set_error(&a->archive, errno, "Can't write to temporary file"); return (ARCHIVE_FATAL); } s -= written; b += written; } return (ARCHIVE_OK); } static int wb_write_to_temp(struct archive_write *a, const void *buff, size_t s) { const char *xp = buff; size_t xs = s; /* * If a written data size is big enough to use system-call * and there is no waiting data, this calls write_to_temp() in * order to reduce a extra memory copy. */ if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; xs = s % LOGICAL_BLOCK_SIZE; iso9660->wbuff_offset += s - xs; if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (xs == 0) return (ARCHIVE_OK); xp += s - xs; } while (xs) { size_t size = xs; if (size > wb_remaining(a)) size = wb_remaining(a); memcpy(wb_buffptr(a), xp, size); if (wb_consume(a, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); xs -= size; xp += size; } return (ARCHIVE_OK); } static int wb_write_padding_to_temp(struct archive_write *a, int64_t csize) { size_t ns; int ret; ns = (size_t)(csize % LOGICAL_BLOCK_SIZE); if (ns != 0) ret = write_null(a, LOGICAL_BLOCK_SIZE - ns); else ret = ARCHIVE_OK; return (ret); } static ssize_t write_iso9660_data(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; size_t ws; if (iso9660->temp_fd < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } ws = s; if (iso9660->need_multi_extent && (iso9660->cur_file->cur_content->size + ws) >= (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) { struct content *con; size_t ts; ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE - iso9660->cur_file->cur_content->size); if (iso9660->zisofs.detect_magic) zisofs_detect_magic(a, buff, ts); if (iso9660->zisofs.making) { if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->cur_file->cur_content->size += ts; } /* Write padding. */ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Compute the logical block number. */ iso9660->cur_file->cur_content->blocks = (int) ((iso9660->cur_file->cur_content->size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); /* * Make next extent. */ ws -= ts; buff = (const void *)(((const unsigned char *)buff) + ts); /* Make a content for next extent. */ con = calloc(1, sizeof(*con)); if (con == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate content data"); return (ARCHIVE_FATAL); } con->offset_of_temp = wb_offset(a); iso9660->cur_file->cur_content->next = con; iso9660->cur_file->cur_content = con; #ifdef HAVE_ZLIB_H iso9660->zisofs.block_offset = 0; #endif } if (iso9660->zisofs.detect_magic) zisofs_detect_magic(a, buff, ws); if (iso9660->zisofs.making) { if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->cur_file->cur_content->size += ws; } return (s); } static ssize_t iso9660_write_data(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; ssize_t r; if (iso9660->cur_file == NULL) return (0); if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) return (0); if (s > iso9660->bytes_remaining) s = (size_t)iso9660->bytes_remaining; if (s == 0) return (0); r = write_iso9660_data(a, buff, s); if (r > 0) iso9660->bytes_remaining -= r; return (r); } static int iso9660_finish_entry(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; if (iso9660->cur_file == NULL) return (ARCHIVE_OK); if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) return (ARCHIVE_OK); if (iso9660->cur_file->content.size == 0) return (ARCHIVE_OK); /* If there are unwritten data, write null data instead. */ while (iso9660->bytes_remaining > 0) { size_t s; s = (iso9660->bytes_remaining > a->null_length)? a->null_length: (size_t)iso9660->bytes_remaining; if (write_iso9660_data(a, a->nulls, s) < 0) return (ARCHIVE_FATAL); iso9660->bytes_remaining -= s; } if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write padding. */ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Compute the logical block number. */ iso9660->cur_file->cur_content->blocks = (int) ((iso9660->cur_file->cur_content->size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); /* Add the current file to data file list. */ isofile_add_data_file(iso9660, iso9660->cur_file); return (ARCHIVE_OK); } static int iso9660_close(struct archive_write *a) { struct iso9660 *iso9660; int ret, blocks; iso9660 = a->format_data; /* * Write remaining data out to the temporary file. */ if (wb_remaining(a) > 0) { ret = wb_write_out(a); if (ret < 0) return (ret); } /* * Preparations... */ #ifdef DEBUG if (iso9660->birth_time == 0) #endif time(&(iso9660->birth_time)); /* * Prepare a bootable ISO image. */ if (iso9660->opt.boot) { /* Find out the boot file entry. */ ret = isoent_find_out_boot_file(a, iso9660->primary.rootent); if (ret < 0) return (ret); /* Reconvert the boot file from zisofs'ed form to * plain form. */ ret = zisofs_rewind_boot_file(a); if (ret < 0) return (ret); /* Write remaining data out to the temporary file. */ if (wb_remaining(a) > 0) { ret = wb_write_out(a); if (ret < 0) return (ret); } /* Create the boot catalog. */ ret = isoent_create_boot_catalog(a, iso9660->primary.rootent); if (ret < 0) return (ret); } /* * Prepare joliet extensions. */ if (iso9660->opt.joliet) { /* Make a new tree for joliet. */ ret = isoent_clone_tree(a, &(iso9660->joliet.rootent), iso9660->primary.rootent); if (ret < 0) return (ret); /* Make sure we have UTF-16BE converters. * if there is no file entry, converters are still * uninitialized. */ if (iso9660->sconv_to_utf16be == NULL) { iso9660->sconv_to_utf16be = archive_string_conversion_to_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_to_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); iso9660->sconv_from_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_from_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } } /* * Make Path Tables. */ ret = isoent_make_path_table(a); if (ret < 0) return (ret); /* * Calculate a total volume size and setup all locations of * contents of an iso9660 image. */ blocks = SYSTEM_AREA_BLOCK + PRIMARY_VOLUME_DESCRIPTOR_BLOCK + VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK + NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; if (iso9660->opt.boot) blocks += BOOT_RECORD_DESCRIPTOR_BLOCK; if (iso9660->opt.joliet) blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; if (iso9660->opt.iso_level == 4) blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; /* Setup the locations of Path Table. */ iso9660->primary.location_type_L_path_table = blocks; blocks += iso9660->primary.path_table_block; iso9660->primary.location_type_M_path_table = blocks; blocks += iso9660->primary.path_table_block; if (iso9660->opt.joliet) { iso9660->joliet.location_type_L_path_table = blocks; blocks += iso9660->joliet.path_table_block; iso9660->joliet.location_type_M_path_table = blocks; blocks += iso9660->joliet.path_table_block; } /* Setup the locations of directories. */ isoent_setup_directory_location(iso9660, blocks, &(iso9660->primary)); blocks += iso9660->primary.total_dir_block; if (iso9660->opt.joliet) { isoent_setup_directory_location(iso9660, blocks, &(iso9660->joliet)); blocks += iso9660->joliet.total_dir_block; } if (iso9660->opt.rr) { iso9660->location_rrip_er = blocks; blocks += RRIP_ER_BLOCK; } /* Setup the locations of all file contents. */ isoent_setup_file_location(iso9660, blocks); blocks += iso9660->total_file_block; if (iso9660->opt.boot && iso9660->opt.boot_info_table) { ret = setup_boot_information(a); if (ret < 0) return (ret); } /* Now we have a total volume size. */ iso9660->volume_space_size = blocks; if (iso9660->opt.pad) iso9660->volume_space_size += PADDING_BLOCK; iso9660->volume_sequence_number = 1; /* * Write an ISO 9660 image. */ /* Switch to start using wbuff as file buffer. */ iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_STREAM; iso9660->wbuff_offset = 0; iso9660->wbuff_written = 0; iso9660->wbuff_tail = 0; /* Write The System Area */ ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Primary Volume Descriptor */ ret = write_VD(a, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.boot) { /* Write Boot Record Volume Descriptor */ ret = write_VD_boot_record(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.iso_level == 4) { /* Write Enhanced Volume Descriptor */ iso9660->primary.vdd_type = VDD_ENHANCED; ret = write_VD(a, &(iso9660->primary)); iso9660->primary.vdd_type = VDD_PRIMARY; if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.joliet) { ret = write_VD(a, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write Volume Descriptor Set Terminator */ ret = write_VD_terminator(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Non-ISO File System Information */ ret = write_information_block(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type L Path Table */ ret = write_path_table(a, 0, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type M Path Table */ ret = write_path_table(a, 1, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.joliet) { /* Write Type L Path Table */ ret = write_path_table(a, 0, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type M Path Table */ ret = write_path_table(a, 1, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write Directory Descriptors */ ret = write_directory_descriptors(a, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.joliet) { ret = write_directory_descriptors(a, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.rr) { /* Write Rockridge ER(Extensions Reference) */ ret = write_rr_ER(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write File Descriptors */ ret = write_file_descriptors(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Padding */ if (iso9660->opt.pad) { ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->directories_too_deep != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Directories too deep.", archive_entry_pathname( iso9660->directories_too_deep->file->entry)); return (ARCHIVE_WARN); } /* Write remaining data out. */ ret = wb_write_out(a); return (ret); } static int iso9660_free(struct archive_write *a) { struct iso9660 *iso9660; int i, ret; iso9660 = a->format_data; /* Close the temporary file. */ if (iso9660->temp_fd >= 0) close(iso9660->temp_fd); /* Free some stuff for zisofs operations. */ ret = zisofs_free(a); /* Remove directory entries in tree which includes file entries. */ isoent_free_all(iso9660->primary.rootent); for (i = 0; i < iso9660->primary.max_depth; i++) free(iso9660->primary.pathtbl[i].sorted); free(iso9660->primary.pathtbl); if (iso9660->opt.joliet) { isoent_free_all(iso9660->joliet.rootent); for (i = 0; i < iso9660->joliet.max_depth; i++) free(iso9660->joliet.pathtbl[i].sorted); free(iso9660->joliet.pathtbl); } /* Remove isofile entries. */ isofile_free_all_entries(iso9660); isofile_free_hardlinks(iso9660); archive_string_free(&(iso9660->cur_dirstr)); archive_string_free(&(iso9660->volume_identifier)); archive_string_free(&(iso9660->publisher_identifier)); archive_string_free(&(iso9660->data_preparer_identifier)); archive_string_free(&(iso9660->application_identifier)); archive_string_free(&(iso9660->copyright_file_identifier)); archive_string_free(&(iso9660->abstract_file_identifier)); archive_string_free(&(iso9660->bibliographic_file_identifier)); archive_string_free(&(iso9660->el_torito.catalog_filename)); archive_string_free(&(iso9660->el_torito.boot_filename)); archive_string_free(&(iso9660->el_torito.id)); archive_string_free(&(iso9660->utf16be)); archive_string_free(&(iso9660->mbs)); free(iso9660); a->format_data = NULL; return (ret); } /* * Get the System Identifier */ static void get_system_identitier(char *system_id, size_t size) { #if defined(HAVE_SYS_UTSNAME_H) struct utsname u; uname(&u); strncpy(system_id, u.sysname, size-1); system_id[size-1] = '\0'; #elif defined(_WIN32) && !defined(__CYGWIN__) strncpy(system_id, "Windows", size-1); system_id[size-1] = '\0'; #else #error no way to get the system identifier on your platform. #endif } static void set_str(unsigned char *p, const char *s, size_t l, char f, const char *map) { unsigned char c; if (s == NULL) s = ""; while ((c = *s++) != 0 && l > 0) { if (c >= 0x80 || map[c] == 0) { /* illegal character */ if (c >= 'a' && c <= 'z') { /* convert c from a-z to A-Z */ c -= 0x20; } else c = 0x5f; } *p++ = c; l--; } /* If l isn't zero, fill p buffer by the character * which indicated by f. */ if (l > 0) memset(p , f, l); } static inline int joliet_allowed_char(unsigned char high, unsigned char low) { int utf16 = (high << 8) | low; if (utf16 <= 0x001F) return (0); switch (utf16) { case 0x002A: /* '*' */ case 0x002F: /* '/' */ case 0x003A: /* ':' */ case 0x003B: /* ';' */ case 0x003F: /* '?' */ case 0x005C: /* '\' */ return (0);/* Not allowed. */ } return (1); } static int set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s, size_t l, uint16_t uf, enum vdc vdc) { size_t size, i; int onepad; if (s == NULL) s = ""; if (l & 0x01) { onepad = 1; l &= ~1; } else onepad = 0; if (vdc == VDC_UCS2) { struct iso9660 *iso9660 = a->format_data; if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s), iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } size = iso9660->utf16be.length; if (size > l) size = l; memcpy(p, iso9660->utf16be.s, size); } else { const uint16_t *u16 = (const uint16_t *)s; size = 0; while (*u16++) size += 2; if (size > l) size = l; memcpy(p, s, size); } for (i = 0; i < size; i += 2, p += 2) { if (!joliet_allowed_char(p[0], p[1])) archive_be16enc(p, 0x005F);/* '_' */ } l -= size; while (l > 0) { archive_be16enc(p, uf); p += 2; l -= 2; } if (onepad) *p = 0; return (ARCHIVE_OK); } static const char a_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char a1_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char d_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char d1_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ }; static int set_str_a_characters_bp(struct archive_write *a, unsigned char *bp, int from, int to, const char *s, enum vdc vdc) { int r; switch (vdc) { case VDC_STD: set_str(bp+from, s, to - from + 1, 0x20, a_characters_map); r = ARCHIVE_OK; break; case VDC_LOWERCASE: set_str(bp+from, s, to - from + 1, 0x20, a1_characters_map); r = ARCHIVE_OK; break; case VDC_UCS2: case VDC_UCS2_DIRECT: r = set_str_utf16be(a, bp+from, s, to - from + 1, 0x0020, vdc); break; default: r = ARCHIVE_FATAL; } return (r); } static int set_str_d_characters_bp(struct archive_write *a, unsigned char *bp, int from, int to, const char *s, enum vdc vdc) { int r; switch (vdc) { case VDC_STD: set_str(bp+from, s, to - from + 1, 0x20, d_characters_map); r = ARCHIVE_OK; break; case VDC_LOWERCASE: set_str(bp+from, s, to - from + 1, 0x20, d1_characters_map); r = ARCHIVE_OK; break; case VDC_UCS2: case VDC_UCS2_DIRECT: r = set_str_utf16be(a, bp+from, s, to - from + 1, 0x0020, vdc); break; default: r = ARCHIVE_FATAL; } return (r); } static void set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver) { /* Volume Descriptor Type */ bp[1] = (unsigned char)type; /* Standard Identifier */ memcpy(bp + 2, "CD001", 5); /* Volume Descriptor Version */ bp[7] = ver; } static inline void set_unused_field_bp(unsigned char *bp, int from, int to) { memset(bp + from, 0, to - from + 1); } /* * 8-bit unsigned numerical values. * ISO9660 Standard 7.1.1 */ static inline void set_num_711(unsigned char *p, unsigned char value) { *p = value; } /* * 8-bit signed numerical values. * ISO9660 Standard 7.1.2 */ static inline void set_num_712(unsigned char *p, char value) { *((char *)p) = value; } /* * Least significant byte first. * ISO9660 Standard 7.2.1 */ static inline void set_num_721(unsigned char *p, uint16_t value) { archive_le16enc(p, value); } /* * Most significant byte first. * ISO9660 Standard 7.2.2 */ static inline void set_num_722(unsigned char *p, uint16_t value) { archive_be16enc(p, value); } /* * Both-byte orders. * ISO9660 Standard 7.2.3 */ static void set_num_723(unsigned char *p, uint16_t value) { archive_le16enc(p, value); archive_be16enc(p+2, value); } /* * Least significant byte first. * ISO9660 Standard 7.3.1 */ static inline void set_num_731(unsigned char *p, uint32_t value) { archive_le32enc(p, value); } /* * Most significant byte first. * ISO9660 Standard 7.3.2 */ static inline void set_num_732(unsigned char *p, uint32_t value) { archive_be32enc(p, value); } /* * Both-byte orders. * ISO9660 Standard 7.3.3 */ static inline void set_num_733(unsigned char *p, uint32_t value) { archive_le32enc(p, value); archive_be32enc(p+4, value); } static void set_digit(unsigned char *p, size_t s, int value) { while (s--) { p[s] = '0' + (value % 10); value /= 10; } } #if defined(HAVE_STRUCT_TM_TM_GMTOFF) #define get_gmoffset(tm) ((tm)->tm_gmtoff) #elif defined(HAVE_STRUCT_TM___TM_GMTOFF) #define get_gmoffset(tm) ((tm)->__tm_gmtoff) #else static long get_gmoffset(struct tm *tm) { long offset; #if defined(HAVE__GET_TIMEZONE) _get_timezone(&offset); #elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) offset = _timezone; #else offset = timezone; #endif offset *= -1; if (tm->tm_isdst) offset += 3600; return (offset); } #endif static void get_tmfromtime(struct tm *tm, time_t *t) { #if HAVE_LOCALTIME_R tzset(); localtime_r(t, tm); #elif HAVE__LOCALTIME64_S __time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits _localtime64_s(tm, &tmp_t); #else memcpy(tm, localtime(t), sizeof(*tm)); #endif } /* * Date and Time Format. * ISO9660 Standard 8.4.26.1 */ static void set_date_time(unsigned char *p, time_t t) { struct tm tm; get_tmfromtime(&tm, &t); set_digit(p, 4, tm.tm_year + 1900); set_digit(p+4, 2, tm.tm_mon + 1); set_digit(p+6, 2, tm.tm_mday); set_digit(p+8, 2, tm.tm_hour); set_digit(p+10, 2, tm.tm_min); set_digit(p+12, 2, tm.tm_sec); set_digit(p+14, 2, 0); set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15))); } static void set_date_time_null(unsigned char *p) { memset(p, (int)'0', 16); p[16] = 0; } static void set_time_915(unsigned char *p, time_t t) { struct tm tm; get_tmfromtime(&tm, &t); set_num_711(p+0, tm.tm_year); set_num_711(p+1, tm.tm_mon+1); set_num_711(p+2, tm.tm_mday); set_num_711(p+3, tm.tm_hour); set_num_711(p+4, tm.tm_min); set_num_711(p+5, tm.tm_sec); set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15))); } /* * Write SUSP "CE" System Use Entry. */ static int set_SUSP_CE(unsigned char *p, int location, int offset, int size) { unsigned char *bp = p -1; /* Extend the System Use Area * "CE" Format: * len ver * +----+----+----+----+-----------+-----------+ * | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 | * +----+----+----+----+-----------+-----------+ * 0 1 2 3 4 12 20 * +-----------+ * | LOCATION3 | * +-----------+ * 20 28 * LOCATION1 : Location of Continuation of System Use Area. * LOCATION2 : Offset to Start of Continuation. * LOCATION3 : Length of the Continuation. */ bp[1] = 'C'; bp[2] = 'E'; bp[3] = RR_CE_SIZE; /* length */ bp[4] = 1; /* version */ set_num_733(bp+5, location); set_num_733(bp+13, offset); set_num_733(bp+21, size); return (RR_CE_SIZE); } /* * The functions, which names are beginning with extra_, are used to * control extra records. * The maximum size of a Directory Record is 254. When a filename is * very long, all of RRIP data of a file won't stored to the Directory * Record and so remaining RRIP data store to an extra record instead. */ static unsigned char * extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent, struct ctl_extr_rec *ctl) { ctl->bp = bp; if (bp != NULL) bp += dr_len; ctl->use_extr = 0; ctl->isoent = isoent; ctl->ce_ptr = NULL; ctl->cur_len = ctl->dr_len = dr_len; ctl->limit = DR_LIMIT; return (bp); } static void extra_close_record(struct ctl_extr_rec *ctl, int ce_size) { int padding = 0; if (ce_size > 0) extra_tell_used_size(ctl, ce_size); /* Padding. */ if (ctl->cur_len & 0x01) { ctl->cur_len++; if (ctl->bp != NULL) ctl->bp[ctl->cur_len] = 0; padding = 1; } if (ctl->use_extr) { if (ctl->ce_ptr != NULL) set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc, ctl->extr_off, ctl->cur_len - padding); } else ctl->dr_len = ctl->cur_len; } #define extra_space(ctl) ((ctl)->limit - (ctl)->cur_len) static unsigned char * extra_next_record(struct ctl_extr_rec *ctl, int length) { int cur_len = ctl->cur_len;/* save cur_len */ /* Close the current extra record or Directory Record. */ extra_close_record(ctl, RR_CE_SIZE); /* Get a next extra record. */ ctl->use_extr = 1; if (ctl->bp != NULL) { /* Storing data into an extra record. */ unsigned char *p; /* Save the pointer where a CE extension will be * stored to. */ ctl->ce_ptr = &ctl->bp[cur_len+1]; p = extra_get_record(ctl->isoent, &ctl->limit, &ctl->extr_off, &ctl->extr_loc); ctl->bp = p - 1;/* the base of bp offset is 1. */ } else /* Calculating the size of an extra record. */ (void)extra_get_record(ctl->isoent, &ctl->limit, NULL, NULL); ctl->cur_len = 0; /* Check if an extra record is almost full. * If so, get a next one. */ if (extra_space(ctl) < length) (void)extra_next_record(ctl, length); return (ctl->bp); } static inline struct extr_rec * extra_last_record(struct isoent *isoent) { if (isoent->extr_rec_list.first == NULL) return (NULL); return ((struct extr_rec *)(void *) ((char *)(isoent->extr_rec_list.last) - offsetof(struct extr_rec, next))); } static unsigned char * extra_get_record(struct isoent *isoent, int *space, int *off, int *loc) { struct extr_rec *rec; isoent = isoent->parent; if (off != NULL) { /* Storing data into an extra record. */ rec = isoent->extr_rec_list.current; if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) rec = rec->next; } else { /* Calculating the size of an extra record. */ rec = extra_last_record(isoent); if (rec == NULL || DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) { rec = malloc(sizeof(*rec)); if (rec == NULL) return (NULL); rec->location = 0; rec->offset = 0; /* Insert `rec` into the tail of isoent->extr_rec_list */ rec->next = NULL; /* * Note: testing isoent->extr_rec_list.last == NULL * here is really unneeded since it has been already * initialized at isoent_new function but Clang Static * Analyzer claims that it is dereference of null * pointer. */ if (isoent->extr_rec_list.last == NULL) isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); *isoent->extr_rec_list.last = rec; isoent->extr_rec_list.last = &(rec->next); } } *space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY; if (*space & 0x01) *space -= 1;/* Keep padding space. */ if (off != NULL) *off = rec->offset; if (loc != NULL) *loc = rec->location; isoent->extr_rec_list.current = rec; return (&rec->buf[rec->offset]); } static void extra_tell_used_size(struct ctl_extr_rec *ctl, int size) { struct isoent *isoent; struct extr_rec *rec; if (ctl->use_extr) { isoent = ctl->isoent->parent; rec = isoent->extr_rec_list.current; if (rec != NULL) rec->offset += size; } ctl->cur_len += size; } static int extra_setup_location(struct isoent *isoent, int location) { struct extr_rec *rec; int cnt; cnt = 0; rec = isoent->extr_rec_list.first; isoent->extr_rec_list.current = rec; while (rec) { cnt++; rec->location = location++; rec->offset = 0; rec = rec->next; } return (cnt); } /* * Create the RRIP entries. */ static int set_directory_record_rr(unsigned char *bp, int dr_len, struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t) { /* Flags(BP 5) of the Rockridge "RR" System Use Field */ unsigned char rr_flag; #define RR_USE_PX 0x01 #define RR_USE_PN 0x02 #define RR_USE_SL 0x04 #define RR_USE_NM 0x08 #define RR_USE_CL 0x10 #define RR_USE_PL 0x20 #define RR_USE_RE 0x40 #define RR_USE_TF 0x80 int length; struct ctl_extr_rec ctl; struct isoent *rr_parent, *pxent; struct isofile *file; bp = extra_open_record(bp, dr_len, isoent, &ctl); if (t == DIR_REC_PARENT) { rr_parent = isoent->rr_parent; pxent = isoent->parent; if (rr_parent != NULL) isoent = rr_parent; else isoent = isoent->parent; } else { rr_parent = NULL; pxent = isoent; } file = isoent->file; if (t != DIR_REC_NORMAL) { rr_flag = RR_USE_PX | RR_USE_TF; if (rr_parent != NULL) rr_flag |= RR_USE_PL; } else { rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF; if (archive_entry_filetype(file->entry) == AE_IFLNK) rr_flag |= RR_USE_SL; if (isoent->rr_parent != NULL) rr_flag |= RR_USE_RE; if (isoent->rr_child != NULL) rr_flag |= RR_USE_CL; if (archive_entry_filetype(file->entry) == AE_IFCHR || archive_entry_filetype(file->entry) == AE_IFBLK) rr_flag |= RR_USE_PN; #ifdef COMPAT_MKISOFS /* * mkisofs 2.01.01a63 records "RE" extension to * the entry of "rr_moved" directory. * I don't understand this behavior. */ if (isoent->virtual && isoent->parent == iso9660->primary.rootent && strcmp(isoent->file->basename.s, "rr_moved") == 0) rr_flag |= RR_USE_RE; #endif } /* Write "SP" System Use Entry. */ if (t == DIR_REC_SELF && isoent == isoent->parent) { length = 7; if (bp != NULL) { bp[1] = 'S'; bp[2] = 'P'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = 0xBE; /* Check Byte */ bp[6] = 0xEF; /* Check Byte */ bp[7] = 0; bp += length; } extra_tell_used_size(&ctl, length); } /* Write "RR" System Use Entry. */ length = 5; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'R'; bp[2] = 'R'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = rr_flag; bp += length; } extra_tell_used_size(&ctl, length); /* Write "NM" System Use Entry. */ if (rr_flag & RR_USE_NM) { /* * "NM" Format: * e.g. a basename is 'foo' * len ver flg * +----+----+----+----+----+----+----+----+ * | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'| * +----+----+----+----+----+----+----+----+ * <----------------- len -----------------> */ size_t nmlen = file->basename.length; const char *nm = file->basename.s; size_t nmmax; if (extra_space(&ctl) < 6) bp = extra_next_record(&ctl, 6); if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; bp[4] = 1; /* version */ } nmmax = extra_space(&ctl); if (nmmax > 0xff) nmmax = 0xff; while (nmlen + 5 > nmmax) { length = (int)nmmax; if (bp != NULL) { bp[3] = length; bp[5] = 0x01;/* Alternate Name continues * in next "NM" field */ memcpy(bp+6, nm, length - 5); bp += length; } nmlen -= length - 5; nm += length - 5; extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 6) { bp = extra_next_record(&ctl, 6); nmmax = extra_space(&ctl); if (nmmax > 0xff) nmmax = 0xff; } if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; bp[4] = 1; /* version */ } } length = 5 + (int)nmlen; if (bp != NULL) { bp[3] = length; bp[5] = 0; memcpy(bp+6, nm, nmlen); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PX" System Use Entry. */ if (rr_flag & RR_USE_PX) { /* * "PX" Format: * len ver * +----+----+----+----+-----------+-----------+ * | 'P'| 'X'| 2C | 01 | FILE MODE | LINKS | * +----+----+----+----+-----------+-----------+ * 0 1 2 3 4 12 20 * +-----------+-----------+------------------+ * | USER ID | GROUP ID |FILE SERIAL NUMBER| * +-----------+-----------+------------------+ * 20 28 36 44 */ length = 44; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { mode_t mode; int64_t uid; int64_t gid; mode = archive_entry_mode(file->entry); uid = archive_entry_uid(file->entry); gid = archive_entry_gid(file->entry); if (iso9660->opt.rr == OPT_RR_USEFUL) { /* * This action is similar to mkisofs -r option * but our rockridge=useful option does not * set a zero to uid and gid. */ /* set all read bit ON */ mode |= 0444; #if !defined(_WIN32) && !defined(__CYGWIN__) if (mode & 0111) #endif /* set all exec bit ON */ mode |= 0111; /* clear all write bits. */ mode &= ~0222; /* clear setuid,setgid,sticky bits. */ mode &= ~07000; } bp[1] = 'P'; bp[2] = 'X'; bp[3] = length; bp[4] = 1; /* version */ /* file mode */ set_num_733(bp+5, mode); /* file links (stat.st_nlink) */ set_num_733(bp+13, archive_entry_nlink(file->entry)); set_num_733(bp+21, (uint32_t)uid); set_num_733(bp+29, (uint32_t)gid); /* File Serial Number */ if (pxent->dir) set_num_733(bp+37, pxent->dir_location); else if (file->hardlink_target != NULL) set_num_733(bp+37, file->hardlink_target->cur_content->location); else set_num_733(bp+37, file->cur_content->location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "SL" System Use Entry. */ if (rr_flag & RR_USE_SL) { /* * "SL" Format: * e.g. a symbolic name is 'foo/bar' * len ver flg * +----+----+----+----+----+------------+ * | 'S'| 'L'| 0F | 01 | 00 | components | * +----+----+----+----+----+-----+------+ * 0 1 2 3 4 5 ...|... 15 * <----------------- len --------+------> * components : | * cflg clen | * +----+----+----+----+----+ | * | 00 | 03 | 'f'| 'o'| 'o'| <---+ * +----+----+----+----+----+ | * 5 6 7 8 9 10 | * cflg clen | * +----+----+----+----+----+ | * | 00 | 03 | 'b'| 'a'| 'r'| <---+ * +----+----+----+----+----+ * 10 11 12 13 14 15 * * - cflg : flag of component * - clen : length of component */ const char *sl; char sl_last; if (extra_space(&ctl) < 7) bp = extra_next_record(&ctl, 7); sl = file->symlink.s; sl_last = '\0'; if (bp != NULL) { bp[1] = 'S'; bp[2] = 'L'; bp[4] = 1; /* version */ } for (;;) { unsigned char *nc, *cf, *cl, cldmy = 0; int sllen, slmax; slmax = extra_space(&ctl); if (slmax > 0xff) slmax = 0xff; if (bp != NULL) nc = &bp[6]; else nc = NULL; cf = cl = NULL; sllen = 0; while (*sl && sllen + 11 < slmax) { if (sl_last == '\0' && sl[0] == '/') { /* * flg len * +----+----+ * | 08 | 00 | ROOT component. * +----+----+ ("/") * * Root component has to appear * at the first component only. */ if (nc != NULL) { cf = nc++; *cf = 0x08; /* ROOT */ *nc++ = 0; } sllen += 2; sl++; sl_last = '/'; cl = NULL; continue; } if (((sl_last == '\0' || sl_last == '/') && sl[0] == '.' && sl[1] == '.' && (sl[2] == '/' || sl[2] == '\0')) || (sl[0] == '/' && sl[1] == '.' && sl[2] == '.' && (sl[3] == '/' || sl[3] == '\0'))) { /* * flg len * +----+----+ * | 04 | 00 | PARENT component. * +----+----+ ("..") */ if (nc != NULL) { cf = nc++; *cf = 0x04; /* PARENT */ *nc++ = 0; } sllen += 2; if (sl[0] == '/') sl += 3;/* skip "/.." */ else sl += 2;/* skip ".." */ sl_last = '.'; cl = NULL; continue; } if (((sl_last == '\0' || sl_last == '/') && sl[0] == '.' && (sl[1] == '/' || sl[1] == '\0')) || (sl[0] == '/' && sl[1] == '.' && (sl[2] == '/' || sl[2] == '\0'))) { /* * flg len * +----+----+ * | 02 | 00 | CURRENT component. * +----+----+ (".") */ if (nc != NULL) { cf = nc++; *cf = 0x02; /* CURRENT */ *nc++ = 0; } sllen += 2; if (sl[0] == '/') sl += 2;/* skip "/." */ else sl ++; /* skip "." */ sl_last = '.'; cl = NULL; continue; } if (sl[0] == '/' || cl == NULL) { if (nc != NULL) { cf = nc++; *cf = 0; cl = nc++; *cl = 0; } else cl = &cldmy; sllen += 2; if (sl[0] == '/') { sl_last = *sl++; continue; } } sl_last = *sl++; if (nc != NULL) { *nc++ = sl_last; (*cl) ++; } sllen++; } if (*sl) { length = 5 + sllen; if (bp != NULL) { /* * Mark flg as CONTINUE component. */ *cf |= 0x01; /* * len ver flg * +----+----+----+----+----+- * | 'S'| 'L'| XX | 01 | 01 | * +----+----+----+----+----+- * ^ * continues in next "SL" */ bp[3] = length; bp[5] = 0x01;/* This Symbolic Link * continues in next * "SL" field */ bp += length; } extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 11) bp = extra_next_record(&ctl, 11); if (bp != NULL) { /* Next 'SL' */ bp[1] = 'S'; bp[2] = 'L'; bp[4] = 1; /* version */ } } else { length = 5 + sllen; if (bp != NULL) { bp[3] = length; bp[5] = 0; bp += length; } extra_tell_used_size(&ctl, length); break; } } } /* Write "TF" System Use Entry. */ if (rr_flag & RR_USE_TF) { /* * "TF" Format: * len ver * +----+----+----+----+-----+-------------+ * | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS | * +----+----+----+----+-----+-------------+ * 0 1 2 3 4 5 XX * TIME STAMPS : ISO 9660 Standard 9.1.5. * If TF_LONG_FORM FLAGS is set, * use ISO9660 Standard 8.4.26.1. */ #define TF_CREATION 0x01 /* Creation time recorded */ #define TF_MODIFY 0x02 /* Modification time recorded */ #define TF_ACCESS 0x04 /* Last Access time recorded */ #define TF_ATTRIBUTES 0x08 /* Last Attribute Change time recorded */ #define TF_BACKUP 0x10 /* Last Backup time recorded */ #define TF_EXPIRATION 0x20 /* Expiration time recorded */ #define TF_EFFECTIVE 0x40 /* Effective time recorded */ #define TF_LONG_FORM 0x80 /* ISO 9660 17-byte time format used */ unsigned char tf_flags; length = 5; tf_flags = 0; #ifndef COMPAT_MKISOFS if (archive_entry_birthtime_is_set(file->entry) && archive_entry_birthtime(file->entry) <= archive_entry_mtime(file->entry)) { length += 7; tf_flags |= TF_CREATION; } #endif if (archive_entry_mtime_is_set(file->entry)) { length += 7; tf_flags |= TF_MODIFY; } if (archive_entry_atime_is_set(file->entry)) { length += 7; tf_flags |= TF_ACCESS; } if (archive_entry_ctime_is_set(file->entry)) { length += 7; tf_flags |= TF_ATTRIBUTES; } if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'T'; bp[2] = 'F'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = tf_flags; bp += 5; /* Creation time */ if (tf_flags & TF_CREATION) { set_time_915(bp+1, archive_entry_birthtime(file->entry)); bp += 7; } /* Modification time */ if (tf_flags & TF_MODIFY) { set_time_915(bp+1, archive_entry_mtime(file->entry)); bp += 7; } /* Last Access time */ if (tf_flags & TF_ACCESS) { set_time_915(bp+1, archive_entry_atime(file->entry)); bp += 7; } /* Last Attribute Change time */ if (tf_flags & TF_ATTRIBUTES) { set_time_915(bp+1, archive_entry_ctime(file->entry)); bp += 7; } } extra_tell_used_size(&ctl, length); } /* Write "RE" System Use Entry. */ if (rr_flag & RR_USE_RE) { /* * "RE" Format: * len ver * +----+----+----+----+ * | 'R'| 'E'| 04 | 01 | * +----+----+----+----+ * 0 1 2 3 4 */ length = 4; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'R'; bp[2] = 'E'; bp[3] = length; bp[4] = 1; /* version */ bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PL" System Use Entry. */ if (rr_flag & RR_USE_PL) { /* * "PL" Format: * len ver * +----+----+----+----+------------+ * | 'P'| 'L'| 0C | 01 | *LOCATION | * +----+----+----+----+------------+ * 0 1 2 3 4 12 * *LOCATION: location of parent directory */ length = 12; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'P'; bp[2] = 'L'; bp[3] = length; bp[4] = 1; /* version */ set_num_733(bp + 5, rr_parent->dir_location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "CL" System Use Entry. */ if (rr_flag & RR_USE_CL) { /* * "CL" Format: * len ver * +----+----+----+----+------------+ * | 'C'| 'L'| 0C | 01 | *LOCATION | * +----+----+----+----+------------+ * 0 1 2 3 4 12 * *LOCATION: location of child directory */ length = 12; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'C'; bp[2] = 'L'; bp[3] = length; bp[4] = 1; /* version */ set_num_733(bp + 5, isoent->rr_child->dir_location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PN" System Use Entry. */ if (rr_flag & RR_USE_PN) { /* * "PN" Format: * len ver * +----+----+----+----+------------+------------+ * | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low | * +----+----+----+----+------------+------------+ * 0 1 2 3 4 12 20 */ length = 20; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { uint64_t dev; bp[1] = 'P'; bp[2] = 'N'; bp[3] = length; bp[4] = 1; /* version */ dev = (uint64_t)archive_entry_rdev(file->entry); set_num_733(bp + 5, (uint32_t)(dev >> 32)); set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF)); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "ZF" System Use Entry. */ if (file->zisofs.header_size) { /* * "ZF" Format: * len ver * +----+----+----+----+----+----+-------------+ * | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size | * +----+----+----+----+----+----+-------------+ * 0 1 2 3 4 5 6 7 * +--------------------+-------------------+ * | Log2 of block Size | Uncompressed Size | * +--------------------+-------------------+ * 7 8 16 */ length = 16; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'Z'; bp[2] = 'F'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = 'p'; bp[6] = 'z'; bp[7] = file->zisofs.header_size; bp[8] = file->zisofs.log2_bs; set_num_733(bp + 9, file->zisofs.uncompressed_size); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "CE" System Use Entry. */ if (t == DIR_REC_SELF && isoent == isoent->parent) { length = RR_CE_SIZE; if (bp != NULL) set_SUSP_CE(bp+1, iso9660->location_rrip_er, 0, RRIP_ER_SIZE); extra_tell_used_size(&ctl, length); } extra_close_record(&ctl, 0); return (ctl.dr_len); } /* * Write data of a Directory Record or calculate writing bytes itself. * If parameter `p' is NULL, calculates the size of writing data, which * a Directory Record needs to write, then it saved and return * the calculated size. * Parameter `n' is a remaining size of buffer. when parameter `p' is * not NULL, check whether that `n' is not less than the saved size. * if that `n' is small, return zero. * * This format of the Directory Record is according to * ISO9660 Standard 9.1 */ static int set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t, enum vdd_type vdd_type) { unsigned char *bp; size_t dr_len; size_t fi_len; if (p != NULL) { /* * Check whether a write buffer size is less than the * saved size which is needed to write this Directory * Record. */ switch (t) { case DIR_REC_VD: dr_len = isoent->dr_len.vd; break; case DIR_REC_SELF: dr_len = isoent->dr_len.self; break; case DIR_REC_PARENT: dr_len = isoent->dr_len.parent; break; case DIR_REC_NORMAL: default: dr_len = isoent->dr_len.normal; break; } if (dr_len > n) return (0);/* Needs more buffer size. */ } if (t == DIR_REC_NORMAL && isoent->identifier != NULL) fi_len = isoent->id_len; else fi_len = 1; if (p != NULL) { struct isoent *xisoent; struct isofile *file; unsigned char flag; if (t == DIR_REC_PARENT) xisoent = isoent->parent; else xisoent = isoent; file = isoent->file; if (file->hardlink_target != NULL) file = file->hardlink_target; /* Make a file flag. */ if (xisoent->dir) flag = FILE_FLAG_DIRECTORY; else { if (file->cur_content->next != NULL) flag = FILE_FLAG_MULTI_EXTENT; else flag = 0; } bp = p -1; /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ if (xisoent->dir) set_num_733(bp+3, xisoent->dir_location); else set_num_733(bp+3, file->cur_content->location); /* Data Length */ if (xisoent->dir) set_num_733(bp+11, xisoent->dir_block * LOGICAL_BLOCK_SIZE); else set_num_733(bp+11, (uint32_t)file->cur_content->size); /* Recording Date and Time */ /* NOTE: * If a file type is symbolic link, you are seeing this * field value is different from a value mkisofs makes. * libarchive uses lstat to get this one, but it * seems mkisofs uses stat to get. */ set_time_915(bp+19, archive_entry_mtime(xisoent->file->entry)); /* File Flags */ bp[26] = flag; /* File Unit Size */ set_num_711(bp+27, 0); /* Interleave Gap Size */ set_num_711(bp+28, 0); /* Volume Sequence Number */ set_num_723(bp+29, iso9660->volume_sequence_number); /* Length of File Identifier */ set_num_711(bp+33, (unsigned char)fi_len); /* File Identifier */ switch (t) { case DIR_REC_VD: case DIR_REC_SELF: set_num_711(bp+34, 0); break; case DIR_REC_PARENT: set_num_711(bp+34, 1); break; case DIR_REC_NORMAL: if (isoent->identifier != NULL) memcpy(bp+34, isoent->identifier, fi_len); else set_num_711(bp+34, 0); break; } } else bp = NULL; dr_len = 33 + fi_len; /* Padding Field */ if (dr_len & 0x01) { dr_len ++; if (p != NULL) bp[dr_len] = 0; } /* Volume Descriptor does not record extension. */ if (t == DIR_REC_VD) { if (p != NULL) /* Length of Directory Record */ set_num_711(p, (unsigned char)dr_len); else isoent->dr_len.vd = (int)dr_len; return ((int)dr_len); } /* Rockridge */ if (iso9660->opt.rr && vdd_type != VDD_JOLIET) dr_len = set_directory_record_rr(bp, (int)dr_len, isoent, iso9660, t); if (p != NULL) /* Length of Directory Record */ set_num_711(p, (unsigned char)dr_len); else { /* * Save the size which is needed to write this * Directory Record. */ switch (t) { case DIR_REC_VD: /* This case does not come, but compiler * complains that DIR_REC_VD not handled * in switch .... */ break; case DIR_REC_SELF: isoent->dr_len.self = (int)dr_len; break; case DIR_REC_PARENT: isoent->dr_len.parent = (int)dr_len; break; case DIR_REC_NORMAL: isoent->dr_len.normal = (int)dr_len; break; } } return ((int)dr_len); } /* * Calculate the size of a directory record. */ static inline int get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent, enum dir_rec_type t, enum vdd_type vdd_type) { return (set_directory_record(NULL, SIZE_MAX, isoent, iso9660, t, vdd_type)); } /* * Manage to write ISO-image data with wbuff to reduce calling * __archive_write_output() for performance. */ static inline unsigned char * wb_buffptr(struct archive_write *a) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; return (&(iso9660->wbuff[sizeof(iso9660->wbuff) - iso9660->wbuff_remaining])); } static int wb_write_out(struct archive_write *a) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; size_t wsize, nw; int r; wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; nw = wsize % LOGICAL_BLOCK_SIZE; if (iso9660->wbuff_type == WB_TO_STREAM) r = __archive_write_output(a, iso9660->wbuff, wsize - nw); else r = write_to_temp(a, iso9660->wbuff, wsize - nw); /* Increase the offset. */ iso9660->wbuff_offset += wsize - nw; if (iso9660->wbuff_offset > iso9660->wbuff_written) iso9660->wbuff_written = iso9660->wbuff_offset; iso9660->wbuff_remaining = sizeof(iso9660->wbuff); if (nw) { iso9660->wbuff_remaining -= nw; memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw); } return (r); } static int wb_consume(struct archive_write *a, size_t size) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; if (size > iso9660->wbuff_remaining || iso9660->wbuff_remaining == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal Programing error: iso9660:wb_consume()" " size=%jd, wbuff_remaining=%jd", (intmax_t)size, (intmax_t)iso9660->wbuff_remaining); return (ARCHIVE_FATAL); } iso9660->wbuff_remaining -= size; if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE) return (wb_write_out(a)); return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int wb_set_offset(struct archive_write *a, int64_t off) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; int64_t used, ext_bytes; if (iso9660->wbuff_type != WB_TO_TEMP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal Programing error: iso9660:wb_set_offset()"); return (ARCHIVE_FATAL); } used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; if (iso9660->wbuff_offset + used > iso9660->wbuff_tail) iso9660->wbuff_tail = iso9660->wbuff_offset + used; if (iso9660->wbuff_offset < iso9660->wbuff_written) { if (used > 0 && write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->wbuff_offset = iso9660->wbuff_written; lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET); iso9660->wbuff_remaining = sizeof(iso9660->wbuff); used = 0; } if (off < iso9660->wbuff_offset) { /* * Write out waiting data. */ if (used > 0) { if (wb_write_out(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); } lseek(iso9660->temp_fd, off, SEEK_SET); iso9660->wbuff_offset = off; iso9660->wbuff_remaining = sizeof(iso9660->wbuff); } else if (off <= iso9660->wbuff_tail) { iso9660->wbuff_remaining = (size_t) (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset)); } else { ext_bytes = off - iso9660->wbuff_tail; iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff) - (iso9660->wbuff_tail - iso9660->wbuff_offset)); while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) { if (write_null(a, (size_t)iso9660->wbuff_remaining) != ARCHIVE_OK) return (ARCHIVE_FATAL); ext_bytes -= iso9660->wbuff_remaining; } if (ext_bytes > 0) { if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ static int write_null(struct archive_write *a, size_t size) { size_t remaining; unsigned char *p, *old; int r; remaining = wb_remaining(a); p = wb_buffptr(a); if (size <= remaining) { memset(p, 0, size); return (wb_consume(a, size)); } memset(p, 0, remaining); r = wb_consume(a, remaining); if (r != ARCHIVE_OK) return (r); size -= remaining; old = p; p = wb_buffptr(a); memset(p, 0, old - p); remaining = wb_remaining(a); while (size) { size_t wsize = size; if (wsize > remaining) wsize = remaining; r = wb_consume(a, wsize); if (r != ARCHIVE_OK) return (r); size -= wsize; } return (ARCHIVE_OK); } /* * Write Volume Descriptor Set Terminator */ static int write_VD_terminator(struct archive_write *a) { unsigned char *bp; bp = wb_buffptr(a) -1; set_VD_bp(bp, VDT_TERMINATOR, 1); set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc, struct archive_write *a, struct vdd *vdd, struct archive_string *id, const char *label, int leading_under, enum char_type char_type) { char identifier[256]; struct isoent *isoent; const char *ids; size_t len; int r; if (id->length > 0 && leading_under && id->s[0] != '_') { if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc); else r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc); } else if (id->length > 0) { ids = id->s; if (leading_under) ids++; isoent = isoent_find_entry(vdd->rootent, ids); if (isoent == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Not Found %s `%s'.", label, ids); return (ARCHIVE_FATAL); } len = isoent->ext_off + isoent->ext_len; if (vdd->vdd_type == VDD_JOLIET) { if (len > sizeof(identifier)-2) len = sizeof(identifier)-2; } else { if (len > sizeof(identifier)-1) len = sizeof(identifier)-1; } memcpy(identifier, isoent->identifier, len); identifier[len] = '\0'; if (vdd->vdd_type == VDD_JOLIET) { identifier[len+1] = 0; vdc = VDC_UCS2_DIRECT; } if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, identifier, vdc); else r = set_str_d_characters_bp(a, bp, from, to, identifier, vdc); } else { if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc); else r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc); } return (r); } /* * Write Primary/Supplementary Volume Descriptor */ static int write_VD(struct archive_write *a, struct vdd *vdd) { struct iso9660 *iso9660; unsigned char *bp; uint16_t volume_set_size = 1; char identifier[256]; enum VD_type vdt; enum vdc vdc; unsigned char vd_ver, fst_ver; int r; iso9660 = a->format_data; switch (vdd->vdd_type) { case VDD_JOLIET: vdt = VDT_SUPPLEMENTARY; vd_ver = fst_ver = 1; vdc = VDC_UCS2; break; case VDD_ENHANCED: vdt = VDT_SUPPLEMENTARY; vd_ver = fst_ver = 2; vdc = VDC_LOWERCASE; break; case VDD_PRIMARY: default: vdt = VDT_PRIMARY; vd_ver = fst_ver = 1; #ifdef COMPAT_MKISOFS vdc = VDC_LOWERCASE; #else vdc = VDC_STD; #endif break; } bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, vdt, vd_ver); /* Unused Field */ set_unused_field_bp(bp, 8, 8); /* System Identifier */ get_system_identitier(identifier, sizeof(identifier)); r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc); if (r != ARCHIVE_OK) return (r); /* Volume Identifier */ r = set_str_d_characters_bp(a, bp, 41, 72, iso9660->volume_identifier.s, vdc); if (r != ARCHIVE_OK) return (r); /* Unused Field */ set_unused_field_bp(bp, 73, 80); /* Volume Space Size */ set_num_733(bp+81, iso9660->volume_space_size); if (vdd->vdd_type == VDD_JOLIET) { /* Escape Sequences */ bp[89] = 0x25;/* UCS-2 Level 3 */ bp[90] = 0x2F; bp[91] = 0x45; memset(bp + 92, 0, 120 - 92 + 1); } else { /* Unused Field */ set_unused_field_bp(bp, 89, 120); } /* Volume Set Size */ set_num_723(bp+121, volume_set_size); /* Volume Sequence Number */ set_num_723(bp+125, iso9660->volume_sequence_number); /* Logical Block Size */ set_num_723(bp+129, LOGICAL_BLOCK_SIZE); /* Path Table Size */ set_num_733(bp+133, vdd->path_table_size); /* Location of Occurrence of Type L Path Table */ set_num_731(bp+141, vdd->location_type_L_path_table); /* Location of Optional Occurrence of Type L Path Table */ set_num_731(bp+145, 0); /* Location of Occurrence of Type M Path Table */ set_num_732(bp+149, vdd->location_type_M_path_table); /* Location of Optional Occurrence of Type M Path Table */ set_num_732(bp+153, 0); /* Directory Record for Root Directory(BP 157 to 190) */ set_directory_record(bp+157, 190-157+1, vdd->rootent, iso9660, DIR_REC_VD, vdd->vdd_type); /* Volume Set Identifier */ r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc); if (r != ARCHIVE_OK) return (r); /* Publisher Identifier */ r = set_file_identifier(bp, 319, 446, vdc, a, vdd, &(iso9660->publisher_identifier), "Publisher File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Data Preparer Identifier */ r = set_file_identifier(bp, 447, 574, vdc, a, vdd, &(iso9660->data_preparer_identifier), "Data Preparer File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Application Identifier */ r = set_file_identifier(bp, 575, 702, vdc, a, vdd, &(iso9660->application_identifier), "Application File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Copyright File Identifier */ r = set_file_identifier(bp, 703, 739, vdc, a, vdd, &(iso9660->copyright_file_identifier), "Copyright File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Abstract File Identifier */ r = set_file_identifier(bp, 740, 776, vdc, a, vdd, &(iso9660->abstract_file_identifier), "Abstract File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Bibliographic File Identifier */ r = set_file_identifier(bp, 777, 813, vdc, a, vdd, &(iso9660->bibliographic_file_identifier), "Bibliongraphic File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Volume Creation Date and Time */ set_date_time(bp+814, iso9660->birth_time); /* Volume Modification Date and Time */ set_date_time(bp+831, iso9660->birth_time); /* Volume Expiration Date and Time(obsolete) */ set_date_time_null(bp+848); /* Volume Effective Date and Time */ set_date_time(bp+865, iso9660->birth_time); /* File Structure Version */ bp[882] = fst_ver; /* Reserved */ bp[883] = 0; /* Application Use */ memset(bp + 884, 0x20, 1395 - 884 + 1); /* Reserved */ set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } /* * Write Boot Record Volume Descriptor */ static int write_VD_boot_record(struct archive_write *a) { struct iso9660 *iso9660; unsigned char *bp; iso9660 = a->format_data; bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, VDT_BOOT_RECORD, 1); /* Boot System Identifier */ memcpy(bp+8, "EL TORITO SPECIFICATION", 23); set_unused_field_bp(bp, 8+23, 39); /* Unused */ set_unused_field_bp(bp, 40, 71); /* Absolute pointer to first sector of Boot Catalog */ set_num_731(bp+72, iso9660->el_torito.catalog->file->content.location); /* Unused */ set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } enum keytype { KEY_FLG, KEY_STR, KEY_INT, KEY_HEX }; static void set_option_info(struct archive_string *info, int *opt, const char *key, enum keytype type, ...) { va_list ap; char prefix; const char *s; int d; prefix = (*opt==0)? ' ':','; va_start(ap, type); switch (type) { case KEY_FLG: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s%s", prefix, (d == 0)?"!":"", key); break; case KEY_STR: s = va_arg(ap, const char *); archive_string_sprintf(info, "%c%s=%s", prefix, key, s); break; case KEY_INT: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s=%d", prefix, key, d); break; case KEY_HEX: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s=%x", prefix, key, d); break; } va_end(ap); *opt = 1; } /* * Make Non-ISO File System Information */ static int write_information_block(struct archive_write *a) { struct iso9660 *iso9660; char buf[128]; const char *v; int opt, r; struct archive_string info; size_t info_size = LOGICAL_BLOCK_SIZE * NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; iso9660 = (struct iso9660 *)a->format_data; if (info_size > wb_remaining(a)) { r = wb_write_out(a); if (r != ARCHIVE_OK) return (r); } archive_string_init(&info); if (archive_string_ensure(&info, info_size) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memset(info.s, 0, info_size); opt = 0; #if defined(HAVE__CTIME64_S) - __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits - _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp)); + { + __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits + _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp)); + } #elif defined(HAVE_CTIME_R) ctime_r(&(iso9660->birth_time), buf); #else strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; #endif archive_string_sprintf(&info, "INFO %s%s", buf, archive_version_string()); if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT) set_option_info(&info, &opt, "abstract-file", KEY_STR, iso9660->abstract_file_identifier.s); if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT) set_option_info(&info, &opt, "application-id", KEY_STR, iso9660->application_identifier.s); if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT) set_option_info(&info, &opt, "allow-vernum", KEY_FLG, iso9660->opt.allow_vernum); if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT) set_option_info(&info, &opt, "biblio-file", KEY_STR, iso9660->bibliographic_file_identifier.s); if (iso9660->opt.boot != OPT_BOOT_DEFAULT) set_option_info(&info, &opt, "boot", KEY_STR, iso9660->el_torito.boot_filename.s); if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT) set_option_info(&info, &opt, "boot-catalog", KEY_STR, iso9660->el_torito.catalog_filename.s); if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT) set_option_info(&info, &opt, "boot-info-table", KEY_FLG, iso9660->opt.boot_info_table); if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT) set_option_info(&info, &opt, "boot-load-seg", KEY_HEX, iso9660->el_torito.boot_load_seg); if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT) set_option_info(&info, &opt, "boot-load-size", KEY_INT, iso9660->el_torito.boot_load_size); if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) { v = "no-emulation"; if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD) v = "fd"; if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK) v = "hard-disk"; set_option_info(&info, &opt, "boot-type", KEY_STR, v); } #ifdef HAVE_ZLIB_H if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT) set_option_info(&info, &opt, "compression-level", KEY_INT, iso9660->zisofs.compression_level); #endif if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT) set_option_info(&info, &opt, "copyright-file", KEY_STR, iso9660->copyright_file_identifier.s); if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT) set_option_info(&info, &opt, "iso-level", KEY_INT, iso9660->opt.iso_level); if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) { if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) set_option_info(&info, &opt, "joliet", KEY_STR, "long"); else set_option_info(&info, &opt, "joliet", KEY_FLG, iso9660->opt.joliet); } if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT) set_option_info(&info, &opt, "limit-depth", KEY_FLG, iso9660->opt.limit_depth); if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT) set_option_info(&info, &opt, "limit-dirs", KEY_FLG, iso9660->opt.limit_dirs); if (iso9660->opt.pad != OPT_PAD_DEFAULT) set_option_info(&info, &opt, "pad", KEY_FLG, iso9660->opt.pad); if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT) set_option_info(&info, &opt, "publisher", KEY_STR, iso9660->publisher_identifier.s); if (iso9660->opt.rr != OPT_RR_DEFAULT) { if (iso9660->opt.rr == OPT_RR_DISABLED) set_option_info(&info, &opt, "rockridge", KEY_FLG, iso9660->opt.rr); else if (iso9660->opt.rr == OPT_RR_STRICT) set_option_info(&info, &opt, "rockridge", KEY_STR, "strict"); else if (iso9660->opt.rr == OPT_RR_USEFUL) set_option_info(&info, &opt, "rockridge", KEY_STR, "useful"); } if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT) set_option_info(&info, &opt, "volume-id", KEY_STR, iso9660->volume_identifier.s); if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT) set_option_info(&info, &opt, "zisofs", KEY_FLG, iso9660->opt.zisofs); memcpy(wb_buffptr(a), info.s, info_size); archive_string_free(&info); return (wb_consume(a, info_size)); } static int write_rr_ER(struct archive_write *a) { unsigned char *p; p = wb_buffptr(a); memset(p, 0, LOGICAL_BLOCK_SIZE); p[0] = 'E'; p[1] = 'R'; p[3] = 0x01; p[2] = RRIP_ER_SIZE; p[4] = RRIP_ER_ID_SIZE; p[5] = RRIP_ER_DSC_SIZE; p[6] = RRIP_ER_SRC_SIZE; p[7] = 0x01; memcpy(&p[8], rrip_identifier, p[4]); memcpy(&p[8+p[4]], rrip_descriptor, p[5]); memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static void calculate_path_table_size(struct vdd *vdd) { int depth, size; struct path_table *pt; pt = vdd->pathtbl; size = 0; for (depth = 0; depth < vdd->max_depth; depth++) { struct isoent **ptbl; int i, cnt; if ((cnt = pt[depth].cnt) == 0) break; ptbl = pt[depth].sorted; for (i = 0; i < cnt; i++) { int len; if (ptbl[i]->identifier == NULL) len = 1; /* root directory */ else len = ptbl[i]->id_len; if (len & 0x01) len++; /* Padding Field */ size += 8 + len; } } vdd->path_table_size = size; vdd->path_table_block = ((size + PATH_TABLE_BLOCK_SIZE -1) / PATH_TABLE_BLOCK_SIZE) * (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE); } static int _write_path_table(struct archive_write *a, int type_m, int depth, struct vdd *vdd) { unsigned char *bp, *wb; struct isoent **ptbl; size_t wbremaining; int i, r, wsize; if (vdd->pathtbl[depth].cnt == 0) return (0); wsize = 0; wb = wb_buffptr(a); wbremaining = wb_remaining(a); bp = wb - 1; ptbl = vdd->pathtbl[depth].sorted; for (i = 0; i < vdd->pathtbl[depth].cnt; i++) { struct isoent *np; size_t len; np = ptbl[i]; if (np->identifier == NULL) len = 1; /* root directory */ else len = np->id_len; if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) { r = wb_consume(a, (bp+1) - wb); if (r < 0) return (r); wb = wb_buffptr(a); wbremaining = wb_remaining(a); bp = wb -1; } /* Length of Directory Identifier */ set_num_711(bp+1, (unsigned char)len); /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ if (type_m) set_num_732(bp+3, np->dir_location); else set_num_731(bp+3, np->dir_location); /* Parent Directory Number */ if (type_m) set_num_722(bp+7, np->parent->dir_number); else set_num_721(bp+7, np->parent->dir_number); /* Directory Identifier */ if (np->identifier == NULL) bp[9] = 0; else memcpy(&bp[9], np->identifier, len); if (len & 0x01) { /* Padding Field */ bp[9+len] = 0; len++; } wsize += 8 + (int)len; bp += 8 + len; } if ((bp + 1) > wb) { r = wb_consume(a, (bp+1)-wb); if (r < 0) return (r); } return (wsize); } static int write_path_table(struct archive_write *a, int type_m, struct vdd *vdd) { int depth, r; size_t path_table_size; r = ARCHIVE_OK; path_table_size = 0; for (depth = 0; depth < vdd->max_depth; depth++) { r = _write_path_table(a, type_m, depth, vdd); if (r < 0) return (r); path_table_size += r; } /* Write padding data. */ path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE; if (path_table_size > 0) r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size); return (r); } static int calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd, struct isoent *isoent, int depth) { struct isoent **enttbl; int bs, block, i; block = 1; bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type); bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type); if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) return (block); enttbl = isoent->children_sorted; for (i = 0; i < isoent->children.cnt; i++) { struct isoent *np = enttbl[i]; struct isofile *file; file = np->file; if (file->hardlink_target != NULL) file = file->hardlink_target; file->cur_content = &(file->content); do { int dr_l; dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL, vdd->vdd_type); if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) { block ++; bs = dr_l; } else bs += dr_l; file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } return (block); } static int _write_directory_descriptors(struct archive_write *a, struct vdd *vdd, struct isoent *isoent, int depth) { struct iso9660 *iso9660 = a->format_data; struct isoent **enttbl; unsigned char *p, *wb; int i, r; int dr_l; p = wb = wb_buffptr(a); #define WD_REMAINING (LOGICAL_BLOCK_SIZE - (p - wb)) p += set_directory_record(p, WD_REMAINING, isoent, iso9660, DIR_REC_SELF, vdd->vdd_type); p += set_directory_record(p, WD_REMAINING, isoent, iso9660, DIR_REC_PARENT, vdd->vdd_type); if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) { memset(p, 0, WD_REMAINING); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } enttbl = isoent->children_sorted; for (i = 0; i < isoent->children.cnt; i++) { struct isoent *np = enttbl[i]; struct isofile *file = np->file; if (file->hardlink_target != NULL) file = file->hardlink_target; file->cur_content = &(file->content); do { dr_l = set_directory_record(p, WD_REMAINING, np, iso9660, DIR_REC_NORMAL, vdd->vdd_type); if (dr_l == 0) { memset(p, 0, WD_REMAINING); r = wb_consume(a, LOGICAL_BLOCK_SIZE); if (r < 0) return (r); p = wb = wb_buffptr(a); dr_l = set_directory_record(p, WD_REMAINING, np, iso9660, DIR_REC_NORMAL, vdd->vdd_type); } p += dr_l; file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } memset(p, 0, WD_REMAINING); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int write_directory_descriptors(struct archive_write *a, struct vdd *vdd) { struct isoent *np; int depth, r; depth = 0; np = vdd->rootent; do { struct extr_rec *extr; r = _write_directory_descriptors(a, vdd, np, depth); if (r < 0) return (r); if (vdd->vdd_type != VDD_JOLIET) { /* * This extract record is used by SUSP,RRIP. * Not for joliet. */ for (extr = np->extr_rec_list.first; extr != NULL; extr = extr->next) { unsigned char *wb; wb = wb_buffptr(a); memcpy(wb, extr->buf, extr->offset); memset(wb + extr->offset, 0, LOGICAL_BLOCK_SIZE - extr->offset); r = wb_consume(a, LOGICAL_BLOCK_SIZE); if (r < 0) return (r); } } if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } /* * Read file contents from the temporary file, and write it. */ static int write_file_contents(struct archive_write *a, int64_t offset, int64_t size) { struct iso9660 *iso9660 = a->format_data; int r; lseek(iso9660->temp_fd, offset, SEEK_SET); while (size) { size_t rsize; ssize_t rs; unsigned char *wb; wb = wb_buffptr(a); rsize = wb_remaining(a); if (rsize > (size_t)size) rsize = (size_t)size; rs = read(iso9660->temp_fd, wb, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } size -= rs; r = wb_consume(a, rs); if (r < 0) return (r); } return (ARCHIVE_OK); } static int write_file_descriptors(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; int64_t blocks, offset; int r; blocks = 0; offset = 0; /* Make the boot catalog contents, and write it. */ if (iso9660->el_torito.catalog != NULL) { r = make_boot_catalog(a); if (r < 0) return (r); } /* Write the boot file contents. */ if (iso9660->el_torito.boot != NULL) { file = iso9660->el_torito.boot->file; blocks = file->content.blocks; offset = file->content.offset_of_temp; if (offset != 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); blocks = 0; offset = 0; } } /* Write out all file contents. */ for (file = iso9660->data_file_list.first; file != NULL; file = file->datanext) { if (!file->write_content) continue; if ((offset + (blocks << LOGICAL_BLOCK_BITS)) < file->content.offset_of_temp) { if (blocks > 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); } blocks = 0; offset = file->content.offset_of_temp; } file->cur_content = &(file->content); do { blocks += file->cur_content->blocks; /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } /* Flush out remaining blocks. */ if (blocks > 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); } return (ARCHIVE_OK); } static void isofile_init_entry_list(struct iso9660 *iso9660) { iso9660->all_file_list.first = NULL; iso9660->all_file_list.last = &(iso9660->all_file_list.first); } static void isofile_add_entry(struct iso9660 *iso9660, struct isofile *file) { file->allnext = NULL; *iso9660->all_file_list.last = file; iso9660->all_file_list.last = &(file->allnext); } static void isofile_free_all_entries(struct iso9660 *iso9660) { struct isofile *file, *file_next; file = iso9660->all_file_list.first; while (file != NULL) { file_next = file->allnext; isofile_free(file); file = file_next; } } static void isofile_init_entry_data_file_list(struct iso9660 *iso9660) { iso9660->data_file_list.first = NULL; iso9660->data_file_list.last = &(iso9660->data_file_list.first); } static void isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file) { file->datanext = NULL; *iso9660->data_file_list.last = file; iso9660->data_file_list.last = &(file->datanext); } static struct isofile * isofile_new(struct archive_write *a, struct archive_entry *entry) { struct isofile *file; file = calloc(1, sizeof(*file)); if (file == NULL) return (NULL); if (entry != NULL) file->entry = archive_entry_clone(entry); else file->entry = archive_entry_new2(&a->archive); if (file->entry == NULL) { free(file); return (NULL); } archive_string_init(&(file->parentdir)); archive_string_init(&(file->basename)); archive_string_init(&(file->basename_utf16)); archive_string_init(&(file->symlink)); file->cur_content = &(file->content); return (file); } static void isofile_free(struct isofile *file) { struct content *con, *tmp; con = file->content.next; while (con != NULL) { tmp = con; con = con->next; free(tmp); } archive_entry_free(file->entry); archive_string_free(&(file->parentdir)); archive_string_free(&(file->basename)); archive_string_free(&(file->basename_utf16)); archive_string_free(&(file->symlink)); free(file); } #if defined(_WIN32) || defined(__CYGWIN__) static int cleanup_backslash_1(char *p) { int mb, dos; mb = dos = 0; while (*p) { if (*(unsigned char *)p > 127) mb = 1; if (*p == '\\') { /* If we have not met any multi-byte characters, * we can replace '\' with '/'. */ if (!mb) *p = '/'; dos = 1; } p++; } if (!mb || !dos) return (0); return (-1); } static void cleanup_backslash_2(wchar_t *p) { /* Convert a path-separator from '\' to '/' */ while (*p != L'\0') { if (*p == L'\\') *p = L'/'; p++; } } #endif /* * Generate a parent directory name and a base name from a pathname. */ static int isofile_gen_utility_names(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660; const char *pathname; char *p, *dirname, *slash; size_t len; int ret = ARCHIVE_OK; iso9660 = a->format_data; archive_string_empty(&(file->parentdir)); archive_string_empty(&(file->basename)); archive_string_empty(&(file->basename_utf16)); archive_string_empty(&(file->symlink)); pathname = archive_entry_pathname(file->entry); if (pathname == NULL || pathname[0] == '\0') {/* virtual root */ file->dircnt = 0; return (ret); } /* * Make a UTF-16BE basename if Joliet extension enabled. */ if (iso9660->opt.joliet) { const char *u16, *ulast; size_t u16len, ulen_last; if (iso9660->sconv_to_utf16be == NULL) { iso9660->sconv_to_utf16be = archive_string_conversion_to_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_to_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); iso9660->sconv_from_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_from_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } /* * Convert a filename to UTF-16BE. */ if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len, iso9660->sconv_to_utf16be)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A filename cannot be converted to UTF-16BE;" "You should disable making Joliet extension"); ret = ARCHIVE_WARN; } /* * Make sure a path separator is not in the last; * Remove trailing '/'. */ while (u16len >= 2) { #if defined(_WIN32) || defined(__CYGWIN__) if (u16[u16len-2] == 0 && (u16[u16len-1] == '/' || u16[u16len-1] == '\\')) #else if (u16[u16len-2] == 0 && u16[u16len-1] == '/') #endif { u16len -= 2; } else break; } /* * Find a basename in UTF-16BE. */ ulast = u16; u16len >>= 1; ulen_last = u16len; while (u16len > 0) { #if defined(_WIN32) || defined(__CYGWIN__) if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\')) #else if (u16[0] == 0 && u16[1] == '/') #endif { ulast = u16 + 2; ulen_last = u16len -1; } u16 += 2; u16len --; } ulen_last <<= 1; if (archive_string_ensure(&(file->basename_utf16), ulen_last) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } /* * Set UTF-16BE basename. */ memcpy(file->basename_utf16.s, ulast, ulen_last); file->basename_utf16.length = ulen_last; } archive_strcpy(&(file->parentdir), pathname); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (cleanup_backslash_1(file->parentdir.s) != 0) { const wchar_t *wp = archive_entry_pathname_w(file->entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->parentdir)); r = archive_string_append_from_wcs(&(file->parentdir), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #endif len = file->parentdir.length; p = dirname = file->parentdir.s; /* * Remove leading '/', '../' and './' elements */ while (*p) { if (p[0] == '/') { p++; len--; } else if (p[0] != '.') break; else if (p[1] == '.' && p[2] == '/') { p += 3; len -= 3; } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) { p += 2; len -= 2; } else if (p[1] == '\0') { p++; len--; } else break; } if (p != dirname) { memmove(dirname, p, len+1); p = dirname; } /* * Remove "/","/." and "/.." elements from tail. */ while (len > 0) { size_t ll = len; if (len > 0 && p[len-1] == '/') { p[len-1] = '\0'; len--; } if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { p[len-2] = '\0'; len -= 2; } if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && p[len-1] == '.') { p[len-3] = '\0'; len -= 3; } if (ll == len) break; } while (*p) { if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ strcpy(p, p+1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ strcpy(p, p+2); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' */ char *rp = p -1; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { strcpy(rp, p+3); p = rp; } else { strcpy(dirname, p+4); p = dirname; } } else p++; } else p++; } p = dirname; len = strlen(p); if (archive_entry_filetype(file->entry) == AE_IFLNK) { /* Convert symlink name too. */ pathname = archive_entry_symlink(file->entry); archive_strcpy(&(file->symlink), pathname); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (archive_strlen(&(file->symlink)) > 0 && cleanup_backslash_1(file->symlink.s) != 0) { const wchar_t *wp = archive_entry_symlink_w(file->entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->symlink)); r = archive_string_append_from_wcs( &(file->symlink), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #endif } /* * - Count up directory elements. * - Find out the position which points the last position of * path separator('/'). */ slash = NULL; file->dircnt = 0; for (; *p != '\0'; p++) if (*p == '/') { slash = p; file->dircnt++; } if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; archive_string_copy(&(file->basename), &(file->parentdir)); archive_string_empty(&(file->parentdir)); *file->parentdir.s = '\0'; return (ret); } /* Make a basename from dirname and slash */ *slash = '\0'; file->parentdir.length = slash - dirname; archive_strcpy(&(file->basename), slash + 1); if (archive_entry_filetype(file->entry) == AE_IFDIR) file->dircnt ++; return (ret); } /* * Register a entry to get a hardlink target. */ static int isofile_register_hardlink(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660 = a->format_data; struct hardlink *hl; const char *pathname; archive_entry_set_nlink(file->entry, 1); pathname = archive_entry_hardlink(file->entry); if (pathname == NULL) { /* This `file` is a hardlink target. */ hl = malloc(sizeof(*hl)); if (hl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } hl->nlink = 1; /* A hardlink target must be the first position. */ file->hlnext = NULL; hl->file_list.first = file; hl->file_list.last = &(file->hlnext); __archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree), (struct archive_rb_node *)hl); } else { hl = (struct hardlink *)__archive_rb_tree_find_node( &(iso9660->hardlink_rbtree), pathname); if (hl != NULL) { /* Insert `file` entry into the tail. */ file->hlnext = NULL; *hl->file_list.last = file; hl->file_list.last = &(file->hlnext); hl->nlink++; } archive_entry_unset_size(file->entry); } return (ARCHIVE_OK); } /* * Hardlinked files have to have the same location of extent. * We have to find out hardlink target entries for the entries * which have a hardlink target name. */ static void isofile_connect_hardlink_files(struct iso9660 *iso9660) { struct archive_rb_node *n; struct hardlink *hl; struct isofile *target, *nf; ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) { hl = (struct hardlink *)n; /* The first entry must be a hardlink target. */ target = hl->file_list.first; archive_entry_set_nlink(target->entry, hl->nlink); /* Set a hardlink target to reference entries. */ for (nf = target->hlnext; nf != NULL; nf = nf->hlnext) { nf->hardlink_target = target; archive_entry_set_nlink(nf->entry, hl->nlink); } } } static int isofile_hd_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct hardlink *h1 = (const struct hardlink *)n1; const struct hardlink *h2 = (const struct hardlink *)n2; return (strcmp(archive_entry_pathname(h1->file_list.first->entry), archive_entry_pathname(h2->file_list.first->entry))); } static int isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key) { const struct hardlink *h = (const struct hardlink *)n; return (strcmp(archive_entry_pathname(h->file_list.first->entry), (const char *)key)); } static void isofile_init_hardlinks(struct iso9660 *iso9660) { static const struct archive_rb_tree_ops rb_ops = { isofile_hd_cmp_node, isofile_hd_cmp_key, }; __archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops); } static void isofile_free_hardlinks(struct iso9660 *iso9660) { struct archive_rb_node *n, *next; for (n = ARCHIVE_RB_TREE_MIN(&(iso9660->hardlink_rbtree)); n;) { next = __archive_rb_tree_iterate(&(iso9660->hardlink_rbtree), n, ARCHIVE_RB_DIR_RIGHT); free(n); n = next; } } static struct isoent * isoent_new(struct isofile *file) { struct isoent *isoent; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node, isoent_cmp_key, }; isoent = calloc(1, sizeof(*isoent)); if (isoent == NULL) return (NULL); isoent->file = file; isoent->children.first = NULL; isoent->children.last = &(isoent->children.first); __archive_rb_tree_init(&(isoent->rbtree), &rb_ops); isoent->subdirs.first = NULL; isoent->subdirs.last = &(isoent->subdirs.first); isoent->extr_rec_list.first = NULL; isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); isoent->extr_rec_list.current = NULL; if (archive_entry_filetype(file->entry) == AE_IFDIR) isoent->dir = 1; return (isoent); } static inline struct isoent * isoent_clone(struct isoent *src) { return (isoent_new(src->file)); } static void _isoent_free(struct isoent *isoent) { struct extr_rec *er, *er_next; free(isoent->children_sorted); free(isoent->identifier); er = isoent->extr_rec_list.first; while (er != NULL) { er_next = er->next; free(er); er = er_next; } free(isoent); } static void isoent_free_all(struct isoent *isoent) { struct isoent *np, *np_temp; if (isoent == NULL) return; np = isoent; for (;;) { if (np->dir) { if (np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; continue; } } for (;;) { np_temp = np; if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; _isoent_free(np_temp); if (np == np_temp) return; } else { np = np->chnext; _isoent_free(np_temp); break; } } } } static struct isoent * isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname) { struct isofile *file; struct isoent *isoent; file = isofile_new(a, NULL); if (file == NULL) return (NULL); archive_entry_set_pathname(file->entry, pathname); archive_entry_unset_mtime(file->entry); archive_entry_unset_atime(file->entry); archive_entry_unset_ctime(file->entry); archive_entry_set_uid(file->entry, getuid()); archive_entry_set_gid(file->entry, getgid()); archive_entry_set_mode(file->entry, 0555 | AE_IFDIR); archive_entry_set_nlink(file->entry, 2); if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { isofile_free(file); return (NULL); } isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) return (NULL); isoent->dir = 1; isoent->virtual = 1; return (isoent); } static int isoent_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct isoent *e1 = (const struct isoent *)n1; const struct isoent *e2 = (const struct isoent *)n2; return (strcmp(e1->file->basename.s, e2->file->basename.s)); } static int isoent_cmp_key(const struct archive_rb_node *n, const void *key) { const struct isoent *e = (const struct isoent *)n; return (strcmp(e->file->basename.s, (const char *)key)); } static int isoent_add_child_head(struct isoent *parent, struct isoent *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); if ((child->chnext = parent->children.first) == NULL) parent->children.last = &(child->chnext); parent->children.first = child; parent->children.cnt++; child->parent = parent; /* Add a child to a sub-directory chain */ if (child->dir) { if ((child->drnext = parent->subdirs.first) == NULL) parent->subdirs.last = &(child->drnext); parent->subdirs.first = child; parent->subdirs.cnt++; child->parent = parent; } else child->drnext = NULL; return (1); } static int isoent_add_child_tail(struct isoent *parent, struct isoent *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); child->chnext = NULL; *parent->children.last = child; parent->children.last = &(child->chnext); parent->children.cnt++; child->parent = parent; /* Add a child to a sub-directory chain */ child->drnext = NULL; if (child->dir) { *parent->subdirs.last = child; parent->subdirs.last = &(child->drnext); parent->subdirs.cnt++; child->parent = parent; } return (1); } static void isoent_remove_child(struct isoent *parent, struct isoent *child) { struct isoent *ent; /* Remove a child entry from children chain. */ ent = parent->children.first; while (ent->chnext != child) ent = ent->chnext; if ((ent->chnext = ent->chnext->chnext) == NULL) parent->children.last = &(ent->chnext); parent->children.cnt--; if (child->dir) { /* Remove a child entry from sub-directory chain. */ ent = parent->subdirs.first; while (ent->drnext != child) ent = ent->drnext; if ((ent->drnext = ent->drnext->drnext) == NULL) parent->subdirs.last = &(ent->drnext); parent->subdirs.cnt--; } __archive_rb_tree_remove_node(&(parent->rbtree), (struct archive_rb_node *)child); } static int isoent_clone_tree(struct archive_write *a, struct isoent **nroot, struct isoent *root) { struct isoent *np, *xroot, *newent; np = root; xroot = NULL; do { newent = isoent_clone(np); if (newent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } if (xroot == NULL) { *nroot = xroot = newent; newent->parent = xroot; } else isoent_add_child_tail(xroot, newent); if (np->dir && np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; xroot = newent; continue; } while (np != np->parent) { if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; xroot = xroot->parent; } else { np = np->chnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } /* * Setup directory locations. */ static void isoent_setup_directory_location(struct iso9660 *iso9660, int location, struct vdd *vdd) { struct isoent *np; int depth; vdd->total_dir_block = 0; depth = 0; np = vdd->rootent; do { int block; np->dir_block = calculate_directory_descriptors( iso9660, vdd, np, depth); vdd->total_dir_block += np->dir_block; np->dir_location = location; location += np->dir_block; block = extra_setup_location(np, location); vdd->total_dir_block += block; location += block; if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); } static void _isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent, int *symlocation) { struct isoent **children; int n; if (isoent->children.cnt == 0) return; children = isoent->children_sorted; for (n = 0; n < isoent->children.cnt; n++) { struct isoent *np; struct isofile *file; np = children[n]; if (np->dir) continue; if (np == iso9660->el_torito.boot) continue; file = np->file; if (file->boot || file->hardlink_target != NULL) continue; if (archive_entry_filetype(file->entry) == AE_IFLNK || file->content.size == 0) { /* * Do not point a valid location. * Make sure entry is not hardlink file. */ file->content.location = (*symlocation)--; continue; } file->write_content = 1; } } /* * Setup file locations. */ static void isoent_setup_file_location(struct iso9660 *iso9660, int location) { struct isoent *isoent; struct isoent *np; struct isofile *file; size_t size; int block; int depth; int joliet; int symlocation; int total_block; iso9660->total_file_block = 0; if ((isoent = iso9660->el_torito.catalog) != NULL) { isoent->file->content.location = location; block = (int)((archive_entry_size(isoent->file->entry) + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); location += block; iso9660->total_file_block += block; } if ((isoent = iso9660->el_torito.boot) != NULL) { isoent->file->content.location = location; size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = (size_t)archive_entry_size(isoent->file->entry); block = ((int)size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS; location += block; iso9660->total_file_block += block; isoent->file->content.blocks = block; } depth = 0; symlocation = -16; if (!iso9660->opt.rr && iso9660->opt.joliet) { joliet = 1; np = iso9660->joliet.rootent; } else { joliet = 0; np = iso9660->primary.rootent; } do { _isoent_file_location(iso9660, np, &symlocation); if (np->subdirs.first != NULL && (joliet || ((iso9660->opt.rr == OPT_RR_DISABLED && depth + 2 < iso9660->primary.max_depth) || (iso9660->opt.rr && depth + 1 < iso9660->primary.max_depth)))) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); total_block = 0; for (file = iso9660->data_file_list.first; file != NULL; file = file->datanext) { if (!file->write_content) continue; file->cur_content = &(file->content); do { file->cur_content->location = location; location += file->cur_content->blocks; total_block += file->cur_content->blocks; /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } iso9660->total_file_block += total_block; } static int get_path_component(char *name, size_t n, const char *fn) { char *p; size_t l; p = strchr(fn, '/'); if (p == NULL) { if ((l = strlen(fn)) == 0) return (0); } else l = p - fn; if (l > n -1) return (-1); memcpy(name, fn, l); name[l] = '\0'; return ((int)l); } /* * Add a new entry into the tree. */ static int isoent_tree(struct archive_write *a, struct isoent **isoentpp) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct iso9660 *iso9660 = a->format_data; struct isoent *dent, *isoent, *np; struct isofile *f1, *f2; const char *fn, *p; int l; isoent = *isoentpp; dent = iso9660->primary.rootent; if (isoent->file->parentdir.length > 0) fn = p = isoent->file->parentdir.s; else fn = p = ""; /* * If the path of the parent directory of `isoent' entry is * the same as the path of `cur_dirent', add isoent to * `cur_dirent'. */ if (archive_strlen(&(iso9660->cur_dirstr)) == archive_strlen(&(isoent->file->parentdir)) && strcmp(iso9660->cur_dirstr.s, fn) == 0) { if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) { np = (struct isoent *)__archive_rb_tree_find_node( &(iso9660->cur_dirent->rbtree), isoent->file->basename.s); goto same_entry; } return (ARCHIVE_OK); } for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) { np = NULL; break; } if (l < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); _isoent_free(isoent); return (ARCHIVE_FATAL); } np = isoent_find_child(dent, name); if (np == NULL || fn[0] == '\0') break; /* Find next subdirectory. */ if (!np->dir) { /* NOT Directory! */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "`%s' is not directory, we cannot insert `%s' ", archive_entry_pathname(np->file->entry), archive_entry_pathname(isoent->file->entry)); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FAILED); } fn += l; if (fn[0] == '/') fn++; dent = np; } if (np == NULL) { /* * Create virtual parent directories. */ while (fn[0] != '\0') { struct isoent *vp; struct archive_string as; archive_string_init(&as); archive_strncat(&as, p, fn - p + l); if (as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } vp = isoent_create_virtual_dir(a, iso9660, as.s); if (vp == NULL) { archive_string_free(&as); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FATAL); } archive_string_free(&as); if (vp->file->dircnt > iso9660->dircnt_max) iso9660->dircnt_max = vp->file->dircnt; isoent_add_child_tail(dent, vp); np = vp; fn += l; if (fn[0] == '/') fn++; l = get_path_component(name, sizeof(name), fn); if (l < 0) { archive_string_free(&as); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FATAL); } dent = np; } /* Found out the parent directory where isoent can be * inserted. */ iso9660->cur_dirent = dent; archive_string_empty(&(iso9660->cur_dirstr)); archive_string_ensure(&(iso9660->cur_dirstr), archive_strlen(&(dent->file->parentdir)) + archive_strlen(&(dent->file->basename)) + 2); if (archive_strlen(&(dent->file->parentdir)) + archive_strlen(&(dent->file->basename)) == 0) iso9660->cur_dirstr.s[0] = 0; else { if (archive_strlen(&(dent->file->parentdir)) > 0) { archive_string_copy(&(iso9660->cur_dirstr), &(dent->file->parentdir)); archive_strappend_char(&(iso9660->cur_dirstr), '/'); } archive_string_concat(&(iso9660->cur_dirstr), &(dent->file->basename)); } if (!isoent_add_child_tail(dent, isoent)) { np = (struct isoent *)__archive_rb_tree_find_node( &(dent->rbtree), isoent->file->basename.s); goto same_entry; } return (ARCHIVE_OK); } same_entry: /* * We have already has the entry the filename of which is * the same. */ f1 = np->file; f2 = isoent->file; /* If the file type of entries is different, * we cannot handle it. */ if (archive_entry_filetype(f1->entry) != archive_entry_filetype(f2->entry)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found duplicate entries `%s' and its file type is " "different", archive_entry_pathname(f1->entry)); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FAILED); } /* Swap file entries. */ np->file = f2; isoent->file = f1; np->virtual = 0; _isoent_free(isoent); *isoentpp = np; return (ARCHIVE_OK); } /* * Find a entry from `isoent' */ static struct isoent * isoent_find_child(struct isoent *isoent, const char *child_name) { struct isoent *np; np = (struct isoent *)__archive_rb_tree_find_node( &(isoent->rbtree), child_name); return (np); } /* * Find a entry full-path of which is specified by `fn' parameter, * in the tree. */ static struct isoent * isoent_find_entry(struct isoent *rootent, const char *fn) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct isoent *isoent, *np; int l; isoent = rootent; np = NULL; for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) break; fn += l; if (fn[0] == '/') fn++; np = isoent_find_child(isoent, name); if (np == NULL) break; if (fn[0] == '\0') break;/* We found out the entry */ /* Try sub directory. */ isoent = np; np = NULL; if (!isoent->dir) break;/* Not directory */ } return (np); } /* * Following idr_* functions are used for resolving duplicated filenames * and unreceivable filenames to generate ISO9660/Joliet Identifiers. */ static void idr_relaxed_filenames(char *map) { int i; for (i = 0x21; i <= 0x2F; i++) map[i] = 1; for (i = 0x3A; i <= 0x41; i++) map[i] = 1; for (i = 0x5B; i <= 0x5E; i++) map[i] = 1; map[0x60] = 1; for (i = 0x7B; i <= 0x7E; i++) map[i] = 1; } static void idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr) { idr->idrent_pool = NULL; idr->pool_size = 0; if (vdd->vdd_type != VDD_JOLIET) { if (iso9660->opt.iso_level <= 3) { memcpy(idr->char_map, d_characters_map, sizeof(idr->char_map)); } else { memcpy(idr->char_map, d1_characters_map, sizeof(idr->char_map)); idr_relaxed_filenames(idr->char_map); } } } static void idr_cleanup(struct idr *idr) { free(idr->idrent_pool); } static int idr_ensure_poolsize(struct archive_write *a, struct idr *idr, int cnt) { if (idr->pool_size < cnt) { void *p; const int bk = (1 << 7) - 1; int psize; psize = (cnt + bk) & ~bk; p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } idr->idrent_pool = (struct idrent *)p; idr->pool_size = psize; } return (ARCHIVE_OK); } static int idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax, int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops) { int r; (void)ffmax; /* UNUSED */ r = idr_ensure_poolsize(a, idr, cnt); if (r != ARCHIVE_OK) return (r); __archive_rb_tree_init(&(idr->rbtree), rbt_ops); idr->wait_list.first = NULL; idr->wait_list.last = &(idr->wait_list.first); idr->pool_idx = 0; idr->num_size = num_size; idr->null_size = null_size; return (ARCHIVE_OK); } static void idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff) { struct idrent *idrent, *n; idrent = &(idr->idrent_pool[idr->pool_idx++]); idrent->wnext = idrent->avail = NULL; idrent->isoent = isoent; idrent->weight = weight; idrent->noff = noff; idrent->rename_num = 0; if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) { n = (struct idrent *)__archive_rb_tree_find_node( &(idr->rbtree), idrent->isoent); if (n != NULL) { /* this `idrent' needs to rename. */ idrent->avail = n; *idr->wait_list.last = idrent; idr->wait_list.last = &(idrent->wnext); } } } static void idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize) { unsigned char *p; int wnp_ext_off; wnp_ext_off = wnp->isoent->ext_off; if (wnp->noff + numsize != wnp_ext_off) { p = (unsigned char *)wnp->isoent->identifier; /* Extend the filename; foo.c --> foo___.c */ memmove(p + wnp->noff + numsize, p + wnp_ext_off, wnp->isoent->ext_len + nullsize); wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize; wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len; } } static void idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) { struct idrent *n; unsigned char *p; for (n = idr->wait_list.first; n != NULL; n = n->wnext) { idr_extend_identifier(n, idr->num_size, idr->null_size); p = (unsigned char *)n->isoent->identifier + n->noff; do { fsetnum(p, n->avail->rename_num++); } while (!__archive_rb_tree_insert_node( &(idr->rbtree), &(n->rbnode))); } } static void idr_set_num(unsigned char *p, int num) { static const char xdig[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig); p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))]; num %= sizeof(xdig) * sizeof(xdig); p[1] = xdig[ (num / sizeof(xdig))]; num %= sizeof(xdig); p[2] = xdig[num]; } static void idr_set_num_beutf16(unsigned char *p, int num) { static const uint16_t xdig[] = { 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A }; #define XDIG_CNT (sizeof(xdig)/sizeof(xdig[0])) num %= XDIG_CNT * XDIG_CNT * XDIG_CNT; archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]); num %= XDIG_CNT * XDIG_CNT; archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]); num %= XDIG_CNT; archive_be16enc(p+4, xdig[num]); } /* * Generate ISO9660 Identifier. */ static int isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct iso9660 *iso9660; struct isoent *np; char *p; int l, r; const char *char_map; char allow_ldots, allow_multidot, allow_period, allow_vernum; int fnmax, ffmax, dnmax; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_iso9660, isoent_cmp_key_iso9660 }; if (isoent->children.cnt == 0) return (0); iso9660 = a->format_data; char_map = idr->char_map; if (iso9660->opt.iso_level <= 3) { allow_ldots = 0; allow_multidot = 0; allow_period = 1; allow_vernum = iso9660->opt.allow_vernum; if (iso9660->opt.iso_level == 1) { fnmax = 8; ffmax = 12;/* fnmax + '.' + 3 */ dnmax = 8; } else { fnmax = 30; ffmax = 31; dnmax = 31; } } else { allow_ldots = allow_multidot = 1; allow_period = allow_vernum = 0; if (iso9660->opt.rr) /* * MDR : The maximum size of Directory Record(254). * DRL : A Directory Record Length(33). * CE : A size of SUSP CE System Use Entry(28). * MDR - DRL - CE = 254 - 33 - 28 = 193. */ fnmax = ffmax = dnmax = 193; else /* * XA : CD-ROM XA System Use Extension * Information(14). * MDR - DRL - XA = 254 - 33 -14 = 207. */ fnmax = ffmax = dnmax = 207; } r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops); if (r < 0) return (r); for (np = isoent->children.first; np != NULL; np = np->chnext) { char *dot, *xdot; int ext_off, noff, weight; l = (int)np->file->basename.length; p = malloc(l+31+2+1); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(p, np->file->basename.s, l); p[l] = '\0'; np->identifier = p; dot = xdot = NULL; if (!allow_ldots) { /* * If there is a '.' character at the first byte, * it has to be replaced by '_' character. */ if (*p == '.') *p++ = '_'; } for (;*p; p++) { if (*p & 0x80) { *p = '_'; continue; } if (char_map[(unsigned char)*p]) { /* if iso-level is '4', a character '.' is * allowed by char_map. */ if (*p == '.') { xdot = dot; dot = p; } continue; } if (*p >= 'a' && *p <= 'z') { *p -= 'a' - 'A'; continue; } if (*p == '.') { xdot = dot; dot = p; if (allow_multidot) continue; } *p = '_'; } p = np->identifier; weight = -1; if (dot == NULL) { int nammax; if (np->dir) nammax = dnmax; else nammax = fnmax; if (l > nammax) { p[nammax] = '\0'; weight = nammax; ext_off = nammax; } else ext_off = l; } else { *dot = '.'; ext_off = (int)(dot - p); if (iso9660->opt.iso_level == 1) { if (dot - p <= 8) { if (strlen(dot) > 4) { /* A length of a file extension * must be less than 4 */ dot[4] = '\0'; weight = 0; } } else { p[8] = dot[0]; p[9] = dot[1]; p[10] = dot[2]; p[11] = dot[3]; p[12] = '\0'; weight = 8; ext_off = 8; } } else if (np->dir) { if (l > dnmax) { p[dnmax] = '\0'; weight = dnmax; if (ext_off > dnmax) ext_off = dnmax; } } else if (l > ffmax) { int extlen = (int)strlen(dot); int xdoff; if (xdot != NULL) xdoff = (int)(xdot - p); else xdoff = 0; if (extlen > 1 && xdoff < fnmax-1) { int off; if (extlen > ffmax) extlen = ffmax; off = ffmax - extlen; if (off == 0) { /* A dot('.') character * doesn't place to the first * byte of identifier. */ off ++; extlen --; } memmove(p+off, dot, extlen); p[ffmax] = '\0'; ext_off = off; weight = off; #ifdef COMPAT_MKISOFS } else if (xdoff >= fnmax-1) { /* Simulate a bug(?) of mkisofs. */ p[fnmax-1] = '\0'; ext_off = fnmax-1; weight = fnmax-1; #endif } else { p[fnmax] = '\0'; ext_off = fnmax; weight = fnmax; } } } /* Save an offset of a file name extension to sort files. */ np->ext_off = ext_off; np->ext_len = (int)strlen(&p[ext_off]); np->id_len = l = ext_off + np->ext_len; /* Make an offset of the number which is used to be set * hexadecimal number to avoid duplicate identifier. */ if (iso9660->opt.iso_level == 1) { if (ext_off >= 5) noff = 5; else noff = ext_off; } else { if (l == ffmax) noff = ext_off - 3; else if (l == ffmax-1) noff = ext_off - 2; else if (l == ffmax-2) noff = ext_off - 1; else noff = ext_off; } /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier. */ idr_resolve(idr, idr_set_num); /* Add a period and a version number to identifiers. */ for (np = isoent->children.first; np != NULL; np = np->chnext) { if (!np->dir && np->rr_child == NULL) { p = np->identifier + np->ext_off + np->ext_len; if (np->ext_len == 0 && allow_period) { *p++ = '.'; np->ext_len = 1; } if (np->ext_len == 1 && !allow_period) { *--p = '\0'; np->ext_len = 0; } np->id_len = np->ext_off + np->ext_len; if (allow_vernum) { *p++ = ';'; *p++ = '1'; np->id_len += 2; } *p = '\0'; } else np->id_len = np->ext_off + np->ext_len; np->mb_len = np->id_len; } return (ARCHIVE_OK); } /* * Generate Joliet Identifier. */ static int isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct iso9660 *iso9660; struct isoent *np; unsigned char *p; size_t l; int r; size_t ffmax, parent_len; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_joliet, isoent_cmp_key_joliet }; if (isoent->children.cnt == 0) return (0); iso9660 = a->format_data; if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) ffmax = 206; else ffmax = 128; r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); if (r < 0) return (r); parent_len = 1; for (np = isoent; np->parent != np; np = np->parent) parent_len += np->mb_len + 1; for (np = isoent->children.first; np != NULL; np = np->chnext) { unsigned char *dot; int ext_off, noff, weight; size_t lt; if ((l = np->file->basename_utf16.length) > ffmax) l = ffmax; p = malloc((l+1)*2); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(p, np->file->basename_utf16.s, l); p[l] = 0; p[l+1] = 0; np->identifier = (char *)p; lt = l; dot = p + l; weight = 0; while (lt > 0) { if (!joliet_allowed_char(p[0], p[1])) archive_be16enc(p, 0x005F); /* '_' */ else if (p[0] == 0 && p[1] == 0x2E) /* '.' */ dot = p; p += 2; lt -= 2; } ext_off = (int)(dot - (unsigned char *)np->identifier); np->ext_off = ext_off; np->ext_len = (int)l - ext_off; np->id_len = (int)l; /* * Get a length of MBS of a full-pathname. */ if (np->file->basename_utf16.length > ffmax) { if (archive_strncpy_l(&iso9660->mbs, (const char *)np->identifier, l, iso9660->sconv_from_utf16be) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, errno, "No memory"); return (ARCHIVE_FATAL); } np->mb_len = (int)iso9660->mbs.length; if (np->mb_len != (int)np->file->basename.length) weight = np->mb_len; } else np->mb_len = (int)np->file->basename.length; /* If a length of full-pathname is longer than 240 bytes, * it violates Joliet extensions regulation. */ if (parent_len > 240 || np->mb_len > 240 || parent_len + np->mb_len > 240) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "The regulation of Joliet extensions;" " A length of a full-pathname of `%s' is " "longer than 240 bytes, (p=%d, b=%d)", archive_entry_pathname(np->file->entry), (int)parent_len, (int)np->mb_len); return (ARCHIVE_FATAL); } /* Make an offset of the number which is used to be set * hexadecimal number to avoid duplicate identifier. */ if (l == ffmax) noff = ext_off - 6; else if (l == ffmax-2) noff = ext_off - 4; else if (l == ffmax-4) noff = ext_off - 2; else noff = ext_off; /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier with Joliet Volume. */ idr_resolve(idr, idr_set_num_beutf16); return (ARCHIVE_OK); } /* * This comparing rule is according to ISO9660 Standard 9.3 */ static int isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2) { const char *s1, *s2; int cmp; int l; s1 = p1->identifier; s2 = p2->identifier; /* Compare File Name */ l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } /* Compare File Name Extension */ if (p1->ext_len == 0 && p2->ext_len == 0) return (0); if (p1->ext_len == 1 && p2->ext_len == 1) return (0); if (p1->ext_len <= 1) return (-1); if (p2->ext_len <= 1) return (1); l = p1->ext_len; if (l > p2->ext_len) l = p2->ext_len; s1 = p1->identifier + p1->ext_off; s2 = p2->identifier + p2->ext_off; if (l > 1) { cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); } if (p1->ext_len < p2->ext_len) { s2 += l; l = p2->ext_len - p1->ext_len; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_len > p2->ext_len) { s1 += l; l = p1->ext_len - p2->ext_len; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } /* Compare File Version Number */ /* No operation. The File Version Number is always one. */ return (cmp); } static int isoent_cmp_node_iso9660(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct idrent *e1 = (const struct idrent *)n1; const struct idrent *e2 = (const struct idrent *)n2; return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent)); } static int isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key) { const struct isoent *isoent = (const struct isoent *)key; const struct idrent *idrent = (const struct idrent *)node; return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent)); } static int isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2) { const unsigned char *s1, *s2; int cmp; int l; s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; /* Compare File Name */ l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } /* Compare File Name Extension */ if (p1->ext_len == 0 && p2->ext_len == 0) return (0); if (p1->ext_len == 2 && p2->ext_len == 2) return (0); if (p1->ext_len <= 2) return (-1); if (p2->ext_len <= 2) return (1); l = p1->ext_len; if (l > p2->ext_len) l = p2->ext_len; s1 = (unsigned char *)(p1->identifier + p1->ext_off); s2 = (unsigned char *)(p2->identifier + p2->ext_off); if (l > 1) { cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); } if (p1->ext_len < p2->ext_len) { s2 += l; l = p2->ext_len - p1->ext_len; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_len > p2->ext_len) { s1 += l; l = p1->ext_len - p2->ext_len; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } /* Compare File Version Number */ /* No operation. The File Version Number is always one. */ return (cmp); } static int isoent_cmp_node_joliet(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct idrent *e1 = (const struct idrent *)n1; const struct idrent *e2 = (const struct idrent *)n2; return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent)); } static int isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key) { const struct isoent *isoent = (const struct isoent *)key; const struct idrent *idrent = (const struct idrent *)node; return (isoent_cmp_joliet_identifier(isoent, idrent->isoent)); } static int isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct archive_rb_node *rn; struct isoent **children; children = malloc(isoent->children.cnt * sizeof(struct isoent *)); if (children == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } isoent->children_sorted = children; ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) { struct idrent *idrent = (struct idrent *)rn; *children ++ = idrent->isoent; } return (ARCHIVE_OK); } /* * - Generate ISO9660 and Joliet identifiers from basenames. * - Sort files by each directory. */ static int isoent_traverse_tree(struct archive_write *a, struct vdd* vdd) { struct iso9660 *iso9660 = a->format_data; struct isoent *np; struct idr idr; int depth; int r; int (*genid)(struct archive_write *, struct isoent *, struct idr *); idr_init(iso9660, vdd, &idr); np = vdd->rootent; depth = 0; if (vdd->vdd_type == VDD_JOLIET) genid = isoent_gen_joliet_identifier; else genid = isoent_gen_iso9660_identifier; do { if (np->virtual && !archive_entry_mtime_is_set(np->file->entry)) { /* Set properly times to virtual directory */ archive_entry_set_mtime(np->file->entry, iso9660->birth_time, 0); archive_entry_set_atime(np->file->entry, iso9660->birth_time, 0); archive_entry_set_ctime(np->file->entry, iso9660->birth_time, 0); } if (np->children.first != NULL) { if (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth) { if (np->children.cnt > 0) iso9660->directories_too_deep = np; } else { /* Generate Identifier */ r = genid(a, np, &idr); if (r < 0) goto exit_traverse_tree; r = isoent_make_sorted_files(a, np, &idr); if (r < 0) goto exit_traverse_tree; if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } } } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); r = ARCHIVE_OK; exit_traverse_tree: idr_cleanup(&idr); return (r); } /* * Collect directory entries into path_table by a directory depth. */ static int isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth) { struct isoent *np; if (rootent == NULL) rootent = vdd->rootent; np = rootent; do { /* Register current directory to pathtable. */ path_table_add_entry(&(vdd->pathtbl[depth]), np); if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != rootent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != rootent); return (ARCHIVE_OK); } /* * The entry whose number of levels in a directory hierarchy is * large than eight relocate to rr_move directory. */ static int isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved, struct isoent *curent, struct isoent **newent) { struct iso9660 *iso9660 = a->format_data; struct isoent *rrmoved, *mvent, *np; if ((rrmoved = *rr_moved) == NULL) { struct isoent *rootent = iso9660->primary.rootent; /* There isn't rr_move entry. * Create rr_move entry and insert it into the root entry. */ rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved"); if (rrmoved == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } /* Add "rr_moved" entry to the root entry. */ isoent_add_child_head(rootent, rrmoved); archive_entry_set_nlink(rootent->file->entry, archive_entry_nlink(rootent->file->entry) + 1); /* Register "rr_moved" entry to second level pathtable. */ path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved); /* Save rr_moved. */ *rr_moved = rrmoved; } /* * Make a clone of curent which is going to be relocated * to rr_moved. */ mvent = isoent_clone(curent); if (mvent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } /* linking.. and use for creating "CL", "PL" and "RE" */ mvent->rr_parent = curent->parent; curent->rr_child = mvent; /* * Move subdirectories from the curent to mvent */ if (curent->children.first != NULL) { *mvent->children.last = curent->children.first; mvent->children.last = curent->children.last; } for (np = mvent->children.first; np != NULL; np = np->chnext) np->parent = mvent; mvent->children.cnt = curent->children.cnt; curent->children.cnt = 0; curent->children.first = NULL; curent->children.last = &curent->children.first; if (curent->subdirs.first != NULL) { *mvent->subdirs.last = curent->subdirs.first; mvent->subdirs.last = curent->subdirs.last; } mvent->subdirs.cnt = curent->subdirs.cnt; curent->subdirs.cnt = 0; curent->subdirs.first = NULL; curent->subdirs.last = &curent->subdirs.first; /* * The mvent becomes a child of the rr_moved entry. */ isoent_add_child_tail(rrmoved, mvent); archive_entry_set_nlink(rrmoved->file->entry, archive_entry_nlink(rrmoved->file->entry) + 1); /* * This entry which relocated to the rr_moved directory * has to set the flag as a file. * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry. */ curent->dir = 0; *newent = mvent; return (ARCHIVE_OK); } static int isoent_rr_move(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct path_table *pt; struct isoent *rootent, *rr_moved; struct isoent *np, *last; int r; pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]); /* There aren't level 8 directories reaching a deeper level. */ if (pt->cnt == 0) return (ARCHIVE_OK); rootent = iso9660->primary.rootent; /* If "rr_moved" directory is already existing, * we have to use it. */ rr_moved = isoent_find_child(rootent, "rr_moved"); if (rr_moved != NULL && rr_moved != rootent->children.first) { /* * It's necessary that rr_move is the first entry * of the root. */ /* Remove "rr_moved" entry from children chain. */ isoent_remove_child(rootent, rr_moved); /* Add "rr_moved" entry into the head of children chain. */ isoent_add_child_head(rootent, rr_moved); } /* * Check level 8 path_table. * If find out sub directory entries, that entries move to rr_move. */ np = pt->first; while (np != NULL) { last = path_table_last_entry(pt); for (; np != NULL; np = np->ptnext) { struct isoent *mvent; struct isoent *newent; if (!np->dir) continue; for (mvent = np->subdirs.first; mvent != NULL; mvent = mvent->drnext) { r = isoent_rr_move_dir(a, &rr_moved, mvent, &newent); if (r < 0) return (r); isoent_collect_dirs(&(iso9660->primary), newent, 2); } } /* If new entries are added to level 8 path_talbe, * its sub directory entries move to rr_move too. */ np = last->ptnext; } return (ARCHIVE_OK); } /* * This comparing rule is according to ISO9660 Standard 6.9.1 */ static int _compare_path_table(const void *v1, const void *v2) { const struct isoent *p1, *p2; const char *s1, *s2; int cmp, l; p1 = *((const struct isoent **)(uintptr_t)v1); p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); /* Compare identifier */ s1 = p1->identifier; s2 = p2->identifier; l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = strncmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } return (0); } static int _compare_path_table_joliet(const void *v1, const void *v2) { const struct isoent *p1, *p2; const unsigned char *s1, *s2; int cmp, l; p1 = *((const struct isoent **)(uintptr_t)v1); p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); /* Compare identifier */ s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } return (0); } static inline void path_table_add_entry(struct path_table *pathtbl, struct isoent *ent) { ent->ptnext = NULL; *pathtbl->last = ent; pathtbl->last = &(ent->ptnext); pathtbl->cnt ++; } static inline struct isoent * path_table_last_entry(struct path_table *pathtbl) { if (pathtbl->first == NULL) return (NULL); return (((struct isoent *)(void *) ((char *)(pathtbl->last) - offsetof(struct isoent, ptnext)))); } /* * Sort directory entries in path_table * and assign directory number to each entries. */ static int isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd, int depth, int *dir_number) { struct isoent *np; struct isoent **enttbl; struct path_table *pt; int i; pt = &vdd->pathtbl[depth]; if (pt->cnt == 0) { pt->sorted = NULL; return (ARCHIVE_OK); } enttbl = malloc(pt->cnt * sizeof(struct isoent *)); if (enttbl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } pt->sorted = enttbl; for (np = pt->first; np != NULL; np = np->ptnext) *enttbl ++ = np; enttbl = pt->sorted; switch (vdd->vdd_type) { case VDD_PRIMARY: case VDD_ENHANCED: #ifdef __COMPAR_FN_T qsort(enttbl, pt->cnt, sizeof(struct isoent *), (__compar_fn_t)_compare_path_table); #else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table); #endif break; case VDD_JOLIET: #ifdef __COMPAR_FN_T qsort(enttbl, pt->cnt, sizeof(struct isoent *), (__compar_fn_t)_compare_path_table_joliet); #else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table_joliet); #endif break; } for (i = 0; i < pt->cnt; i++) enttbl[i]->dir_number = (*dir_number)++; return (ARCHIVE_OK); } static int isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd, int max_depth) { int i; vdd->max_depth = max_depth; vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth); if (vdd->pathtbl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } for (i = 0; i < vdd->max_depth; i++) { vdd->pathtbl[i].first = NULL; vdd->pathtbl[i].last = &(vdd->pathtbl[i].first); vdd->pathtbl[i].sorted = NULL; vdd->pathtbl[i].cnt = 0; } return (ARCHIVE_OK); } /* * Make Path Tables */ static int isoent_make_path_table(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int depth, r; int dir_number; /* * Init Path Table. */ if (iso9660->dircnt_max >= MAX_DEPTH && (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4)) r = isoent_alloc_path_table(a, &(iso9660->primary), iso9660->dircnt_max + 1); else /* The number of levels in the hierarchy cannot exceed * eight. */ r = isoent_alloc_path_table(a, &(iso9660->primary), MAX_DEPTH); if (r < 0) return (r); if (iso9660->opt.joliet) { r = isoent_alloc_path_table(a, &(iso9660->joliet), iso9660->dircnt_max + 1); if (r < 0) return (r); } /* Step 0. * - Collect directories for primary and joliet. */ isoent_collect_dirs(&(iso9660->primary), NULL, 0); if (iso9660->opt.joliet) isoent_collect_dirs(&(iso9660->joliet), NULL, 0); /* * Rockridge; move deeper depth directories to rr_moved. */ if (iso9660->opt.rr) { r = isoent_rr_move(a); if (r < 0) return (r); } /* Update nlink. */ isofile_connect_hardlink_files(iso9660); /* Step 1. * - Renew a value of the depth of that directories. * - Resolve hardlinks. * - Convert pathnames to ISO9660 name or UCS2(joliet). * - Sort files by each directory. */ r = isoent_traverse_tree(a, &(iso9660->primary)); if (r < 0) return (r); if (iso9660->opt.joliet) { r = isoent_traverse_tree(a, &(iso9660->joliet)); if (r < 0) return (r); } /* Step 2. * - Sort directories. * - Assign all directory number. */ dir_number = 1; for (depth = 0; depth < iso9660->primary.max_depth; depth++) { r = isoent_make_path_table_2(a, &(iso9660->primary), depth, &dir_number); if (r < 0) return (r); } if (iso9660->opt.joliet) { dir_number = 1; for (depth = 0; depth < iso9660->joliet.max_depth; depth++) { r = isoent_make_path_table_2(a, &(iso9660->joliet), depth, &dir_number); if (r < 0) return (r); } } if (iso9660->opt.limit_dirs && dir_number > 0xffff) { /* * Maximum number of directories is 65535(0xffff) * doe to size(16bit) of Parent Directory Number of * the Path Table. * See also ISO9660 Standard 9.4. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many directories(%d) over 65535.", dir_number); return (ARCHIVE_FATAL); } /* Get the size of the Path Table. */ calculate_path_table_size(&(iso9660->primary)); if (iso9660->opt.joliet) calculate_path_table_size(&(iso9660->joliet)); return (ARCHIVE_OK); } static int isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent) { struct iso9660 *iso9660 = a->format_data; /* Find a isoent of the boot file. */ iso9660->el_torito.boot = isoent_find_entry(rootent, iso9660->el_torito.boot_filename.s); if (iso9660->el_torito.boot == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't find the boot image file ``%s''", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } iso9660->el_torito.boot->file->boot = BOOT_IMAGE; return (ARCHIVE_OK); } static int isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; struct isoent *isoent; struct archive_entry *entry; (void)rootent; /* UNUSED */ /* * Create the entry which is the "boot.catalog" file. */ file = isofile_new(a, NULL); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } archive_entry_set_pathname(file->entry, iso9660->el_torito.catalog_filename.s); archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE); archive_entry_set_mtime(file->entry, iso9660->birth_time, 0); archive_entry_set_atime(file->entry, iso9660->birth_time, 0); archive_entry_set_ctime(file->entry, iso9660->birth_time, 0); archive_entry_set_uid(file->entry, getuid()); archive_entry_set_gid(file->entry, getgid()); archive_entry_set_mode(file->entry, AE_IFREG | 0444); archive_entry_set_nlink(file->entry, 1); if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { isofile_free(file); return (ARCHIVE_FATAL); } file->boot = BOOT_CATALOG; file->content.size = LOGICAL_BLOCK_SIZE; isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } isoent->virtual = 1; /* Add the "boot.catalog" entry into tree */ if (isoent_tree(a, &isoent) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->el_torito.catalog = isoent; /* * Get a boot media type. */ switch (iso9660->opt.boot_type) { default: case OPT_BOOT_TYPE_AUTO: /* Try detecting a media type of the boot image. */ entry = iso9660->el_torito.boot->file->entry; if (archive_entry_size(entry) == FD_1_2M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_2M_DISKETTE; else if (archive_entry_size(entry) == FD_1_44M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_44M_DISKETTE; else if (archive_entry_size(entry) == FD_2_88M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_2_88M_DISKETTE; else /* We cannot decide whether the boot image is * hard-disk. */ iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; break; case OPT_BOOT_TYPE_NO_EMU: iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; break; case OPT_BOOT_TYPE_HARD_DISK: iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK; break; case OPT_BOOT_TYPE_FD: entry = iso9660->el_torito.boot->file->entry; if (archive_entry_size(entry) <= FD_1_2M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_2M_DISKETTE; else if (archive_entry_size(entry) <= FD_1_44M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_44M_DISKETTE; else if (archive_entry_size(entry) <= FD_2_88M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_2_88M_DISKETTE; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Boot image file(``%s'') size is too big " "for fd type.", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } break; } /* * Get a system type. * TODO: `El Torito' specification says "A copy of byte 5 from the * Partition Table found in the boot image". */ iso9660->el_torito.system_type = 0; /* * Get an ID. */ if (iso9660->opt.publisher) archive_string_copy(&(iso9660->el_torito.id), &(iso9660->publisher_identifier)); return (ARCHIVE_OK); } /* * If a media type is floppy, return its image size. * otherwise return 0. */ static size_t fd_boot_image_size(int media_type) { switch (media_type) { case BOOT_MEDIA_1_2M_DISKETTE: return (FD_1_2M_SIZE); case BOOT_MEDIA_1_44M_DISKETTE: return (FD_1_44M_SIZE); case BOOT_MEDIA_2_88M_DISKETTE: return (FD_2_88M_SIZE); default: return (0); } } /* * Make a boot catalog image data. */ static int make_boot_catalog(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; unsigned char *block; unsigned char *p; uint16_t sum, *wp; block = wb_buffptr(a); memset(block, 0, LOGICAL_BLOCK_SIZE); p = block; /* * Validation Entry */ /* Header ID */ p[0] = 1; /* Platform ID */ p[1] = iso9660->el_torito.platform_id; /* Reserved */ p[2] = p[3] = 0; /* ID */ if (archive_strlen(&(iso9660->el_torito.id)) > 0) strncpy((char *)p+4, iso9660->el_torito.id.s, 23); p[27] = 0; /* Checksum */ p[28] = p[29] = 0; /* Key */ p[30] = 0x55; p[31] = 0xAA; sum = 0; wp = (uint16_t *)block; while (wp < (uint16_t *)&block[32]) sum += archive_le16dec(wp++); set_num_721(&block[28], (~sum) + 1); /* * Initial/Default Entry */ p = &block[32]; /* Boot Indicator */ p[0] = 0x88; /* Boot media type */ p[1] = iso9660->el_torito.media_type; /* Load Segment */ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) set_num_721(&p[2], iso9660->el_torito.boot_load_seg); else set_num_721(&p[2], 0); /* System Type */ p[4] = iso9660->el_torito.system_type; /* Unused */ p[5] = 0; /* Sector Count */ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) set_num_721(&p[6], iso9660->el_torito.boot_load_size); else set_num_721(&p[6], 1); /* Load RBA */ set_num_731(&p[8], iso9660->el_torito.boot->file->content.location); /* Unused */ memset(&p[12], 0, 20); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int setup_boot_information(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isoent *np; int64_t size; uint32_t sum; unsigned char buff[4096]; np = iso9660->el_torito.boot; lseek(iso9660->temp_fd, np->file->content.offset_of_temp + 64, SEEK_SET); size = archive_entry_size(np->file->entry) - 64; if (size <= 0) { archive_set_error(&a->archive, errno, "Boot file(%jd) is too small", (intmax_t)size + 64); return (ARCHIVE_FATAL); } sum = 0; while (size > 0) { size_t rsize; ssize_t i, rs; if (size > (int64_t)sizeof(buff)) rsize = sizeof(buff); else rsize = (size_t)size; rs = read(iso9660->temp_fd, buff, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } for (i = 0; i < rs; i += 4) sum += archive_le32dec(buff + i); size -= rs; } /* Set the location of Primary Volume Descriptor. */ set_num_731(buff, SYSTEM_AREA_BLOCK); /* Set the location of the boot file. */ set_num_731(buff+4, np->file->content.location); /* Set the size of the boot file. */ size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = archive_entry_size(np->file->entry); set_num_731(buff+8, (uint32_t)size); /* Set the sum of the boot file. */ set_num_731(buff+12, sum); /* Clear reserved bytes. */ memset(buff+16, 0, 40); /* Overwrite the boot file. */ lseek(iso9660->temp_fd, np->file->content.offset_of_temp + 8, SEEK_SET); return (write_to_temp(a, buff, 56)); } #ifdef HAVE_ZLIB_H static int zisofs_init_zstream(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int r; iso9660->zisofs.stream.next_in = NULL; iso9660->zisofs.stream.avail_in = 0; iso9660->zisofs.stream.total_in = 0; iso9660->zisofs.stream.total_out = 0; if (iso9660->zisofs.stream_valid) r = deflateReset(&(iso9660->zisofs.stream)); else { r = deflateInit(&(iso9660->zisofs.stream), iso9660->zisofs.compression_level); iso9660->zisofs.stream_valid = 1; } switch (r) { case Z_OK: break; default: case Z_STREAM_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: invalid setup parameter"); return (ARCHIVE_FATAL); case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Internal error initializing " "compression library"); return (ARCHIVE_FATAL); case Z_VERSION_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: invalid library version"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ static int zisofs_init(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660 = a->format_data; #ifdef HAVE_ZLIB_H uint64_t tsize; size_t _ceil, bpsize; int r; #endif iso9660->zisofs.detect_magic = 0; iso9660->zisofs.making = 0; if (!iso9660->opt.rr || !iso9660->opt.zisofs) return (ARCHIVE_OK); if (archive_entry_size(file->entry) >= 24 && archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) { /* Acceptable file size for zisofs. */ iso9660->zisofs.detect_magic = 1; iso9660->zisofs.magic_cnt = 0; } if (!iso9660->zisofs.detect_magic) return (ARCHIVE_OK); #ifdef HAVE_ZLIB_H /* The number of Logical Blocks which uncompressed data * will use in iso-image file is the same as the number of * Logical Blocks which zisofs(compressed) data will use * in ISO-image file. It won't reduce iso-image file size. */ if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE) return (ARCHIVE_OK); /* Initialize compression library */ r = zisofs_init_zstream(a); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Mark file->zisofs to create RRIP 'ZF' Use Entry. */ file->zisofs.header_size = ZF_HEADER_SIZE >> 2; file->zisofs.log2_bs = ZF_LOG2_BS; file->zisofs.uncompressed_size = (uint32_t)archive_entry_size(file->entry); /* Calculate a size of Block Pointers of zisofs. */ _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) >> file->zisofs.log2_bs; iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1; iso9660->zisofs.block_pointers_idx = 0; /* Ensure a buffer size used for Block Pointers */ bpsize = iso9660->zisofs.block_pointers_cnt * sizeof(iso9660->zisofs.block_pointers[0]); if (iso9660->zisofs.block_pointers_allocated < bpsize) { free(iso9660->zisofs.block_pointers); iso9660->zisofs.block_pointers = malloc(bpsize); if (iso9660->zisofs.block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } iso9660->zisofs.block_pointers_allocated = bpsize; } /* * Skip zisofs header and Block Pointers, which we will write * after all compressed data of a file written to the temporary * file. */ tsize = ZF_HEADER_SIZE + bpsize; if (write_null(a, (size_t)tsize) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Initialize some variables to make zisofs. */ archive_le32enc(&(iso9660->zisofs.block_pointers[0]), (uint32_t)tsize); iso9660->zisofs.remaining = file->zisofs.uncompressed_size; iso9660->zisofs.making = 1; iso9660->zisofs.allzero = 1; iso9660->zisofs.block_offset = tsize; iso9660->zisofs.total_size = tsize; iso9660->cur_file->cur_content->size = tsize; #endif return (ARCHIVE_OK); } static void zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; const unsigned char *p, *endp; const unsigned char *magic_buff; uint32_t uncompressed_size; unsigned char header_size; unsigned char log2_bs; size_t _ceil, doff; uint32_t bst, bed; int magic_max; int64_t entry_size; entry_size = archive_entry_size(file->entry); if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size) magic_max = (int)entry_size; else magic_max = sizeof(iso9660->zisofs.magic_buffer); if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max) /* It's unnecessary we copy buffer. */ magic_buff = buff; else { if (iso9660->zisofs.magic_cnt < magic_max) { size_t l; l = sizeof(iso9660->zisofs.magic_buffer) - iso9660->zisofs.magic_cnt; if (l > s) l = s; memcpy(iso9660->zisofs.magic_buffer + iso9660->zisofs.magic_cnt, buff, l); iso9660->zisofs.magic_cnt += (int)l; if (iso9660->zisofs.magic_cnt < magic_max) return; } magic_buff = iso9660->zisofs.magic_buffer; } iso9660->zisofs.detect_magic = 0; p = magic_buff; /* Check the magic code of zisofs. */ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) /* This is not zisofs file which made by mkzftree. */ return; p += sizeof(zisofs_magic); /* Read a zisofs header. */ uncompressed_size = archive_le32dec(p); header_size = p[4]; log2_bs = p[5]; if (uncompressed_size < 24 || header_size != 4 || log2_bs > 30 || log2_bs < 7) return;/* Invalid or not supported header. */ /* Calculate a size of Block Pointers of zisofs. */ _ceil = (uncompressed_size + (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs; doff = (_ceil + 1) * 4 + 16; if (entry_size < (int64_t)doff) return;/* Invalid data. */ /* Check every Block Pointer has valid value. */ p = magic_buff + 16; endp = magic_buff + magic_max; while (_ceil && p + 8 <= endp) { bst = archive_le32dec(p); if (bst != doff) return;/* Invalid data. */ p += 4; bed = archive_le32dec(p); if (bed < bst || bed > entry_size) return;/* Invalid data. */ doff += bed - bst; _ceil--; } file->zisofs.uncompressed_size = uncompressed_size; file->zisofs.header_size = header_size; file->zisofs.log2_bs = log2_bs; /* Disable making a zisofs image. */ iso9660->zisofs.making = 0; } #ifdef HAVE_ZLIB_H /* * Compress data and write it to a temporary file. */ static int zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; const unsigned char *b; z_stream *zstrm; size_t avail, csize; int flush, r; zstrm = &(iso9660->zisofs.stream); zstrm->next_out = wb_buffptr(a); zstrm->avail_out = (uInt)wb_remaining(a); b = (const unsigned char *)buff; do { avail = ZF_BLOCK_SIZE - zstrm->total_in; if (s < avail) { avail = s; flush = Z_NO_FLUSH; } else flush = Z_FINISH; iso9660->zisofs.remaining -= avail; if (iso9660->zisofs.remaining <= 0) flush = Z_FINISH; zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b; zstrm->avail_in = (uInt)avail; /* * Check if current data block are all zero. */ if (iso9660->zisofs.allzero) { const unsigned char *nonzero = b; const unsigned char *nonzeroend = b + avail; while (nonzero < nonzeroend) if (*nonzero++) { iso9660->zisofs.allzero = 0; break; } } b += avail; s -= avail; /* * If current data block are all zero, we do not use * compressed data. */ if (flush == Z_FINISH && iso9660->zisofs.allzero && avail + zstrm->total_in == ZF_BLOCK_SIZE) { if (iso9660->zisofs.block_offset != file->cur_content->size) { int64_t diff; r = wb_set_offset(a, file->cur_content->offset_of_temp + iso9660->zisofs.block_offset); if (r != ARCHIVE_OK) return (r); diff = file->cur_content->size - iso9660->zisofs.block_offset; file->cur_content->size -= diff; iso9660->zisofs.total_size -= diff; } zstrm->avail_in = 0; } /* * Compress file data. */ while (zstrm->avail_in > 0) { csize = zstrm->total_out; r = deflate(zstrm, flush); switch (r) { case Z_OK: case Z_STREAM_END: csize = zstrm->total_out - csize; if (wb_consume(a, csize) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->zisofs.total_size += csize; iso9660->cur_file->cur_content->size += csize; zstrm->next_out = wb_buffptr(a); zstrm->avail_out = (uInt)wb_remaining(a); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Compression failed:" " deflate() call returned status %d", r); return (ARCHIVE_FATAL); } } if (flush == Z_FINISH) { /* * Save the information of one zisofs block. */ iso9660->zisofs.block_pointers_idx ++; archive_le32enc(&(iso9660->zisofs.block_pointers[ iso9660->zisofs.block_pointers_idx]), (uint32_t)iso9660->zisofs.total_size); r = zisofs_init_zstream(a); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->zisofs.allzero = 1; iso9660->zisofs.block_offset = file->cur_content->size; } } while (s); return (ARCHIVE_OK); } static int zisofs_finish_entry(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; unsigned char buff[16]; size_t s; int64_t tail; /* Direct temp file stream to zisofs temp file stream. */ archive_entry_set_size(file->entry, iso9660->zisofs.total_size); /* * Save a file pointer which points the end of current zisofs data. */ tail = wb_offset(a); /* * Make a header. * * +-----------------+----------------+-----------------+ * | Header 16 bytes | Block Pointers | Compressed data | * +-----------------+----------------+-----------------+ * 0 16 +X * Block Pointers : * 4 * (((Uncompressed file size + block_size -1) / block_size) + 1) * * Write zisofs header. * Magic number * +----+----+----+----+----+----+----+----+ * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 | * +----+----+----+----+----+----+----+----+ * 0 1 2 3 4 5 6 7 8 * * +------------------------+------------------+ * | Uncompressed file size | header_size >> 2 | * +------------------------+------------------+ * 8 12 13 * * +-----------------+----------------+ * | log2 block_size | Reserved(0000) | * +-----------------+----------------+ * 13 14 16 */ memcpy(buff, zisofs_magic, 8); set_num_731(buff+8, file->zisofs.uncompressed_size); buff[12] = file->zisofs.header_size; buff[13] = file->zisofs.log2_bs; buff[14] = buff[15] = 0;/* Reserved */ /* Move to the right position to write the header. */ wb_set_offset(a, file->content.offset_of_temp); /* Write the header. */ if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Write zisofs Block Pointers. */ s = iso9660->zisofs.block_pointers_cnt * sizeof(iso9660->zisofs.block_pointers[0]); if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Set a file pointer back to the end of the temporary file. */ wb_set_offset(a, tail); return (ARCHIVE_OK); } static int zisofs_free(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int ret = ARCHIVE_OK; free(iso9660->zisofs.block_pointers); if (iso9660->zisofs.stream_valid && deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } iso9660->zisofs.block_pointers = NULL; iso9660->zisofs.stream_valid = 0; return (ret); } struct zisofs_extract { int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; size_t uncompressed_buffer_size; int initialized:1; int header_passed:1; uint32_t pz_offset; unsigned char *block_pointers; size_t block_pointers_size; size_t block_pointers_avail; size_t block_off; uint32_t block_avail; z_stream stream; int stream_valid; }; static ssize_t zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail = bytes; size_t _ceil, xsize; /* Allocate block pointers buffer. */ _ceil = (size_t)((zisofs->pz_uncompressed_size + (((int64_t)1) << zisofs->pz_log2_bs) - 1) >> zisofs->pz_log2_bs); xsize = (_ceil + 1) * 4; if (zisofs->block_pointers == NULL) { size_t alloc = ((xsize >> 10) + 1) << 10; zisofs->block_pointers = malloc(alloc); if (zisofs->block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for zisofs decompression"); return (ARCHIVE_FATAL); } } zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs; /* * Read the file header, and check the magic code of zisofs. */ if (!zisofs->header_passed) { int err = 0; if (avail < 16) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) err = 1; else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size) err = 1; else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs) err = 1; if (err) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } avail -= 16; p += 16; zisofs->header_passed = 1; } /* * Read block pointers. */ if (zisofs->header_passed && zisofs->block_pointers_avail < zisofs->block_pointers_size) { xsize = zisofs->block_pointers_size - zisofs->block_pointers_avail; if (avail < xsize) xsize = avail; memcpy(zisofs->block_pointers + zisofs->block_pointers_avail, p, xsize); zisofs->block_pointers_avail += xsize; avail -= xsize; if (zisofs->block_pointers_avail == zisofs->block_pointers_size) { /* We've got all block pointers and initialize * related variables. */ zisofs->block_off = 0; zisofs->block_avail = 0; /* Complete a initialization */ zisofs->initialized = 1; } } return ((ssize_t)avail); } static ssize_t zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail; int r; if (!zisofs->initialized) { ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes); if (rs < 0) return (rs); if (!zisofs->initialized) { /* We need more data. */ zisofs->pz_offset += (uint32_t)bytes; return (bytes); } avail = rs; p += bytes - avail; } else avail = bytes; /* * Get block offsets from block pointers. */ if (zisofs->block_avail == 0) { uint32_t bst, bed; if (zisofs->block_off + 4 >= zisofs->block_pointers_size) { /* There isn't a pair of offsets. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } bst = archive_le32dec( zisofs->block_pointers + zisofs->block_off); if (bst != zisofs->pz_offset + (bytes - avail)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers(cannot seek)"); return (ARCHIVE_FATAL); } bed = archive_le32dec( zisofs->block_pointers + zisofs->block_off + 4); if (bed < bst) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } zisofs->block_avail = bed - bst; zisofs->block_off += 4; /* Initialize compression library for new block. */ if (zisofs->stream_valid) r = inflateReset(&zisofs->stream); else r = inflateInit(&zisofs->stream); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize zisofs decompression."); return (ARCHIVE_FATAL); } zisofs->stream_valid = 1; zisofs->stream.total_in = 0; zisofs->stream.total_out = 0; } /* * Make uncompressed data. */ if (zisofs->block_avail == 0) { /* * It's basically 32K bytes NUL data. */ unsigned char *wb; size_t size, wsize; size = zisofs->uncompressed_buffer_size; while (size) { wb = wb_buffptr(a); if (size > wb_remaining(a)) wsize = wb_remaining(a); else wsize = size; memset(wb, 0, wsize); r = wb_consume(a, wsize); if (r < 0) return (r); size -= wsize; } } else { zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; if (avail > zisofs->block_avail) zisofs->stream.avail_in = zisofs->block_avail; else zisofs->stream.avail_in = (uInt)avail; zisofs->stream.next_out = wb_buffptr(a); zisofs->stream.avail_out = (uInt)wb_remaining(a); r = inflate(&zisofs->stream, 0); switch (r) { case Z_OK: /* Decompressor made some progress.*/ case Z_STREAM_END: /* Found end of stream. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zisofs decompression failed (%d)", r); return (ARCHIVE_FATAL); } avail -= zisofs->stream.next_in - p; zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out); if (r < 0) return (r); } zisofs->pz_offset += (uint32_t)bytes; return (bytes - avail); } static int zisofs_rewind_boot_file(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; unsigned char *rbuff; ssize_t r; size_t remaining, rbuff_size; struct zisofs_extract zext; int64_t read_offset, write_offset, new_offset; int fd, ret = ARCHIVE_OK; file = iso9660->el_torito.boot->file; /* * There is nothing to do if this boot file does not have * zisofs header. */ if (file->zisofs.header_size == 0) return (ARCHIVE_OK); /* * Uncompress the zisofs'ed file contents. */ memset(&zext, 0, sizeof(zext)); zext.pz_uncompressed_size = file->zisofs.uncompressed_size; zext.pz_log2_bs = file->zisofs.log2_bs; fd = iso9660->temp_fd; new_offset = wb_offset(a); read_offset = file->content.offset_of_temp; remaining = (size_t)file->content.size; if (remaining > 1024 * 32) rbuff_size = 1024 * 32; else rbuff_size = remaining; rbuff = malloc(rbuff_size); if (rbuff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } while (remaining) { size_t rsize; ssize_t rs; /* Get the current file pointer. */ write_offset = lseek(fd, 0, SEEK_CUR); /* Change the file pointer to read. */ lseek(fd, read_offset, SEEK_SET); rsize = rbuff_size; if (rsize > remaining) rsize = remaining; rs = read(iso9660->temp_fd, rbuff, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); ret = ARCHIVE_FATAL; break; } remaining -= rs; read_offset += rs; /* Put the file pointer back to write. */ lseek(fd, write_offset, SEEK_SET); r = zisofs_extract(a, &zext, rbuff, rs); if (r < 0) { ret = (int)r; break; } } if (ret == ARCHIVE_OK) { /* * Change the boot file content from zisofs'ed data * to plain data. */ file->content.offset_of_temp = new_offset; file->content.size = file->zisofs.uncompressed_size; archive_entry_set_size(file->entry, file->content.size); /* Set to be no zisofs. */ file->zisofs.header_size = 0; file->zisofs.log2_bs = 0; file->zisofs.uncompressed_size = 0; r = wb_write_padding_to_temp(a, file->content.size); if (r < 0) ret = ARCHIVE_FATAL; } /* * Free the resource we used in this function only. */ free(rbuff); free(zext.block_pointers); if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } return (ret); } #else static int zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { (void)buff; /* UNUSED */ (void)s; /* UNUSED */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programing error"); return (ARCHIVE_FATAL); } static int zisofs_rewind_boot_file(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; if (iso9660->el_torito.boot->file->zisofs.header_size != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "We cannot extract the zisofs imaged boot file;" " this may not boot in being zisofs imaged"); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int zisofs_finish_entry(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int zisofs_free(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ Index: vendor/libarchive/dist/libarchive/test/main.c =================================================================== --- vendor/libarchive/dist/libarchive/test/main.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/main.c (nonexistent) @@ -1,3205 +0,0 @@ -/* - * 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" -#include "test_utils.h" -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_ICONV_H -#include -#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 -#include -#include -#ifdef HAVE_SIGNAL_H -#include -#endif -#include -#include - -/* - * This same file is used pretty much verbatim for all test harnesses. - * - * The next few lines are the only differences. - * TODO: Move this into a separate configuration header, have all test - * suites share one copy of this file. - */ -__FBSDID("$FreeBSD: head/lib/libarchive/test/main.c 201247 2009-12-30 05:59:21Z kientzle $"); -#define KNOWNREF "test_compat_gtar_1.tar.uu" -#define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */ -#undef PROGRAM /* Testing a library, not a program. */ -#define LIBRARY "libarchive" -#define EXTRA_DUMP(x) archive_error_string((struct archive *)(x)) -#define EXTRA_ERRNO(x) archive_errno((struct archive *)(x)) -#define EXTRA_VERSION archive_version_details() - -/* - * - * Windows support routines - * - * Note: Configuration is a tricky issue. Using HAVE_* feature macros - * in the test harness is dangerous because they cover up - * configuration errors. The classic example of this is omitting a - * configure check. If libarchive and libarchive_test both look for - * the same feature macro, such errors are hard to detect. Platform - * macros (e.g., _WIN32 or __GNUC__) are a little better, but can - * easily lead to very messy code. It's best to limit yourself - * to only the most generic programming techniques in the test harness - * and thus avoid conditionals altogether. Where that's not possible, - * try to minimize conditionals by grouping platform-specific tests in - * one place (e.g., test_acl_freebsd) or by adding new assert() - * functions (e.g., assertMakeHardlink()) to cover up platform - * differences. Platform-specific coding in libarchive_test is often - * a symptom that some capability is missing from libarchive itself. - */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#include -#include -#include -#ifndef F_OK -#define F_OK (0) -#endif -#ifndef S_ISDIR -#define S_ISDIR(m) ((m) & _S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(m) ((m) & _S_IFREG) -#endif -#if !defined(__BORLANDC__) -#define access _access -#undef chdir -#define chdir _chdir -#endif -#ifndef fileno -#define fileno _fileno -#endif -/*#define fstat _fstat64*/ -#if !defined(__BORLANDC__) -#define getcwd _getcwd -#endif -#define lstat stat -/*#define lstat _stat64*/ -/*#define stat _stat64*/ -#define rmdir _rmdir -#if !defined(__BORLANDC__) -#define strdup _strdup -#define umask _umask -#endif -#define int64_t __int64 -#endif - -#if defined(HAVE__CrtSetReportMode) -# include -#endif - -mode_t umasked(mode_t expected_mode) -{ - mode_t mode = umask(0); - umask(mode); - return expected_mode & ~mode; -} - -/* Path to working directory for current test */ -const char *testworkdir; -#ifdef PROGRAM -/* Pathname of exe to be tested. */ -const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -const char *testprog; -#endif - -#if defined(_WIN32) && !defined(__CYGWIN__) -static void *GetFunctionKernel32(const char *); -static int my_CreateSymbolicLinkA(const char *, const char *, int); -static int my_CreateHardLinkA(const char *, const char *); -static int my_GetFileInformationByName(const char *, - BY_HANDLE_FILE_INFORMATION *); - -static void * -GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary("kernel32.dll"); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} - -static int -my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateSymbolicLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, flags); -} - -static int -my_CreateHardLinkA(const char *linkname, const char *target) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateHardLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, NULL); -} - -static int -my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) -{ - HANDLE h; - int r; - - memset(bhfi, 0, sizeof(*bhfi)); - h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) - return (0); - r = GetFileInformationByHandle(h, bhfi); - CloseHandle(h); - return (r); -} -#endif - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) -static void -invalid_parameter_handler(const wchar_t * expression, - const wchar_t * function, const wchar_t * file, - unsigned int line, uintptr_t pReserved) -{ - /* nop */ - // Silence unused-parameter compiler warnings. - (void)expression; - (void)function; - (void)file; - (void)line; - (void)pReserved; -} -#endif - -/* - * - * OPTIONS FLAGS - * - */ - -/* Enable core dump on failure. */ -static int dump_on_failure = 0; -/* Default is to remove temp dirs and log data for successful tests. */ -static int keep_temp_files = 0; -/* Default is to run the specified tests once and report errors. */ -static int until_failure = 0; -/* Default is to just report pass/fail for each test. */ -static int verbosity = 0; -#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ -#define VERBOSITY_PASSFAIL 0 /* Default */ -#define VERBOSITY_LIGHT_REPORT 1 /* -v */ -#define VERBOSITY_FULL 2 /* -vv */ -/* A few places generate even more output for verbosity > VERBOSITY_FULL, - * mostly for debugging the test harness itself. */ -/* Cumulative count of assertion failures. */ -static int failures = 0; -/* Cumulative count of reported skips. */ -static int skips = 0; -/* Cumulative count of assertions checked. */ -static int assertions = 0; - -/* Directory where uuencoded reference files can be found. */ -static const char *refdir; - -/* - * Report log information selectively to console and/or disk log. - */ -static int log_console = 0; -static FILE *logfile; -static void -vlogprintf(const char *fmt, va_list ap) -{ -#ifdef va_copy - va_list lfap; - va_copy(lfap, ap); -#endif - if (log_console) - vfprintf(stdout, fmt, ap); - if (logfile != NULL) -#ifdef va_copy - vfprintf(logfile, fmt, lfap); - va_end(lfap); -#else - vfprintf(logfile, fmt, ap); -#endif -} - -static void -logprintf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vlogprintf(fmt, ap); - va_end(ap); -} - -/* Set up a message to display only if next assertion fails. */ -static char msgbuff[4096]; -static const char *msg, *nextmsg; -void -failure(const char *fmt, ...) -{ - va_list ap; - if (fmt == NULL) { - nextmsg = NULL; - } else { - va_start(ap, fmt); - vsprintf(msgbuff, fmt, ap); - va_end(ap); - nextmsg = msgbuff; - } -} - -/* - * Copy arguments into file-local variables. - * This was added to permit vararg assert() functions without needing - * variadic wrapper macros. Turns out that the vararg capability is almost - * never used, so almost all of the vararg assertions can be simplified - * by removing the vararg capability and reworking the wrapper macro to - * pass __FILE__, __LINE__ directly into the function instead of using - * this hook. I suspect this machinery is used so rarely that we - * would be better off just removing it entirely. That would simplify - * the code here noticeably. - */ -static const char *skipping_filename; -static int skipping_line; -void skipping_setup(const char *filename, int line) -{ - skipping_filename = filename; - skipping_line = line; -} - -/* Called at the beginning of each assert() function. */ -static void -assertion_count(const char *file, int line) -{ - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - ++assertions; - /* Proper handling of "failure()" message. */ - msg = nextmsg; - nextmsg = NULL; - /* Uncomment to print file:line after every assertion. - * Verbose, but occasionally useful in tracking down crashes. */ - /* printf("Checked %s:%d\n", file, line); */ -} - -/* - * For each test source file, we remember how many times each - * assertion was reported. Cleared before each new test, - * used by test_summarize(). - */ -static struct line { - int count; - int skip; -} failed_lines[10000]; -const char *failed_filename; - -/* Count this failure, setup up log destination and handle initial report. */ -static void -failure_start(const char *filename, int line, const char *fmt, ...) -{ - va_list ap; - - /* Record another failure for this line. */ - ++failures; - failed_filename = filename; - failed_lines[line].count++; - - /* Determine whether to log header to console. */ - switch (verbosity) { - case VERBOSITY_LIGHT_REPORT: - log_console = (failed_lines[line].count < 2); - break; - default: - log_console = (verbosity >= VERBOSITY_FULL); - } - - /* Log file:line header for this failure */ - va_start(ap, fmt); -#if _MSC_VER - logprintf("%s(%d): ", filename, line); -#else - logprintf("%s:%d: ", filename, line); -#endif - vlogprintf(fmt, ap); - va_end(ap); - logprintf("\n"); - - if (msg != NULL && msg[0] != '\0') { - logprintf(" Description: %s\n", msg); - msg = NULL; - } - - /* Determine whether to log details to console. */ - if (verbosity == VERBOSITY_LIGHT_REPORT) - log_console = 0; -} - -/* Complete reporting of failed tests. */ -/* - * The 'extra' hook here is used by libarchive to include libarchive - * error messages with assertion failures. It could also be used - * to add strerror() output, for example. Just define the EXTRA_DUMP() - * macro appropriately. - */ -static void -failure_finish(void *extra) -{ - (void)extra; /* UNUSED (maybe) */ -#ifdef EXTRA_DUMP - if (extra != NULL) { - logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); - logprintf(" detail: %s\n", EXTRA_DUMP(extra)); - } -#endif - - if (dump_on_failure) { - fprintf(stderr, - " *** forcing core dump so failure can be debugged ***\n"); - abort(); - } -} - -/* Inform user that we're skipping some checks. */ -void -test_skipping(const char *fmt, ...) -{ - char buff[1024]; - va_list ap; - - va_start(ap, fmt); - vsprintf(buff, fmt, ap); - va_end(ap); - /* Use failure() message if set. */ - msg = nextmsg; - nextmsg = NULL; - /* failure_start() isn't quite right, but is awfully convenient. */ - failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); - --failures; /* Undo failures++ in failure_start() */ - /* Don't failure_finish() here. */ - /* Mark as skip, so doesn't count as failed test. */ - failed_lines[skipping_line].skip = 1; - ++skips; -} - -/* - * - * ASSERTIONS - * - */ - -/* Generic assert() just displays the failed condition. */ -int -assertion_assert(const char *file, int line, int value, - const char *condition, void *extra) -{ - assertion_count(file, line); - if (!value) { - failure_start(file, line, "Assertion failed: %s", condition); - failure_finish(extra); - } - return (value); -} - -/* chdir() and report any errors */ -int -assertion_chdir(const char *file, int line, const char *pathname) -{ - assertion_count(file, line); - if (chdir(pathname) == 0) - return (1); - failure_start(file, line, "chdir(\"%s\")", pathname); - failure_finish(NULL); - return (0); - -} - -/* Verify two integers are equal. */ -int -assertion_equal_int(const char *file, int line, - long long v1, const char *e1, long long v2, const char *e2, void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); - failure_finish(extra); - return (0); -} - -/* - * Utility to convert a single UTF-8 sequence. - */ -static int -_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) -{ - static const char utf8_count[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ - }; - int ch; - int cnt; - uint32_t wc; - - *pwc = 0; - - /* Sanity check. */ - if (n == 0) - return (0); - /* - * 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. */ - cnt = utf8_count[ch]; - - /* Invalid sequence or there are not plenty bytes. */ - if (n < (size_t)cnt) - return (-1); - - /* Make a Unicode code point from a single UTF-8 sequence. */ - switch (cnt) { - case 1: /* 1 byte sequence. */ - *pwc = ch & 0x7f; - return (cnt); - case 2: /* 2 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); - return (cnt); - case 3: /* 3 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - if ((s[2] & 0xc0) != 0x80) return (-1); - wc = ((ch & 0x0f) << 12) - | ((s[1] & 0x3f) << 6) - | (s[2] & 0x3f); - if (wc < 0x800) - return (-1);/* Overlong sequence. */ - break; - case 4: /* 4 bytes sequence. */ - 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); - wc = ((ch & 0x07) << 18) - | ((s[1] & 0x3f) << 12) - | ((s[2] & 0x3f) << 6) - | (s[3] & 0x3f); - if (wc < 0x10000) - return (-1);/* Overlong sequence. */ - break; - default: - return (-1); - } - - /* The code point larger than 0x10FFFF is not legal - * Unicode values. */ - if (wc > 0x10FFFF) - return (-1); - /* Correctly gets a Unicode, returns used bytes. */ - *pwc = wc; - return (cnt); -} - -static void strdump(const char *e, const char *p, int ewidth, int utf8) -{ - const char *q = p; - - logprintf(" %*s = ", ewidth, e); - if (p == NULL) { - logprintf("NULL\n"); - return; - } - logprintf("\""); - while (*p != '\0') { - unsigned int c = 0xff & *p++; - switch (c) { - case '\a': logprintf("\\a"); break; - case '\b': logprintf("\\b"); break; - case '\n': logprintf("\\n"); break; - case '\r': logprintf("\\r"); break; - default: - if (c >= 32 && c < 127) - logprintf("%c", c); - else - logprintf("\\x%02X", c); - } - } - logprintf("\""); - logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); - - /* - * If the current string is UTF-8, dump its code points. - */ - if (utf8) { - size_t len; - uint32_t uc; - int n; - int cnt = 0; - - p = q; - len = strlen(p); - logprintf(" ["); - while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { - if (p != q) - logprintf(" "); - logprintf("%04X", uc); - p += n; - len -= n; - cnt++; - } - logprintf("]"); - logprintf(" (count %d", cnt); - if (n < 0) { - logprintf(",unknown %d bytes", len); - } - logprintf(")"); - - } - logprintf("\n"); -} - -/* Verify two strings are equal, dump them if not. */ -int -assertion_equal_string(const char *file, int line, - const char *v1, const char *e1, - const char *v2, const char *e2, - void *extra, int utf8) -{ - int l1, l2; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - l1 = (int)strlen(e1); - l2 = (int)strlen(e2); - if (l1 < l2) - l1 = l2; - strdump(e1, v1, l1, utf8); - strdump(e2, v2, l1, utf8); - failure_finish(extra); - return (0); -} - -static void -wcsdump(const char *e, const wchar_t *w) -{ - logprintf(" %s = ", e); - if (w == NULL) { - logprintf("(null)"); - return; - } - logprintf("\""); - while (*w != L'\0') { - unsigned int c = *w++; - if (c >= 32 && c < 127) - logprintf("%c", c); - else if (c < 256) - logprintf("\\x%02X", c); - else if (c < 0x10000) - logprintf("\\u%04X", c); - else - logprintf("\\U%08X", c); - } - logprintf("\"\n"); -} - -#ifndef HAVE_WCSCMP -static int -wcscmp(const wchar_t *s1, const wchar_t *s2) -{ - - while (*s1 == *s2++) { - if (*s1++ == L'\0') - return 0; - } - if (*s1 > *--s2) - return 1; - else - return -1; -} -#endif - -/* Verify that two wide strings are equal, dump them if not. */ -int -assertion_equal_wstring(const char *file, int line, - const wchar_t *v1, const char *e1, - const wchar_t *v2, const char *e2, - void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - wcsdump(e1, v1); - wcsdump(e2, v2); - failure_finish(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; - - if (p == NULL) { - logprintf("(null)\n"); - return; - } - for(i=0; i < l; i+=16) { - logprintf("%04x", (unsigned)(i + offset)); - sep = ' '; - for (j = 0; j < 16 && i + j < l; j++) { - if (ref != NULL && p[i + j] != ref[i + j]) - sep = '_'; - logprintf("%c%02x", sep, 0xff & (int)p[i+j]); - if (ref != NULL && p[i + j] == ref[i + j]) - sep = ' '; - } - for (; j < 16; j++) { - logprintf("%c ", sep); - sep = ' '; - } - logprintf("%c", sep); - for (j=0; j < 16 && i + j < l; j++) { - int c = p[i + j]; - if (c >= ' ' && c <= 126) - logprintf("%c", c); - else - logprintf("."); - } - logprintf("\n"); - } -} - -/* Verify that two blocks of memory are the same, display the first - * block of differences if they're not. */ -int -assertion_equal_mem(const char *file, int line, - const void *_v1, const char *e1, - const void *_v2, const char *e2, - size_t l, const char *ld, void *extra) -{ - const char *v1 = (const char *)_v1; - const char *v2 = (const char *)_v2; - size_t offset; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) - return (1); - if (v1 == NULL || v2 == NULL) - return (0); - - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" size %s = %d\n", ld, (int)l); - /* Dump 48 bytes (3 lines) so that the first difference is - * in the second line. */ - offset = 0; - while (l > 64 && memcmp(v1, v2, 32) == 0) { - /* Two lines agree, so step forward one line. */ - v1 += 16; - v2 += 16; - l -= 16; - offset += 16; - } - logprintf(" Dump of %s\n", e1); - hexdump(v1, v2, l < 128 ? l : 128, offset); - logprintf(" Dump of %s\n", e2); - hexdump(v2, v1, l < 128 ? l : 128, offset); - logprintf("\n"); - failure_finish(extra); - return (0); -} - -/* Verify that a block of memory is filled with the specified byte. */ -int -assertion_memory_filled_with(const char *file, int line, - const void *_v1, const char *vd, - size_t l, const char *ld, - char b, const char *bd, void *extra) -{ - const char *v1 = (const char *)_v1; - size_t c = 0; - size_t i; - (void)ld; /* UNUSED */ - - assertion_count(file, line); - - for (i = 0; i < l; ++i) { - if (v1[i] == b) { - ++c; - } - } - if (c == l) - return (1); - - failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); - logprintf(" Only %d bytes were correct\n", (int)c); - failure_finish(extra); - return (0); -} - -/* Verify that the named file exists and is empty. */ -int -assertion_empty_file(const char *filename, int line, const char *f1) -{ - char buff[1024]; - struct stat st; - ssize_t s; - FILE *f; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) - return (1); - - failure_start(filename, line, "File should be empty: %s", f1); - logprintf(" File size: %d\n", (int)st.st_size); - logprintf(" Contents:\n"); - f = fopen(f1, "rb"); - if (f == NULL) { - logprintf(" Unable to open %s\n", f1); - } else { - s = ((off_t)sizeof(buff) < st.st_size) ? - (ssize_t)sizeof(buff) : (ssize_t)st.st_size; - s = fread(buff, 1, s, f); - hexdump(buff, NULL, s, 0); - fclose(f); - } - failure_finish(NULL); - return (0); -} - -/* Verify that the named file exists and is not empty. */ -int -assertion_non_empty_file(const char *filename, int line, const char *f1) -{ - struct stat st; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) { - failure_start(filename, line, "File empty: %s", f1); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify that two files have the same contents. */ -/* TODO: hexdump the first bytes that actually differ. */ -int -assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) -{ - char buff1[1024]; - char buff2[1024]; - FILE *f1, *f2; - int n1, n2; - - assertion_count(filename, line); - - f1 = fopen(fn1, "rb"); - f2 = fopen(fn2, "rb"); - if (f1 == NULL || f2 == NULL) { - if (f1) fclose(f1); - if (f2) fclose(f2); - return (0); - } - for (;;) { - n1 = (int)fread(buff1, 1, sizeof(buff1), f1); - n2 = (int)fread(buff2, 1, sizeof(buff2), f2); - if (n1 != n2) - break; - if (n1 == 0 && n2 == 0) { - fclose(f1); - fclose(f2); - return (1); - } - if (memcmp(buff1, buff2, n1) != 0) - break; - } - fclose(f1); - fclose(f2); - failure_start(filename, line, "Files not identical"); - logprintf(" file1=\"%s\"\n", fn1); - logprintf(" file2=\"%s\"\n", fn2); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file does exist. */ -int -assertion_file_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (!_access(f, 0)) - return (1); -#else - if (!access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file doesn't exist. */ -int -assertion_file_not_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (_access(f, 0)) - return (1); -#else - if (access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should not exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Compare the contents of a file to a block of memory. */ -int -assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) -{ - char *contents; - FILE *f; - int n; - - assertion_count(filename, line); - - f = fopen(fn, "rb"); - if (f == NULL) { - failure_start(filename, line, - "File should exist: %s", fn); - failure_finish(NULL); - return (0); - } - contents = malloc(s * 2); - n = (int)fread(contents, 1, s * 2, f); - fclose(f); - if (n == s && memcmp(buff, contents, s) == 0) { - free(contents); - return (1); - } - failure_start(filename, line, "File contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) - hexdump(contents, buff, n > 512 ? 512 : n, 0); - else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s > 512 ? 512 : s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Check the contents of a text file, being tolerant of line endings. */ -int -assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) -{ - char *contents; - const char *btxt, *ftxt; - FILE *f; - int n, s; - - assertion_count(filename, line); - f = fopen(fn, "r"); - if (f == NULL) { - failure_start(filename, line, - "File doesn't exist: %s", fn); - failure_finish(NULL); - return (0); - } - s = (int)strlen(buff); - contents = malloc(s * 2 + 128); - n = (int)fread(contents, 1, s * 2 + 128 - 1, f); - if (n >= 0) - contents[n] = '\0'; - fclose(f); - /* Compare texts. */ - btxt = buff; - ftxt = (const char *)contents; - while (*btxt != '\0' && *ftxt != '\0') { - if (*btxt == *ftxt) { - ++btxt; - ++ftxt; - continue; - } - if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { - /* Pass over different new line characters. */ - ++btxt; - ftxt += 2; - continue; - } - break; - } - if (*btxt == '\0' && *ftxt == '\0') { - free(contents); - return (1); - } - failure_start(filename, line, "Contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) { - hexdump(contents, buff, n, 0); - logprintf(" expected\n", fn); - hexdump(buff, contents, s, 0); - } else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Verify that a text file contains the specified lines, regardless of order */ -/* This could be more efficient if we sorted both sets of lines, etc, but - * since this is used only for testing and only ever deals with a dozen or so - * lines at a time, this relatively crude approach is just fine. */ -int -assertion_file_contains_lines_any_order(const char *file, int line, - const char *pathname, const char *lines[]) -{ - char *buff; - size_t buff_size; - size_t expected_count, actual_count, i, j; - char **expected = NULL; - char *p, **actual = NULL; - char c; - int expected_failure = 0, actual_failure = 0; - - assertion_count(file, line); - - buff = slurpfile(&buff_size, "%s", pathname); - if (buff == NULL) { - failure_start(pathname, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - /* Make a copy of the provided lines and count up the expected - * file size. */ - for (i = 0; lines[i] != NULL; ++i) { - } - expected_count = i; - if (expected_count) { - expected = malloc(sizeof(char *) * expected_count); - if (expected == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - free(expected); - return (0); - } - for (i = 0; lines[i] != NULL; ++i) { - expected[i] = strdup(lines[i]); - } - } - - /* Break the file into lines */ - actual_count = 0; - for (c = '\0', p = buff; p < buff + buff_size; ++p) { - if (*p == '\x0d' || *p == '\x0a') - *p = '\0'; - if (c == '\0' && *p != '\0') - ++actual_count; - c = *p; - } - if (actual_count) { - actual = calloc(sizeof(char *), actual_count); - if (actual == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - free(expected); - return (0); - } - for (j = 0, p = buff; p < buff + buff_size; - p += 1 + strlen(p)) { - if (*p != '\0') { - actual[j] = p; - ++j; - } - } - } - - /* Erase matching lines from both lists */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] == NULL) - continue; - for (j = 0; j < actual_count; ++j) { - if (actual[j] == NULL) - continue; - if (strcmp(expected[i], actual[j]) == 0) { - free(expected[i]); - expected[i] = NULL; - actual[j] = NULL; - break; - } - } - } - - /* If there's anything left, it's a failure */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) - ++expected_failure; - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - ++actual_failure; - } - if (expected_failure == 0 && actual_failure == 0) { - free(buff); - free(expected); - free(actual); - return (1); - } - failure_start(file, line, "File doesn't match: %s", pathname); - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) { - logprintf(" Expected but not present: %s\n", expected[i]); - free(expected[i]); - } - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - logprintf(" Present but not expected: %s\n", actual[j]); - } - failure_finish(NULL); - free(buff); - free(expected); - free(actual); - return (0); -} - -/* Verify that a text file does not contains the specified strings */ -int -assertion_file_contains_no_invalid_strings(const char *file, int line, - const char *pathname, const char *strings[]) -{ - char *buff; - int i; - - buff = slurpfile(NULL, "%s", pathname); - if (buff == NULL) { - failure_start(file, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - for (i = 0; strings[i] != NULL; ++i) { - if (strstr(buff, strings[i]) != NULL) { - failure_start(file, line, "Invalid string in %s: %s", pathname, - strings[i]); - failure_finish(NULL); - free(buff); - return(0); - } - } - - free(buff); - return (0); -} - -/* Test that two paths point to the same file. */ -/* As a side-effect, asserts that both files exist. */ -static int -is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(path1, &bhfi1); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path1); - failure_finish(NULL); - return (0); - } - r = my_GetFileInformationByName(path2, &bhfi2); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path2); - failure_finish(NULL); - return (0); - } - return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber - && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh - && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); -#else - struct stat st1, st2; - int r; - - assertion_count(file, line); - r = lstat(path1, &st1); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path1); - failure_finish(NULL); - return (0); - } - r = lstat(path2, &st2); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path2); - failure_finish(NULL); - return (0); - } - return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); -#endif -} - -int -assertion_is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s are not hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -int -assertion_is_not_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (!is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s should not be hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -/* Verify a/b/mtime of 'pathname'. */ -/* If 'recent', verify that it's within last 10 seconds. */ -static int -assertion_file_time(const char *file, int line, - const char *pathname, long t, long nsec, char type, int recent) -{ - long long filet, filet_nsec; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define EPOC_TIME (116444736000000000ULL) - FILETIME fxtime, fbirthtime, fatime, fmtime; - ULARGE_INTEGER wintm; - HANDLE h; - fxtime.dwLowDateTime = 0; - fxtime.dwHighDateTime = 0; - - assertion_count(file, line); - /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open - * a directory file. If not, CreateFile() will fail when - * the pathname is a directory. */ - h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); - switch (type) { - case 'a': fxtime = fatime; break; - case 'b': fxtime = fbirthtime; break; - case 'm': fxtime = fmtime; break; - } - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't GetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - wintm.LowPart = fxtime.dwLowDateTime; - wintm.HighPart = fxtime.dwHighDateTime; - filet = (wintm.QuadPart - EPOC_TIME) / 10000000; - filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; - nsec = (nsec / 100) * 100; /* Round the request */ -#else - struct stat st; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - switch (type) { - case 'a': filet = st.st_atime; break; - case 'm': filet = st.st_mtime; break; - case 'b': filet = 0; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } -#if defined(__FreeBSD__) - switch (type) { - case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; - case 'b': filet = st.st_birthtime; - /* FreeBSD filesystems that don't support birthtime - * (e.g., UFS1) always return -1 here. */ - if (filet == -1) { - return (1); - } - filet_nsec = st.st_birthtimespec.tv_nsec; break; - case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } - /* FreeBSD generally only stores to microsecond res, so round. */ - filet_nsec = (filet_nsec / 1000) * 1000; - nsec = (nsec / 1000) * 1000; -#else - filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ - if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ -#if defined(__HAIKU__) - if (type == 'a') return (1); /* Haiku doesn't have atime. */ -#endif -#endif -#endif - if (recent) { - /* Check that requested time is up-to-date. */ - time_t now = time(NULL); - if (filet < now - 10 || filet > now + 1) { - failure_start(file, line, - "File %s has %ctime %lld, %lld seconds ago\n", - pathname, type, filet, now - filet); - failure_finish(NULL); - return (0); - } - } else if (filet != t || filet_nsec != nsec) { - failure_start(file, line, - "File %s has %ctime %lld.%09lld, expected %lld.%09lld", - pathname, type, filet, filet_nsec, t, nsec); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify atime of 'pathname'. */ -int -assertion_file_atime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); -} - -/* Verify atime of 'pathname' is up-to-date. */ -int -assertion_file_atime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); -} - -/* Verify birthtime of 'pathname'. */ -int -assertion_file_birthtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); -} - -/* Verify birthtime of 'pathname' is up-to-date. */ -int -assertion_file_birthtime_recent(const char *file, int line, - const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); -} - -/* Verify mode of 'pathname'. */ -int -assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) -{ - int mode; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - failure_start(file, line, "assertFileMode not yet implemented for Windows"); - (void)mode; /* UNUSED */ - (void)r; /* UNUSED */ - (void)pathname; /* UNUSED */ - (void)expected_mode; /* UNUSED */ -#else - { - struct stat st; - r = lstat(pathname, &st); - mode = (int)(st.st_mode & 0777); - } - if (r == 0 && mode == expected_mode) - return (1); - failure_start(file, line, "File %s has mode %o, expected %o", - pathname, mode, expected_mode); -#endif - failure_finish(NULL); - return (0); -} - -/* Verify mtime of 'pathname'. */ -int -assertion_file_mtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); -} - -/* Verify mtime of 'pathname' is up-to-date. */ -int -assertion_file_mtime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); -} - -/* Verify number of links to 'pathname'. */ -int -assertion_file_nlinks(const char *file, int line, - const char *pathname, int nlinks) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(pathname, &bhfi); - if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, bhfi.nNumberOfLinks, nlinks); - failure_finish(NULL); - return (0); -#else - struct stat st; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r == 0 && (int)st.st_nlink == nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, st.st_nlink, nlinks); - failure_finish(NULL); - return (0); -#endif -} - -/* Verify size of 'pathname'. */ -int -assertion_file_size(const char *file, int line, const char *pathname, long size) -{ - int64_t filesize; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - { - BY_HANDLE_FILE_INFORMATION bhfi; - r = !my_GetFileInformationByName(pathname, &bhfi); - filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; - } -#else - { - struct stat st; - r = lstat(pathname, &st); - filesize = st.st_size; - } -#endif - if (r == 0 && filesize == size) - return (1); - failure_start(file, line, "File %s has size %ld, expected %ld", - pathname, (long)filesize, (long)size); - failure_finish(NULL); - return (0); -} - -/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ -int -assertion_is_dir(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Dir should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISDIR(st.st_mode)) { - failure_start(file, line, "%s is not a dir", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "Dir %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, - * verify that too. */ -int -assertion_is_reg(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0 || !S_ISREG(st.st_mode)) { - failure_start(file, line, "File should exist: %s", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "File %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ -static int -is_symlink(const char *file, int line, - const char *pathname, const char *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)pathname; /* UNUSED */ - (void)contents; /* UNUSED */ - assertion_count(file, line); - /* Windows sort-of has real symlinks, but they're only usable - * by privileged users and are crippled even then, so there's - * really not much point in bothering with this. */ - return (0); -#else - char buff[300]; - struct stat st; - ssize_t linklen; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, - "Symlink should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISLNK(st.st_mode)) - return (0); - if (contents == NULL) - return (1); - linklen = readlink(pathname, buff, sizeof(buff)); - if (linklen < 0) { - failure_start(file, line, "Can't read symlink %s", pathname); - failure_finish(NULL); - return (0); - } - buff[linklen] = '\0'; - if (strcmp(buff, contents) != 0) - return (0); - return (1); -#endif -} - -/* Assert that path is a symlink that (optionally) contains contents. */ -int -assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) -{ - if (is_symlink(file, line, path, contents)) - return (1); - if (contents) - failure_start(file, line, "File %s is not a symlink to %s", - path, contents); - else - failure_start(file, line, "File %s is not a symlink", path); - failure_finish(NULL); - return (0); -} - - -/* Create a directory and report any errors. */ -int -assertion_make_dir(const char *file, int line, const char *dirname, int mode) -{ - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ - if (0 == _mkdir(dirname)) - return (1); -#else - if (0 == mkdir(dirname, mode)) { - if (0 == chmod(dirname, mode)) { - assertion_file_mode(file, line, dirname, mode); - return (1); - } - } -#endif - failure_start(file, line, "Could not create directory %s", dirname); - failure_finish(NULL); - return(0); -} - -/* Create a file with the specified contents and report any failures. */ -int -assertion_make_file(const char *file, int line, - const char *path, int mode, int csize, const void *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - /* TODO: Rework this to set file mode as well. */ - FILE *f; - (void)mode; /* UNUSED */ - assertion_count(file, line); - f = fopen(path, "wb"); - if (f == NULL) { - failure_start(file, line, "Could not create file %s", path); - failure_finish(NULL); - return (0); - } - if (contents != NULL) { - size_t wsize; - - if (csize < 0) - wsize = strlen(contents); - else - wsize = (size_t)csize; - if (wsize != fwrite(contents, 1, wsize, f)) { - fclose(f); - failure_start(file, line, - "Could not write file %s", path); - failure_finish(NULL); - return (0); - } - } - fclose(f); - return (1); -#else - int fd; - assertion_count(file, line); - fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); - if (fd < 0) { - failure_start(file, line, "Could not create %s", path); - failure_finish(NULL); - return (0); - } - if (0 != chmod(path, mode)) { - failure_start(file, line, "Could not chmod %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - if (contents != NULL) { - ssize_t wsize; - - if (csize < 0) - wsize = (ssize_t)strlen(contents); - else - wsize = (ssize_t)csize; - if (wsize != write(fd, contents, wsize)) { - close(fd); - failure_start(file, line, - "Could not write to %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - } - close(fd); - assertion_file_mode(file, line, path, mode); - return (1); -#endif -} - -/* Create a hardlink and report any failures. */ -int -assertion_make_hardlink(const char *file, int line, - const char *newpath, const char *linkto) -{ - int succeeded; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - succeeded = my_CreateHardLinkA(newpath, linkto); -#elif HAVE_LINK - succeeded = !link(linkto, newpath); -#else - succeeded = 0; -#endif - if (succeeded) - return (1); - failure_start(file, line, "Could not create hardlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Create a symlink and report any failures. */ -int -assertion_make_symlink(const char *file, int line, - const char *newpath, const char *linkto) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - int targetIsDir = 0; /* TODO: Fix this */ - assertion_count(file, line); - if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) - return (1); -#elif HAVE_SYMLINK - assertion_count(file, line); - if (0 == symlink(linkto, newpath)) - return (1); -#endif - failure_start(file, line, "Could not create symlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Set umask, report failures. */ -int -assertion_umask(const char *file, int line, int mask) -{ - assertion_count(file, line); - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - umask(mask); - return (1); -} - -/* Set times, report failures. */ -int -assertion_utimes(const char *file, int line, - const char *pathname, long at, long at_nsec, long mt, long mt_nsec) -{ - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ - + (((nsec)/1000)*10)) - HANDLE h; - ULARGE_INTEGER wintm; - FILETIME fatime, fmtime; - FILETIME *pat, *pmt; - - assertion_count(file, line); - h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (at > 0 || at_nsec > 0) { - wintm.QuadPart = WINTIME(at, at_nsec); - fatime.dwLowDateTime = wintm.LowPart; - fatime.dwHighDateTime = wintm.HighPart; - pat = &fatime; - } else - pat = NULL; - if (mt > 0 || mt_nsec > 0) { - wintm.QuadPart = WINTIME(mt, mt_nsec); - fmtime.dwLowDateTime = wintm.LowPart; - fmtime.dwHighDateTime = wintm.HighPart; - pmt = &fmtime; - } else - pmt = NULL; - if (pat != NULL || pmt != NULL) - r = SetFileTime(h, NULL, pat, pmt); - else - r = 1; - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't SetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#else /* defined(_WIN32) && !defined(__CYGWIN__) */ - struct stat st; - struct timeval times[2]; - -#if !defined(__FreeBSD__) - mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ -#endif - if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) - return (1); - - r = lstat(pathname, &st); - if (r < 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (mt == 0 && mt_nsec == 0) { - mt = st.st_mtime; -#if defined(__FreeBSD__) - mt_nsec = st.st_mtimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - mt_nsec = (mt_nsec / 1000) * 1000; -#endif - } - if (at == 0 && at_nsec == 0) { - at = st.st_atime; -#if defined(__FreeBSD__) - at_nsec = st.st_atimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - at_nsec = (at_nsec / 1000) * 1000; -#endif - } - - times[1].tv_sec = mt; - times[1].tv_usec = mt_nsec / 1000; - - times[0].tv_sec = at; - times[0].tv_usec = at_nsec / 1000; - -#ifdef HAVE_LUTIMES - r = lutimes(pathname, times); -#else - r = utimes(pathname, times); -#endif - if (r < 0) { - failure_start(file, line, "Can't utimes %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ -} - -/* Set nodump, report failures. */ -int -assertion_nodump(const char *file, int line, const char *pathname) -{ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - int r; - - assertion_count(file, line); - r = chflags(pathname, UF_NODUMP); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - int fd, r, flags; - - assertion_count(file, line); - fd = open(pathname, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - failure_start(file, line, "Can't open %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't get flags %s\n", pathname); - failure_finish(NULL); - return (0); - } - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } - close(fd); -#else - (void)pathname; /* UNUSED */ - assertion_count(file, line); -#endif - return (1); -} - -/* - * - * UTILITIES for use by tests. - * - */ - -/* - * Check whether platform supports symlinks. This is intended - * for tests to use in deciding whether to bother testing symlink - * support; if the platform doesn't support symlinks, there's no point - * in checking whether the program being tested can create them. - * - * Note that the first time this test is called, we actually go out to - * disk to create and verify a symlink. This is necessary because - * symlink support is actually a property of a particular filesystem - * and can thus vary between directories on a single system. After - * the first call, this returns the cached result from memory, so it's - * safe to call it as often as you wish. - */ -int -canSymlink(void) -{ - /* Remember the test result */ - static int value = 0, tested = 0; - if (tested) - return (value); - - ++tested; - assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); - /* Note: Cygwin has its own symlink() emulation that does not - * use the Win32 CreateSymbolicLink() function. */ -#if defined(_WIN32) && !defined(__CYGWIN__) - value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); -#elif HAVE_SYMLINK - value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); -#endif - return (value); -} - -/* Platform-dependent options for hiding the output of a subcommand. */ -#if defined(_WIN32) && !defined(__CYGWIN__) -static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ -#else -static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ -#endif -/* - * Can this platform run the bzip2 program? - */ -int -canBzip2(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("bzip2 -d -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the grzip program? - */ -int -canGrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("grzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the gzip program? - */ -int -canGzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("gzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lrzip program? - */ -int -canRunCommand(const char *cmd) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("%s %s", cmd, redirectArgs) == 0) - value = 1; - } - return (value); -} - -int -canLrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lrzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lz4 program? - */ -int -canLz4(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lz4 -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzip program? - */ -int -canLzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzma program? - */ -int -canLzma(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzma -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzop program? - */ -int -canLzop(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzop -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the xz program? - */ -int -canXz(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("xz -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this filesystem handle nodump flags. - */ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - struct stat sb; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - if (chflags(path, UF_NODUMP) < 0) - return (0); - if (stat(path, &sb) < 0) - return (0); - if (sb.st_flags & UF_NODUMP) - return (1); - return (0); -} - -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - int fd, r, flags; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - if (flags & EXT2_NODUMP_FL) - return (1); - return (0); -} - -#else - -int -canNodump() -{ - return (0); -} - -#endif - -/* - * Sleep as needed; useful for verifying disk timestamp changes by - * ensuring that the wall-clock time has actually changed before we - * go back to re-read something from disk. - */ -void -sleepUntilAfter(time_t t) -{ - while (t >= time(NULL)) -#if defined(_WIN32) && !defined(__CYGWIN__) - Sleep(500); -#else - sleep(1); -#endif -} - -/* - * 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); - if (verbosity > VERBOSITY_FULL) - logprintf("Cmd: %s\n", buff); - 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; - FILE *f; - int r; - - va_start(ap, fmt); - vsprintf(filename, fmt, ap); - va_end(ap); - - f = fopen(filename, "rb"); - if (f == NULL) { - /* Note: No error; non-existent file is okay here. */ - return (NULL); - } - r = fstat(fileno(f), &st); - if (r != 0) { - logprintf("Can't stat file %s\n", filename); - fclose(f); - return (NULL); - } - p = malloc((size_t)st.st_size + 1); - if (p == NULL) { - logprintf("Can't allocate %ld bytes of memory to read file %s\n", - (long int)st.st_size, filename); - fclose(f); - return (NULL); - } - bytes_read = fread(p, 1, (size_t)st.st_size, f); - if (bytes_read < st.st_size) { - logprintf("Can't read file %s\n", filename); - fclose(f); - free(p); - return (NULL); - } - p[st.st_size] = '\0'; - if (sizep != NULL) - *sizep = (size_t)st.st_size; - fclose(f); - return (p); -} - -/* - * 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. - */ -void -dumpfile(const char *filename, void *data, size_t len) -{ - ssize_t bytes_written; - FILE *f; - - f = fopen(filename, "wb"); - if (f == NULL) { - logprintf("Can't open file %s for writing\n", filename); - return; - } - bytes_written = fwrite(data, 1, len, f); - if (bytes_written < (ssize_t)len) - logprintf("Can't write file %s\n", filename); - fclose(f); -} - -/* Read a uuencoded file from the reference directory, decode, and - * write the result into the current directory. */ -#define VALID_UUDECODE(c) (c >= 32 && c <= 96) -#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. */ - out = fopen(name, "wb"); - 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) { - assert(VALID_UUDECODE(p[0])); - assert(VALID_UUDECODE(p[1])); - n = UUDECODE(*p++) << 18; - n |= UUDECODE(*p++) << 12; - fputc(n >> 16, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++) << 6; - fputc((n >> 8) & 0xFF, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++); - fputc(n & 0xFF, out); - --bytes; - } - } - } - fclose(out); - fclose(in); -} - -void -copy_reference_file(const char *name) -{ - char buff[1024]; - FILE *in, *out; - size_t rbytes; - - sprintf(buff, "%s/%s", refdir, name); - in = fopen(buff, "rb"); - failure("Couldn't open reference file %s", buff); - assert(in != NULL); - if (in == NULL) - return; - /* Now, decode the rest and write it. */ - /* Not a lot of error checking here; the input better be right. */ - out = fopen(name, "wb"); - while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { - if (fwrite(buff, 1, rbytes, out) != rbytes) { - logprintf("Error: fwrite\n"); - break; - } - } - fclose(out); - fclose(in); -} - -int -is_LargeInode(const char *file) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - r = my_GetFileInformationByName(file, &bhfi); - if (r != 0) - return (0); - return (bhfi.nFileIndexHigh & 0x0000FFFFUL); -#else - struct stat st; - int64_t ino; - - if (stat(file, &st) < 0) - return (0); - ino = (int64_t)st.st_ino; - return (ino > 0xffffffff); -#endif -} - -void -extract_reference_files(const char **names) -{ - while (names && *names) - extract_reference_file(*names++); -} - -/* Set ACLs */ -void -archive_test_set_acls(struct archive_entry *ae, - struct archive_test_acl_t *acls, int n) -{ - int i; - - archive_entry_acl_clear(ae); - for (i = 0; i < n; i++) { - failure("type=%#010x, permset=%#010x, tag=%d, qual=%d name=%s", - acls[i].type, acls[i].permset, acls[i].tag, - acls[i].qual, acls[i].name); - assertEqualInt(ARCHIVE_OK, - archive_entry_acl_add_entry(ae, - acls[i].type, acls[i].permset, acls[i].tag, - acls[i].qual, acls[i].name)); - } -} - -static int -archive_test_acl_match(struct archive_test_acl_t *acl, int type, int permset, - int tag, int qual, const char *name) -{ - if (type != acl->type) - return (0); - if (permset != acl->permset) - return (0); - if (tag != acl->tag) - return (0); - if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) - return (1); - if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) - return (1); - if (tag == ARCHIVE_ENTRY_ACL_EVERYONE) - return (1); - if (tag == ARCHIVE_ENTRY_ACL_OTHER) - return (1); - if (qual != acl->qual) - return (0); - if (name == NULL) { - if (acl->name == NULL || acl->name[0] == '\0') - return (1); - return (0); - } - if (acl->name == NULL) { - if (name[0] == '\0') - return (1); - return (0); - } - return (0 == strcmp(name, acl->name)); -} - -/* Compare ACLs */ -void -archive_test_compare_acls(struct archive_entry *ae, - struct archive_test_acl_t *acls, int cnt, int want_type, int mode) -{ - int *marker; - int i, r, n; - int type, permset, tag, qual; - int matched; - const char *name; - - n = 0; - marker = malloc(sizeof(marker[0]) * cnt); - - for (i = 0; i < cnt; i++) { - if ((acls[i].type & want_type) != 0) { - marker[n] = i; - n++; - } - } - - failure("No ACL's to compare, type mask: %d", want_type); - assert(n > 0); - if (n == 0) - return; - - while (0 == (r = archive_entry_acl_next(ae, want_type, - &type, &permset, &tag, &qual, &name))) { - for (i = 0, matched = 0; i < n && !matched; i++) { - if (archive_test_acl_match(&acls[marker[i]], type, - permset, tag, qual, name)) { - /* We found a match; remove it. */ - marker[i] = marker[n - 1]; - n--; - matched = 1; - } - } - if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS - && tag == ARCHIVE_ENTRY_ACL_USER_OBJ) { - if (!matched) printf("No match for user_obj perm\n"); - failure("USER_OBJ permset (%02o) != user mode (%02o)", - permset, 07 & (mode >> 6)); - assert((permset << 6) == (mode & 0700)); - } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS - && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) { - if (!matched) printf("No match for group_obj perm\n"); - failure("GROUP_OBJ permset %02o != group mode %02o", - permset, 07 & (mode >> 3)); - assert((permset << 3) == (mode & 0070)); - } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS - && tag == ARCHIVE_ENTRY_ACL_OTHER) { - if (!matched) printf("No match for other perm\n"); - failure("OTHER permset (%02o) != other mode (%02o)", - permset, mode & 07); - assert((permset << 0) == (mode & 0007)); - } else { - failure("Could not find match for ACL " - "(type=%#010x,permset=%#010x,tag=%d,qual=%d," - "name=``%s'')", type, permset, tag, qual, name); - assert(matched == 1); - } - } - assertEqualInt(ARCHIVE_EOF, r); - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) - assert((mode_t)(mode & 0777) == (archive_entry_mode(ae) - & 0777)); - failure("Could not find match for ACL " - "(type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s'')", - acls[marker[0]].type, acls[marker[0]].permset, - acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name); - assert(n == 0); /* Number of ACLs not matched should == 0 */ - free(marker); -} - -/* - * - * TEST management - * - */ - -/* - * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has - * a line like - * DEFINE_TEST(test_function) - * for each test. - */ - -/* Use "list.h" to declare all of the test functions. */ -#undef DEFINE_TEST -#define DEFINE_TEST(name) void name(void); -#include "list.h" - -/* Use "list.h" to create a list of all tests (functions and names). */ -#undef DEFINE_TEST -#define DEFINE_TEST(n) { n, #n, 0 }, -struct test_list_t tests[] = { - #include "list.h" -}; - -/* - * Summarize repeated failures in the just-completed test. - */ -static void -test_summarize(int failed, int skips_num) -{ - unsigned int i; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: - printf(failed ? "E" : "."); - fflush(stdout); - break; - case VERBOSITY_PASSFAIL: - printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); - break; - } - - log_console = (verbosity == VERBOSITY_LIGHT_REPORT); - - for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { - if (failed_lines[i].count > 1 && !failed_lines[i].skip) - logprintf("%s:%d: Summary: Failed %d times\n", - failed_filename, i, failed_lines[i].count); - } - /* Clear the failure history for the next file. */ - failed_filename = NULL; - memset(failed_lines, 0, sizeof(failed_lines)); -} - -/* - * Actually run a single test, with appropriate setup and cleanup. - */ -static int -test_run(int i, const char *tmpdir) -{ - char workdir[1024]; - char logfilename[64]; - int failures_before = failures; - int skips_before = skips; - int oldumask; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ - break; - case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ - printf("%3d: %-64s", i, tests[i].name); - fflush(stdout); - break; - default: /* Title of test, details will follow */ - printf("%3d: %s\n", i, tests[i].name); - } - - /* Chdir to the top-level work directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, - "ERROR: Can't chdir to top work dir %s\n", tmpdir); - exit(1); - } - /* Create a log file for this test. */ - sprintf(logfilename, "%s.log", tests[i].name); - logfile = fopen(logfilename, "w"); - fprintf(logfile, "%s\n\n", tests[i].name); - /* Chdir() to a work dir for this specific test. */ - snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); - testworkdir = workdir; - if (!assertMakeDir(testworkdir, 0755) - || !assertChdir(testworkdir)) { - fprintf(stderr, - "ERROR: Can't chdir to work dir %s\n", testworkdir); - exit(1); - } - /* Explicitly reset the locale before each test. */ - setlocale(LC_ALL, "C"); - /* Record the umask before we run the test. */ - umask(oldumask = umask(0)); - /* - * Run the actual test. - */ - (*tests[i].func)(); - /* - * Clean up and report afterwards. - */ - testworkdir = NULL; - /* Restore umask */ - umask(oldumask); - /* Reset locale. */ - setlocale(LC_ALL, "C"); - /* Reset directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", - tmpdir); - exit(1); - } - /* Report per-test summaries. */ - tests[i].failures = failures - failures_before; - test_summarize(tests[i].failures, skips - skips_before); - /* Close the per-test log file. */ - fclose(logfile); - logfile = NULL; - /* If there were no failures, we can remove the work dir and logfile. */ - if (tests[i].failures == 0) { - if (!keep_temp_files && assertChdir(tmpdir)) { -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure not to leave empty directories. - * Sometimes a processing of closing files used by tests - * is not done, then rmdir will be failed and it will - * leave a empty test directory. So we should wait a few - * seconds and retry rmdir. */ - int r, t; - for (t = 0; t < 10; t++) { - if (t > 0) - Sleep(1000); - r = systemf("rmdir /S /Q %s", tests[i].name); - if (r == 0) - break; - } - systemf("del %s", logfilename); -#else - systemf("rm -rf %s", tests[i].name); - systemf("rm %s", logfilename); -#endif - } - } - /* Return appropriate status. */ - return (tests[i].failures); -} - -/* - * - * - * MAIN and support routines. - * - * - */ - -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(" -u Keep running specifies tests until one fails.\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); -} - -static char * -get_refdir(const char *d) -{ - size_t tried_size, buff_size; - char *buff, *tried, *pwd = NULL, *p = NULL; - -#ifdef PATH_MAX - buff_size = PATH_MAX; -#else - buff_size = 8192; -#endif - buff = calloc(buff_size, 1); - if (buff == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* Allocate a buffer to hold the various directories we checked. */ - tried_size = buff_size * 2; - tried = calloc(tried_size, 1); - if (tried == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* If a dir was specified, try that */ - if (d != NULL) { - pwd = NULL; - snprintf(buff, buff_size, "%s", d); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - goto failure; - } - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - - /* Look for a known file. */ - snprintf(buff, buff_size, "%s", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(LIBRARY) - snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); -#else - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); -#endif - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(PROGRAM_ALIAS) - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); -#endif - - if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, buff_size, "%s", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - } - -failure: - printf("Unable to locate known reference file %s\n", KNOWNREF); - printf(" Checked following directories:\n%s\n", tried); - printf("Use -r option to specify full path to reference directory\n"); -#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) - DebugBreak(); -#endif - exit(1); - -success: - free(p); - free(pwd); - free(tried); - - /* Copy result into a fresh buffer to reduce memory usage. */ - p = strdup(buff); - free(buff); - return p; -} - -int -main(int argc, char **argv) -{ - static const int limit = sizeof(tests) / sizeof(tests[0]); - int test_set[sizeof(tests) / sizeof(tests[0])]; - int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; - time_t now; - char *refdir_alloc = NULL; - const char *progname; - char **saved_argv; - const char *tmp, *option_arg, *p; - char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; - char tmpdir_timestamp[256]; - - (void)argc; /* UNUSED */ - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) - /* To stop to run the default invalid parameter handler. */ - _set_invalid_parameter_handler(invalid_parameter_handler); - /* Disable annoying assertion message box. */ - _CrtSetReportMode(_CRT_ASSERT, 0); -#endif - - /* - * Name of this program, used to build root of our temp directory - * tree. - */ - progname = p = argv[0]; - if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(testprogdir, progname); - while (*p != '\0') { - /* Support \ or / dir separators for Windows compat. */ - if (*p == '/' || *p == '\\') - { - progname = p + 1; - i = j; - } - ++p; - j++; - } - testprogdir[i] = '\0'; -#if defined(_WIN32) && !defined(__CYGWIN__) - if (testprogdir[0] != '/' && testprogdir[0] != '\\' && - !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || - (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && - testprogdir[1] == ':' && - (testprogdir[2] == '/' || testprogdir[2] == '\\'))) -#else - if (testprogdir[0] != '/') -#endif - { - /* Fixup path for relative directories. */ - if ((testprogdir = (char *)realloc(testprogdir, - strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - memmove(testprogdir + strlen(pwd) + 1, testprogdir, - strlen(testprogdir) + 1); - memcpy(testprogdir, pwd, strlen(pwd)); - testprogdir[strlen(pwd)] = '/'; - } - -#ifdef PROGRAM - /* Get the target program from environment, if available. */ - testprogfile = 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; - - /* Allow -v to be controlled through the environment. */ - if (getenv("_VERBOSITY_LEVEL") != NULL) - { - vlevel = getenv("_VERBOSITY_LEVEL"); - verbosity = atoi(vlevel); - if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) - { - /* Unsupported verbosity levels are silently ignored */ - vlevel = NULL; - verbosity = VERBOSITY_PASSFAIL; - } - } - - /* 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 - testprogfile = option_arg; -#else - fprintf(stderr, "-p option not permitted\n"); - usage(progname); -#endif - break; - case 'q': - if (!vlevel) - verbosity--; - break; - case 'r': - refdir = option_arg; - break; - case 'u': - until_failure++; - break; - case 'v': - if (!vlevel) - verbosity++; - break; - default: - fprintf(stderr, "Unrecognized option '%c'\n", - option); - usage(progname); - } - } - } - - /* - * Sanity-check that our options make sense. - */ -#ifdef PROGRAM - if (testprogfile == NULL) - { - if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + - strlen(PROGRAM) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(tmp2, testprogdir); - strcat(tmp2, "/"); - strcat(tmp2, PROGRAM); - testprogfile = tmp2; - } - - { - char *testprg; -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Command.com sometimes rejects '/' separators. */ - testprg = strdup(testprogfile); - for (i = 0; testprg[i] != '\0'; i++) { - if (testprg[i] == '/') - testprg[i] = '\\'; - } - testprogfile = testprg; -#endif - /* Quote the name that gets put into shell command lines. */ - testprg = malloc(strlen(testprogfile) + 3); - strcpy(testprg, "\""); - strcat(testprg, testprogfile); - strcat(testprg, "\""); - testprog = testprg; - } -#endif - -#if !defined(_WIN32) && defined(SIGPIPE) - { /* Ignore SIGPIPE signals */ - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGPIPE, &sa, NULL); - } -#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++) { - 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 (assertMakeDir(tmpdir,0755)) - break; - if (i >= 999) { - 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." - */ - refdir = refdir_alloc = get_refdir(refdir); - - /* - * Banner with basic information. - */ - printf("\n"); - printf("If tests fail or crash, details will be in:\n"); - printf(" %s\n", tmpdir); - printf("\n"); - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - 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); - } else { - printf("Running "); - fflush(stdout); - } - - /* - * Run some or all of the individual tests. - */ - saved_argv = argv; - do { - argv = saved_argv; - do { - int test_num; - - test_num = get_test_set(test_set, limit, *argv, tests); - if (test_num < 0) { - printf("*** INVALID Test %s\n", *argv); - free(refdir_alloc); - free(testprogdir); - usage(progname); - return (1); - } - for (i = 0; i < test_num; i++) { - tests_run++; - if (test_run(test_set[i], tmpdir)) { - tests_failed++; - if (until_failure) - goto finish; - } - } - if (*argv != NULL) - argv++; - } while (*argv != NULL); - } while (until_failure); - -finish: - /* Must be freed after all tests run */ - free(tmp2); - free(testprogdir); - free(pwd); - - /* - * Report summary statistics. - */ - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - printf("\n"); - printf("Totals:\n"); - printf(" Tests run: %8d\n", tests_run); - printf(" Tests failed: %8d\n", tests_failed); - printf(" Assertions checked:%8d\n", assertions); - printf(" Assertions failed: %8d\n", failures); - printf(" Skips reported: %8d\n", skips); - } - if (failures) { - printf("\n"); - printf("Failing tests:\n"); - for (i = 0; i < limit; ++i) { - if (tests[i].failures) - printf(" %d: %s (%d failures)\n", i, - tests[i].name, tests[i].failures); - } - printf("\n"); - printf("Details for failing tests: %s\n", tmpdir); - printf("\n"); - } else { - if (verbosity == VERBOSITY_SUMMARY_ONLY) - printf("\n"); - printf("%d tests passed, no failures\n", tests_run); - } - - free(refdir_alloc); - - /* If the final tmpdir is empty, we can remove it. */ - /* This should be the usual case when all tests succeed. */ - assertChdir(".."); - rmdir(tmpdir); - - return (tests_failed ? 1 : 0); -} Property changes on: vendor/libarchive/dist/libarchive/test/main.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: vendor/libarchive/dist/libarchive/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/libarchive/test/CMakeLists.txt (revision 314564) +++ vendor/libarchive/dist/libarchive/test/CMakeLists.txt (revision 314565) @@ -1,312 +1,313 @@ ############################################ # # How to build libarchive_test # ############################################ IF(ENABLE_TEST) SET(libarchive_test_SOURCES ../../test_utils/test_utils.c - main.c + ../../test_utils/test_main.c read_open_memory.c test.h test_acl_nfs4.c test_acl_pax.c test_acl_platform_nfs4.c test_acl_platform_posix1e.c test_acl_posix1e.c test_acl_text.c test_archive_api_feature.c test_archive_clear_error.c test_archive_cmdline.c test_archive_digest.c test_archive_getdate.c test_archive_match_owner.c test_archive_match_path.c test_archive_match_time.c test_archive_pathmatch.c test_archive_read_add_passphrase.c test_archive_read_close_twice.c test_archive_read_close_twice_open_fd.c test_archive_read_close_twice_open_filename.c test_archive_read_multiple_data_objects.c test_archive_read_next_header_empty.c test_archive_read_next_header_raw.c test_archive_read_open2.c test_archive_read_set_filter_option.c test_archive_read_set_format_option.c test_archive_read_set_option.c test_archive_read_set_options.c test_archive_read_support.c test_archive_set_error.c test_archive_string.c test_archive_string_conversion.c test_archive_write_add_filter_by_name.c test_archive_write_set_filter_option.c test_archive_write_set_format_by_name.c test_archive_write_set_format_filter_by_ext.c test_archive_write_set_format_option.c test_archive_write_set_option.c test_archive_write_set_options.c test_archive_write_set_passphrase.c test_bad_fd.c test_compat_bzip2.c test_compat_cpio.c test_compat_gtar.c test_compat_gzip.c test_compat_lz4.c test_compat_lzip.c test_compat_lzma.c test_compat_lzop.c test_compat_mac.c test_compat_pax_libarchive_2x.c test_compat_perl_archive_tar.c test_compat_plexus_archiver_tar.c test_compat_solaris_pax_sparse.c test_compat_solaris_tar_acl.c test_compat_star_acl.c test_compat_tar_hardlink.c test_compat_uudecode.c test_compat_uudecode_large.c test_compat_xz.c test_compat_zip.c test_empty_write.c test_entry.c test_entry_strmode.c test_extattr_freebsd.c test_filter_count.c test_fuzz.c test_gnutar_filename_encoding.c test_link_resolver.c test_open_failure.c test_open_fd.c test_open_file.c test_open_filename.c test_pax_filename_encoding.c test_read_data_large.c test_read_disk.c test_read_disk_directory_traversals.c test_read_disk_entry_from_file.c test_read_extract.c test_read_file_nonexistent.c test_read_filter_compress.c test_read_filter_grzip.c test_read_filter_lrzip.c test_read_filter_lzop.c test_read_filter_lzop_multiple_parts.c test_read_filter_program.c test_read_filter_program_signature.c test_read_filter_uudecode.c test_read_format_7zip.c test_read_format_7zip_encryption_data.c test_read_format_7zip_encryption_header.c test_read_format_7zip_encryption_partially.c test_read_format_7zip_malformed.c test_read_format_ar.c test_read_format_cab.c test_read_format_cab_filename.c test_read_format_cpio_afio.c test_read_format_cpio_bin.c test_read_format_cpio_bin_Z.c test_read_format_cpio_bin_be.c test_read_format_cpio_bin_bz2.c test_read_format_cpio_bin_gz.c test_read_format_cpio_bin_le.c test_read_format_cpio_bin_lzip.c test_read_format_cpio_bin_lzma.c test_read_format_cpio_bin_xz.c test_read_format_cpio_filename.c test_read_format_cpio_odc.c test_read_format_cpio_svr4_bzip2_rpm.c test_read_format_cpio_svr4_gzip.c test_read_format_cpio_svr4_gzip_rpm.c test_read_format_cpio_svr4c_Z.c test_read_format_empty.c test_read_format_gtar_filename.c test_read_format_gtar_gz.c test_read_format_gtar_lzma.c test_read_format_gtar_sparse.c test_read_format_gtar_sparse_skip_entry.c test_read_format_iso_Z.c test_read_format_iso_multi_extent.c test_read_format_iso_xorriso.c test_read_format_isojoliet_bz2.c test_read_format_isojoliet_long.c test_read_format_isojoliet_rr.c test_read_format_isojoliet_versioned.c test_read_format_isorr_bz2.c test_read_format_isorr_ce.c test_read_format_isorr_new_bz2.c test_read_format_isorr_rr_moved.c test_read_format_isozisofs_bz2.c test_read_format_lha.c test_read_format_lha_bugfix_0.c test_read_format_lha_filename.c test_read_format_mtree.c test_read_format_mtree_crash747.c test_read_format_pax_bz2.c test_read_format_rar.c test_read_format_rar_encryption_data.c test_read_format_rar_encryption_header.c test_read_format_rar_encryption_partially.c test_read_format_rar_invalid1.c test_read_format_raw.c test_read_format_tar.c test_read_format_tar_concatenated.c test_read_format_tar_empty_filename.c test_read_format_tar_empty_pax.c test_read_format_tar_filename.c test_read_format_tbz.c test_read_format_tgz.c test_read_format_tlz.c test_read_format_txz.c test_read_format_tz.c test_read_format_ustar_filename.c test_read_format_warc.c test_read_format_xar.c test_read_format_zip.c test_read_format_zip_comment_stored.c test_read_format_zip_encryption_data.c test_read_format_zip_encryption_header.c test_read_format_zip_encryption_partially.c test_read_format_zip_filename.c test_read_format_zip_high_compression.c test_read_format_zip_jar.c test_read_format_zip_mac_metadata.c test_read_format_zip_malformed.c test_read_format_zip_msdos.c test_read_format_zip_nested.c test_read_format_zip_nofiletype.c test_read_format_zip_padded.c test_read_format_zip_sfx.c test_read_format_zip_traditional_encryption_data.c test_read_format_zip_winzip_aes.c test_read_format_zip_winzip_aes_large.c test_read_format_zip_zip64.c test_read_large.c test_read_pax_schily_xattr.c test_read_pax_truncated.c test_read_position.c test_read_set_format.c test_read_too_many_filters.c test_read_truncated.c test_read_truncated_filter.c test_sparse_basic.c test_tar_filenames.c test_tar_large.c test_ustar_filename_encoding.c test_ustar_filenames.c test_warn_missing_hardlink_target.c test_write_disk.c test_write_disk_appledouble.c test_write_disk_failures.c test_write_disk_hardlink.c test_write_disk_hfs_compression.c test_write_disk_lookup.c test_write_disk_mac_metadata.c test_write_disk_no_hfs_compression.c test_write_disk_perms.c test_write_disk_secure.c test_write_disk_secure744.c test_write_disk_secure745.c test_write_disk_secure746.c test_write_disk_sparse.c test_write_disk_symlink.c test_write_disk_times.c test_write_filter_b64encode.c test_write_filter_bzip2.c test_write_filter_compress.c test_write_filter_gzip.c test_write_filter_gzip_timestamp.c test_write_filter_lrzip.c test_write_filter_lz4.c test_write_filter_lzip.c test_write_filter_lzma.c test_write_filter_lzop.c test_write_filter_program.c test_write_filter_uuencode.c test_write_filter_xz.c test_write_format_7zip.c test_write_format_7zip_empty.c test_write_format_7zip_large.c test_write_format_ar.c test_write_format_cpio.c test_write_format_cpio_empty.c test_write_format_cpio_newc.c test_write_format_cpio_odc.c test_write_format_gnutar.c test_write_format_gnutar_filenames.c test_write_format_iso9660.c test_write_format_iso9660_boot.c test_write_format_iso9660_empty.c test_write_format_iso9660_filename.c test_write_format_iso9660_zisofs.c test_write_format_mtree.c test_write_format_mtree_absolute_path.c test_write_format_mtree_classic.c test_write_format_mtree_classic_indent.c test_write_format_mtree_fflags.c test_write_format_mtree_no_separator.c test_write_format_mtree_quoted_filename.c test_write_format_pax.c test_write_format_raw.c test_write_format_raw_b64.c test_write_format_shar_empty.c test_write_format_tar.c test_write_format_tar_empty.c test_write_format_tar_sparse.c test_write_format_tar_ustar.c test_write_format_tar_v7tar.c test_write_format_warc.c test_write_format_warc_empty.c test_write_format_xar.c test_write_format_xar_empty.c test_write_format_zip.c test_write_format_zip_compression_store.c test_write_format_zip_empty.c test_write_format_zip_empty_zip64.c test_write_format_zip_file.c test_write_format_zip_file_zip64.c test_write_format_zip_large.c test_write_format_zip_zip64.c test_write_open_memory.c test_write_read_format_zip.c test_zip_filename_encoding.c ) # # Register target # ADD_EXECUTABLE(libarchive_test ${libarchive_test_SOURCES}) TARGET_LINK_LIBRARIES(libarchive_test archive_static ${ADDITIONAL_LIBS}) SET_PROPERTY(TARGET libarchive_test PROPERTY COMPILE_DEFINITIONS LIBARCHIVE_STATIC LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${libarchive_test_SOURCES}) SET_PROPERTY(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) # list.h has a line DEFINE_TEST(testname) for every # test. We can use that to define the tests for cmake by # defining a DEFINE_TEST macro and reading list.h in. MACRO (DEFINE_TEST _testname) ADD_TEST( NAME libarchive_${_testname} COMMAND libarchive_test -vv -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/libarchive/test) # Experimental new test handling ADD_CUSTOM_TARGET(run_libarchive_test COMMAND libarchive_test -r ${CMAKE_CURRENT_SOURCE_DIR} -vv) ADD_DEPENDENCIES(run_all_tests run_libarchive_test) ENDIF(ENABLE_TEST) Index: vendor/libarchive/dist/libarchive/test/test.h =================================================================== --- vendor/libarchive/dist/libarchive/test/test.h (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test.h (revision 314565) @@ -1,408 +1,36 @@ /* - * Copyright (c) 2003-2006 Tim Kientzle + * Copyright (c) 2003-2017 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: head/lib/libarchive/test/test.h 201247 2009-12-30 05:59:21Z kientzle $ */ /* 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) && !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 +#define KNOWNREF "test_compat_gtar_1.tar.uu" +#define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */ +#undef PROGRAM /* Testing a library, not a program. */ +#define LIBRARY "libarchive" +#define EXTRA_DUMP(x) archive_error_string((struct archive *)(x)) +#define EXTRA_ERRNO(x) archive_errno((struct archive *)(x)) +#define EXTRA_VERSION archive_version_details() -#include /* Windows requires this before sys/stat.h */ -#include - -#if HAVE_DIRENT_H -#include -#endif -#ifdef HAVE_DIRECT_H -#include -#define dirent direct -#endif -#include -#include -#ifdef HAVE_IO_H -#include -#endif -#ifdef HAVE_STDINT_H -#include -#endif -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_WINDOWS_H -#include -#endif - -/* - * System-specific tweaks. We really want to minimize these - * as much as possible, since they make it harder to understand - * the mainline code. - */ - -/* Windows (including Visual Studio and MinGW but not Cygwin) */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#if !defined(__BORLANDC__) -#undef chdir -#define chdir _chdir -#define strdup _strdup -#endif -#endif - -/* Visual Studio */ -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf sprintf_s -#endif - -#if defined(__BORLANDC__) -#pragma warn -8068 /* Constant out of range in comparison. */ -#endif - -/* Haiku OS and QNX */ -#if defined(__HAIKU__) || defined(__QNXNTO__) -/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ -#include -#endif - -/* Get a real definition for __FBSDID if we can */ -#if HAVE_SYS_CDEFS_H -#include -#endif - -/* If not, define it so as to avoid dangling semicolons. */ -#ifndef __FBSDID -#define __FBSDID(a) struct _undefined_hack -#endif - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -/* - * If this platform has , acl_create(), acl_init(), - * acl_set_file(), and ACL_USER, we assume it has the rest of the - * POSIX.1e draft functions used in archive_read_extract.c. - */ -#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE -#if HAVE_ACL_USER -#define HAVE_POSIX_ACL 1 -#elif HAVE_ACL_TYPE_EXTENDED -#define HAVE_DARWIN_ACL 1 -#endif -#endif - -/* - * If this platform has , acl_get(), facl_get(), acl_set(), - * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions - */ -#if HAVE_SYS_ACL_H && HAVE_ACL_GET && HAVE_FACL_GET && HAVE_ACL_SET && HAVE_FACL_SET && HAVE_ACLENT_T && HAVE_ACE_T -#define HAVE_SUN_ACL 1 -#endif - -/* Define if platform supports NFSv4 ACLs */ -#if (HAVE_POSIX_ACL && HAVE_ACL_TYPE_NFS4) || HAVE_SUN_ACL || HAVE_DARWIN_ACL -#define HAVE_NFS4_ACL 1 -#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) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) -/* chdir() and error if it fails */ -#define assertChdir(path) \ - assertion_chdir(__FILE__, __LINE__, path) -/* Assert two integers are the same. Reports value of each one if not. */ -#define assertEqualInt(v1,v2) \ - assertion_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) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) -#define assertEqualUTF8String(v1,v2) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) -/* As above, but v1 and v2 are wchar_t * */ -#define assertEqualWString(v1,v2) \ - assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) -/* As above, but raw blocks of bytes. */ -#define assertEqualMem(v1, v2, l) \ - assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) -/* Assert that memory is full of a specified byte */ -#define assertMemoryFilledWith(v1, l, b) \ - assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) -/* Assert two files are the same. */ -#define assertEqualFile(f1, f2) \ - assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) -/* Assert that a file is empty. */ -#define assertEmptyFile(pathname) \ - assertion_empty_file(__FILE__, __LINE__, (pathname)) -/* Assert that a file is not empty. */ -#define assertNonEmptyFile(pathname) \ - assertion_non_empty_file(__FILE__, __LINE__, (pathname)) -#define assertFileAtime(pathname, sec, nsec) \ - assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileAtimeRecent(pathname) \ - assertion_file_atime_recent(__FILE__, __LINE__, pathname) -#define assertFileBirthtime(pathname, sec, nsec) \ - assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileBirthtimeRecent(pathname) \ - assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) -/* Assert that a file exists; supports printf-style arguments. */ -#define assertFileExists(pathname) \ - assertion_file_exists(__FILE__, __LINE__, pathname) -/* Assert that a file exists. */ -#define assertFileNotExists(pathname) \ - assertion_file_not_exists(__FILE__, __LINE__, pathname) -/* Assert that file contents match a string. */ -#define assertFileContents(data, data_size, pathname) \ - assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) -/* Verify that a file does not contain invalid strings */ -#define assertFileContainsNoInvalidStrings(pathname, strings) \ - assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) -#define assertFileMtime(pathname, sec, nsec) \ - assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileMtimeRecent(pathname) \ - assertion_file_mtime_recent(__FILE__, __LINE__, pathname) -#define assertFileNLinks(pathname, nlinks) \ - assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) -#define assertFileSize(pathname, size) \ - assertion_file_size(__FILE__, __LINE__, pathname, size) -#define assertFileMode(pathname, mode) \ - assertion_file_mode(__FILE__, __LINE__, pathname, mode) -#define assertTextFileContents(text, pathname) \ - assertion_text_file_contents(__FILE__, __LINE__, text, pathname) -#define assertFileContainsLinesAnyOrder(pathname, lines) \ - assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) -#define assertIsDir(pathname, mode) \ - assertion_is_dir(__FILE__, __LINE__, pathname, mode) -#define assertIsHardlink(path1, path2) \ - assertion_is_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsNotHardlink(path1, path2) \ - assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsReg(pathname, mode) \ - assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) -/* Create a directory, report error if it fails. */ -#define assertMakeDir(dirname, mode) \ - assertion_make_dir(__FILE__, __LINE__, dirname, mode) -#define assertMakeFile(path, mode, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) -#define assertMakeBinFile(path, mode, csize, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) -#define assertMakeHardlink(newfile, oldfile) \ - assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) -#define assertMakeSymlink(newfile, linkto) \ - assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) -#define assertNodump(path) \ - assertion_nodump(__FILE__, __LINE__, path) -#define assertUmask(mask) \ - assertion_umask(__FILE__, __LINE__, mask) -#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ - assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) - -/* - * 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 \ - skipping_setup(__FILE__, __LINE__);test_skipping - -/* Function declarations. These are defined in test_utility.c. */ -void failure(const char *fmt, ...); -int assertion_assert(const char *, int, int, const char *, void *); -int assertion_chdir(const char *, int, const char *); -int assertion_empty_file(const char *, int, const char *); -int assertion_equal_file(const char *, int, const char *, const char *); -int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); -int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); -int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); -int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); -int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); -int assertion_file_atime(const char *, int, const char *, long, long); -int assertion_file_atime_recent(const char *, int, const char *); -int assertion_file_birthtime(const char *, int, const char *, long, long); -int assertion_file_birthtime_recent(const char *, int, const char *); -int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); -int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); -int assertion_file_contents(const char *, int, const void *, int, const char *); -int assertion_file_exists(const char *, int, const char *); -int assertion_file_mode(const char *, int, const char *, int); -int assertion_file_mtime(const char *, int, const char *, long, long); -int assertion_file_mtime_recent(const char *, int, const char *); -int assertion_file_nlinks(const char *, int, const char *, int); -int assertion_file_not_exists(const char *, int, const char *); -int assertion_file_size(const char *, int, const char *, long); -int assertion_is_dir(const char *, int, const char *, int); -int assertion_is_hardlink(const char *, int, const char *, const char *); -int assertion_is_not_hardlink(const char *, int, const char *, const char *); -int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); -int assertion_make_dir(const char *, int, const char *, int); -int assertion_make_file(const char *, int, const char *, int, int, const void *); -int assertion_make_hardlink(const char *, int, const char *newpath, const char *); -int assertion_make_symlink(const char *, int, const char *newpath, const char *); -int assertion_nodump(const char *, int, const char *); -int assertion_non_empty_file(const char *, int, const char *); -int assertion_text_file_contents(const char *, int, const char *buff, const char *f); -int assertion_umask(const char *, int, int); -int assertion_utimes(const char *, int, const char *, long, long, long, long ); - -void skipping_setup(const char *, int); -void test_skipping(const char *fmt, ...); - -/* Like sprintf, then system() */ -int systemf(const char * fmt, ...); - -/* Delay until time() returns a value after this. */ -void sleepUntilAfter(time_t); - -/* Return true if this platform can create symlinks. */ -int canSymlink(void); - -/* Return true if this platform can run the "bzip2" program. */ -int canBzip2(void); - -/* Return true if this platform can run the "grzip" program. */ -int canGrzip(void); - -/* Return true if this platform can run the "gzip" program. */ -int canGzip(void); - -/* Return true if this platform can run the specified command. */ -int canRunCommand(const char *); - -/* Return true if this platform can run the "lrzip" program. */ -int canLrzip(void); - -/* Return true if this platform can run the "lz4" program. */ -int canLz4(void); - -/* Return true if this platform can run the "lzip" program. */ -int canLzip(void); - -/* Return true if this platform can run the "lzma" program. */ -int canLzma(void); - -/* Return true if this platform can run the "lzop" program. */ -int canLzop(void); - -/* Return true if this platform can run the "xz" program. */ -int canXz(void); - -/* Return true if this filesystem can handle nodump flags. */ -int canNodump(void); - -/* Return true if the file has large i-node number(>0xffffffff). */ -int is_LargeInode(const char *); - -/* 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, ...); - -/* Dump block of bytes to a file. */ -void dumpfile(const char *filename, void *, size_t); - -/* Extracts named reference file to the current directory. */ -void extract_reference_file(const char *); -/* Copies named reference file to the current directory. */ -void copy_reference_file(const char *); - -/* Extracts a list of files to the current directory. - * List must be NULL terminated. - */ -void extract_reference_files(const char **); - -/* Subtract umask from mode */ -mode_t umasked(mode_t expected_mode); - -/* Path to working directory for current test */ -extern const char *testworkdir; - -/* - * Special interfaces for libarchive test harness. - */ - -#include "archive.h" -#include "archive_entry.h" - -/* ACL structure */ -struct archive_test_acl_t { - int type; /* Type of ACL */ - int permset; /* Permissions for this class of users. */ - int tag; /* Owner, User, Owning group, group, other, etc. */ - int qual; /* GID or UID of user/group, depending on tag. */ - const char *name; /* Name of user/group, depending on tag. */ -}; - -/* Set ACLs */ -void archive_test_set_acls(struct archive_entry *, struct archive_test_acl_t *, - int); - -/* Compare ACLs */ -void archive_test_compare_acls(struct archive_entry *, - struct archive_test_acl_t *, int, int, int); - -/* Special customized read-from-memory interface. */ -int read_open_memory(struct archive *, const void *, size_t, size_t); -/* _minimal version exercises a slightly different set of libarchive APIs. */ -int read_open_memory_minimal(struct archive *, const void *, size_t, size_t); -/* _seek version produces a seekable file. */ -int read_open_memory_seek(struct archive *, const void *, size_t, size_t); - -/* Versions of above that accept an archive argument for additional info. */ -#define assertA(e) assertion_assert(__FILE__, __LINE__, (e), #e, (a)) -#define assertEqualIntA(a,v1,v2) \ - assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a)) -#define assertEqualStringA(a,v1,v2) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a), 0) - -#ifdef USE_DMALLOC -#include -#endif +#include "test_common.h" Index: vendor/libarchive/dist/libarchive/test/test_acl_nfs4.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_acl_nfs4.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_acl_nfs4.c (revision 314565) @@ -1,208 +1,208 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); /* * Exercise the system-independent portion of the ACL support. * Check that archive_entry objects can save and restore NFS4 ACL data. * * This should work on all systems, regardless of whether local * filesystems support ACLs or not. */ static struct archive_test_acl_t acls1[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ_DATA, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }, }; static struct archive_test_acl_t acls2[] = { /* An entry for each type. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0, ARCHIVE_ENTRY_ACL_USER, 108, "user108" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, 0, ARCHIVE_ENTRY_ACL_USER, 109, "user109" }, { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, 0, ARCHIVE_ENTRY_ACL_USER, 110, "user110" }, { ARCHIVE_ENTRY_ACL_TYPE_ALARM, 0, ARCHIVE_ENTRY_ACL_USER, 111, "user111" }, /* An entry for each permission. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 112, "user112" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA, ARCHIVE_ENTRY_ACL_USER, 113, "user113" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_USER, 114, "user114" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_USER, 115, "user115" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_USER, 116, "user116" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_USER, 117, "user117" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_USER, 118, "user118" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_USER, 119, "user119" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_USER, 120, "user120" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_USER, 121, "user121" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER, 122, "user122" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_USER, 123, "user123" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE, ARCHIVE_ENTRY_ACL_USER, 124, "user124" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL, ARCHIVE_ENTRY_ACL_USER, 125, "user125" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_USER, 126, "user126" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_USER, 127, "user127" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER, 128, "user128" }, /* One entry with each inheritance value. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_USER, 129, "user129" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_USER, 130, "user130" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_USER, 131, "user131" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_USER, 132, "user132" }, { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ARCHIVE_ENTRY_ACL_USER, 133, "user133" }, { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ARCHIVE_ENTRY_ACL_USER, 134, "user134" }, /* One entry for each qualifier. */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 135, "user135" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }, }; /* * Entries that should be rejected when we attempt to set them * on an ACL that already has NFS4 entries. */ static struct archive_test_acl_t acls_bad[] = { /* POSIX.1e ACL types */ { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 78, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 78, "" }, /* POSIX.1e tags */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_MASK, -1, "" }, /* POSIX.1e permissions */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }, }; DEFINE_TEST(test_acl_nfs4) { struct archive_entry *ae; int i; /* Create a simple archive_entry. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0777); /* Store and read back some basic ACL entries. */ - archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); + assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); /* Check that entry contains only NFSv4 types */ assert((archive_entry_acl_types(ae) & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0); assert((archive_entry_acl_types(ae) & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0); assertEqualInt(4, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* A more extensive set of ACLs. */ - archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); + assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); assertEqualInt(32, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), + assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* * Check that clearing ACLs gets rid of them all by repeating * the first test. */ - archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); + assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); failure("Basic ACLs shouldn't be stored as extended ACLs"); assertEqualInt(4, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); /* * Different types of malformed ACL entries that should * fail when added to existing NFS4 ACLs. */ - archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); + assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); for (i = 0; i < (int)(sizeof(acls_bad)/sizeof(acls_bad[0])); ++i) { struct archive_test_acl_t *p = &acls_bad[i]; failure("Malformed ACL test #%d", i); assertEqualInt(ARCHIVE_FAILED, archive_entry_acl_add_entry(ae, p->type, p->permset, p->tag, p->qual, p->name)); failure("Malformed ACL test #%d", i); assertEqualInt(32, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); } archive_entry_free(ae); } Index: vendor/libarchive/dist/libarchive/test/test_acl_pax.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_acl_pax.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_acl_pax.c (revision 314565) @@ -1,416 +1,415 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); /* * Exercise the system-independent portion of the ACL support. * Check that pax archive can save and restore POSIX.1e ACL data. * * This should work on all systems, regardless of whether local * filesystems support ACLs or not. */ static unsigned char buff[16384]; static struct archive_test_acl_t acls0[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_OTHER, 0, "" }, }; static struct archive_test_acl_t acls1[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls3[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; static struct archive_test_acl_t acls4[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; static struct archive_test_acl_t acls5[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALARM, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; DEFINE_TEST(test_acl_pax_posix1e) { struct archive *a; struct archive_entry *ae; size_t used; FILE *f; void *reference; size_t reference_size; /* Write an archive to memory. */ assert(NULL != (a = archive_write_new())); assertA(0 == archive_write_set_format_pax(a)); assertA(0 == archive_write_add_filter_none(a)); assertA(0 == archive_write_set_bytes_per_block(a, 1)); assertA(0 == archive_write_set_bytes_in_last_block(a, 1)); assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); /* Write a series of files to the archive with different ACL info. */ /* Create a simple archive_entry. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0777); /* Basic owner/owning group should just update mode bits. */ - archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); + assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); assertA(0 == archive_write_header(a, ae)); /* With any extended ACL entry, we should read back a full set. */ - archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); + assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); assertA(0 == archive_write_header(a, ae)); - /* A more extensive set of ACLs. */ - archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); + assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); assertA(0 == archive_write_header(a, ae)); /* * Check that clearing ACLs gets rid of them all by repeating * the first test. */ - archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); + assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Write out the data we generated to a file for manual inspection. */ assert(NULL != (f = fopen("testout", "wb"))); assertEqualInt(used, (size_t)fwrite(buff, 1, (unsigned int)used, f)); fclose(f); /* Write out the reference data to a file for manual inspection. */ extract_reference_file("test_acl_pax_posix1e.tar"); reference = slurpfile(&reference_size, "test_acl_pax_posix1e.tar"); /* Assert that the generated data matches the built-in reference data.*/ failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_posix1e.tar' reference file."); assertEqualMem(buff, reference, reference_size); failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_posix1e.tar' reference file."); assertEqualInt((int)used, reference_size); free(reference); /* Read back each entry and check that the ACL data is right. */ assert(NULL != (a = archive_read_new())); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_open_memory(a, buff, used)); /* First item has no ACLs */ assertA(0 == archive_read_next_header(a, &ae)); failure("Basic ACLs shouldn't be stored as extended ACLs"); assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* Second item has a few ACLs */ assertA(0 == archive_read_next_header(a, &ae)); failure("One extended ACL should flag all ACLs to be returned."); assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* Third item has pretty extensive ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), + assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543); failure("Basic ACLs should set mode to 0543, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0543); /* Fourth item has no ACLs */ assertA(0 == archive_read_next_header(a, &ae)); failure("Basic ACLs shouldn't be stored as extended ACLs"); assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_acl_pax_nfs4) { struct archive *a; struct archive_entry *ae; size_t used; FILE *f; void *reference; size_t reference_size; /* Write an archive to memory. */ assert(NULL != (a = archive_write_new())); assertA(0 == archive_write_set_format_pax(a)); assertA(0 == archive_write_add_filter_none(a)); assertA(0 == archive_write_set_bytes_per_block(a, 1)); assertA(0 == archive_write_set_bytes_in_last_block(a, 1)); assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); /* Write a series of files to the archive with different ACL info. */ /* Create a simple archive_entry. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0777); /* NFS4 ACLs mirroring 0754 file mode */ - archive_test_set_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0])); + assertEntrySetAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0])); assertA(0 == archive_write_header(a, ae)); /* A more extensive set of NFS4 ACLs. */ - archive_test_set_acls(ae, acls4, sizeof(acls4)/sizeof(acls4[0])); + assertEntrySetAcls(ae, acls4, sizeof(acls4)/sizeof(acls4[0])); assertA(0 == archive_write_header(a, ae)); /* Set with special (audit, alarm) NFS4 ACLs. */ - archive_test_set_acls(ae, acls5, sizeof(acls5)/sizeof(acls5[0])); + assertEntrySetAcls(ae, acls5, sizeof(acls5)/sizeof(acls5[0])); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Write out the data we generated to a file for manual inspection. */ assert(NULL != (f = fopen("testout", "wb"))); assertEqualInt(used, (size_t)fwrite(buff, 1, (unsigned int)used, f)); fclose(f); /* Write out the reference data to a file for manual inspection. */ extract_reference_file("test_acl_pax_nfs4.tar"); reference = slurpfile(&reference_size, "test_acl_pax_nfs4.tar"); /* Assert that the generated data matches the built-in reference data.*/ failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_nfs4.tar' reference file."); assertEqualMem(buff, reference, reference_size); failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_nfs4.tar' reference file."); assertEqualInt((int)used, reference_size); free(reference); /* Read back each entry and check that the ACL data is right. */ assert(NULL != (a = archive_read_new())); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_open_memory(a, buff, used)); /* First item has NFS4 ACLs mirroring file mode */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(3, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ALLOW)); - archive_test_compare_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]), + assertEntryCompareAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]), ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0); /* Second item has has more fine-grained NFS4 ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls4, sizeof(acls4)/sizeof(acls4[0]), + assertEntryCompareAcls(ae, acls4, sizeof(acls4)/sizeof(acls4[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* Third item has has audit and alarm NFS4 ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]), + assertEntryCompareAcls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } Index: vendor/libarchive/dist/libarchive/test/test_acl_platform_posix1e.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_acl_platform_posix1e.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_acl_platform_posix1e.c (revision 314565) @@ -1,653 +1,653 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * Copyright (c) 2017 Martin Matuska * 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: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $"); #if HAVE_POSIX_ACL || HAVE_SUN_ACL #include #if HAVE_ACL_GET_PERM #include #define ACL_GET_PERM acl_get_perm #elif HAVE_ACL_GET_PERM_NP #define ACL_GET_PERM acl_get_perm_np #endif static struct archive_test_acl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_MASK, -1, "" }, }; static int #if HAVE_SUN_ACL acl_entry_get_perm(aclent_t *aclent) #else acl_entry_get_perm(acl_entry_t aclent) #endif { int permset = 0; #if HAVE_POSIX_ACL acl_permset_t opaque_ps; #endif #if HAVE_SUN_ACL if (aclent->a_perm & 1) permset |= ARCHIVE_ENTRY_ACL_EXECUTE; if (aclent->a_perm & 2) permset |= ARCHIVE_ENTRY_ACL_WRITE; if (aclent->a_perm & 4) permset |= ARCHIVE_ENTRY_ACL_READ; #else /* translate the silly opaque permset to a bitmap */ acl_get_permset(aclent, &opaque_ps); if (ACL_GET_PERM(opaque_ps, ACL_EXECUTE)) permset |= ARCHIVE_ENTRY_ACL_EXECUTE; if (ACL_GET_PERM(opaque_ps, ACL_WRITE)) permset |= ARCHIVE_ENTRY_ACL_WRITE; if (ACL_GET_PERM(opaque_ps, ACL_READ)) permset |= ARCHIVE_ENTRY_ACL_READ; #endif return permset; } #if 0 static int acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) { int entry_id = ACL_FIRST_ENTRY; acl_entry_t acl_entry; acl_tag_t acl_tag_type; while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { /* After the first time... */ entry_id = ACL_NEXT_ENTRY; /* If this matches, return perm mask */ acl_get_tag_type(acl_entry, &acl_tag_type); if (acl_tag_type == requested_tag_type) { switch (acl_tag_type) { case ACL_USER_OBJ: if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) { return acl_entry_get_perm(acl_entry); } break; case ACL_GROUP_OBJ: if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) { return acl_entry_get_perm(acl_entry); } break; case ACL_USER: case ACL_GROUP: case ACL_OTHER: return acl_entry_get_perm(acl_entry); default: failure("Unexpected ACL tag type"); assert(0); } } } return -1; } #endif static int #if HAVE_SUN_ACL acl_match(aclent_t *aclent, struct archive_test_acl_t *myacl) #else acl_match(acl_entry_t aclent, struct archive_test_acl_t *myacl) #endif { #if HAVE_POSIX_ACL gid_t g, *gp; uid_t u, *up; acl_tag_t tag_type; #endif if (myacl->permset != acl_entry_get_perm(aclent)) return (0); #if HAVE_SUN_ACL switch (aclent->a_type) #else acl_get_tag_type(aclent, &tag_type); switch (tag_type) #endif { #if HAVE_SUN_ACL case DEF_USER_OBJ: case USER_OBJ: #else case ACL_USER_OBJ: #endif if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); break; #if HAVE_SUN_ACL case DEF_USER: case USER: #else case ACL_USER: #endif if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); #if HAVE_SUN_ACL if ((uid_t)myacl->qual != aclent->a_id) return (0); #else up = acl_get_qualifier(aclent); u = *up; acl_free(up); if ((uid_t)myacl->qual != u) return (0); #endif break; #if HAVE_SUN_ACL case DEF_GROUP_OBJ: case GROUP_OBJ: #else case ACL_GROUP_OBJ: #endif if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); break; #if HAVE_SUN_ACL case DEF_GROUP: case GROUP: #else case ACL_GROUP: #endif if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); #if HAVE_SUN_ACL if ((gid_t)myacl->qual != aclent->a_id) return (0); #else gp = acl_get_qualifier(aclent); g = *gp; acl_free(gp); if ((gid_t)myacl->qual != g) return (0); #endif break; #if HAVE_SUN_ACL case DEF_CLASS_OBJ: case CLASS_OBJ: #else case ACL_MASK: #endif if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); break; #if HAVE_SUN_ACL case DEF_OTHER_OBJ: case OTHER_OBJ: #else case ACL_OTHER: #endif if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0); break; } return (1); } static void #if HAVE_SUN_ACL compare_acls(acl_t *acl, struct archive_test_acl_t *myacls, int n) #else compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n) #endif { int *marker; int matched; int i; #if HAVE_SUN_ACL int e; aclent_t *acl_entry; #else int entry_id = ACL_FIRST_ENTRY; acl_entry_t acl_entry; #endif /* Count ACL entries in myacls array and allocate an indirect array. */ marker = malloc(sizeof(marker[0]) * n); if (marker == NULL) return; for (i = 0; i < n; i++) marker[i] = i; /* * Iterate over acls in system acl object, try to match each * one with an item in the myacls array. */ #if HAVE_SUN_ACL for(e = 0; e < acl->acl_cnt; e++) { acl_entry = &((aclent_t *)acl->acl_aclp)[e]; #else while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { /* After the first time... */ entry_id = ACL_NEXT_ENTRY; #endif /* Search for a matching entry (tag and qualifier) */ for (i = 0, matched = 0; i < n && !matched; i++) { if (acl_match(acl_entry, &myacls[marker[i]])) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } /* TODO: Print out more details in this case. */ failure("ACL entry on file that shouldn't be there"); assert(matched == 1); } /* Dump entries in the myacls array that weren't in the system acl. */ for (i = 0; i < n; ++i) { failure(" ACL entry missing from file: " "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n", myacls[marker[i]].type, myacls[marker[i]].permset, myacls[marker[i]].tag, myacls[marker[i]].qual, myacls[marker[i]].name); assert(0); /* Record this as a failure. */ } free(marker); } #endif /* * Verify ACL restore-to-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_posix1e_restore) { #if !HAVE_SUN_ACL && !HAVE_POSIX_ACL skipping("POSIX.1e ACLs are not supported on this platform"); #else /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ struct stat st; struct archive *a; struct archive_entry *ae; int n, fd; char *func; #if HAVE_SUN_ACL acl_t *acl, *acl2; #else acl_t acl; #endif /* * First, do a quick manual set/read of ACL data to * verify that the local filesystem does support ACLs. * If it doesn't, we'll simply skip the remaining tests. */ #if HAVE_SUN_ACL n = acl_fromtext("user::rwx,user:1:rw-,group::rwx,group:15:r-x,other:rwx,mask:rwx", &acl); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl = acl_from_text("u::rwx,u:1:rw,g::rwx,g:15:rx,o::rwx,m::rwx"); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl != NULL); #endif /* Create a test file and try ACL on it. */ fd = open("pretest", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl); return; } #if HAVE_SUN_ACL n = facl_get(fd, 0, &acl2); if (n != 0) { close(fd); acl_free(acl); } if (errno == ENOSYS) { skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } failure("facl_get(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); if (acl2->acl_type != ACLENT_T) { acl_free(acl2); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } acl_free(acl2); func = "facl_set()"; n = facl_set(fd, acl); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl); #endif acl_free(acl); if (n != 0) { #if HAVE_SUN_ACL if (errno == ENOSYS) #else if (errno == EOPNOTSUPP || errno == EINVAL) #endif { close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } } failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); #if HAVE_SUN_ACL #endif close(fd); /* Create a write-to-disk object. */ assert(NULL != (a = archive_write_disk_new())); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL); /* Populate an archive entry with some metadata, including ACL info */ ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "test0"); archive_entry_set_mtime(ae, 123456, 7890); archive_entry_set_size(ae, 0); - archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); + assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Verify the data on disk. */ assertEqualInt(0, stat("test0", &st)); assertEqualInt(st.st_mtime, 123456); #if HAVE_SUN_ACL n = acl_get("test0", 0, &acl); failure("acl_get(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl = acl_get_file("test0", ACL_TYPE_ACCESS); failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno)); assert(acl != (acl_t)NULL); #endif compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0])); acl_free(acl); #endif /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ } /* * Verify ACL read-from-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_posix1e_read) { #if !HAVE_SUN_ACL && !HAVE_POSIX_ACL skipping("POSIX.1e ACLs are not supported on this platform"); #else struct archive *a; struct archive_entry *ae; int n, fd, flags, dflags; char *func, *acl_text; const char *acl1_text, *acl2_text, *acl3_text; #if HAVE_SUN_ACL acl_t *acl, *acl1, *acl2, *acl3; #else acl_t acl1, acl2, acl3; #endif /* * Manually construct a directory and two files with * different ACLs. This also serves to verify that ACLs * are supported on the local filesystem. */ /* Create a test file f1 with acl1 */ #if HAVE_SUN_ACL acl1_text = "user::rwx," "group::rwx," "other:rwx," "user:1:rw-," "group:15:r-x," "mask:rwx"; n = acl_fromtext(acl1_text, &acl1); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl1_text = "user::rwx\n" "group::rwx\n" "other::rwx\n" "user:1:rw-\n" "group:15:r-x\n" "mask::rwx"; acl1 = acl_from_text(acl1_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl1 != NULL); #endif fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl1); return; } #if HAVE_SUN_ACL /* Check if Solaris filesystem supports POSIX.1e ACLs */ n = facl_get(fd, 0, &acl); if (n != 0) close(fd); if (n != 0 && errno == ENOSYS) { acl_free(acl1); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } failure("facl_get(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); if (acl->acl_type != ACLENT_T) { acl_free(acl); acl_free(acl1); close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } func = "facl_set()"; n = facl_set(fd, acl1); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl1); #endif acl_free(acl1); if (n != 0) { #if HAVE_SUN_ACL if (errno == ENOSYS) #else if (errno == EOPNOTSUPP || errno == EINVAL) #endif { close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } } failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); close(fd); assertMakeDir("d", 0700); /* * Create file d/f1 with acl2 * * This differs from acl1 in the u:1: and g:15: permissions. * * This file deliberately has the same name but a different ACL. * Github Issue #777 explains how libarchive's directory traversal * did not always correctly enter directories before attempting * to read ACLs, resulting in reading the ACL from a like-named * file in the wrong directory. */ #if HAVE_SUN_ACL acl2_text = "user::rwx," "group::rwx," "other:---," "user:1:r--," "group:15:r--," "mask:rwx"; n = acl_fromtext(acl2_text, &acl2); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl2_text = "user::rwx\n" "group::rwx\n" "other::---\n" "user:1:r--\n" "group:15:r--\n" "mask::rwx"; acl2 = acl_from_text(acl2_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl2 != NULL); #endif fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl2); return; } #if HAVE_SUN_ACL func = "facl_set()"; n = facl_set(fd, acl2); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl2); #endif acl_free(acl2); if (n != 0) close(fd); failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); close(fd); - /* Create directory d2 with default ACLs */ - assertMakeDir("d2", 0755); + /* Create nested directory d2 with default ACLs */ + assertMakeDir("d/d2", 0755); #if HAVE_SUN_ACL acl3_text = "user::rwx," "group::r-x," "other:r-x," "user:2:r--," "group:16:-w-," "mask:rwx," "default:user::rwx," "default:user:1:r--," "default:group::r-x," "default:group:15:r--," "default:mask:rwx," "default:other:r-x"; n = acl_fromtext(acl3_text, &acl3); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl3_text = "user::rwx\n" "user:1:r--\n" "group::r-x\n" "group:15:r--\n" "mask::rwx\n" "other::r-x"; acl3 = acl_from_text(acl3_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl3 != NULL); #endif #if HAVE_SUN_ACL func = "acl_set()"; - n = acl_set("d2", acl3); + n = acl_set("d/d2", acl3); #else func = "acl_set_file()"; - n = acl_set_file("d2", ACL_TYPE_DEFAULT, acl3); + n = acl_set_file("d/d2", ACL_TYPE_DEFAULT, acl3); #endif acl_free(acl3); failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); /* Create a read-from-disk object. */ assert(NULL != (a = archive_read_disk_new())); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); assert(NULL != (ae = archive_entry_new())); #if HAVE_SUN_ACL flags = ARCHIVE_ENTRY_ACL_TYPE_POSIX1E | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS; dflags = flags; #else flags = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; dflags = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; #endif /* Walk the dir until we see both of the files */ while (ARCHIVE_OK == archive_read_next_header2(a, ae)) { archive_read_disk_descend(a); if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, flags); assertEqualString(acl_text, acl1_text); free(acl_text); } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, flags); assertEqualString(acl_text, acl2_text); free(acl_text); - } else if (strcmp(archive_entry_pathname(ae), "./d2") == 0) { + } else if (strcmp(archive_entry_pathname(ae), "./d/d2") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, dflags); assertEqualString(acl_text, acl3_text); free(acl_text); } } archive_entry_free(ae); assertEqualInt(ARCHIVE_OK, archive_free(a)); #endif } Index: vendor/libarchive/dist/libarchive/test/test_acl_posix1e.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_acl_posix1e.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_acl_posix1e.c (revision 314565) @@ -1,181 +1,180 @@ /*- * 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: src/lib/libarchive/test/test_acl_basic.c,v 1.6 2008/10/19 00:13:57 kientzle Exp $"); /* * Exercise the system-independent portion of the ACL support. * Check that archive_entry objects can save and restore POSIX.1e-style * ACL data. * * This should work on all systems, regardless of whether local * filesystems support ACLs or not. */ static struct archive_test_acl_t acls0[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_OTHER, 0, "" }, }; static struct archive_test_acl_t acls1[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; /* * NFS4 entry types; attempts to set these on top of POSIX.1e * attributes should fail. */ static struct archive_test_acl_t acls_nfs4[] = { /* NFS4 types */ { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 78, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 78, "" }, { ARCHIVE_ENTRY_ACL_TYPE_AUDIT, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 78, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALARM, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 78, "" }, /* NFS4 tags */ { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }, /* NFS4 inheritance markers */ { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_USER, 79, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, }; DEFINE_TEST(test_acl_posix1e) { struct archive_entry *ae; int i; /* Create a simple archive_entry. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0777); /* Basic owner/owning group should just update mode bits. */ /* * Note: This features of libarchive's ACL implementation * shouldn't be relied on and should probably be removed. It * was done to identify trivial ACLs so we could avoid * triggering unnecessary extensions. It's better to identify * trivial ACLs at the point they are being read from disk. */ - archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); + assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); failure("Basic ACLs shouldn't be stored as extended ACLs"); assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); - /* With any extended ACL entry, we should read back a full set. */ - archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); + assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); failure("One extended ACL should flag all ACLs to be returned."); /* Check that entry contains only POSIX.1e types */ assert((archive_entry_acl_types(ae) & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0); assert((archive_entry_acl_types(ae) & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0); assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* A more extensive set of ACLs. */ - archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); + assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), + assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543); failure("Basic ACLs should set mode to 0543, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0543); /* * Check that clearing ACLs gets rid of them all by repeating * the first test. */ - archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); + assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); failure("Basic ACLs shouldn't be stored as extended ACLs"); assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* * Different types of malformed ACL entries that should * fail when added to existing POSIX.1e ACLs. */ - archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); + assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); for (i = 0; i < (int)(sizeof(acls_nfs4)/sizeof(acls_nfs4[0])); ++i) { struct archive_test_acl_t *p = &acls_nfs4[i]; failure("Malformed ACL test #%d", i); assertEqualInt(ARCHIVE_FAILED, archive_entry_acl_add_entry(ae, p->type, p->permset, p->tag, p->qual, p->name)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); } archive_entry_free(ae); } Index: vendor/libarchive/dist/libarchive/test/test_acl_text.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_acl_text.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_acl_text.c (revision 314565) @@ -1,473 +1,473 @@ /*- * Copyright (c) 2016 Martin Matuska * 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$"); /* * Test converting ACLs to text, both wide and non-wide * * This should work on all systems, regardless of whether local * filesystems support ACLs or not. */ static struct archive_test_acl_t acls0[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 100, "user100" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, 1000, "user1000" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 101, "user101"}, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_GROUP, 79, "group79" }, }; static struct archive_test_acl_t acls1[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_DELETE_CHILD | ARCHIVE_ENTRY_ACL_DELETE | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_USER, 101, "user101" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; const char* acltext[] = { "user::rwx\n" "group::r-x\n" "other::r-x\n" "user:user100:r-x\n" "user:user1000:---\n" "group:group78:rwx\n" "default:user::r-x\n" "default:group::r-x\n" "default:other::---\n" "default:user:user101:r-x\n" "default:group:group79:--x", "user::rwx\n" "group::r-x\n" "other::r-x\n" "user:user100:r-x:100\n" "user:user1000:---:1000\n" "group:group78:rwx:78\n" "default:user::r-x\n" "default:group::r-x\n" "default:other::---\n" "default:user:user101:r-x:101\n" "default:group:group79:--x:79", "u::rwx\n" "g::r-x\n" "o::r-x\n" "u:user100:r-x:100\n" "u:user1000:---:1000\n" "g:group78:rwx:78\n" "d:user::r-x\n" "d:group::r-x\n" "d:other::---\n" "d:user:user101:r-x:101\n" "d:group:group79:--x:79", "user::rwx\n" "group::r-x\n" "other::r-x\n" "user:user100:r-x\n" "user:user1000:---\n" "group:group78:rwx", "user::rwx," "group::r-x," "other::r-x," "user:user100:r-x," "user:user1000:---," "group:group78:rwx", "user::rwx\n" "group::r-x\n" "other::r-x\n" "user:user100:r-x:100\n" "user:user1000:---:1000\n" "group:group78:rwx:78", "user::r-x\n" "group::r-x\n" "other::---\n" "user:user101:r-x\n" "group:group79:--x", "user::r-x\n" "group::r-x\n" "other::---\n" "user:user101:r-x:101\n" "group:group79:--x:79", "default:user::r-x\n" "default:group::r-x\n" "default:other::---\n" "default:user:user101:r-x\n" "default:group:group79:--x", "user:user77:rw-p--a-R-c-o-:-------:allow\n" "user:user101:-w-pdD--------:fdin---:deny\n" "group:group78:r-----a-R-c---:------I:allow\n" "owner@:rwxp--aARWcCo-:-------:allow\n" "group@:rw-p--a-R-c---:-------:allow\n" "everyone@:r-----a-R-c--s:-------:allow", "user:user77:rw-p--a-R-c-o-:-------:allow:77\n" "user:user101:-w-pdD--------:fdin---:deny:101\n" "group:group78:r-----a-R-c---:------I:allow:78\n" "owner@:rwxp--aARWcCo-:-------:allow\n" "group@:rw-p--a-R-c---:-------:allow\n" "everyone@:r-----a-R-c--s:-------:allow", "user:user77:rwpaRco::allow:77\n" "user:user101:wpdD:fdin:deny:101\n" "group:group78:raRc:I:allow:78\n" "owner@:rwxpaARWcCo::allow\n" "group@:rwpaRc::allow\n" "everyone@:raRcs::allow" }; static wchar_t * convert_s_to_ws(const char *s) { size_t len; wchar_t *ws = NULL; if (s != NULL) { len = strlen(s) + 1; ws = malloc(len * sizeof(wchar_t)); assert(mbstowcs(ws, s, len) != (size_t)-1); } return (ws); } static void compare_acl_text(struct archive_entry *ae, int flags, const char *s) { char *text; wchar_t *wtext; wchar_t *ws; ssize_t slen; ws = convert_s_to_ws(s); text = archive_entry_acl_to_text(ae, &slen, flags); assertEqualString(text, s); if (text != NULL) assertEqualInt(strlen(text), slen); wtext = archive_entry_acl_to_text_w(ae, &slen, flags); assertEqualWString(wtext, ws); if (wtext != NULL) { assertEqualInt(wcslen(wtext), slen); } free(text); free(wtext); free(ws); } DEFINE_TEST(test_acl_from_text) { struct archive_entry *ae; wchar_t *ws = NULL; /* Create an empty archive_entry. */ assert((ae = archive_entry_new()) != NULL); /* 1a. Read POSIX.1e access ACLs from text */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text(ae, acltext[5], ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0755); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); /* 1b. Now read POSIX.1e default ACLs and append them */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text(ae, acltext[7], ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755); assertEqualInt(11, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); archive_entry_acl_clear(ae); /* 1a and 1b with wide strings */ ws = convert_s_to_ws(acltext[5]); assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text_w(ae, ws, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0755); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); free(ws); ws = convert_s_to_ws(acltext[7]); assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text_w(ae, ws, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755); assertEqualInt(11, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); archive_entry_acl_clear(ae); /* 2. Read POSIX.1e default ACLs from text */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text(ae, acltext[7], ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0); assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); archive_entry_acl_clear(ae); /* ws is still acltext[7] */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text_w(ae, ws, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0); assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); archive_entry_acl_clear(ae); /* 3. Read POSIX.1e access and default ACLs from text */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text(ae, acltext[1], ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755); assertEqualInt(11, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); archive_entry_acl_clear(ae); free(ws); ws = convert_s_to_ws(acltext[1]); assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text_w(ae, ws, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755); assertEqualInt(11, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); archive_entry_acl_clear(ae); /* 4. Read POSIX.1e access and default ACLs from text (short form) */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text(ae, acltext[2], ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755); assertEqualInt(11, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); archive_entry_acl_clear(ae); free(ws); ws = convert_s_to_ws(acltext[2]); assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text_w(ae, ws, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755); assertEqualInt(11, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); archive_entry_acl_clear(ae); /* 5. Read NFSv4 ACLs from text */ assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text(ae, acltext[10], ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); archive_entry_acl_clear(ae); free(ws); ws = convert_s_to_ws(acltext[10]); assertEqualInt(ARCHIVE_OK, archive_entry_acl_from_text_w(ae, ws, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); archive_entry_acl_clear(ae); free(ws); archive_entry_free(ae); } DEFINE_TEST(test_acl_to_text) { struct archive_entry *ae; /* Create an empty archive_entry. */ assert((ae = archive_entry_new()) != NULL); /* Write POSIX.1e ACLs */ - archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); + assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0])); /* No flags should give output like getfacl(1) on linux */ compare_acl_text(ae, 0, acltext[0]); /* This should give the same output as previous test */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, acltext[0]); /* This should give the same output as previous two tests */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT, acltext[0]); /* POSIX.1e access and default ACLs with appended ID */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[1]); /* POSIX.1e access acls only, like getfacl(1) on FreeBSD */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, acltext[3]); /* POSIX.1e access acls separated with comma */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA, acltext[4]); /* POSIX.1e access acls with appended user or group ID */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[5]); /* POSIX.1e default acls */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, acltext[6]); /* POSIX.1e default acls with appended user or group ID */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[7]); /* POSIX.1e default acls prefixed with default: */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT, acltext[8]); /* Write NFSv4 ACLs */ - archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); + assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0])); /* NFSv4 ACLs like getfacl(1) on FreeBSD */ compare_acl_text(ae, 0, acltext[9]); /* NFSv4 ACLs like "getfacl -i" on FreeBSD */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[10]); /* NFSv4 ACLs like "getfacl -i" on FreeBSD with stripped minus chars */ compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_COMPACT, acltext[11]); archive_entry_free(ae); } Index: vendor/libarchive/dist/libarchive/test/test_archive_api_feature.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_archive_api_feature.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_archive_api_feature.c (revision 314565) @@ -1,50 +1,54 @@ /*- * 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: src/lib/libarchive/test/test_archive_api_feature.c,v 1.5 2008/05/26 17:00:24 kientzle Exp $"); DEFINE_TEST(test_archive_api_feature) { char buff[128]; const char *p; /* This is the (hopefully) final versioning API. */ assertEqualInt(ARCHIVE_VERSION_NUMBER, archive_version_number()); sprintf(buff, "libarchive %d.%d.%d", archive_version_number() / 1000000, (archive_version_number() / 1000) % 1000, archive_version_number() % 1000); failure("Version string is: %s, computed is: %s", archive_version_string(), buff); assertEqualMem(buff, archive_version_string(), strlen(buff)); if (strlen(buff) < strlen(archive_version_string())) { p = archive_version_string() + strlen(buff); failure("Version string is: %s", archive_version_string()); - assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd'); - ++p; + if (p[0] == 'd'&& p[1] == 'e' && p[2] == 'v') + p += 3; + else { + assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd'); + ++p; + } failure("Version string is: %s", archive_version_string()); assert(*p == '\0'); } } Index: vendor/libarchive/dist/libarchive/test/test_compat_solaris_tar_acl.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_compat_solaris_tar_acl.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_compat_solaris_tar_acl.c (revision 314565) @@ -1,267 +1,267 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2016 Martin Matuska * 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$"); /* * Verify reading entries with POSIX.1e and NFSv4 ACLs from archives created * with Solaris tar. * * This should work on all systems, regardless of whether local filesystems * support ACLs or not. */ static struct archive_test_acl_t acls0[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 71, "lp" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 666, "666" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 1000, "1000" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_MASK, -1, ""}, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls1[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 2, "bin" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP, 3, "sys" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_MASK, -1, ""}, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1 ,"" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 2, "bin" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP, 3, "sys" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_MASK, -1, ""}, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls3[] = { { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP, 12, "daemon" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP, 2, "bin" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER, 4, "adm" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; static struct archive_test_acl_t acls4[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_USER, 1100, "1100" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_GROUP, 4, "adm" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_DELETE_CHILD | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; DEFINE_TEST(test_compat_solaris_tar_acl) { char name[] = "test_compat_solaris_tar_acl.tar"; struct archive *a; struct archive_entry *ae; /* Read archive file */ assert(NULL != (a = archive_read_new())); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); /* First item has access ACLs */ assertA(0 == archive_read_next_header(a, &ae)); failure("One extended ACL should flag all ACLs to be returned."); assertEqualInt(7, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0644); failure("Basic ACLs should set mode to 0644, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0644); /* Second item has default and access ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0750); failure("Basic ACLs should set mode to 0750, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0750); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); - archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), + assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0750); /* Third item has NFS4 ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]), + assertEntryCompareAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* Fourth item has NFS4 ACLs and inheritance flags */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]), + assertEntryCompareAcls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } Index: vendor/libarchive/dist/libarchive/test/test_compat_star_acl.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_compat_star_acl.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_compat_star_acl.c (revision 314565) @@ -1,321 +1,321 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2016 Martin Matuska * 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$"); /* * Verify reading entries with POSIX.1e and NFSv4 ACLs from archives created * by star. * * This should work on all systems, regardless of whether local filesystems * support ACLs or not. */ static struct archive_test_acl_t acls0[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, -1, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_MASK, -1, ""}, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls1[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, -1, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, -1, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_GROUP, -1, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_MASK, -1, ""}, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER_OBJ, -1 ,"" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, -1, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_GROUP, -1, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_MASK, -1, ""}, { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_WRITE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, }; static struct archive_test_acl_t acls3[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; static struct archive_test_acl_t acls4[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; static struct archive_test_acl_t acls5[] = { { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" }, { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_DELETE | ARCHIVE_ENTRY_ACL_DELETE_CHILD | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_WRITE_OWNER | ARCHIVE_ENTRY_ACL_SYNCHRONIZE | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" }, }; DEFINE_TEST(test_compat_star_acl_posix1e) { char name[] = "test_compat_star_acl_posix1e.tar"; struct archive *a; struct archive_entry *ae; /* Read archive file */ assert(NULL != (a = archive_read_new())); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); /* First item has a few ACLs */ assertA(0 == archive_read_next_header(a, &ae)); failure("One extended ACL should flag all ACLs to be returned."); assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), + assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* Second item has pretty extensive ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(7, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); - archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), + assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543); failure("Basic ACLs should set mode to 0543, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0543); /* Third item has default ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); - archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), + assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0142); failure("Basic ACLs should set mode to 0142, not %04o", archive_entry_mode(ae)&0777); assert((archive_entry_mode(ae) & 0777) == 0142); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_compat_star_acl_nfs4) { char name[] = "test_compat_star_acl_nfs4.tar"; struct archive *a; struct archive_entry *ae; /* Read archive file */ assert(NULL != (a = archive_read_new())); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240 )); /* First item has NFS4 ACLs mirroring file mode */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(3, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ALLOW)); - archive_test_compare_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]), + assertEntryCompareAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]), ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0); /* Second item has has fine-grained NFS4 ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]), + assertEntryCompareAcls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* Third item has file and directory inheritance NFS4 ACLs */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4)); - archive_test_compare_acls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]), + assertEntryCompareAcls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]), ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } Index: vendor/libarchive/dist/libarchive/test/test_fuzz.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_fuzz.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_fuzz.c (revision 314565) @@ -1,621 +1,622 @@ /*- * 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: head/lib/libarchive/test/test_fuzz.c 201247 2009-12-30 05:59:21Z kientzle $"); /* * This was inspired by an ISO fuzz tester written by Michal Zalewski * and posted to the "vulnwatch" mailing list on March 17, 2005: * http://seclists.org/vulnwatch/2005/q1/0088.html * * This test simply reads each archive image into memory, pokes * random values into it and runs it through libarchive. It tries * to damage about 1% of each file and repeats the exercise 100 times * with each file. * * Unlike most other tests, this test does not verify libarchive's * responses other than to ensure that libarchive doesn't crash. * * Due to the deliberately random nature of this test, it may be hard * to reproduce failures. Because this test deliberately attempts to * induce crashes, there's little that can be done in the way of * post-failure diagnostics. */ /* Because this works for any archive, we can just re-use the archives * developed for other tests. */ struct files { int uncompress; /* If 1, decompress the file before fuzzing. */ const char **names; }; static void test_fuzz(const struct files *filesets) { const void *blk; size_t blk_size; int64_t blk_offset; int n; for (n = 0; filesets[n].names != NULL; ++n) { const size_t buffsize = 30000000; struct archive_entry *ae; struct archive *a; char *rawimage = NULL, *image = NULL, *tmp = NULL; size_t size = 0, oldsize = 0; int i, q; extract_reference_files(filesets[n].names); if (filesets[n].uncompress) { int r; /* Use format_raw to decompress the data. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); r = archive_read_open_filenames(a, filesets[n].names, 16384); if (r != ARCHIVE_OK) { archive_read_free(a); if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) { skipping("Cannot uncompress fileset"); } else { skipping("Cannot uncompress %s", filesets[n].names[0]); } continue; } assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); rawimage = malloc(buffsize); size = archive_read_data(a, rawimage, buffsize); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assert(size > 0); if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) { failure("Internal buffer is not big enough for " "uncompressed test files"); } else { failure("Internal buffer is not big enough for " "uncompressed test file: %s", filesets[n].names[0]); } if (!assert(size < buffsize)) { free(rawimage); rawimage = NULL; continue; } } else { for (i = 0; filesets[n].names[i] != NULL; ++i) { + char *newraw; tmp = slurpfile(&size, filesets[n].names[i]); - char *newraw = realloc(rawimage, oldsize + size); + newraw = realloc(rawimage, oldsize + size); if (!assert(newraw != NULL)) { free(rawimage); rawimage = NULL; free(tmp); continue; } rawimage = newraw; memcpy(rawimage + oldsize, tmp, size); oldsize += size; size = oldsize; free(tmp); } } if (size == 0) { free(rawimage); rawimage = NULL; continue; } image = malloc(size); assert(image != NULL); if (image == NULL) { free(rawimage); rawimage = NULL; return; } assert(rawimage != NULL); srand((unsigned)time(NULL)); for (i = 0; i < 1000; ++i) { FILE *f; int j, numbytes, trycnt; /* Fuzz < 1% of the bytes in the archive. */ memcpy(image, rawimage, size); q = (int)size / 100; if (q < 4) q = 4; numbytes = (int)(rand() % q); for (j = 0; j < numbytes; ++j) image[rand() % size] = (char)rand(); /* Save the messed-up image to a file. * If we crash, that file will be useful. */ for (trycnt = 0; trycnt < 3; trycnt++) { f = fopen("after.test.failure.send.this.file." "to.libarchive.maintainers.with.system.details", "wb"); if (f != NULL) break; #if defined(_WIN32) && !defined(__CYGWIN__) /* * Sometimes previous close operation does not completely * end at this time. So we should take a wait while * the operation running. */ Sleep(100); #endif } assert(f != NULL); assertEqualInt((size_t)size, fwrite(image, 1, (size_t)size, f)); fclose(f); // Try to read all headers and bodies. assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); if (0 == archive_read_open_memory(a, image, size)) { while(0 == archive_read_next_header(a, &ae)) { while (0 == archive_read_data_block(a, &blk, &blk_size, &blk_offset)) continue; } archive_read_close(a); } archive_read_free(a); // Just list headers, skip bodies. assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); if (0 == archive_read_open_memory(a, image, size)) { while(0 == archive_read_next_header(a, &ae)) { } archive_read_close(a); } archive_read_free(a); } free(image); free(rawimage); } } DEFINE_TEST(test_fuzz_ar) { static const char *fileset1[] = { "test_read_format_ar.ar", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_cab) { static const char *fileset1[] = { "test_fuzz.cab", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_cpio) { static const char *fileset1[] = { "test_read_format_cpio_bin_be.cpio", NULL }; static const char *fileset2[] = { "test_read_format_cpio_bin_le.cpio", NULL }; static const char *fileset3[] = { /* Test RPM unwrapper */ "test_read_format_cpio_svr4_gzip_rpm.rpm", NULL }; static const struct files filesets[] = { {0, fileset1}, {0, fileset2}, {0, fileset3}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_iso9660) { static const char *fileset1[] = { "test_fuzz_1.iso.Z", NULL }; static const struct files filesets[] = { {0, fileset1}, /* Exercise compress decompressor. */ {1, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_lzh) { static const char *fileset1[] = { "test_fuzz.lzh", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_mtree) { static const char *fileset1[] = { "test_read_format_mtree.mtree", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_rar) { static const char *fileset1[] = { /* Uncompressed RAR test */ "test_read_format_rar.rar", NULL }; static const char *fileset2[] = { /* RAR file with binary data */ "test_read_format_rar_binary_data.rar", NULL }; static const char *fileset3[] = { /* Best Compressed RAR test */ "test_read_format_rar_compress_best.rar", NULL }; static const char *fileset4[] = { /* Normal Compressed RAR test */ "test_read_format_rar_compress_normal.rar", NULL }; static const char *fileset5[] = { /* Normal Compressed Multi LZSS blocks RAR test */ "test_read_format_rar_multi_lzss_blocks.rar", NULL }; static const char *fileset6[] = { /* RAR with no EOF header */ "test_read_format_rar_noeof.rar", NULL }; static const char *fileset7[] = { /* Best Compressed RAR file with both PPMd and LZSS blocks */ "test_read_format_rar_ppmd_lzss_conversion.rar", NULL }; static const char *fileset8[] = { /* RAR with subblocks */ "test_read_format_rar_subblock.rar", NULL }; static const char *fileset9[] = { /* RAR with Unicode filenames */ "test_read_format_rar_unicode.rar", NULL }; static const char *fileset10[] = { "test_read_format_rar_multivolume.part0001.rar", "test_read_format_rar_multivolume.part0002.rar", "test_read_format_rar_multivolume.part0003.rar", "test_read_format_rar_multivolume.part0004.rar", NULL }; static const struct files filesets[] = { {0, fileset1}, {0, fileset2}, {0, fileset3}, {0, fileset4}, {0, fileset5}, {0, fileset6}, {0, fileset7}, {0, fileset8}, {0, fileset9}, {0, fileset10}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_tar) { static const char *fileset1[] = { "test_compat_bzip2_1.tbz", NULL }; static const char *fileset2[] = { "test_compat_gtar_1.tar", NULL }; static const char *fileset3[] = { "test_compat_gzip_1.tgz", NULL }; static const char *fileset4[] = { "test_compat_gzip_2.tgz", NULL }; static const char *fileset5[] = { "test_compat_tar_hardlink_1.tar", NULL }; static const char *fileset6[] = { "test_compat_xz_1.txz", NULL }; static const char *fileset7[] = { "test_read_format_gtar_sparse_1_17_posix10_modified.tar", NULL }; static const char *fileset8[] = { "test_read_format_tar_empty_filename.tar", NULL }; #if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H static const char *fileset9[] = { "test_compat_lzop_1.tar.lzo", NULL }; #endif static const struct files filesets[] = { {0, fileset1}, /* Exercise bzip2 decompressor. */ {1, fileset1}, {0, fileset2}, {0, fileset3}, /* Exercise gzip decompressor. */ {0, fileset4}, /* Exercise gzip decompressor. */ {0, fileset5}, {0, fileset6}, /* Exercise xz decompressor. */ {0, fileset7}, {0, fileset8}, #if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H {0, fileset9}, /* Exercise lzo decompressor. */ #endif {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_zip) { static const char *fileset1[] = { "test_compat_zip_1.zip", NULL }; static const char *fileset2[] = { "test_compat_zip_2.zip", NULL }; static const char *fileset3[] = { "test_compat_zip_3.zip", NULL }; static const char *fileset4[] = { "test_compat_zip_4.zip", NULL }; static const char *fileset5[] = { "test_compat_zip_5.zip", NULL }; static const char *fileset6[] = { "test_compat_zip_6.zip", NULL }; static const char *fileset7[] = { "test_read_format_zip.zip", NULL }; static const char *fileset8[] = { "test_read_format_zip_comment_stored_1.zip", NULL }; static const char *fileset9[] = { "test_read_format_zip_comment_stored_2.zip", NULL }; static const char *fileset10[] = { "test_read_format_zip_encryption_data.zip", NULL }; static const char *fileset11[] = { "test_read_format_zip_encryption_header.zip", NULL }; static const char *fileset12[] = { "test_read_format_zip_encryption_partially.zip", NULL }; static const char *fileset13[] = { "test_read_format_zip_filename_cp866.zip", NULL }; static const char *fileset14[] = { "test_read_format_zip_filename_cp932.zip", NULL }; static const char *fileset15[] = { "test_read_format_zip_filename_koi8r.zip", NULL }; static const char *fileset16[] = { "test_read_format_zip_filename_utf8_jp.zip", NULL }; static const char *fileset17[] = { "test_read_format_zip_filename_utf8_ru.zip", NULL }; static const char *fileset18[] = { "test_read_format_zip_filename_utf8_ru2.zip", NULL }; static const char *fileset19[] = { "test_read_format_zip_length_at_end.zip", NULL }; static const char *fileset20[] = { "test_read_format_zip_mac_metadata.zip", NULL }; static const char *fileset21[] = { "test_read_format_zip_malformed1.zip", NULL }; static const char *fileset22[] = { "test_read_format_zip_msdos.zip", NULL }; static const char *fileset23[] = { "test_read_format_zip_nested.zip", NULL }; static const char *fileset24[] = { "test_read_format_zip_nofiletype.zip", NULL }; static const char *fileset25[] = { "test_read_format_zip_padded1.zip", NULL }; static const char *fileset26[] = { "test_read_format_zip_padded2.zip", NULL }; static const char *fileset27[] = { "test_read_format_zip_padded3.zip", NULL }; static const char *fileset28[] = { "test_read_format_zip_symlink.zip", NULL }; static const char *fileset29[] = { "test_read_format_zip_traditional_encryption_data.zip", NULL }; static const char *fileset30[] = { "test_read_format_zip_ux.zip", NULL }; static const char *fileset31[] = { "test_read_format_zip_winzip_aes128.zip", NULL }; static const char *fileset32[] = { "test_read_format_zip_winzip_aes256.zip", NULL }; static const char *fileset33[] = { "test_read_format_zip_winzip_aes256_large.zip", NULL }; static const char *fileset34[] = { "test_read_format_zip_winzip_aes256_stored.zip", NULL }; static const char *fileset35[] = { "test_read_format_zip_zip64a.zip", NULL }; static const char *fileset36[] = { "test_read_format_zip_zip64b.zip", NULL }; static const struct files filesets[] = { {0, fileset1}, {0, fileset2}, {0, fileset3}, {0, fileset4}, {0, fileset5}, {0, fileset6}, {0, fileset7}, {0, fileset8}, {0, fileset9}, {0, fileset10}, {0, fileset11}, {0, fileset12}, {0, fileset13}, {0, fileset14}, {0, fileset15}, {0, fileset16}, {0, fileset17}, {0, fileset18}, {0, fileset19}, {0, fileset20}, {0, fileset21}, {0, fileset22}, {0, fileset23}, {0, fileset24}, {0, fileset25}, {0, fileset26}, {0, fileset27}, {0, fileset28}, {0, fileset29}, {0, fileset30}, {0, fileset31}, {0, fileset32}, {0, fileset33}, {0, fileset34}, {0, fileset35}, {0, fileset36}, {1, NULL} }; test_fuzz(filesets); } Index: vendor/libarchive/dist/libarchive/test/test_read_disk.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_read_disk.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_read_disk.c (revision 314565) @@ -1,173 +1,175 @@ /*- * 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: head/lib/libarchive/test/test_read_disk.c 201247 2009-12-30 05:59:21Z kientzle $"); static void gname_cleanup(void *d) { int *mp = d; assertEqualInt(*mp, 0x13579); *mp = 0x2468; } static const char * gname_lookup(void *d, int64_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, int64_t u) { int *mp = d; assertEqualInt(*mp, 0x1234); if (u == 1) return ("FOO"); return ("NOTFOO"); } +#if !defined(__CYGWIN__) && !defined(__HAIKU__) /* 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 */ }; +#endif DEFINE_TEST(test_read_disk) { struct archive *a; int gmagic = 0x13579, umagic = 0x1234; +#if !defined(__CYGWIN__) && !defined(__HAIKU__) const char *p; size_t i; +#endif 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__) || defined(__HAIKU__) /* Some platforms don't have predictable names for * uid=0, so we skip this part of the test. */ skipping("standard uname/gname lookup"); - 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); assert(p != NULL); if (p != NULL) { 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_free(a)); /* Verify our cleanup functions got called. */ assertEqualInt(gmagic, 0x2468); assertEqualInt(umagic, 0x2345); } Index: vendor/libarchive/dist/libarchive/test/test_read_disk_directory_traversals.c =================================================================== --- vendor/libarchive/dist/libarchive/test/test_read_disk_directory_traversals.c (revision 314564) +++ vendor/libarchive/dist/libarchive/test/test_read_disk_directory_traversals.c (revision 314565) @@ -1,1586 +1,1586 @@ /*- * Copyright (c) 2010-2012 Michihiro NAKAJIMA * 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 #if defined(_WIN32) && !defined(__CYGWIN__) # if !defined(__BORLANDC__) # define getcwd _getcwd # endif #endif /* * Test if the current filesystem is mounted with noatime option. */ static int atimeIsUpdated(void) { const char *fn = "fs_noatime"; struct stat st; if (!assertMakeFile(fn, 0666, "a")) return (0); if (!assertUtimes(fn, 1, 0, 1, 0)) return (0); /* Test the file contents in order to update its atime. */ if (!assertTextFileContents("a", fn)) return (0); if (stat(fn, &st) != 0) return (0); /* Is atime updated? */ if (st.st_atime > 1) return (1); return (0); } static void test_basic(void) { struct archive *a; struct archive_entry *ae; const void *p; char *initial_cwd, *cwd; size_t size; int64_t offset; int file_count; #if defined(_WIN32) && !defined(__CYGWIN__) wchar_t *wcwd, *wp, *fullpath; #endif assertMakeDir("dir1", 0755); assertMakeFile("dir1/file1", 0644, "0123456789"); assertMakeFile("dir1/file2", 0644, "hello world"); assertMakeDir("dir1/sub1", 0755); assertMakeFile("dir1/sub1/file1", 0644, "0123456789"); assertMakeDir("dir1/sub2", 0755); assertMakeFile("dir1/sub2/file1", 0644, "0123456789"); assertMakeFile("dir1/sub2/file2", 0644, "0123456789"); assertMakeDir("dir1/sub2/sub1", 0755); assertMakeDir("dir1/sub2/sub2", 0755); assertMakeDir("dir1/sub2/sub3", 0755); assertMakeFile("dir1/sub2/sub3/file", 0644, "xyz"); file_count = 12; assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1")); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub3/file") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 3); assertEqualInt((int)offset, 0); assertEqualMem(p, "xyz", 3); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 3); assertEqualInt(0, archive_read_disk_can_descend(a)); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test that call archive_read_disk_open_w, wchar_t version. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, L"dir1")); file_count = 12; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (wcscmp(archive_entry_pathname_w(ae), L"dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub3/file") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 3); assertEqualInt((int)offset, 0); assertEqualMem(p, "xyz", 3); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 3); assertEqualInt(0, archive_read_disk_can_descend(a)); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test that call archive_read_disk_open with a regular file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1/file1")); /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); #if defined(_WIN32) && !defined(__CYGWIN__) /* * Test for wildcard '*' or '?' */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1/*1")); /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* dir1/sub1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(1, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/sub1"); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); /* dir1/sub1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/sub1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test for a full-path beginning with "//?/" */ wcwd = _wgetcwd(NULL, 0); fullpath = malloc(sizeof(wchar_t) * (wcslen(wcwd) + 32)); wcscpy(fullpath, L"//?/"); wcscat(fullpath, wcwd); wcscat(fullpath, L"/dir1/file1"); free(wcwd); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, fullpath)); while ((wcwd = wcschr(fullpath, L'\\')) != NULL) *wcwd = L'/'; /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); free(fullpath); /* * Test for wild card '*' or '?' with "//?/" prefix. */ wcwd = _wgetcwd(NULL, 0); fullpath = malloc(sizeof(wchar_t) * (wcslen(wcwd) + 32)); wcscpy(fullpath, L"//?/"); wcscat(fullpath, wcwd); wcscat(fullpath, L"/dir1/*1"); free(wcwd); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, fullpath)); while ((wcwd = wcschr(fullpath, L'\\')) != NULL) *wcwd = L'/'; /* dir1/file1 */ wp = wcsrchr(fullpath, L'/'); wcscpy(wp+1, L"file1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* dir1/sub1 */ wcscpy(wp+1, L"sub1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(1, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); /* dir1/sub1/file1 */ wcscpy(wp+1, L"sub1/file1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); free(fullpath); #endif /* * We should be on the initial directory where we performed * archive_read_disk_new() after we perform archive_read_free() * even if we broke off the directory traversals. */ /* Save current working directory. */ #ifdef PATH_MAX initial_cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else initial_cwd = getcwd(NULL, 0); #endif assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1")); /* Step in a deep directory. */ file_count = 12; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "dir1/sub1/file1") == 0) /* * We are on an another directory at this time. */ break; if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* We should be on the initial working directory. */ failure( "Current working directory does not return to the initial" "directory"); #ifdef PATH_MAX cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else cwd = getcwd(NULL, 0); #endif assertEqualString(initial_cwd, cwd); free(initial_cwd); free(cwd); archive_entry_free(ae); } static void test_symlink_hybrid(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("h", 0755); assertChdir("h"); assertMakeDir("d1", 0755); assertMakeSymlink("ld1", "d1"); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); assertMakeSymlink("d1/link1", "file1"); assertMakeSymlink("d1/linkX", "fileX"); assertMakeSymlink("link2", "d1/file2"); assertMakeSymlink("linkY", "d1/fileY"); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_hybrid(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "h/ld1")); file_count = 5; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "h/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Specified file is a directory and it has symbolic files. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "h")); file_count = 9; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "h") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/d1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/d1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/d1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/d1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/link2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/linkY") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_symlink_logical(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("l", 0755); assertChdir("l"); assertMakeDir("d1", 0755); assertMakeSymlink("ld1", "d1"); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); assertMakeSymlink("d1/link1", "file1"); assertMakeSymlink("d1/linkX", "fileX"); assertMakeSymlink("link2", "d1/file2"); assertMakeSymlink("linkY", "d1/fileY"); assertChdir(".."); /* Note: this test uses archive_read_next_header() instead of archive_read_next_header2() */ assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_logical(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l/ld1")); file_count = 5; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); if (strcmp(archive_entry_pathname(ae), "l/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Specified file is a directory and it has symbolic files. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l")); file_count = 13; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); if (strcmp(archive_entry_pathname(ae), "l") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/d1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l/link2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/linkY") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_symlink_logical_loop(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("l2", 0755); assertChdir("l2"); assertMakeDir("d1", 0755); assertMakeDir("d1/d2", 0755); assertMakeDir("d1/d2/d3", 0755); assertMakeDir("d2", 0755); assertMakeFile("d2/file1", 0644, "d2/file1"); assertMakeSymlink("d1/d2/ld1", "../../d1"); assertMakeSymlink("d1/d2/ld2", "../../d2"); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_logical(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l2/d1")); file_count = 6; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "l2/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/d3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d2/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_restore_atime(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!atimeIsUpdated()) { skipping("Can't test restoring atime on this filesystem"); return; } assertMakeDir("at", 0755); assertMakeFile("at/f1", 0644, "0123456789"); assertMakeFile("at/f2", 0644, "hello world"); assertMakeFile("at/fe", 0644, NULL); assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* * Test1: Traversals without archive_read_disk_set_atime_restored(). */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* On FreeBSD (and likely other systems), atime on dirs does not change when it is read. */ /* failure("Atime should be restored"); */ /* assertFileAtimeRecent("at"); */ failure("Atime should be restored"); assertFileAtimeRecent("at/f1"); failure("Atime should be restored"); assertFileAtimeRecent("at/f2"); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test2: Traversals with archive_read_disk_set_atime_restored(). */ assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test3: Traversals with archive_read_disk_set_atime_restored() but * no data read as a listing. */ assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); if (!canNodump()) { /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); skipping("Can't test atime with nodump on this filesystem"); return; } /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* - * Test4: Traversals with archive_read_disk_set_atime_restored() and - * archive_read_disk_honor_nodump(). + * Test4: Traversals with ARCHIVE_READDISK_RESTORE_ATIME and + * ARCHIVE_READDISK_HONOR_NODUMP */ assertNodump("at/f1"); assertNodump("at/f2"); assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 2; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, ARCHIVE_READDISK_RESTORE_ATIME | ARCHIVE_READDISK_HONOR_NODUMP)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static int metadata_filter(struct archive *a, void *data, struct archive_entry *ae) { (void)data; /* UNUSED */ failure("CTime should be set"); assertEqualInt(8, archive_entry_ctime_is_set(ae)); failure("MTime should be set"); assertEqualInt(16, archive_entry_mtime_is_set(ae)); if (archive_entry_mtime(ae) < 886611) return (0); if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ failure("archive_read_disk_can_descend should work" " in metadata filter"); assertEqualIntA(a, 1, archive_read_disk_can_descend(a)); failure("archive_read_disk_descend should work" " in metadata filter"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } return (1); } static void test_callbacks(void) { struct archive *a; struct archive *m; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; assertMakeDir("cb", 0755); assertMakeFile("cb/f1", 0644, "0123456789"); assertMakeFile("cb/f2", 0644, "hello world"); assertMakeFile("cb/fe", 0644, NULL); assertUtimes("cb/f1", 886600, 0, 886600, 0); assertUtimes("cb/f2", 886611, 0, 886611, 0); assertUtimes("cb/fe", 886611, 0, 886611, 0); assertUtimes("cb", 886622, 0, 886622, 0); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); if (a == NULL) { archive_entry_free(ae); return; } assert((m = archive_match_new()) != NULL); if (m == NULL) { archive_entry_free(ae); archive_read_free(a); archive_match_free(m); return; } /* * Test1: Traversals with a name filter. */ file_count = 3; assertEqualIntA(m, ARCHIVE_OK, archive_match_exclude_pattern(m, "cb/f2")); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_matching(a, m, NULL, NULL)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "cb")); while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'cb/f2' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "cb/f2") != 0); if (strcmp(archive_entry_pathname(ae), "cb") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "cb/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "cb/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Reset name filter */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_matching(a, NULL, NULL, NULL)); /* * Test2: Traversals with a metadata filter. */ assertUtimes("cb/f1", 886600, 0, 886600, 0); assertUtimes("cb/f2", 886611, 0, 886611, 0); assertUtimes("cb/fe", 886611, 0, 886611, 0); assertUtimes("cb", 886622, 0, 886622, 0); file_count = 3; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_metadata_filter_callback(a, metadata_filter, NULL)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "cb")); while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'cb/f1' should be excluded"); assert(strcmp(archive_entry_pathname(ae), "cb/f1") != 0); if (strcmp(archive_entry_pathname(ae), "cb") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "cb/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "cb/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualInt(ARCHIVE_OK, archive_match_free(m)); archive_entry_free(ae); } static void test_nodump(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canNodump()) { skipping("Can't test nodump on this filesystem"); return; } assertMakeDir("nd", 0755); assertMakeFile("nd/f1", 0644, "0123456789"); assertMakeFile("nd/f2", 0644, "hello world"); assertMakeFile("nd/fe", 0644, NULL); assertNodump("nd/f2"); assertUtimes("nd/f1", 886600, 0, 886600, 0); assertUtimes("nd/f2", 886611, 0, 886611, 0); assertUtimes("nd/fe", 886611, 0, 886611, 0); assertUtimes("nd", 886622, 0, 886622, 0); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* - * Test1: Traversals without archive_read_disk_honor_nodump(). + * Test1: Traversals without ARCHIVE_READDISK_HONOR_NODUMP */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd")); file_count = 4; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "nd") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "nd/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "nd/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* - * Test2: Traversals with archive_read_disk_honor_nodump(). + * Test2: Traversals with ARCHIVE_READDISK_HONOR_NODUMP */ assertUtimes("nd/f1", 886600, 0, 886600, 0); assertUtimes("nd/f2", 886611, 0, 886611, 0); assertUtimes("nd/fe", 886611, 0, 886611, 0); assertUtimes("nd", 886622, 0, 886622, 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, ARCHIVE_READDISK_RESTORE_ATIME | ARCHIVE_READDISK_HONOR_NODUMP)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd")); file_count = 3; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'nd/f2' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "nd/f2") != 0); if (strcmp(archive_entry_pathname(ae), "nd") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "nd/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("nd/f2", 886611, 0); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } DEFINE_TEST(test_read_disk_directory_traversals) { /* Basic test. */ test_basic(); /* Test hybrid mode; follow symlink initially, then not. */ test_symlink_hybrid(); /* Test logical mode; follow all symlinks. */ test_symlink_logical(); /* Test logical mode; prevent loop in symlinks. */ test_symlink_logical_loop(); /* Test to restore atime. */ test_restore_atime(); /* Test callbacks. */ test_callbacks(); /* Test nodump. */ test_nodump(); } Index: vendor/libarchive/dist/tar/bsdtar.1 =================================================================== --- vendor/libarchive/dist/tar/bsdtar.1 (revision 314564) +++ vendor/libarchive/dist/tar/bsdtar.1 (revision 314565) @@ -1,1175 +1,1249 @@ .\" Copyright (c) 2003-2007 Tim Kientzle +.\" Copyright (c) 2017 Martin Matuska .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd September 16, 2014 +.Dd February 24, 2017 .Dt TAR 1 .Os .Sh NAME .Nm tar .Nd manipulate tape archives .Sh SYNOPSIS .Nm .Op Ar bundled-flags Ao args Ac .Op Ao Ar file Ac | Ao Ar pattern Ac ... .Nm .Brq Fl c .Op Ar options .Op Ar files | Ar directories .Nm .Brq Fl r | Fl u .Fl f Ar archive-file .Op Ar options .Op Ar files | Ar directories .Nm .Brq Fl t | Fl x .Op Ar options .Op Ar patterns .Sh DESCRIPTION .Nm creates and manipulates streaming archive files. This implementation can extract from tar, pax, cpio, zip, jar, ar, xar, rpm, 7-zip, and ISO 9660 cdrom images and can create tar, pax, cpio, ar, zip, 7-zip, and shar archives. .Pp The first synopsis form shows a .Dq bundled option word. This usage is provided for compatibility with historical implementations. See COMPATIBILITY below for details. .Pp The other synopsis forms show the preferred usage. The first option to .Nm is a mode indicator from the following list: .Bl -tag -compact -width indent .It Fl c Create a new archive containing the specified items. The long option form is .Fl Fl create . .It Fl r Like .Fl c , but new entries are appended to the archive. Note that this only works on uncompressed archives stored in regular files. The .Fl f option is required. The long option form is .Fl Fl append . .It Fl t List archive contents to stdout. The long option form is .Fl Fl list . .It Fl u Like .Fl r , but new entries are added only if they have a modification date newer than the corresponding entry in the archive. Note that this only works on uncompressed archives stored in regular files. The .Fl f option is required. The long form is .Fl Fl update . .It Fl x Extract to disk from the archive. If a file with the same name appears more than once in the archive, each copy will be extracted, with later copies overwriting (replacing) earlier copies. The long option form is .Fl Fl extract . .El .Pp In .Fl c , .Fl r , or .Fl u mode, each specified file or directory is added to the archive in the order specified on the command line. By default, the contents of each directory are also archived. .Pp In extract or list mode, the entire command line is read and parsed before the archive is opened. The pathnames or patterns on the command line indicate which items in the archive should be processed. Patterns are shell-style globbing patterns as documented in .Xr tcsh 1 . .Sh OPTIONS Unless specifically stated otherwise, options are applicable in all operating modes. .Bl -tag -width indent .It Cm @ Ns Pa archive -(c and r mode only) +(c and r modes only) The specified archive is opened and the entries in it will be appended to the current archive. As a simple example, .Dl Nm Fl c Fl f Pa - Pa newfile Cm @ Ns Pa original.tar writes a new archive to standard output containing a file .Pa newfile and all of the entries from .Pa original.tar . In contrast, .Dl Nm Fl c Fl f Pa - Pa newfile Pa original.tar creates a new archive with only two entries. Similarly, .Dl Nm Fl czf Pa - Fl Fl format Cm pax Cm @ Ns Pa - reads an archive from standard input (whose format will be determined automatically) and converts it into a gzip-compressed pax-format archive on stdout. In this way, .Nm can be used to convert archives from one format to another. .It Fl a , Fl Fl auto-compress (c mode only) Use the archive suffix to decide a set of the format and the compressions. As a simple example, .Dl Nm Fl a Fl cf Pa archive.tgz source.c source.h creates a new archive with restricted pax format and gzip compression, .Dl Nm Fl a Fl cf Pa archive.tar.bz2.uu source.c source.h creates a new archive with restricted pax format and bzip2 compression and uuencode compression, .Dl Nm Fl a Fl cf Pa archive.zip source.c source.h creates a new archive with zip format, .Dl Nm Fl a Fl jcf Pa archive.tgz source.c source.h ignores the .Dq -j option, and creates a new archive with restricted pax format and gzip compression, .Dl Nm Fl a Fl jcf Pa archive.xxx source.c source.h if it is unknown suffix or no suffix, creates a new archive with restricted pax format and bzip2 compression. +.It Fl Fl acls +(c, r, u, x modes only) +Archive or extract POSIX.1e or NFSv4 ACLs. This is the reverse of +.Fl Fl no-acls +and the default behavior in c, r, and u modes (except Mac OS X) or if +.Nm +is run in x mode as root. On Mac OS X this option translates extended ACLs +to NFSv4 ACLs. To store extended ACLs the +.Fl Fl mac-metadata +option is preferred. .It Fl B , Fl Fl read-full-blocks Ignored for compatibility with other .Xr tar 1 implementations. .It Fl b Ar blocksize , Fl Fl block-size Ar blocksize Specify the block size, in 512-byte records, for tape drive I/O. As a rule, this argument is only needed when reading from or writing to tape drives, and usually not even then as the default block size of 20 records (10240 bytes) is very common. .It Fl C Ar directory , Fl Fl cd Ar directory , Fl Fl directory Ar directory In c and r mode, this changes the directory before adding the following files. In x mode, change directories after opening the archive but before extracting entries from the archive. .It Fl Fl chroot (x mode only) .Fn chroot to the current directory after processing any .Fl C options and before extracting any files. .It Fl Fl clear-nochange-fflags (x mode only) Before removing file system objects to replace them, clear platform-specific file flags that might prevent removal. -.It Fl Fl disable-copyfile -Mac OS X specific. -Disable the use of -.Xr copyfile 3 . .It Fl Fl exclude Ar pattern Do not process files or directories that match the specified pattern. Note that exclusions take precedence over patterns or filenames specified on the command line. +.It Fl Fl fflags +(c, r, u, x modes only) +Archive or extract file flags. This is the reverse of +.Fl Fl no-fflags +and the default behavior in c, r, and u modes or if +.Nm +is run in x mode as root. .It Fl Fl format Ar format (c, r, u mode only) Use the specified format for the created archive. Supported formats include .Dq cpio , .Dq pax , .Dq shar , and .Dq ustar . Other formats may also be supported; see .Xr libarchive-formats 5 for more information about currently-supported formats. In r and u modes, when extending an existing archive, the format specified here must be compatible with the format of the existing archive on disk. .It Fl f Ar file , Fl Fl file Ar file Read the archive from or write the archive to the specified file. The filename can be .Pa - for standard input or standard output. The default varies by system; on .Fx , the default is .Pa /dev/sa0 ; on Linux, the default is .Pa /dev/st0 . .It Fl Fl gid Ar id Use the provided group id number. On extract, this overrides the group id in the archive; the group name in the archive will be ignored. On create, this overrides the group id read from disk; if .Fl Fl gname is not also specified, the group name will be set to match the group id. .It Fl Fl gname Ar name Use the provided group name. On extract, this overrides the group name in the archive; if the provided group name does not exist on the system, the group id (from the archive or from the .Fl Fl gid option) will be used instead. On create, this sets the group name that will be stored in the archive; the name will not be verified against the system group database. .It Fl H -(c and r mode only) +(c and r modes only) Symbolic links named on the command line will be followed; the target of the link will be archived, not the link itself. .It Fl h -(c and r mode only) +(c and r modes only) Synonym for .Fl L . .It Fl I Synonym for .Fl T . .It Fl Fl help Show usage. .It Fl Fl hfsCompression (x mode only) -Mac OS X specific(v10.6 or later). Compress extracted regular files with HFS+ compression. +Mac OS X specific (v10.6 or later). Compress extracted regular files with HFS+ +compression. .It Fl Fl ignore-zeros An alias of .Fl Fl options Cm read_concatenated_archives for compatibility with GNU tar. .It Fl Fl include Ar pattern Process only files or directories that match the specified pattern. Note that exclusions specified with .Fl Fl exclude take precedence over inclusions. If no inclusions are explicitly specified, all entries are processed by default. The .Fl Fl include option is especially useful when filtering archives. For example, the command .Dl Nm Fl c Fl f Pa new.tar Fl Fl include='*foo*' Cm @ Ns Pa old.tgz creates a new archive .Pa new.tar containing only the entries from .Pa old.tgz containing the string .Sq foo . .It Fl J , Fl Fl xz (c mode only) Compress the resulting archive with .Xr xz 1 . In extract or list modes, this option is ignored. Note that, unlike other .Nm tar implementations, this implementation recognizes XZ compression automatically when reading archives. .It Fl j , Fl Fl bzip , Fl Fl bzip2 , Fl Fl bunzip2 (c mode only) Compress the resulting archive with .Xr bzip2 1 . In extract or list modes, this option is ignored. Note that, unlike other .Nm tar implementations, this implementation recognizes bzip2 compression automatically when reading archives. .It Fl k , Fl Fl keep-old-files (x mode only) Do not overwrite existing files. In particular, if a file appears more than once in an archive, later copies will not overwrite earlier copies. .It Fl Fl keep-newer-files (x mode only) Do not overwrite existing files that are newer than the versions appearing in the archive being extracted. .It Fl L , Fl Fl dereference -(c and r mode only) +(c and r modes only) All symbolic links will be followed. Normally, symbolic links are archived as such. With this option, the target of the link will be archived instead. .It Fl l , Fl Fl check-links (c and r modes only) Issue a warning message unless all links to each file are archived. .It Fl Fl lrzip (c mode only) Compress the resulting archive with .Xr lrzip 1 . In extract or list modes, this option is ignored. .It Fl Fl lz4 (c mode only) Compress the archive with lz4-compatible compression before writing it. In input mode, this option is ignored; lz4 compression is recognized automatically on input. .It Fl Fl lzma (c mode only) Compress the resulting archive with the original LZMA algorithm. Use of this option is discouraged and new archives should be created with .Fl Fl xz instead. Note that, unlike other .Nm tar implementations, this implementation recognizes LZMA compression automatically when reading archives. .It Fl Fl lzop (c mode only) Compress the resulting archive with .Xr lzop 1 . In extract or list modes, this option is ignored. .It Fl m , Fl Fl modification-time (x mode only) Do not extract modification time. By default, the modification time is set to the time stored in the archive. +.It Fl Fl mac-metadata +(c, r, u and x mode only) +Mac OS X specific. Archive or extract extended ACLs and extended attributes +using +.Xr copyfile 3 +in AppleDouble format. This is the reverse of +.Fl Fl no-mac-metadata . +and the default behavior in c, r, and u modes or if +.Nm +is run in x mode as root. .It Fl n , Fl Fl norecurse , Fl Fl no-recursion (c, r, u modes only) Do not recursively archive the contents of directories. .It Fl Fl newer Ar date (c, r, u modes only) Only include files and directories newer than the specified date. This compares ctime entries. .It Fl Fl newer-mtime Ar date (c, r, u modes only) Like .Fl Fl newer , except it compares mtime entries instead of ctime entries. .It Fl Fl newer-than Pa file (c, r, u modes only) Only include files and directories newer than the specified file. This compares ctime entries. .It Fl Fl newer-mtime-than Pa file (c, r, u modes only) Like .Fl Fl newer-than , except it compares mtime entries instead of ctime entries. .It Fl Fl nodump (c and r modes only) Honor the nodump file flag by skipping this file. .It Fl Fl nopreserveHFSCompression (x mode only) Mac OS X specific(v10.6 or later). Do not compress extracted regular files which were compressed with HFS+ compression before archived. By default, compress the regular files again with HFS+ compression. .It Fl Fl null (use with .Fl I or .Fl T ) Filenames or patterns are separated by null characters, not by newlines. This is often used to read filenames output by the .Fl print0 option to .Xr find 1 . +.It Fl Fl no-acls +(c, r, u, x modes only) +Do not archive or extract POSIX.1e or NFSv4 ACLs. This is the reverse of +.Fl Fl acls +and the default behavior if +.Nm +is run as non-root in x mode (on Mac OS X also in c, r and u modes). +.It Fl Fl no-fflags +(c, r, u, x modes only) +Do not archive or extract file flags. This is the reverse of +.Fl Fl fflags +and the default behavior if +.Nm +is run as non-root in x mode. +.It Fl Fl no-mac-metadata +(x mode only) +Mac OS X specific. Do not archive or extract ACLs and extended attributes using +.Xr copyfile 3 +in AppleDouble format. This is the reverse of +.Fl Fl mac-metadata . +and the default behavior if +.Nm +is run as non-root in x mode. +.It Fl n , Fl Fl norecurse , Fl Fl no-recursion .It Fl Fl no-same-owner (x mode only) Do not extract owner and group IDs. This is the reverse of .Fl Fl same-owner and the default behavior if .Nm is run as non-root. .It Fl Fl no-same-permissions (x mode only) Do not extract full permissions (SGID, SUID, sticky bit, ACLs, extended attributes or extended file flags). This is the reverse of .Fl p and the default behavior if .Nm -is run as non-root. +is run as non-root and can be overridden by also specifying +.Fl Fl acls , +.Fl Fl fflags , +.Fl Fl mac-metadata, +.Fl Fl same-owner , +.Fl Fl same-permissions +and +.Fl Fl xattrs . +.It Fl Fl no-xattrs +(c, r, u, x modes only) +Do not archive or extract extended attributes. This is the reverse of +.Fl Fl xattrs +and the default behavior if +.Nm +is run as non-root in x mode. .It Fl Fl numeric-owner This is equivalent to .Fl Fl uname .Qq .Fl Fl gname .Qq . On extract, it causes user and group names in the archive to be ignored in favor of the numeric user and group ids. On create, it causes user and group names to not be stored in the archive. .It Fl O , Fl Fl to-stdout (x, t modes only) In extract (-x) mode, files will be written to standard out rather than being extracted to disk. In list (-t) mode, the file listing will be written to stderr rather than the usual stdout. .It Fl o (x mode) Use the user and group of the user running the program rather than those specified in the archive. Note that this has no significance unless .Fl p is specified, and the program is being run by the root user. In this case, the file modes and flags from the archive will be restored, but ACLs or owner information in the archive will be discarded. .It Fl o (c, r, u mode) A synonym for .Fl Fl format Ar ustar .It Fl Fl older Ar date (c, r, u modes only) Only include files and directories older than the specified date. This compares ctime entries. .It Fl Fl older-mtime Ar date (c, r, u modes only) Like .Fl Fl older , except it compares mtime entries instead of ctime entries. .It Fl Fl older-than Pa file (c, r, u modes only) Only include files and directories older than the specified file. This compares ctime entries. .It Fl Fl older-mtime-than Pa file (c, r, u modes only) Like .Fl Fl older-than , except it compares mtime entries instead of ctime entries. .It Fl Fl one-file-system (c, r, and u modes) Do not cross mount points. .It Fl Fl options Ar options Select optional behaviors for particular modules. The argument is a text string containing comma-separated keywords and values. These are passed to the modules that handle particular formats to control how those formats will behave. Each option has one of the following forms: .Bl -tag -compact -width indent .It Ar key=value The key will be set to the specified value in every module that supports it. Modules that do not support this key will ignore it. .It Ar key The key will be enabled in every module that supports it. This is equivalent to .Ar key Ns Cm =1 . .It Ar !key The key will be disabled in every module that supports it. .It Ar module:key=value , Ar module:key , Ar module:!key As above, but the corresponding key and value will be provided only to modules whose name matches .Ar module . .El The currently supported modules and keys are: .Bl -tag -compact -width indent .It Cm iso9660:joliet Support Joliet extensions. This is enabled by default, use .Cm !joliet or .Cm iso9660:!joliet to disable. .It Cm iso9660:rockridge Support Rock Ridge extensions. This is enabled by default, use .Cm !rockridge or .Cm iso9660:!rockridge to disable. .It Cm gzip:compression-level A decimal integer from 1 to 9 specifying the gzip compression level. .It Cm gzip:timestamp Store timestamp. This is enabled by default, use .Cm !timestamp or .Cm gzip:!timestamp to disable. .It Cm lrzip:compression Ns = Ns Ar type Use .Ar type as compression method. Supported values are bzip2, gzip, lzo (ultra fast), and zpaq (best, extremely slow). .It Cm lrzip:compression-level A decimal integer from 1 to 9 specifying the lrzip compression level. .It Cm lz4:compression-level A decimal integer from 1 to 9 specifying the lzop compression level. .It Cm lz4:stream-checksum Enable stream checksum. This is by default, use .Cm lz4:!stream-checksum to disable. .It Cm lz4:block-checksum Enable block checksum (Disabled by default). .It Cm lz4:block-size A decimal integer from 4 to 7 specifying the lz4 compression block size (7 is set by default). .It Cm lz4:block-dependence Use the previous block of the block being compressed for a compression dictionary to improve compression ratio. .It Cm lzop:compression-level A decimal integer from 1 to 9 specifying the lzop compression level. .It Cm xz:compression-level A decimal integer from 0 to 9 specifying the xz compression level. .It Cm mtree: Ns Ar keyword The mtree writer module allows you to specify which mtree keywords will be included in the output. Supported keywords include: .Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , .Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , .Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname . The default is equivalent to: .Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . .It Cm mtree:all Enables all of the above keywords. You can also use .Cm mtree:!all to disable all keywords. .It Cm mtree:use-set Enable generation of .Cm /set lines in the output. .It Cm mtree:indent Produce human-readable output by indenting options and splitting lines to fit into 80 columns. .It Cm zip:compression Ns = Ns Ar type Use .Ar type as compression method. Supported values are store (uncompressed) and deflate (gzip algorithm). .It Cm zip:encryption Enable encryption using traditional zip encryption. .It Cm zip:encryption Ns = Ns Ar type Use .Ar type as encryption type. Supported values are zipcrypt (traditional zip encryption), aes128 (WinZip AES-128 encryption) and aes256 (WinZip AES-256 encryption). .It Cm read_concatenated_archives Ignore zeroed blocks in the archive, which occurs when multiple tar archives have been concatenated together. Without this option, only the contents of the first concatenated archive would be read. This option is comparable to the .Fl i , Fl Fl ignore-zeros option of GNU tar. .El If a provided option is not supported by any module, that is a fatal error. .It Fl P , Fl Fl absolute-paths Preserve pathnames. By default, absolute pathnames (those that begin with a / character) have the leading slash removed both when creating archives and extracting from them. Also, .Nm will refuse to extract archive entries whose pathnames contain .Pa .. or whose target directory would be altered by a symlink. This option suppresses these behaviors. .It Fl p , Fl Fl insecure , Fl Fl preserve-permissions (x mode only) Preserve file permissions. -Attempt to restore the full permissions, including owner, file modes, file -flags and ACLs, if available, for each item extracted from the archive. -This is the default, if +Attempt to restore the full permissions, including owner, file modes, ACLs, +extended atributes and extended file flags, if available, for each item +extracted from the archive. This is the default, if .Nm is being run by root and can be overridden by also specifying -.Fl Fl no-same-owner +.Fl Fl no-acls , +.Fl Fl no-fflags , +.Fl Fl no-mac-metadata, +.Fl Fl no-same-owner , +.Fl Fl no-same-permissions and -.Fl Fl no-same-permissions . +.Fl Fl no-xattrs . .It Fl Fl passphrase Ar passphrase The .Pa passphrase is used to extract or create an encrypted archive. Currently, zip is the only supported format that supports encryption. You shouldn't use this option unless you realize how insecure use of this option is. .It Fl Fl posix (c, r, u mode only) Synonym for .Fl Fl format Ar pax .It Fl q , Fl Fl fast-read (x and t mode only) Extract or list only the first archive entry that matches each pattern or filename operand. Exit as soon as each specified pattern or filename has been matched. By default, the archive is always read to the very end, since there can be multiple entries with the same name and, by convention, later entries overwrite earlier entries. This option is provided as a performance optimization. .It Fl S (x mode only) Extract files as sparse files. For every block on disk, check first if it contains only NULL bytes and seek over it otherwise. This works similar to the conv=sparse option of dd. .It Fl s Ar pattern Modify file or archive member names according to .Pa pattern . The pattern has the format .Ar /old/new/ Ns Op ghHprRsS where .Ar old is a basic regular expression, .Ar new is the replacement string of the matched part, and the optional trailing letters modify how the replacement is handled. If .Ar old is not matched, the pattern is skipped. Within .Ar new , ~ is substituted with the match, \e1 to \e9 with the content of the corresponding captured group. The optional trailing g specifies that matching should continue after the matched part and stop on the first unmatched pattern. The optional trailing s specifies that the pattern applies to the value of symbolic links. The optional trailing p specifies that after a successful substitution the original path name and the new path name should be printed to standard error. Optional trailing H, R, or S characters suppress substitutions for hardlink targets, regular filenames, or symlink targets, respectively. Optional trailing h, r, or s characters enable substitutions for hardlink targets, regular filenames, or symlink targets, respectively. The default is .Ar hrs which applies substitutions to all names. In particular, it is never necessary to specify h, r, or s. .It Fl Fl same-owner (x mode only) Extract owner and group IDs. This is the reverse of .Fl Fl no-same-owner and the default behavior if .Nm is run as root. .It Fl Fl strip-components Ar count Remove the specified number of leading path elements. Pathnames with fewer elements will be silently skipped. Note that the pathname is edited after checking inclusion/exclusion patterns but before security checks. .It Fl T Ar filename , Fl Fl files-from Ar filename In x or t mode, .Nm will read the list of names to be extracted from .Pa filename . In c mode, .Nm will read names to be archived from .Pa filename . The special name .Dq -C on a line by itself will cause the current directory to be changed to the directory specified on the following line. Names are terminated by newlines unless .Fl Fl null is specified. Note that .Fl Fl null also disables the special handling of lines containing .Dq -C . Note: If you are generating lists of files using .Xr find 1 , you probably want to use .Fl n as well. .It Fl Fl totals -(c, r, u mode only) +(c, r, u modes only) After archiving all files, print a summary to stderr. .It Fl U , Fl Fl unlink , Fl Fl unlink-first (x mode only) Unlink files before creating them. This can be a minor performance optimization if most files already exist, but can make things slower if most files do not already exist. This flag also causes .Nm to remove intervening directory symlinks instead of reporting an error. See the SECURITY section below for more details. .It Fl Fl uid Ar id Use the provided user id number and ignore the user name from the archive. On create, if .Fl Fl uname is not also specified, the user name will be set to match the user id. .It Fl Fl uname Ar name Use the provided user name. On extract, this overrides the user name in the archive; if the provided user name does not exist on the system, it will be ignored and the user id (from the archive or from the .Fl Fl uid option) will be used instead. On create, this sets the user name that will be stored in the archive; the name is not verified against the system user database. .It Fl Fl use-compress-program Ar program Pipe the input (in x or t mode) or the output (in c mode) through .Pa program instead of using the builtin compression support. .It Fl v , Fl Fl verbose Produce verbose output. In create and extract modes, .Nm will list each file name as it is read from or written to the archive. In list mode, .Nm will produce output similar to that of .Xr ls 1 . An additional .Fl v option will also provide ls-like details in create and extract mode. .It Fl Fl version Print version of .Nm and .Nm libarchive , and exit. .It Fl w , Fl Fl confirmation , Fl Fl interactive Ask for confirmation for every action. .It Fl X Ar filename , Fl Fl exclude-from Ar filename Read a list of exclusion patterns from the specified file. See .Fl Fl exclude for more information about the handling of exclusions. +.It Fl Fl xattrs +(c, r, u, x modes only) +Archive or extract extended attributes. This is the reverse of +.Fl Fl no-xattrs +and the default behavior in c, r, and u modes or if +.Nm +is run in x mode as root. .It Fl y (c mode only) Compress the resulting archive with .Xr bzip2 1 . In extract or list modes, this option is ignored. Note that, unlike other .Nm tar implementations, this implementation recognizes bzip2 compression automatically when reading archives. .It Fl Z , Fl Fl compress , Fl Fl uncompress (c mode only) Compress the resulting archive with .Xr compress 1 . In extract or list modes, this option is ignored. Note that, unlike other .Nm tar implementations, this implementation recognizes compress compression automatically when reading archives. .It Fl z , Fl Fl gunzip , Fl Fl gzip (c mode only) Compress the resulting archive with .Xr gzip 1 . In extract or list modes, this option is ignored. Note that, unlike other .Nm tar implementations, this implementation recognizes gzip compression automatically when reading archives. .El .Sh ENVIRONMENT The following environment variables affect the execution of .Nm : .Bl -tag -width ".Ev BLOCKSIZE" .It Ev TAR_READER_OPTIONS The default options for format readers and compression readers. The .Fl Fl options option overrides this. .It Ev TAR_WRITER_OPTIONS The default options for format writers and compression writers. The .Fl Fl options option overrides this. .It Ev LANG The locale to use. See .Xr environ 7 for more information. .It Ev TAPE The default device. The .Fl f option overrides this. Please see the description of the .Fl f option above for more details. .It Ev TZ The timezone to use when displaying dates. See .Xr environ 7 for more information. .El .Sh EXIT STATUS .Ex -std .Sh EXAMPLES The following creates a new archive called .Ar file.tar.gz that contains two files .Ar source.c and .Ar source.h : .Dl Nm Fl czf Pa file.tar.gz Pa source.c Pa source.h .Pp To view a detailed table of contents for this archive: .Dl Nm Fl tvf Pa file.tar.gz .Pp To extract all entries from the archive on the default tape drive: .Dl Nm Fl x .Pp To examine the contents of an ISO 9660 cdrom image: .Dl Nm Fl tf Pa image.iso .Pp To move file hierarchies, invoke .Nm as .Dl Nm Fl cf Pa - Fl C Pa srcdir\ . | Nm Fl xpf Pa - Fl C Pa destdir or more traditionally .Dl cd srcdir \&; Nm Fl cf Pa -\ . | ( cd destdir \&; Nm Fl xpf Pa - ) .Pp In create mode, the list of files and directories to be archived can also include directory change instructions of the form .Cm -C Ns Pa foo/baz and archive inclusions of the form .Cm @ Ns Pa archive-file . For example, the command line .Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm -C Ns Pa /tmp Pa foo2 will create a new archive .Pa new.tar . .Nm will read the file .Pa foo1 from the current directory and add it to the output archive. It will then read each entry from .Pa old.tgz and add those entries to the output archive. Finally, it will switch to the .Pa /tmp directory and add .Pa foo2 to the output archive. .Pp An input file in .Xr mtree 5 format can be used to create an output archive with arbitrary ownership, permissions, or names that differ from existing data on disk: .Pp .Bd -literal -offset indent $ cat input.mtree #mtree usr/bin uid=0 gid=0 mode=0755 type=dir usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls $ tar -cvf output.tar @input.mtree .Ed .Pp The .Fl Fl newer and .Fl Fl newer-mtime switches accept a variety of common date and time specifications, including .Dq 12 Mar 2005 7:14:29pm , .Dq 2005-03-12 19:14 , .Dq 5 minutes ago , and .Dq 19:14 PST May 1 . .Pp The .Fl Fl options argument can be used to control various details of archive generation or reading. For example, you can generate mtree output which only contains .Cm type , Cm time , and .Cm uid keywords: .Dl Nm Fl cf Pa file.tar Fl Fl format=mtree Fl Fl options='!all,type,time,uid' Pa dir or you can set the compression level used by gzip or xz compression: .Dl Nm Fl czf Pa file.tar Fl Fl options='compression-level=9' . For more details, see the explanation of the .Fn archive_read_set_options and .Fn archive_write_set_options API calls that are described in .Xr archive_read 3 and .Xr archive_write 3 . .Sh COMPATIBILITY The bundled-arguments format is supported for compatibility with historic implementations. It consists of an initial word (with no leading - character) in which each character indicates an option. Arguments follow as separate words. The order of the arguments must match the order of the corresponding characters in the bundled command word. For example, .Dl Nm Cm tbf 32 Pa file.tar specifies three flags .Cm t , .Cm b , and .Cm f . The .Cm b and .Cm f flags both require arguments, so there must be two additional items on the command line. The .Ar 32 is the argument to the .Cm b flag, and .Ar file.tar is the argument to the .Cm f flag. .Pp The mode options c, r, t, u, and x and the options b, f, l, m, o, v, and w comply with SUSv2. .Pp For maximum portability, scripts that invoke .Nm tar should use the bundled-argument format above, should limit themselves to the .Cm c , .Cm t , and .Cm x modes, and the .Cm b , .Cm f , .Cm m , .Cm v , and .Cm w options. .Pp Additional long options are provided to improve compatibility with other tar implementations. .Sh SECURITY Certain security issues are common to many archiving programs, including .Nm . In particular, carefully-crafted archives can request that .Nm extract files to locations outside of the target directory. This can potentially be used to cause unwitting users to overwrite files they did not intend to overwrite. If the archive is being extracted by the superuser, any file on the system can potentially be overwritten. There are three ways this can happen. Although .Nm has mechanisms to protect against each one, savvy users should be aware of the implications: .Bl -bullet -width indent .It Archive entries can have absolute pathnames. By default, .Nm removes the leading .Pa / character from filenames before restoring them to guard against this problem. .It Archive entries can have pathnames that include .Pa .. components. By default, .Nm will not extract files containing .Pa .. components in their pathname. .It Archive entries can exploit symbolic links to restore files to other directories. An archive can restore a symbolic link to another directory, then use that link to restore a file into that directory. To guard against this, .Nm checks each extracted path for symlinks. If the final path element is a symlink, it will be removed and replaced with the archive entry. If .Fl U is specified, any intermediate symlink will also be unconditionally removed. If neither .Fl U nor .Fl P is specified, .Nm will refuse to extract the entry. .El To protect yourself, you should be wary of any archives that come from untrusted sources. You should examine the contents of an archive with .Dl Nm Fl tf Pa filename before extraction. You should use the .Fl k option to ensure that .Nm will not overwrite any existing files or the .Fl U option to remove any pre-existing files. You should generally not extract archives while running with super-user privileges. Note that the .Fl P option to .Nm disables the security checks above and allows you to extract an archive while preserving any absolute pathnames, .Pa .. components, or symlinks to other directories. .Sh SEE ALSO .Xr bzip2 1 , .Xr compress 1 , .Xr cpio 1 , .Xr gzip 1 , .Xr mt 1 , .Xr pax 1 , .Xr shar 1 , .Xr xz 1 , .Xr libarchive 3 , .Xr libarchive-formats 5 , .Xr tar 5 .Sh STANDARDS There is no current POSIX standard for the tar command; it appeared in .St -p1003.1-96 but was dropped from .St -p1003.1-2001 . The options supported by this implementation were developed by surveying a number of existing tar implementations as well as the old POSIX specification for tar and the current POSIX specification for pax. .Pp The ustar and pax interchange file formats are defined by .St -p1003.1-2001 for the pax command. .Sh HISTORY A .Nm tar command appeared in Seventh Edition Unix, which was released in January, 1979. There have been numerous other implementations, many of which extended the file format. John Gilmore's .Nm pdtar public-domain implementation (circa November, 1987) was quite influential, and formed the basis of GNU tar. GNU tar was included as the standard system tar in .Fx beginning with .Fx 1.0 . .Pp This is a complete re-implementation based on the .Xr libarchive 3 library. It was first released with .Fx 5.4 in May, 2005. .Sh BUGS This program follows .St -p1003.1-96 for the definition of the .Fl l option. Note that GNU tar prior to version 1.15 treated .Fl l as a synonym for the .Fl Fl one-file-system option. .Pp The .Fl C Pa dir option may differ from historic implementations. .Pp All archive output is written in correctly-sized blocks, even if the output is being compressed. Whether or not the last output block is padded to a full block size varies depending on the format and the output device. For tar and cpio formats, the last block of output is padded to a full block size if the output is being written to standard output or to a character or block device such as a tape drive. If the output is being written to a regular file, the last block will not be padded. Many compressors, including .Xr gzip 1 and .Xr bzip2 1 , complain about the null padding when decompressing an archive created by .Nm , although they still extract it correctly. .Pp The compression and decompression is implemented internally, so there may be insignificant differences between the compressed output generated by .Dl Nm Fl czf Pa - file and that generated by .Dl Nm Fl cf Pa - file | Nm gzip .Pp The default should be to read and write archives to the standard I/O paths, but tradition (and POSIX) dictates otherwise. .Pp The .Cm r and .Cm u modes require that the archive be uncompressed and located in a regular file on disk. Other archives can be modified using .Cm c mode with the .Pa @archive-file extension. .Pp To archive a file called .Pa @foo or .Pa -foo you must specify it as .Pa ./@foo or .Pa ./-foo , respectively. .Pp In create mode, a leading .Pa ./ is always removed. A leading .Pa / is stripped unless the .Fl P option is specified. .Pp There needs to be better support for file selection on both create and extract. .Pp There is not yet any support for multi-volume archives. .Pp Converting between dissimilar archive formats (such as tar and cpio) using the .Cm @ Ns Pa - convention can cause hard link information to be lost. (This is a consequence of the incompatible ways that different archive formats store hardlink information.) Index: vendor/libarchive/dist/tar/bsdtar.c =================================================================== --- vendor/libarchive/dist/tar/bsdtar.c (revision 314564) +++ vendor/libarchive/dist/tar/bsdtar.c (revision 314565) @@ -1,931 +1,986 @@ /*- * 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. */ #include "bsdtar_platform.h" __FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.93 2008/11/08 04:43:24 kientzle Exp $"); #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "bsdtar.h" #include "err.h" /* * Per POSIX.1-1988, tar defaults to reading/writing archives to/from * the default tape device for the system. Pick something reasonable here. */ #ifdef __linux #define _PATH_DEFTAPE "/dev/st0" #endif #if defined(_WIN32) && !defined(__CYGWIN__) #define _PATH_DEFTAPE "\\\\.\\tape0" #endif #if defined(__APPLE__) #undef _PATH_DEFTAPE #define _PATH_DEFTAPE "-" /* Mac OS has no tape support, default to stdio. */ #endif #ifndef _PATH_DEFTAPE #define _PATH_DEFTAPE "/dev/tape" #endif #ifdef __MINGW32__ int _CRT_glob = 0; /* Disable broken CRT globbing. */ #endif #if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) static volatile int siginfo_occurred; static void siginfo_handler(int sig) { (void)sig; /* UNUSED */ siginfo_occurred = 1; } int need_report(void) { int r = siginfo_occurred; siginfo_occurred = 0; return (r); } #else int need_report(void) { return (0); } #endif static void long_help(void); static void only_mode(struct bsdtar *, const char *opt, const char *valid); static void set_mode(struct bsdtar *, char opt); static void version(void); /* A basic set of security flags to request from libarchive. */ #define SECURITY \ (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ | ARCHIVE_EXTRACT_SECURE_NODOTDOT) int main(int argc, char **argv) { struct bsdtar *bsdtar, bsdtar_storage; int opt, t; char compression, compression2; const char *compression_name, *compression2_name; const char *compress_program; - char option_a, option_o; char possible_help_request; char buff[16]; /* * Use a pointer for consistency, but stack-allocated storage * for ease of cleanup. */ bsdtar = &bsdtar_storage; memset(bsdtar, 0, sizeof(*bsdtar)); bsdtar->fd = -1; /* Mark as "unused" */ bsdtar->gid = -1; bsdtar->uid = -1; - option_a = option_o = 0; + bsdtar->flags = 0; compression = compression2 = '\0'; compression_name = compression2_name = NULL; compress_program = NULL; #if defined(HAVE_SIGACTION) { /* Set up signal handling. */ struct sigaction sa; sa.sa_handler = siginfo_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; #ifdef SIGINFO if (sigaction(SIGINFO, &sa, NULL)) lafe_errc(1, errno, "sigaction(SIGINFO) failed"); #endif #ifdef SIGUSR1 /* ... and treat SIGUSR1 the same way as SIGINFO. */ if (sigaction(SIGUSR1, &sa, NULL)) lafe_errc(1, errno, "sigaction(SIGUSR1) failed"); #endif #ifdef SIGPIPE /* Ignore SIGPIPE signals. */ sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); #endif } #endif /* Set lafe_progname before calling lafe_warnc. */ lafe_setprogname(*argv, "bsdtar"); #if HAVE_SETLOCALE if (setlocale(LC_ALL, "") == NULL) lafe_warnc(0, "Failed to set default locale"); #endif #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); #endif possible_help_request = 0; /* Look up uid of current user for future reference */ bsdtar->user_uid = geteuid(); /* Default: open tape drive. */ bsdtar->filename = getenv("TAPE"); if (bsdtar->filename == NULL) bsdtar->filename = _PATH_DEFTAPE; /* Default block size settings. */ bsdtar->bytes_per_block = DEFAULT_BYTES_PER_BLOCK; /* Allow library to default this unless user specifies -b. */ bsdtar->bytes_in_last_block = -1; /* Default: preserve mod time on extract */ bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME; /* Default: Perform basic security checks. */ bsdtar->extract_flags |= SECURITY; #ifndef _WIN32 /* On POSIX systems, assume --same-owner and -p when run by * the root user. This doesn't make any sense on Windows. */ if (bsdtar->user_uid == 0) { /* --same-owner */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; /* -p */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; } #endif /* * Enable Mac OS "copyfile()" extension by default. * This has no effect on other platforms. */ bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE; #ifdef COPYFILE_DISABLE_VAR if (getenv(COPYFILE_DISABLE_VAR)) bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE; #endif +#if defined(__APPLE__) + /* + * On Mac OS ACLs are archived with copyfile() (--mac-metadata) + * Translation to NFSv4 ACLs has to be requested explicitly with --acls + */ + bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL; +#endif + bsdtar->matching = archive_match_new(); if (bsdtar->matching == NULL) lafe_errc(1, errno, "Out of memory"); bsdtar->cset = cset_new(); if (bsdtar->cset == NULL) lafe_errc(1, errno, "Out of memory"); bsdtar->argv = argv; bsdtar->argc = argc; /* * Comments following each option indicate where that option * originated: SUSv2, POSIX, GNU tar, star, etc. If there's * no such comment, then I don't know of anyone else who * implements that option. */ while ((opt = bsdtar_getopt(bsdtar)) != -1) { switch (opt) { case 'a': /* GNU tar */ - option_a = 1; /* Record it and resolve it later. */ + bsdtar->flags |= OPTFLAG_AUTO_COMPRESS; break; + case OPTION_ACLS: /* GNU tar */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; + bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_ACL; + bsdtar->flags |= OPTFLAG_ACLS; + break; case 'B': /* GNU tar */ /* libarchive doesn't need this; just ignore it. */ break; case 'b': /* SUSv2 */ t = atoi(bsdtar->argument); if (t <= 0 || t > 8192) lafe_errc(1, 0, "Argument to -b is out of range (1..8192)"); bsdtar->bytes_per_block = 512 * t; /* Explicit -b forces last block size. */ bsdtar->bytes_in_last_block = bsdtar->bytes_per_block; break; case OPTION_B64ENCODE: if (compression2 != '\0') lafe_errc(1, 0, "Can't specify both --uuencode and " "--b64encode"); compression2 = opt; compression2_name = "b64encode"; break; case 'C': /* GNU tar */ if (strlen(bsdtar->argument) == 0) lafe_errc(1, 0, "Meaningless option: -C ''"); set_chdir(bsdtar, bsdtar->argument); break; case 'c': /* SUSv2 */ set_mode(bsdtar, opt); break; case OPTION_CHECK_LINKS: /* GNU tar */ - bsdtar->option_warn_links = 1; + bsdtar->flags |= OPTFLAG_WARN_LINKS; break; case OPTION_CHROOT: /* NetBSD */ - bsdtar->option_chroot = 1; + bsdtar->flags |= OPTFLAG_CHROOT; break; case OPTION_CLEAR_NOCHANGE_FFLAGS: bsdtar->extract_flags |= ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS; break; - case OPTION_DISABLE_COPYFILE: /* Mac OS X */ - bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE; - break; case OPTION_EXCLUDE: /* GNU tar */ if (archive_match_exclude_pattern( bsdtar->matching, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Couldn't exclude %s\n", bsdtar->argument); break; + case OPTION_FFLAGS: + bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; + bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_FFLAGS; + bsdtar->flags |= OPTFLAG_FFLAGS; + break; case OPTION_FORMAT: /* GNU tar, others */ cset_set_format(bsdtar->cset, bsdtar->argument); break; case 'f': /* SUSv2 */ bsdtar->filename = bsdtar->argument; break; case OPTION_GID: /* cpio */ t = atoi(bsdtar->argument); if (t < 0) lafe_errc(1, 0, "Argument to --gid must be positive"); bsdtar->gid = t; break; case OPTION_GNAME: /* cpio */ bsdtar->gname = bsdtar->argument; break; case OPTION_GRZIP: if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; compression_name = "grzip"; break; case 'H': /* BSD convention */ bsdtar->symlink_mode = 'H'; break; case 'h': /* Linux Standards Base, gtar; synonym for -L */ bsdtar->symlink_mode = 'L'; /* Hack: -h by itself is the "help" command. */ possible_help_request = 1; break; case OPTION_HELP: /* GNU tar, others */ long_help(); exit(0); break; case OPTION_HFS_COMPRESSION: /* Mac OS X v10.6 or later */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED; break; case OPTION_IGNORE_ZEROS: - bsdtar->option_ignore_zeros = 1; + bsdtar->flags |= OPTFLAG_IGNORE_ZEROS; break; case 'I': /* GNU tar */ /* * TODO: Allow 'names' to come from an archive, * not just a text file. Design a good UI for * allowing names and mode/owner to be read * from an archive, with contents coming from * disk. This can be used to "refresh" an * archive or to design archives with special * permissions without having to create those * permissions on disk. */ bsdtar->names_from_file = bsdtar->argument; break; case OPTION_INCLUDE: /* * No one else has the @archive extension, so * no one else needs this to filter entries * when transforming archives. */ if (archive_match_include_pattern(bsdtar->matching, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Failed to add %s to inclusion list", bsdtar->argument); break; case 'j': /* GNU tar */ if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; compression_name = "bzip2"; break; case 'J': /* GNU tar 1.21 and later */ if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; compression_name = "xz"; break; case 'k': /* GNU tar */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE; break; case OPTION_KEEP_NEWER_FILES: /* GNU tar */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; break; case 'L': /* BSD convention */ bsdtar->symlink_mode = 'L'; break; case 'l': /* SUSv2 and GNU tar beginning with 1.16 */ /* GNU tar 1.13 used -l for --one-file-system */ - bsdtar->option_warn_links = 1; + bsdtar->flags |= OPTFLAG_WARN_LINKS; break; case OPTION_LRZIP: case OPTION_LZ4: case OPTION_LZIP: /* GNU tar beginning with 1.23 */ case OPTION_LZMA: /* GNU tar beginning with 1.20 */ case OPTION_LZOP: /* GNU tar beginning with 1.21 */ if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; switch (opt) { case OPTION_LRZIP: compression_name = "lrzip"; break; case OPTION_LZ4: compression_name = "lz4"; break; case OPTION_LZIP: compression_name = "lzip"; break; case OPTION_LZMA: compression_name = "lzma"; break; case OPTION_LZOP: compression_name = "lzop"; break; } break; case 'm': /* SUSv2 */ bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; break; + case OPTION_MAC_METADATA: /* Mac OS X */ + bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE; + bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; + bsdtar->flags |= OPTFLAG_MAC_METADATA; + break; case 'n': /* GNU tar */ - bsdtar->option_no_subdirs = 1; + bsdtar->flags |= OPTFLAG_NO_SUBDIRS; break; /* * Selecting files by time: * --newer-?time='date' Only files newer than 'date' * --newer-?time-than='file' Only files newer than time * on specified file (useful for incremental backups) */ case OPTION_NEWER_CTIME: /* GNU tar */ if (archive_match_include_date(bsdtar->matching, ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_NEWER_CTIME_THAN: if (archive_match_include_file_time(bsdtar->matching, ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_NEWER_MTIME: /* GNU tar */ if (archive_match_include_date(bsdtar->matching, ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_NEWER_MTIME_THAN: if (archive_match_include_file_time(bsdtar->matching, ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_NODUMP: /* star */ bsdtar->readdisk_flags |= ARCHIVE_READDISK_HONOR_NODUMP; break; case OPTION_NOPRESERVE_HFS_COMPRESSION: /* Mac OS X v10.6 or later */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_HFS_COMPRESSION; break; + case OPTION_NO_ACLS: /* GNU tar */ + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL; + bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL; + bsdtar->flags |= OPTFLAG_NO_ACLS; + break; + case OPTION_NO_FFLAGS: + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; + bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_FFLAGS; + bsdtar->flags |= OPTFLAG_NO_FFLAGS; + break; + case OPTION_NO_MAC_METADATA: /* Mac OS X */ + bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE; + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA; + bsdtar->flags |= OPTFLAG_NO_MAC_METADATA; + break; case OPTION_NO_SAME_OWNER: /* GNU tar */ bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; break; case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */ bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM; bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL; bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA; break; - case OPTION_NO_XATTR: /* Issue #131 */ + case OPTION_NO_XATTRS: /* GNU tar */ bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_XATTR; + bsdtar->flags |= OPTFLAG_NO_XATTRS; break; case OPTION_NULL: /* GNU tar */ - bsdtar->option_null++; + bsdtar->flags |= OPTFLAG_NULL; break; case OPTION_NUMERIC_OWNER: /* GNU tar */ bsdtar->uname = ""; bsdtar->gname = ""; - bsdtar->option_numeric_owner++; + bsdtar->flags |= OPTFLAG_NUMERIC_OWNER; break; case 'O': /* GNU tar */ - bsdtar->option_stdout = 1; + bsdtar->flags |= OPTFLAG_STDOUT; break; case 'o': /* SUSv2 and GNU conflict here, but not fatally */ - option_o = 1; /* Record it and resolve it later. */ + bsdtar->flags |= OPTFLAG_O; break; /* * Selecting files by time: * --older-?time='date' Only files older than 'date' * --older-?time-than='file' Only files older than time * on specified file */ case OPTION_OLDER_CTIME: if (archive_match_include_date(bsdtar->matching, ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_OLDER_CTIME_THAN: if (archive_match_include_file_time(bsdtar->matching, ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_OLDER_MTIME: if (archive_match_include_date(bsdtar->matching, ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_OLDER_MTIME_THAN: if (archive_match_include_file_time(bsdtar->matching, ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER, bsdtar->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case OPTION_ONE_FILE_SYSTEM: /* GNU tar */ bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS; break; case OPTION_OPTIONS: bsdtar->option_options = bsdtar->argument; break; #if 0 /* * The common BSD -P option is not necessary, since * our default is to archive symlinks, not follow * them. This is convenient, as -P conflicts with GNU * tar anyway. */ case 'P': /* BSD convention */ /* Default behavior, no option necessary. */ break; #endif case 'P': /* GNU tar */ bsdtar->extract_flags &= ~SECURITY; - bsdtar->option_absolute_paths = 1; + bsdtar->flags |= OPTFLAG_ABSOLUTE_PATHS; break; case 'p': /* GNU tar, star */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; break; case OPTION_PASSPHRASE: bsdtar->passphrase = bsdtar->argument; break; case OPTION_POSIX: /* GNU tar */ cset_set_format(bsdtar->cset, "pax"); break; case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */ - bsdtar->option_fast_read = 1; + bsdtar->flags |= OPTFLAG_FAST_READ; break; case 'r': /* SUSv2 */ set_mode(bsdtar, opt); break; case 'S': /* NetBSD pax-as-tar */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE; break; case 's': /* NetBSD pax-as-tar */ #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) add_substitution(bsdtar, bsdtar->argument); #else lafe_warnc(0, "-s is not supported by this version of bsdtar"); usage(); #endif break; case OPTION_SAME_OWNER: /* GNU tar */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; break; case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */ errno = 0; bsdtar->strip_components = strtol(bsdtar->argument, NULL, 0); if (errno) lafe_errc(1, 0, "Invalid --strip-components argument: %s", bsdtar->argument); break; case 'T': /* GNU tar */ bsdtar->names_from_file = bsdtar->argument; break; case 't': /* SUSv2 */ set_mode(bsdtar, opt); bsdtar->verbose++; break; case OPTION_TOTALS: /* GNU tar */ - bsdtar->option_totals++; + bsdtar->flags |= OPTFLAG_TOTALS; break; case 'U': /* GNU tar */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK; - bsdtar->option_unlink_first = 1; + bsdtar->flags |= OPTFLAG_UNLINK_FIRST; break; case 'u': /* SUSv2 */ set_mode(bsdtar, opt); break; case OPTION_UID: /* cpio */ t = atoi(bsdtar->argument); if (t < 0) lafe_errc(1, 0, "Argument to --uid must be positive"); bsdtar->uid = t; break; case OPTION_UNAME: /* cpio */ bsdtar->uname = bsdtar->argument; break; case OPTION_UUENCODE: if (compression2 != '\0') lafe_errc(1, 0, "Can't specify both --uuencode and " "--b64encode"); compression2 = opt; compression2_name = "uuencode"; break; case 'v': /* SUSv2 */ bsdtar->verbose++; break; case OPTION_VERSION: /* GNU convention */ version(); break; #if 0 /* * The -W longopt feature is handled inside of * bsdtar_getopt(), so -W is not available here. */ case 'W': /* Obscure GNU convention. */ break; #endif case 'w': /* SUSv2 */ - bsdtar->option_interactive = 1; + bsdtar->flags |= OPTFLAG_INTERACTIVE; break; case 'X': /* GNU tar */ if (archive_match_exclude_pattern_from_file( bsdtar->matching, bsdtar->argument, 0) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); break; case 'x': /* SUSv2 */ set_mode(bsdtar, opt); break; + case OPTION_XATTRS: /* GNU tar */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; + bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_XATTR; + bsdtar->flags |= OPTFLAG_XATTRS; + break; case 'y': /* FreeBSD version of GNU tar */ if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; compression_name = "bzip2"; break; case 'Z': /* GNU tar */ if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; compression_name = "compress"; break; case 'z': /* GNU tar, star, many others */ if (compression != '\0') lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, compression); compression = opt; compression_name = "gzip"; break; case OPTION_USE_COMPRESS_PROGRAM: compress_program = bsdtar->argument; break; default: usage(); } } /* * Sanity-check options. */ /* If no "real" mode was specified, treat -h as --help. */ if ((bsdtar->mode == '\0') && possible_help_request) { long_help(); exit(0); } /* Otherwise, a mode is required. */ if (bsdtar->mode == '\0') lafe_errc(1, 0, "Must specify one of -c, -r, -t, -u, -x"); /* Check boolean options only permitted in certain modes. */ - if (option_a) + if (bsdtar->flags & OPTFLAG_AUTO_COMPRESS) only_mode(bsdtar, "-a", "c"); if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) only_mode(bsdtar, "--one-file-system", "cru"); - if (bsdtar->option_fast_read) + if (bsdtar->flags & OPTFLAG_FAST_READ) only_mode(bsdtar, "--fast-read", "xt"); if (bsdtar->extract_flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) only_mode(bsdtar, "--hfsCompression", "x"); if (bsdtar->extract_flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) only_mode(bsdtar, "--nopreserveHFSCompression", "x"); if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP) only_mode(bsdtar, "--nodump", "cru"); - if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_XATTR) - only_mode(bsdtar, "--no-xattr", "crux"); - if (option_o > 0) { + if (bsdtar->flags & OPTFLAG_ACLS) + only_mode(bsdtar, "--acls", "crux"); + if (bsdtar->flags & OPTFLAG_NO_ACLS) + only_mode(bsdtar, "--no-acls", "crux"); + if (bsdtar->flags & OPTFLAG_XATTRS) + only_mode(bsdtar, "--xattrs", "crux"); + if (bsdtar->flags & OPTFLAG_NO_XATTRS) + only_mode(bsdtar, "--no-xattrs", "crux"); + if (bsdtar->flags & OPTFLAG_FFLAGS) + only_mode(bsdtar, "--fflags", "crux"); + if (bsdtar->flags & OPTFLAG_NO_FFLAGS) + only_mode(bsdtar, "--no-fflags", "crux"); + if (bsdtar->flags & OPTFLAG_MAC_METADATA) + only_mode(bsdtar, "--mac-metadata", "crux"); + if (bsdtar->flags & OPTFLAG_NO_MAC_METADATA) + only_mode(bsdtar, "--no-mac-metadata", "crux"); + if (bsdtar->flags & OPTFLAG_O) { switch (bsdtar->mode) { case 'c': /* * In GNU tar, -o means "old format." The * "ustar" format is the closest thing * supported by libarchive. */ cset_set_format(bsdtar->cset, "ustar"); /* TODO: bsdtar->create_format = "v7"; */ break; case 'x': /* POSIX-compatible behavior. */ - bsdtar->option_no_owner = 1; + bsdtar->flags |= OPTFLAG_NO_OWNER; bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; break; default: only_mode(bsdtar, "-o", "xc"); break; } } - if (bsdtar->option_no_subdirs) + if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) only_mode(bsdtar, "-n", "cru"); - if (bsdtar->option_stdout) + if (bsdtar->flags & OPTFLAG_STDOUT) only_mode(bsdtar, "-O", "xt"); - if (bsdtar->option_unlink_first) + if (bsdtar->flags & OPTFLAG_UNLINK_FIRST) only_mode(bsdtar, "-U", "x"); - if (bsdtar->option_warn_links) + if (bsdtar->flags & OPTFLAG_WARN_LINKS) only_mode(bsdtar, "--check-links", "cr"); - if (option_a && cset_auto_compress(bsdtar->cset, bsdtar->filename)) { + if ((bsdtar->flags & OPTFLAG_AUTO_COMPRESS) && + cset_auto_compress(bsdtar->cset, bsdtar->filename)) { /* Ignore specified compressions if auto-compress works. */ compression = '\0'; compression2 = '\0'; } /* Check other parameters only permitted in certain modes. */ if (compress_program != NULL) { only_mode(bsdtar, "--use-compress-program", "cxt"); cset_add_filter_program(bsdtar->cset, compress_program); /* Ignore specified compressions. */ compression = '\0'; compression2 = '\0'; } if (compression != '\0') { switch (compression) { case 'J': case 'j': case 'y': case 'Z': case 'z': strcpy(buff, "-?"); buff[1] = compression; break; default: strcpy(buff, "--"); strcat(buff, compression_name); break; } only_mode(bsdtar, buff, "cxt"); cset_add_filter(bsdtar->cset, compression_name); } if (compression2 != '\0') { strcpy(buff, "--"); strcat(buff, compression2_name); only_mode(bsdtar, buff, "cxt"); cset_add_filter(bsdtar->cset, compression2_name); } if (cset_get_format(bsdtar->cset) != NULL) only_mode(bsdtar, "--format", "cru"); if (bsdtar->symlink_mode != '\0') { strcpy(buff, "-?"); buff[1] = bsdtar->symlink_mode; only_mode(bsdtar, buff, "cru"); } /* Filename "-" implies stdio. */ if (strcmp(bsdtar->filename, "-") == 0) bsdtar->filename = NULL; switch(bsdtar->mode) { case 'c': tar_mode_c(bsdtar); break; case 'r': tar_mode_r(bsdtar); break; case 't': tar_mode_t(bsdtar); break; case 'u': tar_mode_u(bsdtar); break; case 'x': tar_mode_x(bsdtar); break; } archive_match_free(bsdtar->matching); #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) cleanup_substitution(bsdtar); #endif cset_free(bsdtar->cset); passphrase_free(bsdtar->ppbuff); if (bsdtar->return_value != 0) lafe_warnc(0, "Error exit delayed from previous errors."); return (bsdtar->return_value); } static void set_mode(struct bsdtar *bsdtar, char opt) { if (bsdtar->mode != '\0' && bsdtar->mode != opt) lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->mode); bsdtar->mode = opt; } /* * Verify that the mode is correct. */ static void only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) { if (strchr(valid_modes, bsdtar->mode) == NULL) lafe_errc(1, 0, "Option %s is not permitted in mode -%c", opt, bsdtar->mode); } void usage(void) { const char *p; p = lafe_getprogname(); fprintf(stderr, "Usage:\n"); fprintf(stderr, " List: %s -tf \n", p); fprintf(stderr, " Extract: %s -xf \n", p); fprintf(stderr, " Create: %s -cf [filenames...]\n", p); fprintf(stderr, " Help: %s --help\n", p); exit(1); } static void version(void) { printf("bsdtar %s - %s\n", BSDTAR_VERSION_STRING, archive_version_details()); exit(0); } static const char *long_help_msg = "First option must be a mode specifier:\n" " -c Create -r Add/Replace -t List -u Update -x Extract\n" "Common Options:\n" " -b # Use # 512-byte records per I/O block\n" " -f Location of archive (default " _PATH_DEFTAPE ")\n" " -v Verbose\n" " -w Interactive\n" "Create: %p -c [options] [ | | @ | -C ]\n" " , add these items to archive\n" " -z, -j, -J, --lzma Compress archive with gzip/bzip2/xz/lzma\n" " --format {ustar|pax|cpio|shar} Select archive format\n" " --exclude Skip files that match pattern\n" " -C Change to before processing remaining files\n" " @ Add entries from to output\n" "List: %p -t [options] []\n" " If specified, list only entries that match\n" "Extract: %p -x [options] []\n" " If specified, extract only entries that match\n" " -k Keep (don't overwrite) existing files\n" " -m Don't restore modification times\n" " -O Write entries to stdout, don't restore to disk\n" " -p Restore permissions (including ACLs, owner, file flags)\n"; /* * Note that the word 'bsdtar' will always appear in the first line * of output. * * In particular, /bin/sh scripts that need to test for the presence * of bsdtar can use the following template: * * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \ * echo bsdtar; else echo not bsdtar; fi */ static void long_help(void) { const char *prog; const char *p; prog = lafe_getprogname(); fflush(stderr); p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : ""; printf("%s%s: manipulate archive files\n", prog, p); for (p = long_help_msg; *p != '\0'; p++) { if (*p == '%') { if (p[1] == 'p') { fputs(prog, stdout); p++; } else putchar('%'); } else putchar(*p); } version(); } Index: vendor/libarchive/dist/tar/bsdtar.h =================================================================== --- vendor/libarchive/dist/tar/bsdtar.h (revision 314564) +++ vendor/libarchive/dist/tar/bsdtar.h (revision 314565) @@ -1,205 +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. * * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.37 2008/12/06 07:37:14 kientzle Exp $ */ #include "bsdtar_platform.h" #include #define DEFAULT_BYTES_PER_BLOCK (20*512) #define ENV_READER_OPTIONS "TAR_READER_OPTIONS" #define ENV_WRITER_OPTIONS "TAR_WRITER_OPTIONS" #define IGNORE_WRONG_MODULE_NAME "__ignore_wrong_module_name__," struct creation_set; /* * The internal state for the "bsdtar" program. * * Keeping all of the state in a structure like this simplifies memory * leak testing (at exit, anything left on the heap is suspect). A * pointer to this structure is passed to most bsdtar internal * functions. */ struct bsdtar { /* Options */ const char *filename; /* -f filename */ char *pending_chdir; /* -C dir */ const char *names_from_file; /* -T file */ int bytes_per_block; /* -b block_size */ int bytes_in_last_block; /* See -b handling. */ int verbose; /* -v */ + unsigned int flags; /* Bitfield of boolean options */ int extract_flags; /* Flags for extract operation */ int readdisk_flags; /* Flags for read disk operation */ int strip_components; /* Remove this many leading dirs */ int gid; /* --gid */ const char *gname; /* --gname */ int uid; /* --uid */ const char *uname; /* --uname */ const char *passphrase; /* --passphrase */ char mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */ char symlink_mode; /* H or L, per BSD conventions */ - char option_absolute_paths; /* -P */ - char option_chroot; /* --chroot */ - char option_fast_read; /* --fast-read */ const char *option_options; /* --options */ - char option_ignore_zeros; /* --ignore-zeros */ - char option_interactive; /* -w */ - char option_no_owner; /* -o */ - char option_no_subdirs; /* -n */ - char option_numeric_owner; /* --numeric-owner */ - char option_null; /* --null */ - char option_stdout; /* -O */ - char option_totals; /* --totals */ - char option_unlink_first; /* -U */ - char option_warn_links; /* --check-links */ char day_first; /* show day before month in -tv output */ struct creation_set *cset; /* Option parser state */ int getopt_state; char *getopt_word; /* If >= 0, then close this when done. */ int fd; /* Miscellaneous state information */ int argc; char **argv; const char *argument; size_t gs_width; /* For 'list_item' in read.c */ size_t u_width; /* for 'list_item' in read.c */ uid_t user_uid; /* UID running this program */ int return_value; /* Value returned by main() */ char warned_lead_slash; /* Already displayed warning */ char next_line_is_dir; /* Used for -C parsing in -cT */ /* * Data for various subsystems. Full definitions are located in * the file where they are used. */ struct archive *diskreader; /* for write.c */ struct archive_entry_linkresolver *resolver; /* for write.c */ struct archive_dir *archive_dir; /* for write.c */ struct name_cache *gname_cache; /* for write.c */ char *buff; /* for write.c */ size_t buff_size; /* for write.c */ int first_fs; /* for write.c */ struct archive *matching; /* for matching.c */ struct security *security; /* for read.c */ struct name_cache *uname_cache; /* for write.c */ struct siginfo_data *siginfo; /* for siginfo.c */ struct substitution *substitution; /* for subst.c */ char *ppbuff; /* for util.c */ }; +/* Options for flags bitfield */ +#define OPTFLAG_AUTO_COMPRESS (0x00000001) /* -a */ +#define OPTFLAG_ABSOLUTE_PATHS (0x00000002) /* -P */ +#define OPTFLAG_CHROOT (0x00000004) /* --chroot */ +#define OPTFLAG_FAST_READ (0x00000008) /* --fast-read */ +#define OPTFLAG_IGNORE_ZEROS (0x00000010) /* --ignore-zeros */ +#define OPTFLAG_INTERACTIVE (0x00000020) /* -w */ +#define OPTFLAG_NO_OWNER (0x00000040) /* -o */ +#define OPTFLAG_NO_SUBDIRS (0x00000080) /* -n */ +#define OPTFLAG_NULL (0x00000100) /* --null */ +#define OPTFLAG_NUMERIC_OWNER (0x00000200) /* --numeric-owner */ +#define OPTFLAG_O (0x00000400) /* -o */ +#define OPTFLAG_STDOUT (0x00000800) /* -O */ +#define OPTFLAG_TOTALS (0x00001000) /* --totals */ +#define OPTFLAG_UNLINK_FIRST (0x00002000) /* -U */ +#define OPTFLAG_WARN_LINKS (0x00004000) /* --check-links */ +#define OPTFLAG_NO_XATTRS (0x00008000) /* --no-xattrs */ +#define OPTFLAG_XATTRS (0x00010000) /* --xattrs */ +#define OPTFLAG_NO_ACLS (0x00020000) /* --no-acls */ +#define OPTFLAG_ACLS (0x00040000) /* --acls */ +#define OPTFLAG_NO_FFLAGS (0x00080000) /* --no-fflags */ +#define OPTFLAG_FFLAGS (0x00100000) /* --fflags */ +#define OPTFLAG_NO_MAC_METADATA (0x00200000) /* --no-mac-metadata */ +#define OPTFLAG_MAC_METADATA (0x00400000) /* --mac-metadata */ + /* Fake short equivalents for long options that otherwise lack them. */ enum { - OPTION_B64ENCODE = 1, + OPTION_ACLS = 1, + OPTION_B64ENCODE, OPTION_CHECK_LINKS, OPTION_CHROOT, OPTION_CLEAR_NOCHANGE_FFLAGS, - OPTION_DISABLE_COPYFILE, OPTION_EXCLUDE, + OPTION_FFLAGS, OPTION_FORMAT, OPTION_GID, OPTION_GNAME, OPTION_GRZIP, OPTION_HELP, OPTION_HFS_COMPRESSION, OPTION_IGNORE_ZEROS, OPTION_INCLUDE, OPTION_KEEP_NEWER_FILES, OPTION_LRZIP, OPTION_LZ4, OPTION_LZIP, OPTION_LZMA, OPTION_LZOP, + OPTION_MAC_METADATA, OPTION_NEWER_CTIME, OPTION_NEWER_CTIME_THAN, OPTION_NEWER_MTIME, OPTION_NEWER_MTIME_THAN, OPTION_NODUMP, OPTION_NOPRESERVE_HFS_COMPRESSION, + OPTION_NO_ACLS, + OPTION_NO_FFLAGS, + OPTION_NO_MAC_METADATA, OPTION_NO_SAME_OWNER, OPTION_NO_SAME_PERMISSIONS, - OPTION_NO_XATTR, + OPTION_NO_XATTRS, OPTION_NULL, OPTION_NUMERIC_OWNER, OPTION_OLDER_CTIME, OPTION_OLDER_CTIME_THAN, OPTION_OLDER_MTIME, OPTION_OLDER_MTIME_THAN, OPTION_ONE_FILE_SYSTEM, OPTION_OPTIONS, OPTION_PASSPHRASE, OPTION_POSIX, OPTION_SAME_OWNER, OPTION_STRIP_COMPONENTS, OPTION_TOTALS, OPTION_UID, OPTION_UNAME, OPTION_USE_COMPRESS_PROGRAM, OPTION_UUENCODE, - OPTION_VERSION + OPTION_VERSION, + OPTION_XATTRS }; int bsdtar_getopt(struct bsdtar *); void do_chdir(struct bsdtar *); int edit_pathname(struct bsdtar *, struct archive_entry *); int need_report(void); int pathcmp(const char *a, const char *b); void safe_fprintf(FILE *, const char *fmt, ...); void set_chdir(struct bsdtar *, const char *newdir); const char *tar_i64toa(int64_t); void tar_mode_c(struct bsdtar *bsdtar); void tar_mode_r(struct bsdtar *bsdtar); void tar_mode_t(struct bsdtar *bsdtar); void tar_mode_u(struct bsdtar *bsdtar); void tar_mode_x(struct bsdtar *bsdtar); void usage(void); int yes(const char *fmt, ...); #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) void add_substitution(struct bsdtar *, const char *); int apply_substitution(struct bsdtar *, const char *, char **, int, int); void cleanup_substitution(struct bsdtar *); #endif void cset_add_filter(struct creation_set *, const char *); void cset_add_filter_program(struct creation_set *, const char *); int cset_auto_compress(struct creation_set *, const char *); void cset_free(struct creation_set *); const char * cset_get_format(struct creation_set *); struct creation_set *cset_new(void); int cset_read_support_filter_program(struct creation_set *, struct archive *); void cset_set_format(struct creation_set *, const char *); int cset_write_add_filters(struct creation_set *, struct archive *, const void **); const char * passphrase_callback(struct archive *, void *); void passphrase_free(char *); void list_item_verbose(struct bsdtar *, FILE *, struct archive_entry *); Index: vendor/libarchive/dist/tar/cmdline.c =================================================================== --- vendor/libarchive/dist/tar/cmdline.c (revision 314564) +++ vendor/libarchive/dist/tar/cmdline.c (revision 314565) @@ -1,405 +1,413 @@ /*- * 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. */ /* * Command line parser for tar. */ #include "bsdtar_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "bsdtar.h" #include "err.h" /* * Short options for tar. Please keep this sorted. */ static const char *short_options = "aBb:C:cf:HhI:JjkLlmnOoPpqrSs:T:tUuvW:wX:xyZz"; /* * Long options for tar. Please keep this list sorted. * * The symbolic names for options that lack a short equivalent are * defined in bsdtar.h. Also note that so far I've found no need * to support optional arguments to long options. That would be * a small change to the code below. */ static const struct bsdtar_option { const char *name; int required; /* 1 if this option requires an argument. */ int equivalent; /* Equivalent short option. */ } tar_longopts[] = { { "absolute-paths", 0, 'P' }, { "append", 0, 'r' }, + { "acls", 0, OPTION_ACLS }, { "auto-compress", 0, 'a' }, { "b64encode", 0, OPTION_B64ENCODE }, { "block-size", 1, 'b' }, { "blocking-factor", 1, 'b' }, { "bunzip2", 0, 'j' }, { "bzip", 0, 'j' }, { "bzip2", 0, 'j' }, { "cd", 1, 'C' }, { "check-links", 0, OPTION_CHECK_LINKS }, { "chroot", 0, OPTION_CHROOT }, { "clear-nochange-fflags", 0, OPTION_CLEAR_NOCHANGE_FFLAGS }, { "compress", 0, 'Z' }, { "confirmation", 0, 'w' }, { "create", 0, 'c' }, { "dereference", 0, 'L' }, { "directory", 1, 'C' }, - { "disable-copyfile", 0, OPTION_DISABLE_COPYFILE }, + { "disable-copyfile", 0, OPTION_NO_MAC_METADATA }, { "exclude", 1, OPTION_EXCLUDE }, { "exclude-from", 1, 'X' }, { "extract", 0, 'x' }, { "fast-read", 0, 'q' }, + { "fflags", 0, OPTION_FFLAGS }, { "file", 1, 'f' }, { "files-from", 1, 'T' }, { "format", 1, OPTION_FORMAT }, { "gid", 1, OPTION_GID }, { "gname", 1, OPTION_GNAME }, { "grzip", 0, OPTION_GRZIP }, { "gunzip", 0, 'z' }, { "gzip", 0, 'z' }, { "help", 0, OPTION_HELP }, { "hfsCompression", 0, OPTION_HFS_COMPRESSION }, { "ignore-zeros", 0, OPTION_IGNORE_ZEROS }, { "include", 1, OPTION_INCLUDE }, { "insecure", 0, 'P' }, { "interactive", 0, 'w' }, { "keep-newer-files", 0, OPTION_KEEP_NEWER_FILES }, { "keep-old-files", 0, 'k' }, { "list", 0, 't' }, { "lrzip", 0, OPTION_LRZIP }, { "lz4", 0, OPTION_LZ4 }, { "lzip", 0, OPTION_LZIP }, { "lzma", 0, OPTION_LZMA }, { "lzop", 0, OPTION_LZOP }, + { "mac-metadata", 0, OPTION_MAC_METADATA }, { "modification-time", 0, 'm' }, { "newer", 1, OPTION_NEWER_CTIME }, { "newer-ctime", 1, OPTION_NEWER_CTIME }, { "newer-ctime-than", 1, OPTION_NEWER_CTIME_THAN }, { "newer-mtime", 1, OPTION_NEWER_MTIME }, { "newer-mtime-than", 1, OPTION_NEWER_MTIME_THAN }, { "newer-than", 1, OPTION_NEWER_CTIME_THAN }, + { "no-acls", 0, OPTION_NO_ACLS }, + { "no-fflags", 0, OPTION_NO_FFLAGS }, + { "no-mac-metadata", 0, OPTION_NO_MAC_METADATA }, { "no-recursion", 0, 'n' }, { "no-same-owner", 0, OPTION_NO_SAME_OWNER }, { "no-same-permissions", 0, OPTION_NO_SAME_PERMISSIONS }, - { "no-xattr", 0, OPTION_NO_XATTR }, + { "no-xattr", 0, OPTION_NO_XATTRS }, + { "no-xattrs", 0, OPTION_NO_XATTRS }, { "nodump", 0, OPTION_NODUMP }, { "nopreserveHFSCompression",0, OPTION_NOPRESERVE_HFS_COMPRESSION }, { "norecurse", 0, 'n' }, { "null", 0, OPTION_NULL }, { "numeric-owner", 0, OPTION_NUMERIC_OWNER }, { "older", 1, OPTION_OLDER_CTIME }, { "older-ctime", 1, OPTION_OLDER_CTIME }, { "older-ctime-than", 1, OPTION_OLDER_CTIME_THAN }, { "older-mtime", 1, OPTION_OLDER_MTIME }, { "older-mtime-than", 1, OPTION_OLDER_MTIME_THAN }, { "older-than", 1, OPTION_OLDER_CTIME_THAN }, { "one-file-system", 0, OPTION_ONE_FILE_SYSTEM }, { "options", 1, OPTION_OPTIONS }, { "passphrase", 1, OPTION_PASSPHRASE }, { "posix", 0, OPTION_POSIX }, { "preserve-permissions", 0, 'p' }, { "read-full-blocks", 0, 'B' }, { "same-owner", 0, OPTION_SAME_OWNER }, { "same-permissions", 0, 'p' }, { "strip-components", 1, OPTION_STRIP_COMPONENTS }, { "to-stdout", 0, 'O' }, { "totals", 0, OPTION_TOTALS }, { "uid", 1, OPTION_UID }, { "uname", 1, OPTION_UNAME }, { "uncompress", 0, 'Z' }, { "unlink", 0, 'U' }, { "unlink-first", 0, 'U' }, { "update", 0, 'u' }, { "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM }, { "uuencode", 0, OPTION_UUENCODE }, { "verbose", 0, 'v' }, { "version", 0, OPTION_VERSION }, + { "xattrs", 0, OPTION_XATTRS }, { "xz", 0, 'J' }, { NULL, 0, 0 } }; /* * This getopt implementation has two key features that common * getopt_long() implementations lack. Apart from those, it's a * straightforward option parser, considerably simplified by not * needing to support the wealth of exotic getopt_long() features. It * has, of course, been shamelessly tailored for bsdtar. (If you're * looking for a generic getopt_long() implementation for your * project, I recommend Gregory Pietsch's public domain getopt_long() * implementation.) The two additional features are: * * Old-style tar arguments: The original tar implementation treated * the first argument word as a list of single-character option * letters. All arguments follow as separate words. For example, * tar xbf 32 /dev/tape * Here, the "xbf" is three option letters, "32" is the argument for * "b" and "/dev/tape" is the argument for "f". We support this usage * if the first command-line argument does not begin with '-'. We * also allow regular short and long options to follow, e.g., * tar xbf 32 /dev/tape -P --format=pax * * -W long options: There's an obscure GNU convention (only rarely * supported even there) that allows "-W option=argument" as an * alternative way to support long options. This was supported in * early bsdtar as a way to access long options on platforms that did * not support getopt_long() and is preserved here for backwards * compatibility. (Of course, if I'd started with a custom * command-line parser from the beginning, I would have had normal * long option support on every platform so that hack wouldn't have * been necessary. Oh, well. Some mistakes you just have to live * with.) * * TODO: We should be able to use this to pull files and intermingled * options (such as -C) from the command line in write mode. That * will require a little rethinking of the argument handling in * bsdtar.c. * * TODO: If we want to support arbitrary command-line options from -T * input (as GNU tar does), we may need to extend this to handle option * words from sources other than argv/argc. I'm not really sure if I * like that feature of GNU tar, so it's certainly not a priority. */ int bsdtar_getopt(struct bsdtar *bsdtar) { enum { state_start = 0, state_old_tar, state_next_word, state_short, state_long }; const struct bsdtar_option *popt, *match = NULL, *match2 = NULL; const char *p, *long_prefix = "--"; size_t optlength; int opt = '?'; int required = 0; bsdtar->argument = NULL; /* First time through, initialize everything. */ if (bsdtar->getopt_state == state_start) { /* Skip program name. */ ++bsdtar->argv; --bsdtar->argc; if (*bsdtar->argv == NULL) return (-1); /* Decide between "new style" and "old style" arguments. */ if (bsdtar->argv[0][0] == '-') { bsdtar->getopt_state = state_next_word; } else { bsdtar->getopt_state = state_old_tar; bsdtar->getopt_word = *bsdtar->argv++; --bsdtar->argc; } } /* * We're parsing old-style tar arguments */ if (bsdtar->getopt_state == state_old_tar) { /* Get the next option character. */ opt = *bsdtar->getopt_word++; if (opt == '\0') { /* New-style args can follow old-style. */ bsdtar->getopt_state = state_next_word; } else { /* See if it takes an argument. */ p = strchr(short_options, opt); if (p == NULL) return ('?'); if (p[1] == ':') { bsdtar->argument = *bsdtar->argv; if (bsdtar->argument == NULL) { lafe_warnc(0, "Option %c requires an argument", opt); return ('?'); } ++bsdtar->argv; --bsdtar->argc; } } } /* * We're ready to look at the next word in argv. */ if (bsdtar->getopt_state == state_next_word) { /* No more arguments, so no more options. */ if (bsdtar->argv[0] == NULL) return (-1); /* Doesn't start with '-', so no more options. */ if (bsdtar->argv[0][0] != '-') return (-1); /* "--" marks end of options; consume it and return. */ if (strcmp(bsdtar->argv[0], "--") == 0) { ++bsdtar->argv; --bsdtar->argc; return (-1); } /* Get next word for parsing. */ bsdtar->getopt_word = *bsdtar->argv++; --bsdtar->argc; if (bsdtar->getopt_word[1] == '-') { /* Set up long option parser. */ bsdtar->getopt_state = state_long; bsdtar->getopt_word += 2; /* Skip leading '--' */ } else { /* Set up short option parser. */ bsdtar->getopt_state = state_short; ++bsdtar->getopt_word; /* Skip leading '-' */ } } /* * We're parsing a group of POSIX-style single-character options. */ if (bsdtar->getopt_state == state_short) { /* Peel next option off of a group of short options. */ opt = *bsdtar->getopt_word++; if (opt == '\0') { /* End of this group; recurse to get next option. */ bsdtar->getopt_state = state_next_word; return bsdtar_getopt(bsdtar); } /* Does this option take an argument? */ p = strchr(short_options, opt); if (p == NULL) return ('?'); if (p[1] == ':') required = 1; /* If it takes an argument, parse that. */ if (required) { /* If arg is run-in, bsdtar->getopt_word already points to it. */ if (bsdtar->getopt_word[0] == '\0') { /* Otherwise, pick up the next word. */ bsdtar->getopt_word = *bsdtar->argv; if (bsdtar->getopt_word == NULL) { lafe_warnc(0, "Option -%c requires an argument", opt); return ('?'); } ++bsdtar->argv; --bsdtar->argc; } if (opt == 'W') { bsdtar->getopt_state = state_long; long_prefix = "-W "; /* For clearer errors. */ } else { bsdtar->getopt_state = state_next_word; bsdtar->argument = bsdtar->getopt_word; } } } /* We're reading a long option, including -W long=arg convention. */ if (bsdtar->getopt_state == state_long) { /* After this long option, we'll be starting a new word. */ bsdtar->getopt_state = state_next_word; /* Option name ends at '=' if there is one. */ p = strchr(bsdtar->getopt_word, '='); if (p != NULL) { optlength = (size_t)(p - bsdtar->getopt_word); bsdtar->argument = (char *)(uintptr_t)(p + 1); } else { optlength = strlen(bsdtar->getopt_word); } /* Search the table for an unambiguous match. */ for (popt = tar_longopts; popt->name != NULL; popt++) { /* Short-circuit if first chars don't match. */ if (popt->name[0] != bsdtar->getopt_word[0]) continue; /* If option is a prefix of name in table, record it.*/ if (strncmp(bsdtar->getopt_word, popt->name, optlength) == 0) { match2 = match; /* Record up to two matches. */ match = popt; /* If it's an exact match, we're done. */ if (strlen(popt->name) == optlength) { match2 = NULL; /* Forget the others. */ break; } } } /* Fail if there wasn't a unique match. */ if (match == NULL) { lafe_warnc(0, "Option %s%s is not supported", long_prefix, bsdtar->getopt_word); return ('?'); } if (match2 != NULL) { lafe_warnc(0, "Ambiguous option %s%s (matches --%s and --%s)", long_prefix, bsdtar->getopt_word, match->name, match2->name); return ('?'); } /* We've found a unique match; does it need an argument? */ if (match->required) { /* Argument required: get next word if necessary. */ if (bsdtar->argument == NULL) { bsdtar->argument = *bsdtar->argv; if (bsdtar->argument == NULL) { lafe_warnc(0, "Option %s%s requires an argument", long_prefix, match->name); return ('?'); } ++bsdtar->argv; --bsdtar->argc; } } else { /* Argument forbidden: fail if there is one. */ if (bsdtar->argument != NULL) { lafe_warnc(0, "Option %s%s does not allow an argument", long_prefix, match->name); return ('?'); } } return (match->equivalent); } return (opt); } Index: vendor/libarchive/dist/tar/read.c =================================================================== --- vendor/libarchive/dist/tar/read.c (revision 314564) +++ vendor/libarchive/dist/tar/read.c (revision 314565) @@ -1,419 +1,420 @@ /*- * 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 "bsdtar_platform.h" __FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "bsdtar.h" #include "err.h" struct progress_data { struct bsdtar *bsdtar; struct archive *archive; struct archive_entry *entry; }; static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *); static int unmatched_inclusions_warn(struct archive *matching, const char *); void tar_mode_t(struct bsdtar *bsdtar) { read_archive(bsdtar, 't', NULL); if (unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0) bsdtar->return_value = 1; } void tar_mode_x(struct bsdtar *bsdtar) { struct archive *writer; writer = archive_write_disk_new(); if (writer == NULL) lafe_errc(1, ENOMEM, "Cannot allocate disk writer object"); - if (!bsdtar->option_numeric_owner) + if ((bsdtar->flags & OPTFLAG_NUMERIC_OWNER) == 0) archive_write_disk_set_standard_lookup(writer); archive_write_disk_set_options(writer, bsdtar->extract_flags); read_archive(bsdtar, 'x', writer); if (unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0) bsdtar->return_value = 1; archive_write_free(writer); } static void progress_func(void *cookie) { struct progress_data *progress_data = (struct progress_data *)cookie; struct bsdtar *bsdtar = progress_data->bsdtar; struct archive *a = progress_data->archive; struct archive_entry *entry = progress_data->entry; uint64_t comp, uncomp; int compression; if (!need_report()) return; if (bsdtar->verbose) fprintf(stderr, "\n"); if (a != NULL) { comp = archive_filter_bytes(a, -1); uncomp = archive_filter_bytes(a, 0); if (comp > uncomp) compression = 0; else compression = (int)((uncomp - comp) * 100 / uncomp); fprintf(stderr, "In: %s bytes, compression %d%%;", tar_i64toa(comp), compression); fprintf(stderr, " Out: %d files, %s bytes\n", archive_file_count(a), tar_i64toa(uncomp)); } if (entry != NULL) { safe_fprintf(stderr, "Current: %s", archive_entry_pathname(entry)); fprintf(stderr, " (%s bytes)\n", tar_i64toa(archive_entry_size(entry))); } } /* * Handle 'x' and 't' modes. */ static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) { struct progress_data progress_data; FILE *out; struct archive *a; struct archive_entry *entry; const char *reader_options; int r; while (*bsdtar->argv) { if (archive_match_include_pattern(bsdtar->matching, *bsdtar->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) if (archive_match_include_pattern_from_file( bsdtar->matching, bsdtar->names_from_file, - bsdtar->option_null) != ARCHIVE_OK) + (bsdtar->flags & OPTFLAG_NULL)) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); a = archive_read_new(); if (cset_read_support_filter_program(bsdtar->cset, a) == 0) archive_read_support_filter_all(a); archive_read_support_format_all(a); reader_options = getenv(ENV_READER_OPTIONS); if (reader_options != NULL) { size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; size_t opt_len = strlen(reader_options) + 1; char *p; /* Set default read options. */ if ((p = malloc(module_len + opt_len)) == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or modules which are not added to * the archive read object. */ memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); memcpy(p + module_len, reader_options, opt_len); r = archive_read_set_options(a, p); free(p); if (r == ARCHIVE_FATAL) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); - if (bsdtar->option_ignore_zeros) + if (bsdtar->flags & OPTFLAG_IGNORE_ZEROS) if (archive_read_set_options(a, "read_concatenated_archives") != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (bsdtar->passphrase != NULL) r = archive_read_add_passphrase(a, bsdtar->passphrase); else r = archive_read_set_passphrase_callback(a, bsdtar, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, bsdtar->filename, bsdtar->bytes_per_block)) lafe_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); if (mode == 'x') { /* Set an extract callback so that we can handle SIGINFO. */ progress_data.bsdtar = bsdtar; progress_data.archive = a; archive_read_extract_set_progress_callback(a, progress_func, &progress_data); } - if (mode == 'x' && bsdtar->option_chroot) { + if (mode == 'x' && (bsdtar->flags & OPTFLAG_CHROOT)) { #if HAVE_CHROOT if (chroot(".") != 0) lafe_errc(1, errno, "Can't chroot to \".\""); #else lafe_errc(1, 0, "chroot isn't supported on this platform"); #endif } #if defined(_WIN32) && !defined(__CYGWIN__) - if (mode == 'x' && bsdtar->option_stdout) { + if (mode == 'x' && (bsdtar->flags & OPTFLAG_STDOUT)) { _setmode(1, _O_BINARY); } #endif for (;;) { /* Support --fast-read option */ const char *p; - if (bsdtar->option_fast_read && + if ((bsdtar->flags & OPTFLAG_FAST_READ) && archive_match_path_unmatched_inclusions(bsdtar->matching) == 0) break; r = archive_read_next_header(a, &entry); progress_data.entry = entry; if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ lafe_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) break; p = archive_entry_pathname(entry); if (p == NULL || p[0] == '\0') { lafe_warnc(0, "Archive entry has empty or unreadable filename ... skipping."); bsdtar->return_value = 1; continue; } if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); archive_entry_set_uname(entry, NULL); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); archive_entry_set_gname(entry, NULL); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more * control over exclusions, since rewrites always lose * information. (For example, consider a rewrite * s/foo[0-9]/foo/. If we check exclusions after the * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ if (archive_match_excluded(bsdtar->matching, entry)) continue; /* Excluded by a pattern test. */ if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ - out = bsdtar->option_stdout ? stderr : stdout; + out = (bsdtar->flags & OPTFLAG_STDOUT) ? + stderr : stdout; /* * TODO: Provide some reasonable way to * preview rewrites. gtar always displays * the unedited path in -t output, which means * you cannot easily preview rewrites. */ if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); else list_item_verbose(bsdtar, out, entry); fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } fprintf(out, "\n"); } else { /* Note: some rewrite failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) continue; /* Excluded by a rewrite failure. */ - if (bsdtar->option_interactive && + if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && !yes("extract '%s'", archive_entry_pathname(entry))) continue; if (bsdtar->verbose > 1) { /* GNU tar uses -tv format with -xvv */ safe_fprintf(stderr, "x "); list_item_verbose(bsdtar, stderr, entry); fflush(stderr); } else if (bsdtar->verbose > 0) { /* Format follows SUSv2, including the * deferred '\n'. */ safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); } /* TODO siginfo_printinfo(bsdtar, 0); */ - if (bsdtar->option_stdout) + if (bsdtar->flags & OPTFLAG_STDOUT) r = archive_read_data_into_fd(a, 1); else r = archive_read_extract2(a, entry, writer); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) fprintf(stderr, "\n"); bsdtar->return_value = 1; } if (bsdtar->verbose) fprintf(stderr, "\n"); if (r == ARCHIVE_FATAL) break; } } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_filter_name(a, 0)); archive_read_free(a); } static int unmatched_inclusions_warn(struct archive *matching, const char *msg) { const char *p; int r; if (matching == NULL) return (0); while ((r = archive_match_path_unmatched_inclusions_next( matching, &p)) == ARCHIVE_OK) lafe_warnc(0, "%s: %s", p, msg); if (r == ARCHIVE_FATAL) lafe_errc(1, errno, "Out of memory"); return (archive_match_path_unmatched_inclusions(matching)); } Index: vendor/libarchive/dist/tar/test/main.c =================================================================== --- vendor/libarchive/dist/tar/test/main.c (revision 314564) +++ vendor/libarchive/dist/tar/test/main.c (nonexistent) @@ -1,3072 +0,0 @@ -/* - * 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" -#include "test_utils.h" -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_ICONV_H -#include -#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 -#include -#include -#ifdef HAVE_SIGNAL_H -#include -#endif -#include -#include - -/* - * This same file is used pretty much verbatim for all test harnesses. - * - * The next few lines are the only differences. - * TODO: Move this into a separate configuration header, have all test - * suites share one copy of this file. - */ -__FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.6 2008/11/05 06:40:53 kientzle Exp $"); -#define KNOWNREF "test_patterns_2.tar.uu" -#define ENVBASE "BSDTAR" /* Prefix for environment variables. */ -#define PROGRAM "bsdtar" /* Name of program being tested. */ -#define PROGRAM_ALIAS "tar" /* Generic alias for program */ -#undef LIBRARY /* Not testing a library. */ -#undef EXTRA_DUMP /* How to dump extra data */ -#undef EXTRA_ERRNO /* How to dump errno */ -/* How to generate extra version info. */ -#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") - -/* - * - * Windows support routines - * - * Note: Configuration is a tricky issue. Using HAVE_* feature macros - * in the test harness is dangerous because they cover up - * configuration errors. The classic example of this is omitting a - * configure check. If libarchive and libarchive_test both look for - * the same feature macro, such errors are hard to detect. Platform - * macros (e.g., _WIN32 or __GNUC__) are a little better, but can - * easily lead to very messy code. It's best to limit yourself - * to only the most generic programming techniques in the test harness - * and thus avoid conditionals altogether. Where that's not possible, - * try to minimize conditionals by grouping platform-specific tests in - * one place (e.g., test_acl_freebsd) or by adding new assert() - * functions (e.g., assertMakeHardlink()) to cover up platform - * differences. Platform-specific coding in libarchive_test is often - * a symptom that some capability is missing from libarchive itself. - */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#include -#include -#include -#ifndef F_OK -#define F_OK (0) -#endif -#ifndef S_ISDIR -#define S_ISDIR(m) ((m) & _S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(m) ((m) & _S_IFREG) -#endif -#if !defined(__BORLANDC__) -#define access _access -#undef chdir -#define chdir _chdir -#endif -#ifndef fileno -#define fileno _fileno -#endif -/*#define fstat _fstat64*/ -#if !defined(__BORLANDC__) -#define getcwd _getcwd -#endif -#define lstat stat -/*#define lstat _stat64*/ -/*#define stat _stat64*/ -#define rmdir _rmdir -#if !defined(__BORLANDC__) -#define strdup _strdup -#define umask _umask -#endif -#define int64_t __int64 -#endif - -#if defined(HAVE__CrtSetReportMode) -# include -#endif - -mode_t umasked(mode_t expected_mode) -{ - mode_t mode = umask(0); - umask(mode); - return expected_mode & ~mode; -} - -/* Path to working directory for current test */ -const char *testworkdir; -#ifdef PROGRAM -/* Pathname of exe to be tested. */ -const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -const char *testprog; -#endif - -#if defined(_WIN32) && !defined(__CYGWIN__) -static void *GetFunctionKernel32(const char *); -static int my_CreateSymbolicLinkA(const char *, const char *, int); -static int my_CreateHardLinkA(const char *, const char *); -static int my_GetFileInformationByName(const char *, - BY_HANDLE_FILE_INFORMATION *); - -static void * -GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary("kernel32.dll"); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} - -static int -my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateSymbolicLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, flags); -} - -static int -my_CreateHardLinkA(const char *linkname, const char *target) -{ - static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); - static int set; - if (!set) { - set = 1; - f = GetFunctionKernel32("CreateHardLinkA"); - } - return f == NULL ? 0 : (*f)(linkname, target, NULL); -} - -static int -my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) -{ - HANDLE h; - int r; - - memset(bhfi, 0, sizeof(*bhfi)); - h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) - return (0); - r = GetFileInformationByHandle(h, bhfi); - CloseHandle(h); - return (r); -} -#endif - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) -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 - -/* - * - * OPTIONS FLAGS - * - */ - -/* Enable core dump on failure. */ -static int dump_on_failure = 0; -/* Default is to remove temp dirs and log data for successful tests. */ -static int keep_temp_files = 0; -/* Default is to run the specified tests once and report errors. */ -static int until_failure = 0; -/* Default is to just report pass/fail for each test. */ -static int verbosity = 0; -#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ -#define VERBOSITY_PASSFAIL 0 /* Default */ -#define VERBOSITY_LIGHT_REPORT 1 /* -v */ -#define VERBOSITY_FULL 2 /* -vv */ -/* A few places generate even more output for verbosity > VERBOSITY_FULL, - * mostly for debugging the test harness itself. */ -/* Cumulative count of assertion failures. */ -static int failures = 0; -/* Cumulative count of reported skips. */ -static int skips = 0; -/* Cumulative count of assertions checked. */ -static int assertions = 0; - -/* Directory where uuencoded reference files can be found. */ -static const char *refdir; - -/* - * Report log information selectively to console and/or disk log. - */ -static int log_console = 0; -static FILE *logfile; -static void -vlogprintf(const char *fmt, va_list ap) -{ -#ifdef va_copy - va_list lfap; - va_copy(lfap, ap); -#endif - if (log_console) - vfprintf(stdout, fmt, ap); - if (logfile != NULL) -#ifdef va_copy - vfprintf(logfile, fmt, lfap); - va_end(lfap); -#else - vfprintf(logfile, fmt, ap); -#endif -} - -static void -logprintf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vlogprintf(fmt, ap); - va_end(ap); -} - -/* Set up a message to display only if next assertion fails. */ -static char msgbuff[4096]; -static const char *msg, *nextmsg; -void -failure(const char *fmt, ...) -{ - va_list ap; - if (fmt == NULL) { - nextmsg = NULL; - } else { - va_start(ap, fmt); - vsprintf(msgbuff, fmt, ap); - va_end(ap); - nextmsg = msgbuff; - } -} - -/* - * Copy arguments into file-local variables. - * This was added to permit vararg assert() functions without needing - * variadic wrapper macros. Turns out that the vararg capability is almost - * never used, so almost all of the vararg assertions can be simplified - * by removing the vararg capability and reworking the wrapper macro to - * pass __FILE__, __LINE__ directly into the function instead of using - * this hook. I suspect this machinery is used so rarely that we - * would be better off just removing it entirely. That would simplify - * the code here noticeably. - */ -static const char *skipping_filename; -static int skipping_line; -void skipping_setup(const char *filename, int line) -{ - skipping_filename = filename; - skipping_line = line; -} - -/* Called at the beginning of each assert() function. */ -static void -assertion_count(const char *file, int line) -{ - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - ++assertions; - /* Proper handling of "failure()" message. */ - msg = nextmsg; - nextmsg = NULL; - /* Uncomment to print file:line after every assertion. - * Verbose, but occasionally useful in tracking down crashes. */ - /* printf("Checked %s:%d\n", file, line); */ -} - -/* - * For each test source file, we remember how many times each - * assertion was reported. Cleared before each new test, - * used by test_summarize(). - */ -static struct line { - int count; - int skip; -} failed_lines[10000]; -const char *failed_filename; - -/* Count this failure, setup up log destination and handle initial report. */ -static void -failure_start(const char *filename, int line, const char *fmt, ...) -{ - va_list ap; - - /* Record another failure for this line. */ - ++failures; - failed_filename = filename; - failed_lines[line].count++; - - /* Determine whether to log header to console. */ - switch (verbosity) { - case VERBOSITY_LIGHT_REPORT: - log_console = (failed_lines[line].count < 2); - break; - default: - log_console = (verbosity >= VERBOSITY_FULL); - } - - /* Log file:line header for this failure */ - va_start(ap, fmt); -#if _MSC_VER - logprintf("%s(%d): ", filename, line); -#else - logprintf("%s:%d: ", filename, line); -#endif - vlogprintf(fmt, ap); - va_end(ap); - logprintf("\n"); - - if (msg != NULL && msg[0] != '\0') { - logprintf(" Description: %s\n", msg); - msg = NULL; - } - - /* Determine whether to log details to console. */ - if (verbosity == VERBOSITY_LIGHT_REPORT) - log_console = 0; -} - -/* Complete reporting of failed tests. */ -/* - * The 'extra' hook here is used by libarchive to include libarchive - * error messages with assertion failures. It could also be used - * to add strerror() output, for example. Just define the EXTRA_DUMP() - * macro appropriately. - */ -static void -failure_finish(void *extra) -{ - (void)extra; /* UNUSED (maybe) */ -#ifdef EXTRA_DUMP - if (extra != NULL) { - logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); - logprintf(" detail: %s\n", EXTRA_DUMP(extra)); - } -#endif - - if (dump_on_failure) { - fprintf(stderr, - " *** forcing core dump so failure can be debugged ***\n"); - abort(); - } -} - -/* Inform user that we're skipping some checks. */ -void -test_skipping(const char *fmt, ...) -{ - char buff[1024]; - va_list ap; - - va_start(ap, fmt); - vsprintf(buff, fmt, ap); - va_end(ap); - /* Use failure() message if set. */ - msg = nextmsg; - nextmsg = NULL; - /* failure_start() isn't quite right, but is awfully convenient. */ - failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); - --failures; /* Undo failures++ in failure_start() */ - /* Don't failure_finish() here. */ - /* Mark as skip, so doesn't count as failed test. */ - failed_lines[skipping_line].skip = 1; - ++skips; -} - -/* - * - * ASSERTIONS - * - */ - -/* Generic assert() just displays the failed condition. */ -int -assertion_assert(const char *file, int line, int value, - const char *condition, void *extra) -{ - assertion_count(file, line); - if (!value) { - failure_start(file, line, "Assertion failed: %s", condition); - failure_finish(extra); - } - return (value); -} - -/* chdir() and report any errors */ -int -assertion_chdir(const char *file, int line, const char *pathname) -{ - assertion_count(file, line); - if (chdir(pathname) == 0) - return (1); - failure_start(file, line, "chdir(\"%s\")", pathname); - failure_finish(NULL); - return (0); - -} - -/* Verify two integers are equal. */ -int -assertion_equal_int(const char *file, int line, - long long v1, const char *e1, long long v2, const char *e2, void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); - logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); - failure_finish(extra); - return (0); -} - -/* - * Utility to convert a single UTF-8 sequence. - */ -static int -_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) -{ - static const char utf8_count[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ - }; - int ch; - int cnt; - uint32_t wc; - - *pwc = 0; - - /* Sanity check. */ - if (n == 0) - return (0); - /* - * 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. */ - cnt = utf8_count[ch]; - - /* Invalid sequence or there are not plenty bytes. */ - if (n < (size_t)cnt) - return (-1); - - /* Make a Unicode code point from a single UTF-8 sequence. */ - switch (cnt) { - case 1: /* 1 byte sequence. */ - *pwc = ch & 0x7f; - return (cnt); - case 2: /* 2 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); - return (cnt); - case 3: /* 3 bytes sequence. */ - if ((s[1] & 0xc0) != 0x80) return (-1); - if ((s[2] & 0xc0) != 0x80) return (-1); - wc = ((ch & 0x0f) << 12) - | ((s[1] & 0x3f) << 6) - | (s[2] & 0x3f); - if (wc < 0x800) - return (-1);/* Overlong sequence. */ - break; - case 4: /* 4 bytes sequence. */ - 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); - wc = ((ch & 0x07) << 18) - | ((s[1] & 0x3f) << 12) - | ((s[2] & 0x3f) << 6) - | (s[3] & 0x3f); - if (wc < 0x10000) - return (-1);/* Overlong sequence. */ - break; - default: - return (-1); - } - - /* The code point larger than 0x10FFFF is not legal - * Unicode values. */ - if (wc > 0x10FFFF) - return (-1); - /* Correctly gets a Unicode, returns used bytes. */ - *pwc = wc; - return (cnt); -} - -static void strdump(const char *e, const char *p, int ewidth, int utf8) -{ - const char *q = p; - - logprintf(" %*s = ", ewidth, e); - if (p == NULL) { - logprintf("NULL\n"); - return; - } - logprintf("\""); - while (*p != '\0') { - unsigned int c = 0xff & *p++; - switch (c) { - case '\a': logprintf("\\a"); break; - case '\b': logprintf("\\b"); break; - case '\n': logprintf("\\n"); break; - case '\r': logprintf("\\r"); break; - default: - if (c >= 32 && c < 127) - logprintf("%c", c); - else - logprintf("\\x%02X", c); - } - } - logprintf("\""); - logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); - - /* - * If the current string is UTF-8, dump its code points. - */ - if (utf8) { - size_t len; - uint32_t uc; - int n; - int cnt = 0; - - p = q; - len = strlen(p); - logprintf(" ["); - while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { - if (p != q) - logprintf(" "); - logprintf("%04X", uc); - p += n; - len -= n; - cnt++; - } - logprintf("]"); - logprintf(" (count %d", cnt); - if (n < 0) { - logprintf(",unknown %d bytes", len); - } - logprintf(")"); - - } - logprintf("\n"); -} - -/* Verify two strings are equal, dump them if not. */ -int -assertion_equal_string(const char *file, int line, - const char *v1, const char *e1, - const char *v2, const char *e2, - void *extra, int utf8) -{ - int l1, l2; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - l1 = (int)strlen(e1); - l2 = (int)strlen(e2); - if (l1 < l2) - l1 = l2; - strdump(e1, v1, l1, utf8); - strdump(e2, v2, l1, utf8); - failure_finish(extra); - return (0); -} - -static void -wcsdump(const char *e, const wchar_t *w) -{ - logprintf(" %s = ", e); - if (w == NULL) { - logprintf("(null)"); - return; - } - logprintf("\""); - while (*w != L'\0') { - unsigned int c = *w++; - if (c >= 32 && c < 127) - logprintf("%c", c); - else if (c < 256) - logprintf("\\x%02X", c); - else if (c < 0x10000) - logprintf("\\u%04X", c); - else - logprintf("\\U%08X", c); - } - logprintf("\"\n"); -} - -#ifndef HAVE_WCSCMP -static int -wcscmp(const wchar_t *s1, const wchar_t *s2) -{ - - while (*s1 == *s2++) { - if (*s1++ == L'\0') - return 0; - } - if (*s1 > *--s2) - return 1; - else - return -1; -} -#endif - -/* Verify that two wide strings are equal, dump them if not. */ -int -assertion_equal_wstring(const char *file, int line, - const wchar_t *v1, const char *e1, - const wchar_t *v2, const char *e2, - void *extra) -{ - assertion_count(file, line); - if (v1 == v2) - return (1); - if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) - return (1); - failure_start(file, line, "%s != %s", e1, e2); - wcsdump(e1, v1); - wcsdump(e2, v2); - failure_finish(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; - - if (p == NULL) { - logprintf("(null)\n"); - return; - } - for(i=0; i < l; i+=16) { - logprintf("%04x", (unsigned)(i + offset)); - sep = ' '; - for (j = 0; j < 16 && i + j < l; j++) { - if (ref != NULL && p[i + j] != ref[i + j]) - sep = '_'; - logprintf("%c%02x", sep, 0xff & (int)p[i+j]); - if (ref != NULL && p[i + j] == ref[i + j]) - sep = ' '; - } - for (; j < 16; j++) { - logprintf("%c ", sep); - sep = ' '; - } - logprintf("%c", sep); - for (j=0; j < 16 && i + j < l; j++) { - int c = p[i + j]; - if (c >= ' ' && c <= 126) - logprintf("%c", c); - else - logprintf("."); - } - logprintf("\n"); - } -} - -/* Verify that two blocks of memory are the same, display the first - * block of differences if they're not. */ -int -assertion_equal_mem(const char *file, int line, - const void *_v1, const char *e1, - const void *_v2, const char *e2, - size_t l, const char *ld, void *extra) -{ - const char *v1 = (const char *)_v1; - const char *v2 = (const char *)_v2; - size_t offset; - - assertion_count(file, line); - if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) - return (1); - if (v1 == NULL || v2 == NULL) - return (0); - - failure_start(file, line, "%s != %s", e1, e2); - logprintf(" size %s = %d\n", ld, (int)l); - /* Dump 48 bytes (3 lines) so that the first difference is - * in the second line. */ - offset = 0; - while (l > 64 && memcmp(v1, v2, 32) == 0) { - /* Two lines agree, so step forward one line. */ - v1 += 16; - v2 += 16; - l -= 16; - offset += 16; - } - logprintf(" Dump of %s\n", e1); - hexdump(v1, v2, l < 128 ? l : 128, offset); - logprintf(" Dump of %s\n", e2); - hexdump(v2, v1, l < 128 ? l : 128, offset); - logprintf("\n"); - failure_finish(extra); - return (0); -} - -/* Verify that a block of memory is filled with the specified byte. */ -int -assertion_memory_filled_with(const char *file, int line, - const void *_v1, const char *vd, - size_t l, const char *ld, - char b, const char *bd, void *extra) -{ - const char *v1 = (const char *)_v1; - size_t c = 0; - size_t i; - (void)ld; /* UNUSED */ - - assertion_count(file, line); - - for (i = 0; i < l; ++i) { - if (v1[i] == b) { - ++c; - } - } - if (c == l) - return (1); - - failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); - logprintf(" Only %d bytes were correct\n", (int)c); - failure_finish(extra); - return (0); -} - -/* Verify that the named file exists and is empty. */ -int -assertion_empty_file(const char *filename, int line, const char *f1) -{ - char buff[1024]; - struct stat st; - ssize_t s; - FILE *f; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) - return (1); - - failure_start(filename, line, "File should be empty: %s", f1); - logprintf(" File size: %d\n", (int)st.st_size); - logprintf(" Contents:\n"); - f = fopen(f1, "rb"); - if (f == NULL) { - logprintf(" Unable to open %s\n", f1); - } else { - s = ((off_t)sizeof(buff) < st.st_size) ? - (ssize_t)sizeof(buff) : (ssize_t)st.st_size; - s = fread(buff, 1, s, f); - hexdump(buff, NULL, s, 0); - fclose(f); - } - failure_finish(NULL); - return (0); -} - -/* Verify that the named file exists and is not empty. */ -int -assertion_non_empty_file(const char *filename, int line, const char *f1) -{ - struct stat st; - - assertion_count(filename, line); - - if (stat(f1, &st) != 0) { - failure_start(filename, line, "Stat failed: %s", f1); - failure_finish(NULL); - return (0); - } - if (st.st_size == 0) { - failure_start(filename, line, "File empty: %s", f1); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify that two files have the same contents. */ -/* TODO: hexdump the first bytes that actually differ. */ -int -assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) -{ - char buff1[1024]; - char buff2[1024]; - FILE *f1, *f2; - int n1, n2; - - assertion_count(filename, line); - - f1 = fopen(fn1, "rb"); - f2 = fopen(fn2, "rb"); - if (f1 == NULL || f2 == NULL) { - if (f1) fclose(f1); - if (f2) fclose(f2); - return (0); - } - for (;;) { - n1 = (int)fread(buff1, 1, sizeof(buff1), f1); - n2 = (int)fread(buff2, 1, sizeof(buff2), f2); - if (n1 != n2) - break; - if (n1 == 0 && n2 == 0) { - fclose(f1); - fclose(f2); - return (1); - } - if (memcmp(buff1, buff2, n1) != 0) - break; - } - fclose(f1); - fclose(f2); - failure_start(filename, line, "Files not identical"); - logprintf(" file1=\"%s\"\n", fn1); - logprintf(" file2=\"%s\"\n", fn2); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file does exist. */ -int -assertion_file_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (!_access(f, 0)) - return (1); -#else - if (!access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Verify that the named file doesn't exist. */ -int -assertion_file_not_exists(const char *filename, int line, const char *f) -{ - assertion_count(filename, line); - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (_access(f, 0)) - return (1); -#else - if (access(f, F_OK)) - return (1); -#endif - failure_start(filename, line, "File should not exist: %s", f); - failure_finish(NULL); - return (0); -} - -/* Compare the contents of a file to a block of memory. */ -int -assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) -{ - char *contents; - FILE *f; - int n; - - assertion_count(filename, line); - - f = fopen(fn, "rb"); - if (f == NULL) { - failure_start(filename, line, - "File should exist: %s", fn); - failure_finish(NULL); - return (0); - } - contents = malloc(s * 2); - n = (int)fread(contents, 1, s * 2, f); - fclose(f); - if (n == s && memcmp(buff, contents, s) == 0) { - free(contents); - return (1); - } - failure_start(filename, line, "File contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) - hexdump(contents, buff, n > 512 ? 512 : n, 0); - else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s > 512 ? 512 : s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Check the contents of a text file, being tolerant of line endings. */ -int -assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) -{ - char *contents; - const char *btxt, *ftxt; - FILE *f; - int n, s; - - assertion_count(filename, line); - f = fopen(fn, "r"); - if (f == NULL) { - failure_start(filename, line, - "File doesn't exist: %s", fn); - failure_finish(NULL); - return (0); - } - s = (int)strlen(buff); - contents = malloc(s * 2 + 128); - n = (int)fread(contents, 1, s * 2 + 128 - 1, f); - if (n >= 0) - contents[n] = '\0'; - fclose(f); - /* Compare texts. */ - btxt = buff; - ftxt = (const char *)contents; - while (*btxt != '\0' && *ftxt != '\0') { - if (*btxt == *ftxt) { - ++btxt; - ++ftxt; - continue; - } - if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { - /* Pass over different new line characters. */ - ++btxt; - ftxt += 2; - continue; - } - break; - } - if (*btxt == '\0' && *ftxt == '\0') { - free(contents); - return (1); - } - failure_start(filename, line, "Contents don't match"); - logprintf(" file=\"%s\"\n", fn); - if (n > 0) { - hexdump(contents, buff, n, 0); - logprintf(" expected\n", fn); - hexdump(buff, contents, s, 0); - } else { - logprintf(" File empty, contents should be:\n"); - hexdump(buff, NULL, s, 0); - } - failure_finish(NULL); - free(contents); - return (0); -} - -/* Verify that a text file contains the specified lines, regardless of order */ -/* This could be more efficient if we sorted both sets of lines, etc, but - * since this is used only for testing and only ever deals with a dozen or so - * lines at a time, this relatively crude approach is just fine. */ -int -assertion_file_contains_lines_any_order(const char *file, int line, - const char *pathname, const char *lines[]) -{ - char *buff; - size_t buff_size; - size_t expected_count, actual_count, i, j; - char **expected = NULL; - char *p, **actual = NULL; - char c; - int expected_failure = 0, actual_failure = 0, retval = 0; - - assertion_count(file, line); - - buff = slurpfile(&buff_size, "%s", pathname); - if (buff == NULL) { - failure_start(pathname, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - /* Make a copy of the provided lines and count up the expected - * file size. */ - for (i = 0; lines[i] != NULL; ++i) { - } - expected_count = i; - if (expected_count) { - expected = malloc(sizeof(char *) * expected_count); - if (expected == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - goto done; - } - for (i = 0; lines[i] != NULL; ++i) { - expected[i] = strdup(lines[i]); - } - } - - /* Break the file into lines */ - actual_count = 0; - for (c = '\0', p = buff; p < buff + buff_size; ++p) { - if (*p == '\x0d' || *p == '\x0a') - *p = '\0'; - if (c == '\0' && *p != '\0') - ++actual_count; - c = *p; - } - if (actual_count) { - actual = calloc(sizeof(char *), actual_count); - if (actual == NULL) { - failure_start(pathname, line, "Can't allocate memory"); - failure_finish(NULL); - goto done; - } - for (j = 0, p = buff; p < buff + buff_size; - p += 1 + strlen(p)) { - if (*p != '\0') { - actual[j] = p; - ++j; - } - } - } - - /* Erase matching lines from both lists */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] == NULL) - continue; - for (j = 0; j < actual_count; ++j) { - if (actual[j] == NULL) - continue; - if (strcmp(expected[i], actual[j]) == 0) { - free(expected[i]); - expected[i] = NULL; - actual[j] = NULL; - break; - } - } - } - - /* If there's anything left, it's a failure */ - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) - ++expected_failure; - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - ++actual_failure; - } - if (expected_failure == 0 && actual_failure == 0) { - retval = 1; - goto done; - } - failure_start(file, line, "File doesn't match: %s", pathname); - for (i = 0; i < expected_count; ++i) { - if (expected[i] != NULL) - logprintf(" Expected but not present: %s\n", expected[i]); - } - for (j = 0; j < actual_count; ++j) { - if (actual[j] != NULL) - logprintf(" Present but not expected: %s\n", actual[j]); - } - failure_finish(NULL); -done: - free(actual); - free(buff); - for (i = 0; i < expected_count; ++i) - free(expected[i]); - free(expected); - - return (retval); -} - -/* Verify that a text file does not contains the specified strings */ -int -assertion_file_contains_no_invalid_strings(const char *file, int line, - const char *pathname, const char *strings[]) -{ - char *buff; - int i; - - buff = slurpfile(NULL, "%s", pathname); - if (buff == NULL) { - failure_start(file, line, "Can't read file: %s", pathname); - failure_finish(NULL); - return (0); - } - - for (i = 0; strings[i] != NULL; ++i) { - if (strstr(buff, strings[i]) != NULL) { - failure_start(file, line, "Invalid string in %s: %s", pathname, - strings[i]); - failure_finish(NULL); - free(buff); - return(0); - } - } - - free(buff); - return (0); -} - -/* Test that two paths point to the same file. */ -/* As a side-effect, asserts that both files exist. */ -static int -is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(path1, &bhfi1); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path1); - failure_finish(NULL); - return (0); - } - r = my_GetFileInformationByName(path2, &bhfi2); - if (r == 0) { - failure_start(file, line, "File %s can't be inspected?", path2); - failure_finish(NULL); - return (0); - } - return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber - && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh - && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); -#else - struct stat st1, st2; - int r; - - assertion_count(file, line); - r = lstat(path1, &st1); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path1); - failure_finish(NULL); - return (0); - } - r = lstat(path2, &st2); - if (r != 0) { - failure_start(file, line, "File should exist: %s", path2); - failure_finish(NULL); - return (0); - } - return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); -#endif -} - -int -assertion_is_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s are not hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -int -assertion_is_not_hardlink(const char *file, int line, - const char *path1, const char *path2) -{ - if (!is_hardlink(file, line, path1, path2)) - return (1); - failure_start(file, line, - "Files %s and %s should not be hardlinked", path1, path2); - failure_finish(NULL); - return (0); -} - -/* Verify a/b/mtime of 'pathname'. */ -/* If 'recent', verify that it's within last 10 seconds. */ -static int -assertion_file_time(const char *file, int line, - const char *pathname, long t, long nsec, char type, int recent) -{ - long long filet, filet_nsec; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define EPOC_TIME (116444736000000000ULL) - FILETIME fxtime, fbirthtime, fatime, fmtime; - ULARGE_INTEGER wintm; - HANDLE h; - fxtime.dwLowDateTime = 0; - fxtime.dwHighDateTime = 0; - - assertion_count(file, line); - /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open - * a directory file. If not, CreateFile() will fail when - * the pathname is a directory. */ - h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); - switch (type) { - case 'a': fxtime = fatime; break; - case 'b': fxtime = fbirthtime; break; - case 'm': fxtime = fmtime; break; - } - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't GetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - wintm.LowPart = fxtime.dwLowDateTime; - wintm.HighPart = fxtime.dwHighDateTime; - filet = (wintm.QuadPart - EPOC_TIME) / 10000000; - filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; - nsec = (nsec / 100) * 100; /* Round the request */ -#else - struct stat st; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - switch (type) { - case 'a': filet = st.st_atime; break; - case 'm': filet = st.st_mtime; break; - case 'b': filet = 0; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } -#if defined(__FreeBSD__) - switch (type) { - case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; - case 'b': filet = st.st_birthtime; - /* FreeBSD filesystems that don't support birthtime - * (e.g., UFS1) always return -1 here. */ - if (filet == -1) { - return (1); - } - filet_nsec = st.st_birthtimespec.tv_nsec; break; - case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; - default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); - exit(1); - } - /* FreeBSD generally only stores to microsecond res, so round. */ - filet_nsec = (filet_nsec / 1000) * 1000; - nsec = (nsec / 1000) * 1000; -#else - filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ - if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ -#if defined(__HAIKU__) - if (type == 'a') return (1); /* Haiku doesn't have atime. */ -#endif -#endif -#endif - if (recent) { - /* Check that requested time is up-to-date. */ - time_t now = time(NULL); - if (filet < now - 10 || filet > now + 1) { - failure_start(file, line, - "File %s has %ctime %lld, %lld seconds ago\n", - pathname, type, filet, now - filet); - failure_finish(NULL); - return (0); - } - } else if (filet != t || filet_nsec != nsec) { - failure_start(file, line, - "File %s has %ctime %lld.%09lld, expected %lld.%09lld", - pathname, type, filet, filet_nsec, t, nsec); - failure_finish(NULL); - return (0); - } - return (1); -} - -/* Verify atime of 'pathname'. */ -int -assertion_file_atime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); -} - -/* Verify atime of 'pathname' is up-to-date. */ -int -assertion_file_atime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); -} - -/* Verify birthtime of 'pathname'. */ -int -assertion_file_birthtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); -} - -/* Verify birthtime of 'pathname' is up-to-date. */ -int -assertion_file_birthtime_recent(const char *file, int line, - const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); -} - -/* Verify mode of 'pathname'. */ -int -assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) -{ - int mode; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - failure_start(file, line, "assertFileMode not yet implemented for Windows"); - (void)mode; /* UNUSED */ - (void)r; /* UNUSED */ -#else - { - struct stat st; - r = lstat(pathname, &st); - mode = (int)(st.st_mode & 0777); - } - if (r == 0 && mode == expected_mode) - return (1); - failure_start(file, line, "File %s has mode %o, expected %o", - pathname, mode, expected_mode); -#endif - failure_finish(NULL); - return (0); -} - -/* Verify mtime of 'pathname'. */ -int -assertion_file_mtime(const char *file, int line, - const char *pathname, long t, long nsec) -{ - return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); -} - -/* Verify mtime of 'pathname' is up-to-date. */ -int -assertion_file_mtime_recent(const char *file, int line, const char *pathname) -{ - return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); -} - -/* Verify number of links to 'pathname'. */ -int -assertion_file_nlinks(const char *file, int line, - const char *pathname, int nlinks) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - assertion_count(file, line); - r = my_GetFileInformationByName(pathname, &bhfi); - if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, bhfi.nNumberOfLinks, nlinks); - failure_finish(NULL); - return (0); -#else - struct stat st; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r == 0 && (int)st.st_nlink == nlinks) - return (1); - failure_start(file, line, "File %s has %d links, expected %d", - pathname, st.st_nlink, nlinks); - failure_finish(NULL); - return (0); -#endif -} - -/* Verify size of 'pathname'. */ -int -assertion_file_size(const char *file, int line, const char *pathname, long size) -{ - int64_t filesize; - int r; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - { - BY_HANDLE_FILE_INFORMATION bhfi; - r = !my_GetFileInformationByName(pathname, &bhfi); - filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; - } -#else - { - struct stat st; - r = lstat(pathname, &st); - filesize = st.st_size; - } -#endif - if (r == 0 && filesize == size) - return (1); - failure_start(file, line, "File %s has size %ld, expected %ld", - pathname, (long)filesize, (long)size); - failure_finish(NULL); - return (0); -} - -/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ -int -assertion_is_dir(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, "Dir should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISDIR(st.st_mode)) { - failure_start(file, line, "%s is not a dir", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "Dir %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, - * verify that too. */ -int -assertion_is_reg(const char *file, int line, const char *pathname, int mode) -{ - struct stat st; - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ -#endif - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0 || !S_ISREG(st.st_mode)) { - failure_start(file, line, "File should exist: %s", pathname); - failure_finish(NULL); - return (0); - } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Windows doesn't handle permissions the same way as POSIX, - * so just ignore the mode tests. */ - /* TODO: Can we do better here? */ - if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { - failure_start(file, line, "File %s has wrong mode", pathname); - logprintf(" Expected: 0%3o\n", mode); - logprintf(" Found: 0%3o\n", st.st_mode & 07777); - failure_finish(NULL); - return (0); - } -#endif - return (1); -} - -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ -static int -is_symlink(const char *file, int line, - const char *pathname, const char *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)pathname; /* UNUSED */ - (void)contents; /* UNUSED */ - assertion_count(file, line); - /* Windows sort-of has real symlinks, but they're only usable - * by privileged users and are crippled even then, so there's - * really not much point in bothering with this. */ - return (0); -#else - char buff[301]; - struct stat st; - ssize_t linklen; - int r; - - assertion_count(file, line); - r = lstat(pathname, &st); - if (r != 0) { - failure_start(file, line, - "Symlink should exist: %s", pathname); - failure_finish(NULL); - return (0); - } - if (!S_ISLNK(st.st_mode)) - return (0); - if (contents == NULL) - return (1); - linklen = readlink(pathname, buff, sizeof(buff) - 1); - if (linklen < 0) { - failure_start(file, line, "Can't read symlink %s", pathname); - failure_finish(NULL); - return (0); - } - buff[linklen] = '\0'; - if (strcmp(buff, contents) != 0) - return (0); - return (1); -#endif -} - -/* Assert that path is a symlink that (optionally) contains contents. */ -int -assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) -{ - if (is_symlink(file, line, path, contents)) - return (1); - if (contents) - failure_start(file, line, "File %s is not a symlink to %s", - path, contents); - else - failure_start(file, line, "File %s is not a symlink", path); - failure_finish(NULL); - return (0); -} - - -/* Create a directory and report any errors. */ -int -assertion_make_dir(const char *file, int line, const char *dirname, int mode) -{ - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - (void)mode; /* UNUSED */ - if (0 == _mkdir(dirname)) - return (1); -#else - if (0 == mkdir(dirname, mode)) { - if (0 == chmod(dirname, mode)) { - assertion_file_mode(file, line, dirname, mode); - return (1); - } - } -#endif - failure_start(file, line, "Could not create directory %s", dirname); - failure_finish(NULL); - return(0); -} - -/* Create a file with the specified contents and report any failures. */ -int -assertion_make_file(const char *file, int line, - const char *path, int mode, int csize, const void *contents) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - /* TODO: Rework this to set file mode as well. */ - FILE *f; - (void)mode; /* UNUSED */ - assertion_count(file, line); - f = fopen(path, "wb"); - if (f == NULL) { - failure_start(file, line, "Could not create file %s", path); - failure_finish(NULL); - return (0); - } - if (contents != NULL) { - size_t wsize; - - if (csize < 0) - wsize = strlen(contents); - else - wsize = (size_t)csize; - if (wsize != fwrite(contents, 1, wsize, f)) { - fclose(f); - failure_start(file, line, - "Could not write file %s", path); - failure_finish(NULL); - return (0); - } - } - fclose(f); - return (1); -#else - int fd; - assertion_count(file, line); - fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); - if (fd < 0) { - failure_start(file, line, "Could not create %s", path); - failure_finish(NULL); - return (0); - } - if (0 != chmod(path, mode)) { - failure_start(file, line, "Could not chmod %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - if (contents != NULL) { - ssize_t wsize; - - if (csize < 0) - wsize = (ssize_t)strlen(contents); - else - wsize = (ssize_t)csize; - if (wsize != write(fd, contents, wsize)) { - close(fd); - failure_start(file, line, - "Could not write to %s", path); - failure_finish(NULL); - close(fd); - return (0); - } - } - close(fd); - assertion_file_mode(file, line, path, mode); - return (1); -#endif -} - -/* Create a hardlink and report any failures. */ -int -assertion_make_hardlink(const char *file, int line, - const char *newpath, const char *linkto) -{ - int succeeded; - - assertion_count(file, line); -#if defined(_WIN32) && !defined(__CYGWIN__) - succeeded = my_CreateHardLinkA(newpath, linkto); -#elif HAVE_LINK - succeeded = !link(linkto, newpath); -#else - succeeded = 0; -#endif - if (succeeded) - return (1); - failure_start(file, line, "Could not create hardlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Create a symlink and report any failures. */ -int -assertion_make_symlink(const char *file, int line, - const char *newpath, const char *linkto) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - int targetIsDir = 0; /* TODO: Fix this */ - assertion_count(file, line); - if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) - return (1); -#elif HAVE_SYMLINK - assertion_count(file, line); - if (0 == symlink(linkto, newpath)) - return (1); -#endif - failure_start(file, line, "Could not create symlink"); - logprintf(" New link: %s\n", newpath); - logprintf(" Old name: %s\n", linkto); - failure_finish(NULL); - return(0); -} - -/* Set umask, report failures. */ -int -assertion_umask(const char *file, int line, int mask) -{ - assertion_count(file, line); - (void)file; /* UNUSED */ - (void)line; /* UNUSED */ - umask(mask); - return (1); -} - -/* Set times, report failures. */ -int -assertion_utimes(const char *file, int line, - const char *pathname, long at, long at_nsec, long mt, long mt_nsec) -{ - int r; - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ - + (((nsec)/1000)*10)) - HANDLE h; - ULARGE_INTEGER wintm; - FILETIME fatime, fmtime; - FILETIME *pat, *pmt; - - assertion_count(file, line); - h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - failure_start(file, line, "Can't access %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (at > 0 || at_nsec > 0) { - wintm.QuadPart = WINTIME(at, at_nsec); - fatime.dwLowDateTime = wintm.LowPart; - fatime.dwHighDateTime = wintm.HighPart; - pat = &fatime; - } else - pat = NULL; - if (mt > 0 || mt_nsec > 0) { - wintm.QuadPart = WINTIME(mt, mt_nsec); - fmtime.dwLowDateTime = wintm.LowPart; - fmtime.dwHighDateTime = wintm.HighPart; - pmt = &fmtime; - } else - pmt = NULL; - if (pat != NULL || pmt != NULL) - r = SetFileTime(h, NULL, pat, pmt); - else - r = 1; - CloseHandle(h); - if (r == 0) { - failure_start(file, line, "Can't SetFileTime %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#else /* defined(_WIN32) && !defined(__CYGWIN__) */ - struct stat st; - struct timeval times[2]; - -#if !defined(__FreeBSD__) - mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ -#endif - if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) - return (1); - - r = lstat(pathname, &st); - if (r < 0) { - failure_start(file, line, "Can't stat %s\n", pathname); - failure_finish(NULL); - return (0); - } - - if (mt == 0 && mt_nsec == 0) { - mt = st.st_mtime; -#if defined(__FreeBSD__) - mt_nsec = st.st_mtimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - mt_nsec = (mt_nsec / 1000) * 1000; -#endif - } - if (at == 0 && at_nsec == 0) { - at = st.st_atime; -#if defined(__FreeBSD__) - at_nsec = st.st_atimespec.tv_nsec; - /* FreeBSD generally only stores to microsecond res, so round. */ - at_nsec = (at_nsec / 1000) * 1000; -#endif - } - - times[1].tv_sec = mt; - times[1].tv_usec = mt_nsec / 1000; - - times[0].tv_sec = at; - times[0].tv_usec = at_nsec / 1000; - -#ifdef HAVE_LUTIMES - r = lutimes(pathname, times); -#else - r = utimes(pathname, times); -#endif - if (r < 0) { - failure_start(file, line, "Can't utimes %s\n", pathname); - failure_finish(NULL); - return (0); - } - return (1); -#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ -} - -/* Set nodump, report failures. */ -int -assertion_nodump(const char *file, int line, const char *pathname) -{ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - int r; - - assertion_count(file, line); - r = chflags(pathname, UF_NODUMP); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - int fd, r, flags; - - assertion_count(file, line); - fd = open(pathname, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - failure_start(file, line, "Can't open %s\n", pathname); - failure_finish(NULL); - return (0); - } - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't get flags %s\n", pathname); - failure_finish(NULL); - return (0); - } - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) { - failure_start(file, line, "Can't set nodump %s\n", pathname); - failure_finish(NULL); - return (0); - } - close(fd); -#else - (void)pathname; /* UNUSED */ - assertion_count(file, line); -#endif - return (1); -} - -/* - * - * UTILITIES for use by tests. - * - */ - -/* - * Check whether platform supports symlinks. This is intended - * for tests to use in deciding whether to bother testing symlink - * support; if the platform doesn't support symlinks, there's no point - * in checking whether the program being tested can create them. - * - * Note that the first time this test is called, we actually go out to - * disk to create and verify a symlink. This is necessary because - * symlink support is actually a property of a particular filesystem - * and can thus vary between directories on a single system. After - * the first call, this returns the cached result from memory, so it's - * safe to call it as often as you wish. - */ -int -canSymlink(void) -{ - /* Remember the test result */ - static int value = 0, tested = 0; - if (tested) - return (value); - - ++tested; - assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); - /* Note: Cygwin has its own symlink() emulation that does not - * use the Win32 CreateSymbolicLink() function. */ -#if defined(_WIN32) && !defined(__CYGWIN__) - value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); -#elif HAVE_SYMLINK - value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); -#endif - return (value); -} - -/* Platform-dependent options for hiding the output of a subcommand. */ -#if defined(_WIN32) && !defined(__CYGWIN__) -static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ -#else -static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ -#endif -/* - * Can this platform run the bzip2 program? - */ -int -canBzip2(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("bzip2 -d -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the grzip program? - */ -int -canGrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("grzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the gzip program? - */ -int -canGzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("gzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lrzip program? - */ -int -canRunCommand(const char *cmd) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("%s %s", cmd, redirectArgs) == 0) - value = 1; - } - return (value); -} - -int -canLrzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lrzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lz4 program? - */ -int -canLz4(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lz4 -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzip program? - */ -int -canLzip(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzip -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzma program? - */ -int -canLzma(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzma -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the lzop program? - */ -int -canLzop(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("lzop -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this platform run the xz program? - */ -int -canXz(void) -{ - static int tested = 0, value = 0; - if (!tested) { - tested = 1; - if (systemf("xz -V %s", redirectArgs) == 0) - value = 1; - } - return (value); -} - -/* - * Can this filesystem handle nodump flags. - */ -#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - struct stat sb; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - if (chflags(path, UF_NODUMP) < 0) - return (0); - if (stat(path, &sb) < 0) - return (0); - if (sb.st_flags & UF_NODUMP) - return (1); - return (0); -} - -#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ - && defined(EXT2_NODUMP_FL) - -int -canNodump(void) -{ - const char *path = "cannodumptest"; - int fd, r, flags; - - assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - flags |= EXT2_NODUMP_FL; - r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - return (0); - r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); - if (r < 0) - return (0); - close(fd); - if (flags & EXT2_NODUMP_FL) - return (1); - return (0); -} - -#else - -int -canNodump() -{ - return (0); -} - -#endif - -/* - * Sleep as needed; useful for verifying disk timestamp changes by - * ensuring that the wall-clock time has actually changed before we - * go back to re-read something from disk. - */ -void -sleepUntilAfter(time_t t) -{ - while (t >= time(NULL)) -#if defined(_WIN32) && !defined(__CYGWIN__) - Sleep(500); -#else - sleep(1); -#endif -} - -/* - * 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); - if (verbosity > VERBOSITY_FULL) - logprintf("Cmd: %s\n", buff); - 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; - FILE *f; - int r; - - va_start(ap, fmt); - vsprintf(filename, fmt, ap); - va_end(ap); - - f = fopen(filename, "rb"); - if (f == NULL) { - /* Note: No error; non-existent file is okay here. */ - return (NULL); - } - r = fstat(fileno(f), &st); - if (r != 0) { - logprintf("Can't stat file %s\n", filename); - fclose(f); - return (NULL); - } - p = malloc((size_t)st.st_size + 1); - if (p == NULL) { - logprintf("Can't allocate %ld bytes of memory to read file %s\n", - (long int)st.st_size, filename); - fclose(f); - return (NULL); - } - bytes_read = fread(p, 1, (size_t)st.st_size, f); - if (bytes_read < st.st_size) { - logprintf("Can't read file %s\n", filename); - fclose(f); - free(p); - return (NULL); - } - p[st.st_size] = '\0'; - if (sizep != NULL) - *sizep = (size_t)st.st_size; - fclose(f); - return (p); -} - -/* - * 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. - */ -void -dumpfile(const char *filename, void *data, size_t len) -{ - ssize_t bytes_written; - FILE *f; - - f = fopen(filename, "wb"); - if (f == NULL) { - logprintf("Can't open file %s for writing\n", filename); - return; - } - bytes_written = fwrite(data, 1, len, f); - if (bytes_written < (ssize_t)len) - logprintf("Can't write file %s\n", filename); - fclose(f); -} - -/* Read a uuencoded file from the reference directory, decode, and - * write the result into the current directory. */ -#define VALID_UUDECODE(c) (c >= 32 && c <= 96) -#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. */ - goto done; - } - if (memcmp(buff, "begin ", 6) == 0) - break; - } - /* Now, decode the rest and write it. */ - out = fopen(name, "wb"); - 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) { - assert(VALID_UUDECODE(p[0])); - assert(VALID_UUDECODE(p[1])); - n = UUDECODE(*p++) << 18; - n |= UUDECODE(*p++) << 12; - fputc(n >> 16, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++) << 6; - fputc((n >> 8) & 0xFF, out); - --bytes; - } - if (bytes > 0) { - assert(VALID_UUDECODE(p[0])); - n |= UUDECODE(*p++); - fputc(n & 0xFF, out); - --bytes; - } - } - } - fclose(out); -done: - fclose(in); -} - -void -copy_reference_file(const char *name) -{ - char buff[1024]; - FILE *in, *out; - size_t rbytes; - - sprintf(buff, "%s/%s", refdir, name); - in = fopen(buff, "rb"); - failure("Couldn't open reference file %s", buff); - assert(in != NULL); - if (in == NULL) - return; - /* Now, decode the rest and write it. */ - /* Not a lot of error checking here; the input better be right. */ - out = fopen(name, "wb"); - while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { - if (fwrite(buff, 1, rbytes, out) != rbytes) { - logprintf("Error: fwrite\n"); - break; - } - } - fclose(out); - fclose(in); -} - -int -is_LargeInode(const char *file) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - r = my_GetFileInformationByName(file, &bhfi); - if (r != 0) - return (0); - return (bhfi.nFileIndexHigh & 0x0000FFFFUL); -#else - struct stat st; - int64_t ino; - - if (stat(file, &st) < 0) - return (0); - ino = (int64_t)st.st_ino; - return (ino > 0xffffffff); -#endif -} - -void -extract_reference_files(const char **names) -{ - while (names && *names) - extract_reference_file(*names++); -} - -/* - * - * TEST management - * - */ - -/* - * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has - * a line like - * DEFINE_TEST(test_function) - * for each test. - */ - -/* Use "list.h" to declare all of the test functions. */ -#undef DEFINE_TEST -#define DEFINE_TEST(name) void name(void); -#include "list.h" - -/* Use "list.h" to create a list of all tests (functions and names). */ -#undef DEFINE_TEST -#define DEFINE_TEST(n) { n, #n, 0 }, -struct test_list_t tests[] = { - #include "list.h" -}; - -/* - * Summarize repeated failures in the just-completed test. - */ -static void -test_summarize(int failed, int skips_num) -{ - unsigned int i; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: - printf(failed ? "E" : "."); - fflush(stdout); - break; - case VERBOSITY_PASSFAIL: - printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); - break; - } - - log_console = (verbosity == VERBOSITY_LIGHT_REPORT); - - for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { - if (failed_lines[i].count > 1 && !failed_lines[i].skip) - logprintf("%s:%d: Summary: Failed %d times\n", - failed_filename, i, failed_lines[i].count); - } - /* Clear the failure history for the next file. */ - failed_filename = NULL; - memset(failed_lines, 0, sizeof(failed_lines)); -} - -/* - * Actually run a single test, with appropriate setup and cleanup. - */ -static int -test_run(int i, const char *tmpdir) -{ - char workdir[1024]; - char logfilename[64]; - int failures_before = failures; - int skips_before = skips; - int oldumask; - - switch (verbosity) { - case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ - break; - case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ - printf("%3d: %-64s", i, tests[i].name); - fflush(stdout); - break; - default: /* Title of test, details will follow */ - printf("%3d: %s\n", i, tests[i].name); - } - - /* Chdir to the top-level work directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, - "ERROR: Can't chdir to top work dir %s\n", tmpdir); - exit(1); - } - /* Create a log file for this test. */ - sprintf(logfilename, "%s.log", tests[i].name); - logfile = fopen(logfilename, "w"); - fprintf(logfile, "%s\n\n", tests[i].name); - /* Chdir() to a work dir for this specific test. */ - snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); - testworkdir = workdir; - if (!assertMakeDir(testworkdir, 0755) - || !assertChdir(testworkdir)) { - fprintf(stderr, - "ERROR: Can't chdir to work dir %s\n", testworkdir); - exit(1); - } - /* Explicitly reset the locale before each test. */ - setlocale(LC_ALL, "C"); - /* Record the umask before we run the test. */ - umask(oldumask = umask(0)); - /* - * Run the actual test. - */ - (*tests[i].func)(); - /* - * Clean up and report afterwards. - */ - testworkdir = NULL; - /* Restore umask */ - umask(oldumask); - /* Reset locale. */ - setlocale(LC_ALL, "C"); - /* Reset directory. */ - if (!assertChdir(tmpdir)) { - fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", - tmpdir); - exit(1); - } - /* Report per-test summaries. */ - tests[i].failures = failures - failures_before; - test_summarize(tests[i].failures, skips - skips_before); - /* Close the per-test log file. */ - fclose(logfile); - logfile = NULL; - /* If there were no failures, we can remove the work dir and logfile. */ - if (tests[i].failures == 0) { - if (!keep_temp_files && assertChdir(tmpdir)) { -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure not to leave empty directories. - * Sometimes a processing of closing files used by tests - * is not done, then rmdir will be failed and it will - * leave a empty test directory. So we should wait a few - * seconds and retry rmdir. */ - int r, t; - for (t = 0; t < 10; t++) { - if (t > 0) - Sleep(1000); - r = systemf("rmdir /S /Q %s", tests[i].name); - if (r == 0) - break; - } - systemf("del %s", logfilename); -#else - systemf("rm -rf %s", tests[i].name); - systemf("rm %s", logfilename); -#endif - } - } - /* Return appropriate status. */ - return (tests[i].failures); -} - -/* - * - * - * MAIN and support routines. - * - * - */ - -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(" -u Keep running specifies tests until one fails.\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); -} - -static char * -get_refdir(const char *d) -{ - size_t tried_size, buff_size; - char *buff, *tried, *pwd = NULL, *p = NULL; - -#ifdef PATH_MAX - buff_size = PATH_MAX; -#else - buff_size = 8192; -#endif - buff = calloc(buff_size, 1); - if (buff == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* Allocate a buffer to hold the various directories we checked. */ - tried_size = buff_size * 2; - tried = calloc(tried_size, 1); - if (tried == NULL) { - fprintf(stderr, "Unable to allocate memory\n"); - exit(1); - } - - /* If a dir was specified, try that */ - if (d != NULL) { - pwd = NULL; - snprintf(buff, buff_size, "%s", d); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - goto failure; - } - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - - /* Look for a known file. */ - snprintf(buff, buff_size, "%s", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(LIBRARY) - snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); -#else - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); -#endif - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - -#if defined(PROGRAM_ALIAS) - snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); -#endif - - if (memcmp(pwd, "/usr/obj", 8) == 0) { - snprintf(buff, buff_size, "%s", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - - snprintf(buff, buff_size, "%s/test", pwd + 8); - p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); - if (p != NULL) goto success; - strncat(tried, buff, tried_size - strlen(tried) - 1); - strncat(tried, "\n", tried_size - strlen(tried) - 1); - } - -failure: - printf("Unable to locate known reference file %s\n", KNOWNREF); - printf(" Checked following directories:\n%s\n", tried); - printf("Use -r option to specify full path to reference directory\n"); -#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) - DebugBreak(); -#endif - exit(1); - -success: - free(p); - free(pwd); - free(tried); - - /* Copy result into a fresh buffer to reduce memory usage. */ - p = strdup(buff); - free(buff); - return p; -} - -int -main(int argc, char **argv) -{ - static const int limit = sizeof(tests) / sizeof(tests[0]); - int test_set[sizeof(tests) / sizeof(tests[0])]; - int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; - time_t now; - char *refdir_alloc = NULL; - const char *progname; - char **saved_argv; - const char *tmp, *option_arg, *p; - char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; - char tmpdir_timestamp[256]; - - (void)argc; /* UNUSED */ - - /* Get the current dir. */ -#ifdef PATH_MAX - pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ -#else - pwd = getcwd(NULL, 0); -#endif - while (pwd[strlen(pwd) - 1] == '\n') - pwd[strlen(pwd) - 1] = '\0'; - -#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) - /* To stop to run the default invalid parameter handler. */ - _set_invalid_parameter_handler(invalid_parameter_handler); - /* Disable annoying assertion message box. */ - _CrtSetReportMode(_CRT_ASSERT, 0); -#endif - - /* - * Name of this program, used to build root of our temp directory - * tree. - */ - progname = p = argv[0]; - if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(testprogdir, progname); - while (*p != '\0') { - /* Support \ or / dir separators for Windows compat. */ - if (*p == '/' || *p == '\\') - { - progname = p + 1; - i = j; - } - ++p; - j++; - } - testprogdir[i] = '\0'; -#if defined(_WIN32) && !defined(__CYGWIN__) - if (testprogdir[0] != '/' && testprogdir[0] != '\\' && - !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || - (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && - testprogdir[1] == ':' && - (testprogdir[2] == '/' || testprogdir[2] == '\\'))) -#else - if (testprogdir[0] != '/') -#endif - { - /* Fixup path for relative directories. */ - if ((testprogdir = (char *)realloc(testprogdir, - strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - memmove(testprogdir + strlen(pwd) + 1, testprogdir, - strlen(testprogdir) + 1); - memcpy(testprogdir, pwd, strlen(pwd)); - testprogdir[strlen(pwd)] = '/'; - } - -#ifdef PROGRAM - /* Get the target program from environment, if available. */ - testprogfile = 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; - - /* Allow -v to be controlled through the environment. */ - if (getenv("_VERBOSITY_LEVEL") != NULL) - { - vlevel = getenv("_VERBOSITY_LEVEL"); - verbosity = atoi(vlevel); - if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) - { - /* Unsupported verbosity levels are silently ignored */ - vlevel = NULL; - verbosity = VERBOSITY_PASSFAIL; - } - } - - /* 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 - testprogfile = option_arg; -#else - fprintf(stderr, "-p option not permitted\n"); - usage(progname); -#endif - break; - case 'q': - if (!vlevel) - verbosity--; - break; - case 'r': - refdir = option_arg; - break; - case 'u': - until_failure++; - break; - case 'v': - if (!vlevel) - verbosity++; - break; - default: - fprintf(stderr, "Unrecognized option '%c'\n", - option); - usage(progname); - } - } - } - - /* - * Sanity-check that our options make sense. - */ -#ifdef PROGRAM - if (testprogfile == NULL) - { - if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + - strlen(PROGRAM) + 1)) == NULL) - { - fprintf(stderr, "ERROR: Out of memory."); - exit(1); - } - strcpy(tmp2, testprogdir); - strcat(tmp2, "/"); - strcat(tmp2, PROGRAM); - testprogfile = tmp2; - } - - { - char *testprg; -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Command.com sometimes rejects '/' separators. */ - testprg = strdup(testprogfile); - for (i = 0; testprg[i] != '\0'; i++) { - if (testprg[i] == '/') - testprg[i] = '\\'; - } - testprogfile = testprg; -#endif - /* Quote the name that gets put into shell command lines. */ - testprg = malloc(strlen(testprogfile) + 3); - strcpy(testprg, "\""); - strcat(testprg, testprogfile); - strcat(testprg, "\""); - testprog = testprg; - } -#endif - -#if !defined(_WIN32) && defined(SIGPIPE) - { /* Ignore SIGPIPE signals */ - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGPIPE, &sa, NULL); - } -#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++) { - strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), - "%Y-%m-%dT%H.%M.%S", - localtime(&now)); - snprintf(tmpdir, sizeof(tmpdir), "%s/%s.%s-%03d", tmp, - progname, tmpdir_timestamp, i); - if (assertMakeDir(tmpdir,0755)) - break; - if (i >= 999) { - 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." - */ - refdir = refdir_alloc = get_refdir(refdir); - - /* - * Banner with basic information. - */ - printf("\n"); - printf("If tests fail or crash, details will be in:\n"); - printf(" %s\n", tmpdir); - printf("\n"); - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - 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); - } else { - printf("Running "); - fflush(stdout); - } - - /* - * Run some or all of the individual tests. - */ - saved_argv = argv; - do { - argv = saved_argv; - do { - int test_num; - - test_num = get_test_set(test_set, limit, *argv, tests); - if (test_num < 0) { - printf("*** INVALID Test %s\n", *argv); - free(refdir_alloc); - free(testprogdir); - usage(progname); - return (1); - } - for (i = 0; i < test_num; i++) { - tests_run++; - if (test_run(test_set[i], tmpdir)) { - tests_failed++; - if (until_failure) - goto finish; - } - } - if (*argv != NULL) - argv++; - } while (*argv != NULL); - } while (until_failure); - -finish: - /* Must be freed after all tests run */ - free(tmp2); - free(testprogdir); - free(pwd); - - /* - * Report summary statistics. - */ - if (verbosity > VERBOSITY_SUMMARY_ONLY) { - printf("\n"); - printf("Totals:\n"); - printf(" Tests run: %8d\n", tests_run); - printf(" Tests failed: %8d\n", tests_failed); - printf(" Assertions checked:%8d\n", assertions); - printf(" Assertions failed: %8d\n", failures); - printf(" Skips reported: %8d\n", skips); - } - if (failures) { - printf("\n"); - printf("Failing tests:\n"); - for (i = 0; i < limit; ++i) { - if (tests[i].failures) - printf(" %d: %s (%d failures)\n", i, - tests[i].name, tests[i].failures); - } - printf("\n"); - printf("Details for failing tests: %s\n", tmpdir); - printf("\n"); - } else { - if (verbosity == VERBOSITY_SUMMARY_ONLY) - printf("\n"); - printf("%d tests passed, no failures\n", tests_run); - } - - free(refdir_alloc); - - /* If the final tmpdir is empty, we can remove it. */ - /* This should be the usual case when all tests succeed. */ - assertChdir(".."); - rmdir(tmpdir); - - return (tests_failed ? 1 : 0); -} Property changes on: vendor/libarchive/dist/tar/test/main.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: vendor/libarchive/dist/tar/test/CMakeLists.txt =================================================================== --- vendor/libarchive/dist/tar/test/CMakeLists.txt (revision 314564) +++ vendor/libarchive/dist/tar/test/CMakeLists.txt (revision 314565) @@ -1,108 +1,109 @@ ############################################ # # How to build bsdtar_test # ############################################ IF(ENABLE_TAR AND ENABLE_TEST) SET(bsdtar_test_SOURCES ../../test_utils/test_utils.c - main.c + ../../test_utils/test_main.c test.h test_0.c test_basic.c test_copy.c test_empty_mtree.c test_extract_tar_Z.c test_extract_tar_bz2.c test_extract_tar_grz.c test_extract_tar_gz.c test_extract_tar_lrz.c test_extract_tar_lz.c test_extract_tar_lz4.c test_extract_tar_lzma.c test_extract_tar_lzo.c test_extract_tar_xz.c test_format_newc.c test_help.c test_leading_slash.c test_missing_file.c test_option_C_upper.c test_option_H_upper.c test_option_L_upper.c test_option_O_upper.c test_option_T_upper.c test_option_U_upper.c test_option_X_upper.c test_option_a.c test_option_b.c test_option_b64encode.c test_option_exclude.c test_option_gid_gname.c test_option_grzip.c test_option_j.c test_option_k.c test_option_keep_newer_files.c test_option_lrzip.c test_option_lz4.c test_option_lzma.c test_option_lzop.c test_option_n.c test_option_newer_than.c test_option_nodump.c test_option_older_than.c test_option_passphrase.c test_option_q.c test_option_r.c test_option_s.c test_option_uid_uname.c test_option_uuencode.c test_option_xz.c test_option_z.c test_patterns.c test_print_longpath.c test_stdio.c test_strip_components.c test_symlink_dir.c test_version.c test_windows.c ) # # Register target # ADD_EXECUTABLE(bsdtar_test ${bsdtar_test_SOURCES}) SET_PROPERTY(TARGET bsdtar_test PROPERTY COMPILE_DEFINITIONS LIST_H) # # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. # GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h ${CMAKE_CURRENT_LIST_FILE} ${bsdtar_test_SOURCES}) SET_PROPERTY(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) # list.h has a line DEFINE_TEST(testname) for every # test. We can use that to define the tests for cmake by # defining a DEFINE_TEST macro and reading list.h in. MACRO (DEFINE_TEST _testname) ADD_TEST( NAME bsdtar_${_testname} COMMAND bsdtar_test -vv -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} ${_testname}) ENDMACRO (DEFINE_TEST _testname) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/tar/test) # Experimental new test handling ADD_CUSTOM_TARGET(run_bsdtar_test COMMAND bsdtar_test -p $ -r ${CMAKE_CURRENT_SOURCE_DIR} -vv) ADD_DEPENDENCIES(run_bsdtar_test bsdtar) ADD_DEPENDENCIES(run_all_tests run_bsdtar_test) ENDIF(ENABLE_TAR AND ENABLE_TEST) Index: vendor/libarchive/dist/tar/test/test.h =================================================================== --- vendor/libarchive/dist/tar/test/test.h (revision 314564) +++ vendor/libarchive/dist/tar/test/test.h (revision 314565) @@ -1,354 +1,40 @@ /* - * Copyright (c) 2003-2006 Tim Kientzle + * Copyright (c) 2003-2017 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: src/usr.bin/tar/test/test.h,v 1.4 2008/08/21 07:04:57 kientzle Exp $ + * $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) && !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 +#define KNOWNREF "test_patterns_2.tar.uu" +#define ENVBASE "BSDTAR" /* Prefix for environment variables. */ +#define PROGRAM "bsdtar" /* Name of program being tested. */ +#define PROGRAM_ALIAS "tar" /* Generic alias for program */ +#undef LIBRARY /* Not testing a library. */ +#undef EXTRA_DUMP /* How to dump extra data */ +#undef EXTRA_ERRNO /* How to dump errno */ +/* How to generate extra version info. */ +#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") -#include /* Windows requires this before sys/stat.h */ -#include - -#if HAVE_DIRENT_H -#include -#endif -#ifdef HAVE_DIRECT_H -#include -#define dirent direct -#endif -#include -#include -#ifdef HAVE_IO_H -#include -#endif -#ifdef HAVE_STDINT_H -#include -#endif -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_WINDOWS_H -#include -#endif - -/* - * System-specific tweaks. We really want to minimize these - * as much as possible, since they make it harder to understand - * the mainline code. - */ - -/* Windows (including Visual Studio and MinGW but not Cygwin) */ -#if defined(_WIN32) && !defined(__CYGWIN__) -#if !defined(__BORLANDC__) -#undef chdir -#define chdir _chdir -#define strdup _strdup -#endif -#endif - -/* Visual Studio */ -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf sprintf_s -#endif - -#if defined(__BORLANDC__) -#pragma warn -8068 /* Constant out of range in comparison. */ -#endif - -/* Haiku OS and QNX */ -#if defined(__HAIKU__) || defined(__QNXNTO__) -/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ -#include -#endif - -/* Get a real definition for __FBSDID if we can */ -#if HAVE_SYS_CDEFS_H -#include -#endif - -/* If not, define it so as to avoid dangling semicolons. */ -#ifndef __FBSDID -#define __FBSDID(a) struct _undefined_hack -#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) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) -/* chdir() and error if it fails */ -#define assertChdir(path) \ - assertion_chdir(__FILE__, __LINE__, path) -/* Assert two integers are the same. Reports value of each one if not. */ -#define assertEqualInt(v1,v2) \ - assertion_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) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) -#define assertEqualUTF8String(v1,v2) \ - assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) -/* As above, but v1 and v2 are wchar_t * */ -#define assertEqualWString(v1,v2) \ - assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) -/* As above, but raw blocks of bytes. */ -#define assertEqualMem(v1, v2, l) \ - assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) -/* Assert that memory is full of a specified byte */ -#define assertMemoryFilledWith(v1, l, b) \ - assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) -/* Assert two files are the same. */ -#define assertEqualFile(f1, f2) \ - assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) -/* Assert that a file is empty. */ -#define assertEmptyFile(pathname) \ - assertion_empty_file(__FILE__, __LINE__, (pathname)) -/* Assert that a file is not empty. */ -#define assertNonEmptyFile(pathname) \ - assertion_non_empty_file(__FILE__, __LINE__, (pathname)) -#define assertFileAtime(pathname, sec, nsec) \ - assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileAtimeRecent(pathname) \ - assertion_file_atime_recent(__FILE__, __LINE__, pathname) -#define assertFileBirthtime(pathname, sec, nsec) \ - assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileBirthtimeRecent(pathname) \ - assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) -/* Assert that a file exists; supports printf-style arguments. */ -#define assertFileExists(pathname) \ - assertion_file_exists(__FILE__, __LINE__, pathname) -/* Assert that a file exists. */ -#define assertFileNotExists(pathname) \ - assertion_file_not_exists(__FILE__, __LINE__, pathname) -/* Assert that file contents match a string. */ -#define assertFileContents(data, data_size, pathname) \ - assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) -/* Verify that a file does not contain invalid strings */ -#define assertFileContainsNoInvalidStrings(pathname, strings) \ - assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) -#define assertFileMtime(pathname, sec, nsec) \ - assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) -#define assertFileMtimeRecent(pathname) \ - assertion_file_mtime_recent(__FILE__, __LINE__, pathname) -#define assertFileNLinks(pathname, nlinks) \ - assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) -#define assertFileSize(pathname, size) \ - assertion_file_size(__FILE__, __LINE__, pathname, size) -#define assertFileMode(pathname, mode) \ - assertion_file_mode(__FILE__, __LINE__, pathname, mode) -#define assertTextFileContents(text, pathname) \ - assertion_text_file_contents(__FILE__, __LINE__, text, pathname) -#define assertFileContainsLinesAnyOrder(pathname, lines) \ - assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) -#define assertIsDir(pathname, mode) \ - assertion_is_dir(__FILE__, __LINE__, pathname, mode) -#define assertIsHardlink(path1, path2) \ - assertion_is_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsNotHardlink(path1, path2) \ - assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) -#define assertIsReg(pathname, mode) \ - assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) -/* Create a directory, report error if it fails. */ -#define assertMakeDir(dirname, mode) \ - assertion_make_dir(__FILE__, __LINE__, dirname, mode) -#define assertMakeFile(path, mode, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) -#define assertMakeBinFile(path, mode, csize, contents) \ - assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) -#define assertMakeHardlink(newfile, oldfile) \ - assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) -#define assertMakeSymlink(newfile, linkto) \ - assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) -#define assertNodump(path) \ - assertion_nodump(__FILE__, __LINE__, path) -#define assertUmask(mask) \ - assertion_umask(__FILE__, __LINE__, mask) -#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ - assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) - -/* - * 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 \ - skipping_setup(__FILE__, __LINE__);test_skipping - -/* Function declarations. These are defined in test_utility.c. */ -void failure(const char *fmt, ...); -int assertion_assert(const char *, int, int, const char *, void *); -int assertion_chdir(const char *, int, const char *); -int assertion_empty_file(const char *, int, const char *); -int assertion_equal_file(const char *, int, const char *, const char *); -int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); -int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); -int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); -int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); -int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); -int assertion_file_atime(const char *, int, const char *, long, long); -int assertion_file_atime_recent(const char *, int, const char *); -int assertion_file_birthtime(const char *, int, const char *, long, long); -int assertion_file_birthtime_recent(const char *, int, const char *); -int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); -int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); -int assertion_file_contents(const char *, int, const void *, int, const char *); -int assertion_file_exists(const char *, int, const char *); -int assertion_file_mode(const char *, int, const char *, int); -int assertion_file_mtime(const char *, int, const char *, long, long); -int assertion_file_mtime_recent(const char *, int, const char *); -int assertion_file_nlinks(const char *, int, const char *, int); -int assertion_file_not_exists(const char *, int, const char *); -int assertion_file_size(const char *, int, const char *, long); -int assertion_is_dir(const char *, int, const char *, int); -int assertion_is_hardlink(const char *, int, const char *, const char *); -int assertion_is_not_hardlink(const char *, int, const char *, const char *); -int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); -int assertion_make_dir(const char *, int, const char *, int); -int assertion_make_file(const char *, int, const char *, int, int, const void *); -int assertion_make_hardlink(const char *, int, const char *newpath, const char *); -int assertion_make_symlink(const char *, int, const char *newpath, const char *); -int assertion_nodump(const char *, int, const char *); -int assertion_non_empty_file(const char *, int, const char *); -int assertion_text_file_contents(const char *, int, const char *buff, const char *f); -int assertion_umask(const char *, int, int); -int assertion_utimes(const char *, int, const char *, long, long, long, long ); - -void skipping_setup(const char *, int); -void test_skipping(const char *fmt, ...); - -/* Like sprintf, then system() */ -int systemf(const char * fmt, ...); - -/* Delay until time() returns a value after this. */ -void sleepUntilAfter(time_t); - -/* Return true if this platform can create symlinks. */ -int canSymlink(void); - -/* Return true if this platform can run the "bzip2" program. */ -int canBzip2(void); - -/* Return true if this platform can run the "grzip" program. */ -int canGrzip(void); - -/* Return true if this platform can run the "gzip" program. */ -int canGzip(void); - -/* Return true if this platform can run the specified command. */ -int canRunCommand(const char *); - -/* Return true if this platform can run the "lrzip" program. */ -int canLrzip(void); - -/* Return true if this platform can run the "lz4" program. */ -int canLz4(void); - -/* Return true if this platform can run the "lzip" program. */ -int canLzip(void); - -/* Return true if this platform can run the "lzma" program. */ -int canLzma(void); - -/* Return true if this platform can run the "lzop" program. */ -int canLzop(void); - -/* Return true if this platform can run the "xz" program. */ -int canXz(void); - -/* Return true if this filesystem can handle nodump flags. */ -int canNodump(void); - -/* Return true if the file has large i-node number(>0xffffffff). */ -int is_LargeInode(const char *); - -/* 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, ...); - -/* Dump block of bytes to a file. */ -void dumpfile(const char *filename, void *, size_t); - -/* Extracts named reference file to the current directory. */ -void extract_reference_file(const char *); -/* Copies named reference file to the current directory. */ -void copy_reference_file(const char *); - -/* Extracts a list of files to the current directory. - * List must be NULL terminated. - */ -void extract_reference_files(const char **); - -/* Subtract umask from mode */ -mode_t umasked(mode_t expected_mode); - -/* Path to working directory for current test */ -extern const char *testworkdir; - -/* - * Special interfaces for program test harness. - */ - -/* Pathname of exe to be tested. */ -extern const char *testprogfile; -/* Name of exe to use in printf-formatted command strings. */ -/* On Windows, this includes leading/trailing quotes. */ -extern const char *testprog; - -#ifdef USE_DMALLOC -#include -#endif +#include "test_common.h" Index: vendor/libarchive/dist/tar/test/test_version.c =================================================================== --- vendor/libarchive/dist/tar/test/test_version.c (revision 314564) +++ vendor/libarchive/dist/tar/test/test_version.c (revision 314565) @@ -1,103 +1,34 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2017 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: src/usr.bin/tar/test/test_version.c,v 1.2 2008/05/26 17:10:10 kientzle Exp $"); /* * Test that --version option works and generates reasonable output. */ DEFINE_TEST(test_version) { - int r; - char *p, *q; - size_t s; - - - r = systemf("%s --version >version.stdout 2>version.stderr", testprog); - if (r != 0) - r = systemf("%s -W version >version.stdout 2>version.stderr", - testprog); - failure("Unable to run either %s --version or %s -W version", - testprog, testprog); - if (!assert(r == 0)) - return; - - /* --version should generate nothing to stdout. */ - assertEmptyFile("version.stderr"); - /* Verify format of version message. */ - q = p = slurpfile(&s, "version.stdout"); - /* Version message should start with name of program, then space. */ - assert(s > 6); - failure("Version must start with 'bsdtar': ``%s''", p); - if (!assertEqualMem(q, "bsdtar ", 7)) - goto done; - q += 7; s -= 7; - /* Version number is a series of digits and periods. */ - while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { - ++q; - --s; - } - /* Version number terminated by space. */ - failure("No space after bsdtar version: ``%s''", p); - assert(s > 1); - /* Skip a single trailing a,b,c, or d. */ - if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') - ++q; - failure("No space after bsdtar version: ``%s''", p); - assert(*q == ' '); - ++q; --s; - /* Separator. */ - failure("No `-' between bsdtar and libarchive versions: ``%s''", p); - assertEqualMem(q, "- ", 2); - q += 2; s -= 2; - /* libarchive name and version number */ - failure("Not long enough for libarchive version: ``%s''", p); - assert(s > 11); - failure("Libarchive version must start with `libarchive': ``%s''", p); - assertEqualMem(q, "libarchive ", 11); - q += 11; s -= 11; - /* Version number is a series of digits and periods. */ - while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { - ++q; - --s; - } - /* Skip a single trailing a,b,c, or d. */ - if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') - ++q; - /* Skip arbitrary third-party version numbers. */ - while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) { - ++q; - --s; - } - /* All terminated by end-of-line. */ - assert(s >= 1); - /* Skip an optional CR character (e.g., Windows) */ - failure("Version output must end with \\n or \\r\\n"); - if (*q == '\r') { ++q; --s; } - assertEqualMem(q, "\n", 1); -done: - free(p); + assertVersion(testprog, "bsdtar"); } Index: vendor/libarchive/dist/tar/util.c =================================================================== --- vendor/libarchive/dist/tar/util.c (revision 314564) +++ vendor/libarchive/dist/tar/util.c (revision 314565) @@ -1,750 +1,750 @@ /*- * 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 "bsdtar_platform.h" __FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.23 2008/12/15 06:00:25 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include /* Linux doesn't define mode_t, etc. in sys/stat.h. */ #endif #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCTYPE_H #include #else /* If we don't have wctype, we need to hack up some version of iswprint(). */ #define iswprint isprint #endif #include "bsdtar.h" #include "err.h" #include "passphrase.h" static size_t bsdtar_expand_char(char *, size_t, char); static const char *strip_components(const char *path, int elements); #if defined(_WIN32) && !defined(__CYGWIN__) #define read _read #endif /* TODO: Hack up a version of mbtowc for platforms with no wide * character support at all. I think the following might suffice, * but it needs careful testing. * #if !HAVE_MBTOWC * #define mbtowc(wcp, p, n) ((*wcp = *p), 1) * #endif */ /* * Print a string, taking care with any non-printable characters. * * Note that we use a stack-allocated buffer to receive the formatted * string if we can. This is partly performance (avoiding a call to * malloc()), partly out of expedience (we have to call vsnprintf() * before malloc() anyway to find out how big a buffer we need; we may * as well point that first call at a small local buffer in case it * works), but mostly for safety (so we can use this to print messages * about out-of-memory conditions). */ void safe_fprintf(FILE *f, const char *fmt, ...) { char fmtbuff_stack[256]; /* Place to format the printf() string. */ char outbuff[256]; /* Buffer for outgoing characters. */ char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ int fmtbuff_length; int length, n; va_list ap; const char *p; unsigned i; wchar_t wc; char try_wc; /* Use a stack-allocated buffer if we can, for speed and safety. */ fmtbuff_heap = NULL; fmtbuff_length = sizeof(fmtbuff_stack); fmtbuff = fmtbuff_stack; /* Try formatting into the stack buffer. */ va_start(ap, fmt); length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); /* If the result was too large, allocate a buffer on the heap. */ while (length < 0 || length >= fmtbuff_length) { if (length >= fmtbuff_length) fmtbuff_length = length+1; else if (fmtbuff_length < 8192) fmtbuff_length *= 2; else if (fmtbuff_length < 1000000) fmtbuff_length += fmtbuff_length / 4; else { length = fmtbuff_length; fmtbuff_heap[length-1] = '\0'; break; } free(fmtbuff_heap); fmtbuff_heap = malloc(fmtbuff_length); /* Reformat the result into the heap buffer if we can. */ if (fmtbuff_heap != NULL) { fmtbuff = fmtbuff_heap; va_start(ap, fmt); length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); } else { /* Leave fmtbuff pointing to the truncated * string in fmtbuff_stack. */ fmtbuff = fmtbuff_stack; length = sizeof(fmtbuff_stack) - 1; break; } } /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit * more portable, so we use that here instead. */ if (mbtowc(NULL, NULL, 1) == -1) { /* Reset the shift state. */ /* mbtowc() should never fail in practice, but * handle the theoretical error anyway. */ free(fmtbuff_heap); return; } /* Write data, expanding unprintable characters. */ p = fmtbuff; i = 0; try_wc = 1; while (*p != '\0') { /* Convert to wide char, test if the wide * char is printable in the current locale. */ if (try_wc && (n = mbtowc(&wc, p, length)) != -1) { length -= n; if (iswprint(wc) && wc != L'\\') { /* Printable, copy the bytes through. */ while (n-- > 0) outbuff[i++] = *p++; } else { /* Not printable, format the bytes. */ while (n-- > 0) i += (unsigned)bsdtar_expand_char( outbuff, i, *p++); } } else { /* After any conversion failure, don't bother * trying to convert the rest. */ i += (unsigned)bsdtar_expand_char(outbuff, i, *p++); try_wc = 0; } /* If our output buffer is full, dump it and keep going. */ if (i > (sizeof(outbuff) - 128)) { outbuff[i] = '\0'; fprintf(f, "%s", outbuff); i = 0; } } outbuff[i] = '\0'; fprintf(f, "%s", outbuff); /* If we allocated a heap-based formatting buffer, free it now. */ free(fmtbuff_heap); } /* * Render an arbitrary sequence of bytes into printable ASCII characters. */ static size_t bsdtar_expand_char(char *buff, size_t offset, char c) { size_t i = offset; if (isprint((unsigned char)c) && c != '\\') buff[i++] = c; else { buff[i++] = '\\'; switch (c) { case '\a': buff[i++] = 'a'; break; case '\b': buff[i++] = 'b'; break; case '\f': buff[i++] = 'f'; break; case '\n': buff[i++] = 'n'; break; #if '\r' != '\n' /* On some platforms, \n and \r are the same. */ case '\r': buff[i++] = 'r'; break; #endif case '\t': buff[i++] = 't'; break; case '\v': buff[i++] = 'v'; break; case '\\': buff[i++] = '\\'; break; default: sprintf(buff + i, "%03o", 0xFF & (int)c); i += 3; } } return (i - offset); } int yes(const char *fmt, ...) { char buff[32]; char *p; ssize_t l; va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, " (y/N)? "); fflush(stderr); l = read(2, buff, sizeof(buff) - 1); if (l < 0) { fprintf(stderr, "Keyboard read failed\n"); exit(1); } if (l == 0) return (0); buff[l] = 0; for (p = buff; *p != '\0'; p++) { if (isspace((unsigned char)*p)) continue; switch(*p) { case 'y': case 'Y': return (1); case 'n': case 'N': return (0); default: return (0); } } return (0); } /*- * The logic here for -C attempts to avoid * chdir() as long as possible. For example: * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") * "-C /foo -C bar file" needs chdir("/foo/bar") * "-C /foo -C bar /file1" does not need chdir() * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 * * The only correct way to handle this is to record a "pending" chdir * request and combine multiple requests intelligently until we * need to process a non-absolute file. set_chdir() adds the new dir * to the pending list; do_chdir() actually executes any pending chdir. * * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. * */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) { #if defined(_WIN32) && !defined(__CYGWIN__) if (newdir[0] == '/' || newdir[0] == '\\' || /* Detect this type, for example, "C:\" or "C:/" */ (((newdir[0] >= 'a' && newdir[0] <= 'z') || (newdir[0] >= 'A' && newdir[0] <= 'Z')) && newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { #else if (newdir[0] == '/') { #endif /* The -C /foo -C /bar case; dump first one. */ free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } if (bsdtar->pending_chdir == NULL) /* Easy case: no previously-saved dir. */ bsdtar->pending_chdir = strdup(newdir); else { /* The -C /foo -C bar case; concatenate */ char *old_pending = bsdtar->pending_chdir; size_t old_len = strlen(old_pending); bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2); if (old_pending[old_len - 1] == '/') old_pending[old_len - 1] = '\0'; if (bsdtar->pending_chdir != NULL) sprintf(bsdtar->pending_chdir, "%s/%s", old_pending, newdir); free(old_pending); } if (bsdtar->pending_chdir == NULL) lafe_errc(1, errno, "No memory"); } void do_chdir(struct bsdtar *bsdtar) { if (bsdtar->pending_chdir == NULL) return; if (chdir(bsdtar->pending_chdir) != 0) { lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } static const char * strip_components(const char *p, int elements) { /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif elements--; break; case '\0': /* Path is too short, skip it. */ return (NULL); } } /* Skip any / characters. This handles short paths that have * additional / termination. This also handles the case where * the logic above stops in the middle of a duplicate // * sequence (which would otherwise get converted to an * absolute path). */ for (;;) { switch (*p) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif ++p; break; case '\0': return (NULL); default: return (p); } } } static void warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading '%c' from member names", c[0]); bsdtar->warned_lead_slash = 1; } } static void warn_strip_drive_letter(struct bsdtar *bsdtar) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; } } /* * Convert absolute path to non-absolute path by skipping leading * absolute path prefixes. */ static const char* strip_absolute_path(struct bsdtar *bsdtar, const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; warn_strip_drive_letter(bsdtar); } /* Remove multiple leading slashes and Windows drive letters. */ do { rp = p; if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; warn_strip_drive_letter(bsdtar); } /* Remove leading "/../", "/./", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" for next pass. */ } else if (p[1] == '.' && (p[2] == '/' || p[2] == '\\')) { p += 2; /* Remove "/.", leave "/" for next pass. */ } else p += 1; /* Remove "/". */ warn_strip_leading_char(bsdtar, rp); } } while (rp != p); return (p); } /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * * Note: The rewrites are applied uniformly to pathnames and hardlink * names but not to symlink bodies. This is deliberate: Symlink * bodies are not necessarily filenames. Even when they are, they * need to be interpreted relative to the directory containing them, * so simple rewrites like this are rarely appropriate. * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); const char *original_name = name; const char *hardlinkname = archive_entry_hardlink(entry); const char *original_hardlinkname = hardlinkname; #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; /* Apply user-specified substitution to pathname. */ r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_pathname(entry, subst_name); if (*subst_name == '\0') { free(subst_name); return -1; } else free(subst_name); name = archive_entry_pathname(entry); original_name = name; } /* Apply user-specified substitution to hardlink target. */ if (hardlinkname != NULL) { r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } hardlinkname = archive_entry_hardlink(entry); original_hardlinkname = hardlinkname; } /* Apply user-specified substitution to symlink body. */ if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_symlink(entry, subst_name); free(subst_name); } } #endif /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); if (hardlinkname != NULL) { hardlinkname = strip_components(hardlinkname, bsdtar->strip_components); if (hardlinkname == NULL) return (1); } } - if (!bsdtar->option_absolute_paths) { + if ((bsdtar->flags & OPTFLAG_ABSOLUTE_PATHS) == 0) { /* By default, don't write or restore absolute pathnames. */ name = strip_absolute_path(bsdtar, name); if (*name == '\0') name = "."; if (hardlinkname != NULL) { hardlinkname = strip_absolute_path(bsdtar, hardlinkname); if (*hardlinkname == '\0') return (1); } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } /* Replace name in archive_entry. */ if (name != original_name) { archive_entry_copy_pathname(entry, name); } if (hardlinkname != original_hardlinkname) { archive_entry_copy_hardlink(entry, hardlinkname); } return (0); } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are quite a headache. Hence the * following simple utility function. */ const char * tar_i64toa(int64_t n0) { static char buff[24]; uint64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); } while (n /= 10); if (n0 < 0) *--p = '-'; return p; } /* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" * * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" * TODO: After this works, push it down into libarchive. * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. * * Note: This is currently only used within write.c, so should * not handle \ path separators. */ int pathcmp(const char *a, const char *b) { /* Skip leading './' */ if (a[0] == '.' && a[1] == '/' && a[2] != '\0') a += 2; if (b[0] == '.' && b[1] == '/' && b[2] != '\0') b += 2; /* Find the first difference, or return (0) if none. */ while (*a == *b) { if (*a == '\0') return (0); a++; b++; } /* * If one ends in '/' and the other one doesn't, * they're the same. */ if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') return (0); if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') return (0); /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); } #define PPBUFF_SIZE 1024 const char * passphrase_callback(struct archive *a, void *_client_data) { struct bsdtar *bsdtar = (struct bsdtar *)_client_data; (void)a; /* UNUSED */ if (bsdtar->ppbuff == NULL) { bsdtar->ppbuff = malloc(PPBUFF_SIZE); if (bsdtar->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", bsdtar->ppbuff, PPBUFF_SIZE); } void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { sprintf(tmp, "%lu ", (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { sprintf(tmp, "%lu", (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { sprintf(tmp, "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); #define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); } Index: vendor/libarchive/dist/tar/write.c =================================================================== --- vendor/libarchive/dist/tar/write.c (revision 314564) +++ vendor/libarchive/dist/tar/write.c (revision 314565) @@ -1,1052 +1,1055 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2012 Michihiro NAKAJIMA * 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 "bsdtar_platform.h" __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_LIBGEN_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "bsdtar.h" #include "err.h" #include "line_reader.h" #ifndef O_BINARY #define O_BINARY 0 #endif struct archive_dir_entry { struct archive_dir_entry *next; time_t mtime_sec; int mtime_nsec; char *name; }; struct archive_dir { struct archive_dir_entry *head, *tail; }; static int append_archive(struct bsdtar *, struct archive *, struct archive *ina); static int append_archive_filename(struct bsdtar *, struct archive *, const char *fname); static void archive_names_from_file(struct bsdtar *bsdtar, struct archive *a); static int copy_file_data_block(struct bsdtar *, struct archive *a, struct archive *, struct archive_entry *); static void excluded_callback(struct archive *, void *, struct archive_entry *); static void report_write(struct bsdtar *, struct archive *, struct archive_entry *, int64_t progress); static void test_for_append(struct bsdtar *); static int metadata_filter(struct archive *, void *, struct archive_entry *); static void write_archive(struct archive *, struct bsdtar *); static void write_entry(struct bsdtar *, struct archive *, struct archive_entry *); static void write_file(struct bsdtar *, struct archive *, struct archive_entry *); static void write_hierarchy(struct bsdtar *, struct archive *, const char *); #if defined(_WIN32) && !defined(__CYGWIN__) /* Not a full lseek() emulation, but enough for our needs here. */ static int seek_file(int fd, int64_t offset, int whence) { LARGE_INTEGER distance; (void)whence; /* UNUSED */ distance.QuadPart = offset; return (SetFilePointerEx((HANDLE)_get_osfhandle(fd), distance, NULL, FILE_BEGIN) ? 1 : -1); } #define open _open #define close _close #define read _read #ifdef lseek #undef lseek #endif #define lseek seek_file #endif static void set_writer_options(struct bsdtar *bsdtar, struct archive *a) { const char *writer_options; int r; writer_options = getenv(ENV_WRITER_OPTIONS); if (writer_options != NULL) { size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; size_t opt_len = strlen(writer_options) + 1; char *p; /* Set default write options. */ if ((p = malloc(module_len + opt_len)) == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or filters which are not added to * the archive write object. */ memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); memcpy(p, writer_options, opt_len); r = archive_write_set_options(a, p); free(p); if (r < ARCHIVE_WARN) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); } static void set_reader_options(struct bsdtar *bsdtar, struct archive *a) { const char *reader_options; int r; (void)bsdtar; /* UNUSED */ reader_options = getenv(ENV_READER_OPTIONS); if (reader_options != NULL) { size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; size_t opt_len = strlen(reader_options) + 1; char *p; /* Set default write options. */ if ((p = malloc(module_len + opt_len)) == NULL) if (p == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or filters which are not added to * the archive write object. */ memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); memcpy(p, reader_options, opt_len); r = archive_read_set_options(a, p); free(p); if (r < ARCHIVE_WARN) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } } void tar_mode_c(struct bsdtar *bsdtar) { struct archive *a; const void *filter_name; int r; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) lafe_errc(1, 0, "no files or directories specified"); a = archive_write_new(); /* Support any format that the library supports. */ if (cset_get_format(bsdtar->cset) == NULL) { r = archive_write_set_format_pax_restricted(a); cset_set_format(bsdtar->cset, "pax restricted"); } else { r = archive_write_set_format_by_name(a, cset_get_format(bsdtar->cset)); } if (r != ARCHIVE_OK) { fprintf(stderr, "Can't use format %s: %s\n", cset_get_format(bsdtar->cset), archive_error_string(a)); usage(); } archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block); r = cset_write_add_filters(bsdtar->cset, a, &filter_name); if (r < ARCHIVE_WARN) { lafe_errc(1, 0, "Unsupported compression option --%s", (const char *)filter_name); } set_writer_options(bsdtar, a); if (bsdtar->passphrase != NULL) r = archive_write_set_passphrase(a, bsdtar->passphrase); else r = archive_write_set_passphrase_callback(a, bsdtar, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_filename(a, bsdtar->filename)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); } /* * Same as 'c', except we only support tar or empty formats in * uncompressed files on disk. */ void tar_mode_r(struct bsdtar *bsdtar) { int64_t end_offset; int format; struct archive *a; struct archive_entry *entry; int r; /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; #if defined(__BORLANDC__) bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY); #else bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY, 0666); #endif if (bsdtar->fd < 0) lafe_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); archive_read_support_filter_all(a); archive_read_support_format_empty(a); archive_read_support_format_tar(a); archive_read_support_format_gnutar(a); set_reader_options(bsdtar, a); r = archive_read_open_fd(a, bsdtar->fd, 10240); if (r != ARCHIVE_OK) lafe_errc(1, archive_errno(a), "Can't read archive %s: %s", bsdtar->filename, archive_error_string(a)); while (0 == archive_read_next_header(a, &entry)) { if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { archive_read_free(a); close(bsdtar->fd); lafe_errc(1, 0, "Cannot append to compressed archive."); } /* Keep going until we hit end-of-archive */ format = archive_format(a); } end_offset = archive_read_header_position(a); archive_read_free(a); /* Re-open archive for writing */ a = archive_write_new(); /* * Set the format to be used for writing. To allow people to * extend empty files, we need to allow them to specify the format, * which opens the possibility that they will specify a format that * doesn't match the existing format. Hence, the following bit * of arcane ugliness. */ if (cset_get_format(bsdtar->cset) != NULL) { /* If the user requested a format, use that, but ... */ archive_write_set_format_by_name(a, cset_get_format(bsdtar->cset)); /* ... complain if it's not compatible. */ format &= ARCHIVE_FORMAT_BASE_MASK; if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK) && format != ARCHIVE_FORMAT_EMPTY) { lafe_errc(1, 0, "Format %s is incompatible with the archive %s.", cset_get_format(bsdtar->cset), bsdtar->filename); } } else { /* * Just preserve the current format, with a little care * for formats that libarchive can't write. */ if (format == ARCHIVE_FORMAT_EMPTY) format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; archive_write_set_format(a, format); } if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) lafe_errc(1, errno, "Could not seek to archive end"); set_writer_options(bsdtar, a); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); /* XXX check return val XXX */ close(bsdtar->fd); bsdtar->fd = -1; } void tar_mode_u(struct bsdtar *bsdtar) { int64_t end_offset; struct archive *a; struct archive_entry *entry; int format; struct archive_dir_entry *p; struct archive_dir archive_dir; bsdtar->archive_dir = &archive_dir; memset(&archive_dir, 0, sizeof(archive_dir)); format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); bsdtar->fd = open(bsdtar->filename, O_RDWR | O_BINARY); if (bsdtar->fd < 0) lafe_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); archive_read_support_filter_all(a); archive_read_support_format_tar(a); archive_read_support_format_gnutar(a); set_reader_options(bsdtar, a); if (archive_read_open_fd(a, bsdtar->fd, bsdtar->bytes_per_block) != ARCHIVE_OK) { lafe_errc(1, 0, "Can't open %s: %s", bsdtar->filename, archive_error_string(a)); } /* Build a list of all entries and their recorded mod times. */ while (0 == archive_read_next_header(a, &entry)) { if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { archive_read_free(a); close(bsdtar->fd); lafe_errc(1, 0, "Cannot append to compressed archive."); } if (archive_match_exclude_entry(bsdtar->matching, ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER | ARCHIVE_MATCH_EQUAL, entry) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(bsdtar->matching)); /* Record the last format determination we see */ format = archive_format(a); /* Keep going until we hit end-of-archive */ } end_offset = archive_read_header_position(a); archive_read_free(a); /* Re-open archive for writing. */ a = archive_write_new(); /* * Set format to same one auto-detected above. */ archive_write_set_format(a, format); archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block); if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) lafe_errc(1, errno, "Could not seek to archive end"); set_writer_options(bsdtar, a); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); close(bsdtar->fd); bsdtar->fd = -1; while (bsdtar->archive_dir->head != NULL) { p = bsdtar->archive_dir->head->next; free(bsdtar->archive_dir->head->name); free(bsdtar->archive_dir->head); bsdtar->archive_dir->head = p; } bsdtar->archive_dir->tail = NULL; } /* * Write user-specified files/dirs to opened archive. */ static void write_archive(struct archive *a, struct bsdtar *bsdtar) { const char *arg; struct archive_entry *entry, *sparse_entry; /* Choose a suitable copy buffer size */ bsdtar->buff_size = 64 * 1024; while (bsdtar->buff_size < (size_t)bsdtar->bytes_per_block) bsdtar->buff_size *= 2; /* Try to compensate for space we'll lose to alignment. */ bsdtar->buff_size += 16 * 1024; /* Allocate a buffer for file data. */ if ((bsdtar->buff = malloc(bsdtar->buff_size)) == NULL) lafe_errc(1, 0, "cannot allocate memory"); if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) lafe_errc(1, 0, "cannot create link resolver"); archive_entry_linkresolver_set_strategy(bsdtar->resolver, archive_format(a)); /* Create a read_disk object. */ if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) lafe_errc(1, 0, "Cannot create read_disk object"); /* Tell the read_disk how handle symlink. */ switch (bsdtar->symlink_mode) { case 'H': archive_read_disk_set_symlink_hybrid(bsdtar->diskreader); break; case 'L': archive_read_disk_set_symlink_logical(bsdtar->diskreader); break; default: archive_read_disk_set_symlink_physical(bsdtar->diskreader); break; } /* Register entry filters. */ archive_read_disk_set_matching(bsdtar->diskreader, bsdtar->matching, excluded_callback, bsdtar); archive_read_disk_set_metadata_filter_callback( bsdtar->diskreader, metadata_filter, bsdtar); /* Set the behavior of archive_read_disk. */ archive_read_disk_set_behavior(bsdtar->diskreader, bsdtar->readdisk_flags); archive_read_disk_set_standard_lookup(bsdtar->diskreader); if (bsdtar->names_from_file != NULL) archive_names_from_file(bsdtar, a); while (*bsdtar->argv) { arg = *bsdtar->argv; if (arg[0] == '-' && arg[1] == 'C') { arg += 2; if (*arg == '\0') { bsdtar->argv++; arg = *bsdtar->argv; if (arg == NULL) { lafe_warnc(0, "%s", "Missing argument for -C"); bsdtar->return_value = 1; goto cleanup; } if (*arg == '\0') { lafe_warnc(0, "Meaningless argument for -C: ''"); bsdtar->return_value = 1; goto cleanup; } } set_chdir(bsdtar, arg); } else { if (*arg != '/' && (arg[0] != '@' || arg[1] != '/')) do_chdir(bsdtar); /* Handle a deferred -C */ if (*arg == '@') { if (append_archive_filename(bsdtar, a, arg + 1) != 0) break; } else write_hierarchy(bsdtar, a, arg); } bsdtar->argv++; } archive_read_disk_set_matching(bsdtar->diskreader, NULL, NULL, NULL); archive_read_disk_set_metadata_filter_callback( bsdtar->diskreader, NULL, NULL); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); while (entry != NULL) { int r; struct archive_entry *entry2; struct archive *disk = bsdtar->diskreader; /* * This tricky code here is to correctly read the contents * of the entry because the disk reader bsdtar->diskreader * is pointing at does not have any information about the * entry by this time and using archive_read_data_block() * with the disk reader consequently must fail. And we * have to re-open the entry to read the contents. */ /* TODO: Work with -C option as well. */ r = archive_read_disk_open(disk, archive_entry_sourcepath(entry)); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); bsdtar->return_value = 1; archive_entry_free(entry); continue; } /* * Invoke archive_read_next_header2() to work * archive_read_data_block(), which is called via write_file(), * without failure. */ entry2 = archive_entry_new(); r = archive_read_next_header2(disk, entry2); archive_entry_free(entry2); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); if (r == ARCHIVE_FATAL) bsdtar->return_value = 1; else archive_read_close(disk); archive_entry_free(entry); continue; } write_file(bsdtar, a, entry); archive_entry_free(entry); archive_read_close(disk); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); } if (archive_write_close(a)) { lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; } cleanup: /* Free file data buffer. */ free(bsdtar->buff); archive_entry_linkresolver_free(bsdtar->resolver); bsdtar->resolver = NULL; archive_read_free(bsdtar->diskreader); bsdtar->diskreader = NULL; - if (bsdtar->option_totals) { + if (bsdtar->flags & OPTFLAG_TOTALS) { fprintf(stderr, "Total bytes written: %s\n", tar_i64toa(archive_filter_bytes(a, -1))); } archive_write_free(a); } /* * Archive names specified in file. * * Unless --null was specified, a line containing exactly "-C" will * cause the next line to be a directory to pass to chdir(). If * --null is specified, then a line "-C" is just another filename. */ static void archive_names_from_file(struct bsdtar *bsdtar, struct archive *a) { struct lafe_line_reader *lr; const char *line; bsdtar->next_line_is_dir = 0; - lr = lafe_line_reader(bsdtar->names_from_file, bsdtar->option_null); + lr = lafe_line_reader(bsdtar->names_from_file, + (bsdtar->flags & OPTFLAG_NULL)); while ((line = lafe_line_reader_next(lr)) != NULL) { if (bsdtar->next_line_is_dir) { if (*line != '\0') set_chdir(bsdtar, line); else { lafe_warnc(0, "Meaningless argument for -C: ''"); bsdtar->return_value = 1; } bsdtar->next_line_is_dir = 0; - } else if (!bsdtar->option_null && strcmp(line, "-C") == 0) + } else if (((bsdtar->flags & OPTFLAG_NULL) == 0) && + strcmp(line, "-C") == 0) bsdtar->next_line_is_dir = 1; else { if (*line != '/') do_chdir(bsdtar); /* Handle a deferred -C */ write_hierarchy(bsdtar, a, line); } } lafe_line_reader_free(lr); if (bsdtar->next_line_is_dir) lafe_errc(1, errno, "Unexpected end of filename list; " "directory expected after -C"); } /* * Copy from specified archive to current archive. Returns non-zero * for write errors (which force us to terminate the entire archiving * operation). If there are errors reading the input archive, we set * bsdtar->return_value but return zero, so the overall archiving * operation will complete and return non-zero. */ static int append_archive_filename(struct bsdtar *bsdtar, struct archive *a, const char *raw_filename) { struct archive *ina; const char *filename = raw_filename; int rc; if (strcmp(filename, "-") == 0) filename = NULL; /* Library uses NULL for stdio. */ ina = archive_read_new(); archive_read_support_format_all(ina); archive_read_support_filter_all(ina); set_reader_options(bsdtar, ina); archive_read_set_options(ina, "mtree:checkfs"); if (bsdtar->passphrase != NULL) rc = archive_read_add_passphrase(a, bsdtar->passphrase); else rc = archive_read_set_passphrase_callback(ina, bsdtar, &passphrase_callback); if (rc != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(ina, filename, bsdtar->bytes_per_block)) { lafe_warnc(0, "%s", archive_error_string(ina)); bsdtar->return_value = 1; return (0); } rc = append_archive(bsdtar, a, ina); if (rc != ARCHIVE_OK) { lafe_warnc(0, "Error reading archive %s: %s", raw_filename, archive_error_string(ina)); bsdtar->return_value = 1; } archive_read_free(ina); return (rc); } static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { struct archive_entry *in_entry; int e; while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) { if (archive_match_excluded(bsdtar->matching, in_entry)) continue; - if (bsdtar->option_interactive && + if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, in_entry); } else if (bsdtar->verbose > 0) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); if (need_report()) report_write(bsdtar, a, in_entry, 0); e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) lafe_warnc(0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); if (e >= ARCHIVE_WARN) { if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); else if (copy_file_data_block(bsdtar, a, ina, in_entry)) exit(1); } if (bsdtar->verbose) fprintf(stderr, "\n"); } return (e == ARCHIVE_EOF ? ARCHIVE_OK : e); } /* Helper function to copy file to archive. */ static int copy_file_data_block(struct bsdtar *bsdtar, struct archive *a, struct archive *in_a, struct archive_entry *entry) { size_t bytes_read; ssize_t bytes_written; int64_t offset, progress = 0; char *null_buff = NULL; const void *buff; int r; while ((r = archive_read_data_block(in_a, &buff, &bytes_read, &offset)) == ARCHIVE_OK) { if (need_report()) report_write(bsdtar, a, entry, progress); if (offset > progress) { int64_t sparse = offset - progress; size_t ns; if (null_buff == NULL) { null_buff = bsdtar->buff; memset(null_buff, 0, bsdtar->buff_size); } while (sparse > 0) { if (sparse > (int64_t)bsdtar->buff_size) ns = bsdtar->buff_size; else ns = (size_t)sparse; bytes_written = archive_write_data(a, null_buff, ns); if (bytes_written < 0) { /* Write failed; this is bad */ lafe_warnc(0, "%s", archive_error_string(a)); return (-1); } if ((size_t)bytes_written < ns) { /* Write was truncated; warn but * continue. */ lafe_warnc(0, "%s: Truncated write; file may " "have grown while being archived.", archive_entry_pathname(entry)); return (0); } progress += bytes_written; sparse -= bytes_written; } } bytes_written = archive_write_data(a, buff, bytes_read); if (bytes_written < 0) { /* Write failed; this is bad */ lafe_warnc(0, "%s", archive_error_string(a)); return (-1); } if ((size_t)bytes_written < bytes_read) { /* Write was truncated; warn but continue. */ lafe_warnc(0, "%s: Truncated write; file may have grown " "while being archived.", archive_entry_pathname(entry)); return (0); } progress += bytes_written; } if (r < ARCHIVE_WARN) { lafe_warnc(archive_errno(a), "%s", archive_error_string(a)); return (-1); } return (0); } static void excluded_callback(struct archive *a, void *_data, struct archive_entry *entry) { struct bsdtar *bsdtar = (struct bsdtar *)_data; - if (bsdtar->option_no_subdirs) + if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) return; if (!archive_read_disk_can_descend(a)) return; - if (bsdtar->option_interactive && + if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && !yes("add '%s'", archive_entry_pathname(entry))) return; archive_read_disk_descend(a); } static int metadata_filter(struct archive *a, void *_data, struct archive_entry *entry) { struct bsdtar *bsdtar = (struct bsdtar *)_data; /* XXX TODO: check whether this filesystem is * synthetic and/or local. Add a new * --local-only option to skip non-local * filesystems. Skip synthetic filesystems * regardless. * * The results should be cached, since * tree.c doesn't usually visit a directory * and the directory contents together. A simple * move-to-front list should perform quite well. * * Use archive_read_disk_current_filesystem_is_remote(). */ /* * If the user vetoes this file/directory, skip it. * We want this to be fairly late; if some other * check would veto this file, we shouldn't bother * the user with it. */ - if (bsdtar->option_interactive && + if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && !yes("add '%s'", archive_entry_pathname(entry))) return (0); /* Note: if user vetoes, we won't descend. */ - if (!bsdtar->option_no_subdirs && archive_read_disk_can_descend(a)) + if (((bsdtar->flags & OPTFLAG_NO_SUBDIRS) == 0) && + archive_read_disk_can_descend(a)) archive_read_disk_descend(a); return (1); } /* * Add the file or dir hierarchy named by 'path' to the archive */ static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { struct archive *disk = bsdtar->diskreader; struct archive_entry *entry = NULL, *spare_entry = NULL; int r; r = archive_read_disk_open(disk, path); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); bsdtar->return_value = 1; return; } bsdtar->first_fs = -1; for (;;) { archive_entry_free(entry); entry = archive_entry_new(); r = archive_read_next_header2(disk, entry); if (r == ARCHIVE_EOF) break; else if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); if (r == ARCHIVE_FATAL || r == ARCHIVE_FAILED) { bsdtar->return_value = 1; archive_entry_free(entry); archive_read_close(disk); return; } else if (r < ARCHIVE_WARN) continue; } if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); if (!bsdtar->uname) archive_entry_set_uname(entry, archive_read_disk_uname(bsdtar->diskreader, bsdtar->uid)); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); if (!bsdtar->gname) archive_entry_set_gname(entry, archive_read_disk_gname(bsdtar->diskreader, bsdtar->gid)); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Rewrite the pathname to be archived. If rewrite * fails, skip the entry. */ if (edit_pathname(bsdtar, entry)) continue; /* Display entry as we process it. */ if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, entry); } else if (bsdtar->verbose > 0) { /* This format is required by SUSv2. */ safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); } /* Non-regular files get archived with zero size. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); while (entry != NULL) { write_file(bsdtar, a, entry); archive_entry_free(entry); entry = spare_entry; spare_entry = NULL; } if (bsdtar->verbose) fprintf(stderr, "\n"); } archive_entry_free(entry); archive_read_close(disk); } /* * Write a single file (or directory or other filesystem object) to * the archive. */ static void write_file(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry) { write_entry(bsdtar, a, entry); } /* * Write a single entry to the archive. */ static void write_entry(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry) { int e; e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, entry); lafe_warnc(0, ": %s", archive_error_string(a)); } else if (bsdtar->verbose > 0) { lafe_warnc(0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); } else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); /* * If we opened a file earlier, write it out now. Note that * the format handler might have reset the size field to zero * to inform us that the archive body won't get stored. In * that case, just skip the write. */ if (e >= ARCHIVE_WARN && archive_entry_size(entry) > 0) { if (copy_file_data_block(bsdtar, a, bsdtar->diskreader, entry)) exit(1); } } static void report_write(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry, int64_t progress) { uint64_t comp, uncomp; int compression; if (bsdtar->verbose) fprintf(stderr, "\n"); comp = archive_filter_bytes(a, -1); uncomp = archive_filter_bytes(a, 0); fprintf(stderr, "In: %d files, %s bytes;", archive_file_count(a), tar_i64toa(uncomp)); - if (comp > uncomp) + if (comp >= uncomp) compression = 0; else compression = (int)((uncomp - comp) * 100 / uncomp); fprintf(stderr, " Out: %s bytes, compression %d%%\n", tar_i64toa(comp), compression); /* Can't have two calls to tar_i64toa() pending, so split the output. */ safe_fprintf(stderr, "Current: %s (%s", archive_entry_pathname(entry), tar_i64toa(progress)); fprintf(stderr, "/%s bytes)\n", tar_i64toa(archive_entry_size(entry))); } static void test_for_append(struct bsdtar *bsdtar) { struct stat s; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) lafe_errc(1, 0, "no files or directories specified"); if (bsdtar->filename == NULL) lafe_errc(1, 0, "Cannot append to stdout."); if (stat(bsdtar->filename, &s) != 0) return; if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) lafe_errc(1, 0, "Cannot append to %s: not a regular file.", bsdtar->filename); /* Is this an appropriate check here on Windows? */ /* if (GetFileType(handle) != FILE_TYPE_DISK) lafe_errc(1, 0, "Cannot append"); */ } Index: vendor/libarchive/dist/test_utils/test_common.h =================================================================== --- vendor/libarchive/dist/test_utils/test_common.h (nonexistent) +++ vendor/libarchive/dist/test_utils/test_common.h (revision 314565) @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2003-2017 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 TEST_COMMON_H +#define TEST_COMMON_H + +/* + * 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) && !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 + +#include /* Windows requires this before sys/stat.h */ +#include + +#if HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_DIRECT_H +#include +#define dirent direct +#endif +#include +#include +#ifdef HAVE_IO_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_WINDOWS_H +#include +#endif + +/* + * System-specific tweaks. We really want to minimize these + * as much as possible, since they make it harder to understand + * the mainline code. + */ + +/* Windows (including Visual Studio and MinGW but not Cygwin) */ +#if defined(_WIN32) && !defined(__CYGWIN__) +#if !defined(__BORLANDC__) +#undef chdir +#define chdir _chdir +#define strdup _strdup +#endif +#endif + +/* Visual Studio */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf sprintf_s +#endif + +#if defined(__BORLANDC__) +#pragma warn -8068 /* Constant out of range in comparison. */ +#endif + +/* Haiku OS and QNX */ +#if defined(__HAIKU__) || defined(__QNXNTO__) +/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ +#include +#endif + +/* Get a real definition for __FBSDID if we can */ +#if HAVE_SYS_CDEFS_H +#include +#endif + +/* If not, define it so as to avoid dangling semicolons. */ +#ifndef __FBSDID +#define __FBSDID(a) struct _undefined_hack +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* + * If this platform has , acl_create(), acl_init(), + * acl_set_file(), and ACL_USER, we assume it has the rest of the + * POSIX.1e draft functions used in archive_read_extract.c. + */ +#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE +#if HAVE_ACL_USER +#define HAVE_POSIX_ACL 1 +#elif HAVE_ACL_TYPE_EXTENDED +#define HAVE_DARWIN_ACL 1 +#endif +#endif + +/* + * If this platform has , acl_get(), facl_get(), acl_set(), + * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions + */ +#if HAVE_SYS_ACL_H && HAVE_ACL_GET && HAVE_FACL_GET && HAVE_ACL_SET && HAVE_FACL_SET && HAVE_ACLENT_T && HAVE_ACE_T +#define HAVE_SUN_ACL 1 +#endif + +/* Define if platform supports NFSv4 ACLs */ +#if (HAVE_POSIX_ACL && HAVE_ACL_TYPE_NFS4) || HAVE_SUN_ACL || HAVE_DARWIN_ACL +#define HAVE_NFS4_ACL 1 +#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) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) +/* chdir() and error if it fails */ +#define assertChdir(path) \ + assertion_chdir(__FILE__, __LINE__, path) +/* Assert two integers are the same. Reports value of each one if not. */ +#define assertEqualInt(v1,v2) \ + assertion_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) \ + assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) +#define assertEqualUTF8String(v1,v2) \ + assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) +/* As above, but v1 and v2 are wchar_t * */ +#define assertEqualWString(v1,v2) \ + assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) +/* As above, but raw blocks of bytes. */ +#define assertEqualMem(v1, v2, l) \ + assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) +/* Assert that memory is full of a specified byte */ +#define assertMemoryFilledWith(v1, l, b) \ + assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) +/* Assert two files are the same. */ +#define assertEqualFile(f1, f2) \ + assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) +/* Assert that a file is empty. */ +#define assertEmptyFile(pathname) \ + assertion_empty_file(__FILE__, __LINE__, (pathname)) +/* Assert that a file is not empty. */ +#define assertNonEmptyFile(pathname) \ + assertion_non_empty_file(__FILE__, __LINE__, (pathname)) +#define assertFileAtime(pathname, sec, nsec) \ + assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) +#define assertFileAtimeRecent(pathname) \ + assertion_file_atime_recent(__FILE__, __LINE__, pathname) +#define assertFileBirthtime(pathname, sec, nsec) \ + assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) +#define assertFileBirthtimeRecent(pathname) \ + assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) +/* Assert that a file exists; supports printf-style arguments. */ +#define assertFileExists(pathname) \ + assertion_file_exists(__FILE__, __LINE__, pathname) +/* Assert that a file exists. */ +#define assertFileNotExists(pathname) \ + assertion_file_not_exists(__FILE__, __LINE__, pathname) +/* Assert that file contents match a string. */ +#define assertFileContents(data, data_size, pathname) \ + assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) +/* Verify that a file does not contain invalid strings */ +#define assertFileContainsNoInvalidStrings(pathname, strings) \ + assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) +#define assertFileMtime(pathname, sec, nsec) \ + assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) +#define assertFileMtimeRecent(pathname) \ + assertion_file_mtime_recent(__FILE__, __LINE__, pathname) +#define assertFileNLinks(pathname, nlinks) \ + assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) +#define assertFileSize(pathname, size) \ + assertion_file_size(__FILE__, __LINE__, pathname, size) +#define assertFileMode(pathname, mode) \ + assertion_file_mode(__FILE__, __LINE__, pathname, mode) +#define assertTextFileContents(text, pathname) \ + assertion_text_file_contents(__FILE__, __LINE__, text, pathname) +#define assertFileContainsLinesAnyOrder(pathname, lines) \ + assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) +#define assertIsDir(pathname, mode) \ + assertion_is_dir(__FILE__, __LINE__, pathname, mode) +#define assertIsHardlink(path1, path2) \ + assertion_is_hardlink(__FILE__, __LINE__, path1, path2) +#define assertIsNotHardlink(path1, path2) \ + assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) +#define assertIsReg(pathname, mode) \ + assertion_is_reg(__FILE__, __LINE__, pathname, mode) +#define assertIsSymlink(pathname, contents) \ + assertion_is_symlink(__FILE__, __LINE__, pathname, contents) +/* Create a directory, report error if it fails. */ +#define assertMakeDir(dirname, mode) \ + assertion_make_dir(__FILE__, __LINE__, dirname, mode) +#define assertMakeFile(path, mode, contents) \ + assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) +#define assertMakeBinFile(path, mode, csize, contents) \ + assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) +#define assertMakeHardlink(newfile, oldfile) \ + assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) +#define assertMakeSymlink(newfile, linkto) \ + assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) +#define assertNodump(path) \ + assertion_nodump(__FILE__, __LINE__, path) +#define assertUmask(mask) \ + assertion_umask(__FILE__, __LINE__, mask) +#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ + assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) +#ifndef PROGRAM +#define assertEntrySetAcls(entry, acls, count) \ + assertion_entry_set_acls(__FILE__, __LINE__, entry, acls, count) +#define assertEntryCompareAcls(entry, acls, count, type, mode) \ + assertion_entry_compare_acls(__FILE__, __LINE__, entry, acls, count, type, mode) +#endif + +/* + * 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 \ + skipping_setup(__FILE__, __LINE__);test_skipping + +/* Function declarations. These are defined in test_utility.c. */ +void failure(const char *fmt, ...); +int assertion_assert(const char *, int, int, const char *, void *); +int assertion_chdir(const char *, int, const char *); +int assertion_empty_file(const char *, int, const char *); +int assertion_equal_file(const char *, int, const char *, const char *); +int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); +int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); +int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); +int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); +int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); +int assertion_file_atime(const char *, int, const char *, long, long); +int assertion_file_atime_recent(const char *, int, const char *); +int assertion_file_birthtime(const char *, int, const char *, long, long); +int assertion_file_birthtime_recent(const char *, int, const char *); +int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); +int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); +int assertion_file_contents(const char *, int, const void *, int, const char *); +int assertion_file_exists(const char *, int, const char *); +int assertion_file_mode(const char *, int, const char *, int); +int assertion_file_mtime(const char *, int, const char *, long, long); +int assertion_file_mtime_recent(const char *, int, const char *); +int assertion_file_nlinks(const char *, int, const char *, int); +int assertion_file_not_exists(const char *, int, const char *); +int assertion_file_size(const char *, int, const char *, long); +int assertion_is_dir(const char *, int, const char *, int); +int assertion_is_hardlink(const char *, int, const char *, const char *); +int assertion_is_not_hardlink(const char *, int, const char *, const char *); +int assertion_is_reg(const char *, int, const char *, int); +int assertion_is_symlink(const char *, int, const char *, const char *); +int assertion_make_dir(const char *, int, const char *, int); +int assertion_make_file(const char *, int, const char *, int, int, const void *); +int assertion_make_hardlink(const char *, int, const char *newpath, const char *); +int assertion_make_symlink(const char *, int, const char *newpath, const char *); +int assertion_nodump(const char *, int, const char *); +int assertion_non_empty_file(const char *, int, const char *); +int assertion_text_file_contents(const char *, int, const char *buff, const char *f); +int assertion_umask(const char *, int, int); +int assertion_utimes(const char *, int, const char *, long, long, long, long ); +int assertion_version(const char*, int, const char *, const char *); + +void skipping_setup(const char *, int); +void test_skipping(const char *fmt, ...); + +/* Like sprintf, then system() */ +int systemf(const char * fmt, ...); + +/* Delay until time() returns a value after this. */ +void sleepUntilAfter(time_t); + +/* Return true if this platform can create symlinks. */ +int canSymlink(void); + +/* Return true if this platform can run the "bzip2" program. */ +int canBzip2(void); + +/* Return true if this platform can run the "grzip" program. */ +int canGrzip(void); + +/* Return true if this platform can run the "gzip" program. */ +int canGzip(void); + +/* Return true if this platform can run the specified command. */ +int canRunCommand(const char *); + +/* Return true if this platform can run the "lrzip" program. */ +int canLrzip(void); + +/* Return true if this platform can run the "lz4" program. */ +int canLz4(void); + +/* Return true if this platform can run the "lzip" program. */ +int canLzip(void); + +/* Return true if this platform can run the "lzma" program. */ +int canLzma(void); + +/* Return true if this platform can run the "lzop" program. */ +int canLzop(void); + +/* Return true if this platform can run the "xz" program. */ +int canXz(void); + +/* Return true if this filesystem can handle nodump flags. */ +int canNodump(void); + +/* Return true if the file has large i-node number(>0xffffffff). */ +int is_LargeInode(const char *); + +/* 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, ...); + +/* Dump block of bytes to a file. */ +void dumpfile(const char *filename, void *, size_t); + +/* Extracts named reference file to the current directory. */ +void extract_reference_file(const char *); +/* Copies named reference file to the current directory. */ +void copy_reference_file(const char *); + +/* Extracts a list of files to the current directory. + * List must be NULL terminated. + */ +void extract_reference_files(const char **); + +/* Subtract umask from mode */ +mode_t umasked(mode_t expected_mode); + +/* Path to working directory for current test */ +extern const char *testworkdir; + +#ifndef PROGRAM +/* + * Special interfaces for libarchive test harness. + */ + +#include "archive.h" +#include "archive_entry.h" + +/* ACL structure */ +struct archive_test_acl_t { + int type; /* Type of ACL */ + int permset; /* Permissions for this class of users. */ + int tag; /* Owner, User, Owning group, group, other, etc. */ + int qual; /* GID or UID of user/group, depending on tag. */ + const char *name; /* Name of user/group, depending on tag. */ +}; + +/* Set ACLs */ +int assertion_entry_set_acls(const char *, int, struct archive_entry *, + struct archive_test_acl_t *, int); + +/* Compare ACLs */ +int assertion_entry_compare_acls(const char *, int, struct archive_entry *, + struct archive_test_acl_t *, int, int, int); + +/* Special customized read-from-memory interface. */ +int read_open_memory(struct archive *, const void *, size_t, size_t); +/* _minimal version exercises a slightly different set of libarchive APIs. */ +int read_open_memory_minimal(struct archive *, const void *, size_t, size_t); +/* _seek version produces a seekable file. */ +int read_open_memory_seek(struct archive *, const void *, size_t, size_t); + +/* Versions of above that accept an archive argument for additional info. */ +#define assertA(e) assertion_assert(__FILE__, __LINE__, (e), #e, (a)) +#define assertEqualIntA(a,v1,v2) \ + assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a)) +#define assertEqualStringA(a,v1,v2) \ + assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a), 0) + +#else /* defined(PROGRAM) */ +/* + * Special interfaces for program test harness. + */ + +/* Pathname of exe to be tested. */ +extern const char *testprogfile; +/* Name of exe to use in printf-formatted command strings. */ +/* On Windows, this includes leading/trailing quotes. */ +extern const char *testprog; + +void assertVersion(const char *prog, const char *base); + +#endif /* defined(PROGRAM) */ + +#ifdef USE_DMALLOC +#include +#endif + +#endif /* TEST_COMMON_H */ Property changes on: vendor/libarchive/dist/test_utils/test_common.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/libarchive/dist/test_utils/test_main.c =================================================================== --- vendor/libarchive/dist/test_utils/test_main.c (nonexistent) +++ vendor/libarchive/dist/test_utils/test_main.c (revision 314565) @@ -0,0 +1,3407 @@ +/* + * 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" +#include "test_utils.h" +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_ICONV_H +#include +#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_LINUX_FS_H +#include +#endif +#include +#include +#ifdef HAVE_SIGNAL_H +#include +#endif +#include +#include + +/* + * + * Windows support routines + * + * Note: Configuration is a tricky issue. Using HAVE_* feature macros + * in the test harness is dangerous because they cover up + * configuration errors. The classic example of this is omitting a + * configure check. If libarchive and libarchive_test both look for + * the same feature macro, such errors are hard to detect. Platform + * macros (e.g., _WIN32 or __GNUC__) are a little better, but can + * easily lead to very messy code. It's best to limit yourself + * to only the most generic programming techniques in the test harness + * and thus avoid conditionals altogether. Where that's not possible, + * try to minimize conditionals by grouping platform-specific tests in + * one place (e.g., test_acl_freebsd) or by adding new assert() + * functions (e.g., assertMakeHardlink()) to cover up platform + * differences. Platform-specific coding in libarchive_test is often + * a symptom that some capability is missing from libarchive itself. + */ +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +#include +#include +#ifndef F_OK +#define F_OK (0) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) ((m) & _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(m) ((m) & _S_IFREG) +#endif +#if !defined(__BORLANDC__) +#define access _access +#undef chdir +#define chdir _chdir +#endif +#ifndef fileno +#define fileno _fileno +#endif +/*#define fstat _fstat64*/ +#if !defined(__BORLANDC__) +#define getcwd _getcwd +#endif +#define lstat stat +/*#define lstat _stat64*/ +/*#define stat _stat64*/ +#define rmdir _rmdir +#if !defined(__BORLANDC__) +#define strdup _strdup +#define umask _umask +#endif +#define int64_t __int64 +#endif + +#if defined(HAVE__CrtSetReportMode) +# include +#endif + +mode_t umasked(mode_t expected_mode) +{ + mode_t mode = umask(0); + umask(mode); + return expected_mode & ~mode; +} + +/* Path to working directory for current test */ +const char *testworkdir; +#ifdef PROGRAM +/* Pathname of exe to be tested. */ +const char *testprogfile; +/* Name of exe to use in printf-formatted command strings. */ +/* On Windows, this includes leading/trailing quotes. */ +const char *testprog; +#endif + +#if defined(_WIN32) && !defined(__CYGWIN__) +static void *GetFunctionKernel32(const char *); +static int my_CreateSymbolicLinkA(const char *, const char *, int); +static int my_CreateHardLinkA(const char *, const char *); +static int my_GetFileInformationByName(const char *, + BY_HANDLE_FILE_INFORMATION *); + +static void * +GetFunctionKernel32(const char *name) +{ + static HINSTANCE lib; + static int set; + if (!set) { + set = 1; + lib = LoadLibrary("kernel32.dll"); + } + if (lib == NULL) { + fprintf(stderr, "Can't load kernel32.dll?!\n"); + exit(1); + } + return (void *)GetProcAddress(lib, name); +} + +static int +my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) +{ + static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); + static int set; + if (!set) { + set = 1; + f = GetFunctionKernel32("CreateSymbolicLinkA"); + } + return f == NULL ? 0 : (*f)(linkname, target, flags); +} + +static int +my_CreateHardLinkA(const char *linkname, const char *target) +{ + static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); + static int set; + if (!set) { + set = 1; + f = GetFunctionKernel32("CreateHardLinkA"); + } + return f == NULL ? 0 : (*f)(linkname, target, NULL); +} + +static int +my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) +{ + HANDLE h; + int r; + + memset(bhfi, 0, sizeof(*bhfi)); + h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h == INVALID_HANDLE_VALUE) + return (0); + r = GetFileInformationByHandle(h, bhfi); + CloseHandle(h); + return (r); +} +#endif + +#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) +static void +invalid_parameter_handler(const wchar_t * expression, + const wchar_t * function, const wchar_t * file, + unsigned int line, uintptr_t pReserved) +{ + /* nop */ + // Silence unused-parameter compiler warnings. + (void)expression; + (void)function; + (void)file; + (void)line; + (void)pReserved; +} +#endif + +/* + * + * OPTIONS FLAGS + * + */ + +/* Enable core dump on failure. */ +static int dump_on_failure = 0; +/* Default is to remove temp dirs and log data for successful tests. */ +static int keep_temp_files = 0; +/* Default is to run the specified tests once and report errors. */ +static int until_failure = 0; +/* Default is to just report pass/fail for each test. */ +static int verbosity = 0; +#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ +#define VERBOSITY_PASSFAIL 0 /* Default */ +#define VERBOSITY_LIGHT_REPORT 1 /* -v */ +#define VERBOSITY_FULL 2 /* -vv */ +/* A few places generate even more output for verbosity > VERBOSITY_FULL, + * mostly for debugging the test harness itself. */ +/* Cumulative count of assertion failures. */ +static int failures = 0; +/* Cumulative count of reported skips. */ +static int skips = 0; +/* Cumulative count of assertions checked. */ +static int assertions = 0; + +/* Directory where uuencoded reference files can be found. */ +static const char *refdir; + +/* + * Report log information selectively to console and/or disk log. + */ +static int log_console = 0; +static FILE *logfile; +static void +vlogprintf(const char *fmt, va_list ap) +{ +#ifdef va_copy + va_list lfap; + va_copy(lfap, ap); +#endif + if (log_console) + vfprintf(stdout, fmt, ap); + if (logfile != NULL) +#ifdef va_copy + vfprintf(logfile, fmt, lfap); + va_end(lfap); +#else + vfprintf(logfile, fmt, ap); +#endif +} + +static void +logprintf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vlogprintf(fmt, ap); + va_end(ap); +} + +/* Set up a message to display only if next assertion fails. */ +static char msgbuff[4096]; +static const char *msg, *nextmsg; +void +failure(const char *fmt, ...) +{ + va_list ap; + if (fmt == NULL) { + nextmsg = NULL; + } else { + va_start(ap, fmt); + vsprintf(msgbuff, fmt, ap); + va_end(ap); + nextmsg = msgbuff; + } +} + +/* + * Copy arguments into file-local variables. + * This was added to permit vararg assert() functions without needing + * variadic wrapper macros. Turns out that the vararg capability is almost + * never used, so almost all of the vararg assertions can be simplified + * by removing the vararg capability and reworking the wrapper macro to + * pass __FILE__, __LINE__ directly into the function instead of using + * this hook. I suspect this machinery is used so rarely that we + * would be better off just removing it entirely. That would simplify + * the code here noticeably. + */ +static const char *skipping_filename; +static int skipping_line; +void skipping_setup(const char *filename, int line) +{ + skipping_filename = filename; + skipping_line = line; +} + +/* Called at the beginning of each assert() function. */ +static void +assertion_count(const char *file, int line) +{ + (void)file; /* UNUSED */ + (void)line; /* UNUSED */ + ++assertions; + /* Proper handling of "failure()" message. */ + msg = nextmsg; + nextmsg = NULL; + /* Uncomment to print file:line after every assertion. + * Verbose, but occasionally useful in tracking down crashes. */ + /* printf("Checked %s:%d\n", file, line); */ +} + +/* + * For each test source file, we remember how many times each + * assertion was reported. Cleared before each new test, + * used by test_summarize(). + */ +static struct line { + int count; + int skip; +} failed_lines[10000]; +const char *failed_filename; + +/* Count this failure, setup up log destination and handle initial report. */ +static void +failure_start(const char *filename, int line, const char *fmt, ...) +{ + va_list ap; + + /* Record another failure for this line. */ + ++failures; + failed_filename = filename; + failed_lines[line].count++; + + /* Determine whether to log header to console. */ + switch (verbosity) { + case VERBOSITY_LIGHT_REPORT: + log_console = (failed_lines[line].count < 2); + break; + default: + log_console = (verbosity >= VERBOSITY_FULL); + } + + /* Log file:line header for this failure */ + va_start(ap, fmt); +#if _MSC_VER + logprintf("%s(%d): ", filename, line); +#else + logprintf("%s:%d: ", filename, line); +#endif + vlogprintf(fmt, ap); + va_end(ap); + logprintf("\n"); + + if (msg != NULL && msg[0] != '\0') { + logprintf(" Description: %s\n", msg); + msg = NULL; + } + + /* Determine whether to log details to console. */ + if (verbosity == VERBOSITY_LIGHT_REPORT) + log_console = 0; +} + +/* Complete reporting of failed tests. */ +/* + * The 'extra' hook here is used by libarchive to include libarchive + * error messages with assertion failures. It could also be used + * to add strerror() output, for example. Just define the EXTRA_DUMP() + * macro appropriately. + */ +static void +failure_finish(void *extra) +{ + (void)extra; /* UNUSED (maybe) */ +#ifdef EXTRA_DUMP + if (extra != NULL) { + logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); + logprintf(" detail: %s\n", EXTRA_DUMP(extra)); + } +#endif + + if (dump_on_failure) { + fprintf(stderr, + " *** forcing core dump so failure can be debugged ***\n"); + abort(); + } +} + +/* Inform user that we're skipping some checks. */ +void +test_skipping(const char *fmt, ...) +{ + char buff[1024]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buff, fmt, ap); + va_end(ap); + /* Use failure() message if set. */ + msg = nextmsg; + nextmsg = NULL; + /* failure_start() isn't quite right, but is awfully convenient. */ + failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); + --failures; /* Undo failures++ in failure_start() */ + /* Don't failure_finish() here. */ + /* Mark as skip, so doesn't count as failed test. */ + failed_lines[skipping_line].skip = 1; + ++skips; +} + +/* + * + * ASSERTIONS + * + */ + +/* Generic assert() just displays the failed condition. */ +int +assertion_assert(const char *file, int line, int value, + const char *condition, void *extra) +{ + assertion_count(file, line); + if (!value) { + failure_start(file, line, "Assertion failed: %s", condition); + failure_finish(extra); + } + return (value); +} + +/* chdir() and report any errors */ +int +assertion_chdir(const char *file, int line, const char *pathname) +{ + assertion_count(file, line); + if (chdir(pathname) == 0) + return (1); + failure_start(file, line, "chdir(\"%s\")", pathname); + failure_finish(NULL); + return (0); + +} + +/* Verify two integers are equal. */ +int +assertion_equal_int(const char *file, int line, + long long v1, const char *e1, long long v2, const char *e2, void *extra) +{ + assertion_count(file, line); + if (v1 == v2) + return (1); + failure_start(file, line, "%s != %s", e1, e2); + logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); + logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); + failure_finish(extra); + return (0); +} + +/* + * Utility to convert a single UTF-8 sequence. + */ +static int +_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) +{ + static const char utf8_count[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ + }; + int ch; + int cnt; + uint32_t wc; + + *pwc = 0; + + /* Sanity check. */ + if (n == 0) + return (0); + /* + * 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. */ + cnt = utf8_count[ch]; + + /* Invalid sequence or there are not plenty bytes. */ + if (n < (size_t)cnt) + return (-1); + + /* Make a Unicode code point from a single UTF-8 sequence. */ + switch (cnt) { + case 1: /* 1 byte sequence. */ + *pwc = ch & 0x7f; + return (cnt); + case 2: /* 2 bytes sequence. */ + if ((s[1] & 0xc0) != 0x80) return (-1); + *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); + return (cnt); + case 3: /* 3 bytes sequence. */ + if ((s[1] & 0xc0) != 0x80) return (-1); + if ((s[2] & 0xc0) != 0x80) return (-1); + wc = ((ch & 0x0f) << 12) + | ((s[1] & 0x3f) << 6) + | (s[2] & 0x3f); + if (wc < 0x800) + return (-1);/* Overlong sequence. */ + break; + case 4: /* 4 bytes sequence. */ + 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); + wc = ((ch & 0x07) << 18) + | ((s[1] & 0x3f) << 12) + | ((s[2] & 0x3f) << 6) + | (s[3] & 0x3f); + if (wc < 0x10000) + return (-1);/* Overlong sequence. */ + break; + default: + return (-1); + } + + /* The code point larger than 0x10FFFF is not legal + * Unicode values. */ + if (wc > 0x10FFFF) + return (-1); + /* Correctly gets a Unicode, returns used bytes. */ + *pwc = wc; + return (cnt); +} + +static void strdump(const char *e, const char *p, int ewidth, int utf8) +{ + const char *q = p; + + logprintf(" %*s = ", ewidth, e); + if (p == NULL) { + logprintf("NULL\n"); + return; + } + logprintf("\""); + while (*p != '\0') { + unsigned int c = 0xff & *p++; + switch (c) { + case '\a': logprintf("\\a"); break; + case '\b': logprintf("\\b"); break; + case '\n': logprintf("\\n"); break; + case '\r': logprintf("\\r"); break; + default: + if (c >= 32 && c < 127) + logprintf("%c", c); + else + logprintf("\\x%02X", c); + } + } + logprintf("\""); + logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); + + /* + * If the current string is UTF-8, dump its code points. + */ + if (utf8) { + size_t len; + uint32_t uc; + int n; + int cnt = 0; + + p = q; + len = strlen(p); + logprintf(" ["); + while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { + if (p != q) + logprintf(" "); + logprintf("%04X", uc); + p += n; + len -= n; + cnt++; + } + logprintf("]"); + logprintf(" (count %d", cnt); + if (n < 0) { + logprintf(",unknown %d bytes", len); + } + logprintf(")"); + + } + logprintf("\n"); +} + +/* Verify two strings are equal, dump them if not. */ +int +assertion_equal_string(const char *file, int line, + const char *v1, const char *e1, + const char *v2, const char *e2, + void *extra, int utf8) +{ + int l1, l2; + + assertion_count(file, line); + if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) + return (1); + failure_start(file, line, "%s != %s", e1, e2); + l1 = (int)strlen(e1); + l2 = (int)strlen(e2); + if (l1 < l2) + l1 = l2; + strdump(e1, v1, l1, utf8); + strdump(e2, v2, l1, utf8); + failure_finish(extra); + return (0); +} + +static void +wcsdump(const char *e, const wchar_t *w) +{ + logprintf(" %s = ", e); + if (w == NULL) { + logprintf("(null)"); + return; + } + logprintf("\""); + while (*w != L'\0') { + unsigned int c = *w++; + if (c >= 32 && c < 127) + logprintf("%c", c); + else if (c < 256) + logprintf("\\x%02X", c); + else if (c < 0x10000) + logprintf("\\u%04X", c); + else + logprintf("\\U%08X", c); + } + logprintf("\"\n"); +} + +#ifndef HAVE_WCSCMP +static int +wcscmp(const wchar_t *s1, const wchar_t *s2) +{ + + while (*s1 == *s2++) { + if (*s1++ == L'\0') + return 0; + } + if (*s1 > *--s2) + return 1; + else + return -1; +} +#endif + +/* Verify that two wide strings are equal, dump them if not. */ +int +assertion_equal_wstring(const char *file, int line, + const wchar_t *v1, const char *e1, + const wchar_t *v2, const char *e2, + void *extra) +{ + assertion_count(file, line); + if (v1 == v2) + return (1); + if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) + return (1); + failure_start(file, line, "%s != %s", e1, e2); + wcsdump(e1, v1); + wcsdump(e2, v2); + failure_finish(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; + + if (p == NULL) { + logprintf("(null)\n"); + return; + } + for(i=0; i < l; i+=16) { + logprintf("%04x", (unsigned)(i + offset)); + sep = ' '; + for (j = 0; j < 16 && i + j < l; j++) { + if (ref != NULL && p[i + j] != ref[i + j]) + sep = '_'; + logprintf("%c%02x", sep, 0xff & (int)p[i+j]); + if (ref != NULL && p[i + j] == ref[i + j]) + sep = ' '; + } + for (; j < 16; j++) { + logprintf("%c ", sep); + sep = ' '; + } + logprintf("%c", sep); + for (j=0; j < 16 && i + j < l; j++) { + int c = p[i + j]; + if (c >= ' ' && c <= 126) + logprintf("%c", c); + else + logprintf("."); + } + logprintf("\n"); + } +} + +/* Verify that two blocks of memory are the same, display the first + * block of differences if they're not. */ +int +assertion_equal_mem(const char *file, int line, + const void *_v1, const char *e1, + const void *_v2, const char *e2, + size_t l, const char *ld, void *extra) +{ + const char *v1 = (const char *)_v1; + const char *v2 = (const char *)_v2; + size_t offset; + + assertion_count(file, line); + if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) + return (1); + if (v1 == NULL || v2 == NULL) + return (0); + + failure_start(file, line, "%s != %s", e1, e2); + logprintf(" size %s = %d\n", ld, (int)l); + /* Dump 48 bytes (3 lines) so that the first difference is + * in the second line. */ + offset = 0; + while (l > 64 && memcmp(v1, v2, 32) == 0) { + /* Two lines agree, so step forward one line. */ + v1 += 16; + v2 += 16; + l -= 16; + offset += 16; + } + logprintf(" Dump of %s\n", e1); + hexdump(v1, v2, l < 128 ? l : 128, offset); + logprintf(" Dump of %s\n", e2); + hexdump(v2, v1, l < 128 ? l : 128, offset); + logprintf("\n"); + failure_finish(extra); + return (0); +} + +/* Verify that a block of memory is filled with the specified byte. */ +int +assertion_memory_filled_with(const char *file, int line, + const void *_v1, const char *vd, + size_t l, const char *ld, + char b, const char *bd, void *extra) +{ + const char *v1 = (const char *)_v1; + size_t c = 0; + size_t i; + (void)ld; /* UNUSED */ + + assertion_count(file, line); + + for (i = 0; i < l; ++i) { + if (v1[i] == b) { + ++c; + } + } + if (c == l) + return (1); + + failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); + logprintf(" Only %d bytes were correct\n", (int)c); + failure_finish(extra); + return (0); +} + +/* Verify that the named file exists and is empty. */ +int +assertion_empty_file(const char *filename, int line, const char *f1) +{ + char buff[1024]; + struct stat st; + ssize_t s; + FILE *f; + + assertion_count(filename, line); + + if (stat(f1, &st) != 0) { + failure_start(filename, line, "Stat failed: %s", f1); + failure_finish(NULL); + return (0); + } + if (st.st_size == 0) + return (1); + + failure_start(filename, line, "File should be empty: %s", f1); + logprintf(" File size: %d\n", (int)st.st_size); + logprintf(" Contents:\n"); + f = fopen(f1, "rb"); + if (f == NULL) { + logprintf(" Unable to open %s\n", f1); + } else { + s = ((off_t)sizeof(buff) < st.st_size) ? + (ssize_t)sizeof(buff) : (ssize_t)st.st_size; + s = fread(buff, 1, s, f); + hexdump(buff, NULL, s, 0); + fclose(f); + } + failure_finish(NULL); + return (0); +} + +/* Verify that the named file exists and is not empty. */ +int +assertion_non_empty_file(const char *filename, int line, const char *f1) +{ + struct stat st; + + assertion_count(filename, line); + + if (stat(f1, &st) != 0) { + failure_start(filename, line, "Stat failed: %s", f1); + failure_finish(NULL); + return (0); + } + if (st.st_size == 0) { + failure_start(filename, line, "File empty: %s", f1); + failure_finish(NULL); + return (0); + } + return (1); +} + +/* Verify that two files have the same contents. */ +/* TODO: hexdump the first bytes that actually differ. */ +int +assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) +{ + char buff1[1024]; + char buff2[1024]; + FILE *f1, *f2; + int n1, n2; + + assertion_count(filename, line); + + f1 = fopen(fn1, "rb"); + f2 = fopen(fn2, "rb"); + if (f1 == NULL || f2 == NULL) { + if (f1) fclose(f1); + if (f2) fclose(f2); + return (0); + } + for (;;) { + n1 = (int)fread(buff1, 1, sizeof(buff1), f1); + n2 = (int)fread(buff2, 1, sizeof(buff2), f2); + if (n1 != n2) + break; + if (n1 == 0 && n2 == 0) { + fclose(f1); + fclose(f2); + return (1); + } + if (memcmp(buff1, buff2, n1) != 0) + break; + } + fclose(f1); + fclose(f2); + failure_start(filename, line, "Files not identical"); + logprintf(" file1=\"%s\"\n", fn1); + logprintf(" file2=\"%s\"\n", fn2); + failure_finish(NULL); + return (0); +} + +/* Verify that the named file does exist. */ +int +assertion_file_exists(const char *filename, int line, const char *f) +{ + assertion_count(filename, line); + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!_access(f, 0)) + return (1); +#else + if (!access(f, F_OK)) + return (1); +#endif + failure_start(filename, line, "File should exist: %s", f); + failure_finish(NULL); + return (0); +} + +/* Verify that the named file doesn't exist. */ +int +assertion_file_not_exists(const char *filename, int line, const char *f) +{ + assertion_count(filename, line); + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (_access(f, 0)) + return (1); +#else + if (access(f, F_OK)) + return (1); +#endif + failure_start(filename, line, "File should not exist: %s", f); + failure_finish(NULL); + return (0); +} + +/* Compare the contents of a file to a block of memory. */ +int +assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) +{ + char *contents; + FILE *f; + int n; + + assertion_count(filename, line); + + f = fopen(fn, "rb"); + if (f == NULL) { + failure_start(filename, line, + "File should exist: %s", fn); + failure_finish(NULL); + return (0); + } + contents = malloc(s * 2); + n = (int)fread(contents, 1, s * 2, f); + fclose(f); + if (n == s && memcmp(buff, contents, s) == 0) { + free(contents); + return (1); + } + failure_start(filename, line, "File contents don't match"); + logprintf(" file=\"%s\"\n", fn); + if (n > 0) + hexdump(contents, buff, n > 512 ? 512 : n, 0); + else { + logprintf(" File empty, contents should be:\n"); + hexdump(buff, NULL, s > 512 ? 512 : s, 0); + } + failure_finish(NULL); + free(contents); + return (0); +} + +/* Check the contents of a text file, being tolerant of line endings. */ +int +assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) +{ + char *contents; + const char *btxt, *ftxt; + FILE *f; + int n, s; + + assertion_count(filename, line); + f = fopen(fn, "r"); + if (f == NULL) { + failure_start(filename, line, + "File doesn't exist: %s", fn); + failure_finish(NULL); + return (0); + } + s = (int)strlen(buff); + contents = malloc(s * 2 + 128); + n = (int)fread(contents, 1, s * 2 + 128 - 1, f); + if (n >= 0) + contents[n] = '\0'; + fclose(f); + /* Compare texts. */ + btxt = buff; + ftxt = (const char *)contents; + while (*btxt != '\0' && *ftxt != '\0') { + if (*btxt == *ftxt) { + ++btxt; + ++ftxt; + continue; + } + if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { + /* Pass over different new line characters. */ + ++btxt; + ftxt += 2; + continue; + } + break; + } + if (*btxt == '\0' && *ftxt == '\0') { + free(contents); + return (1); + } + failure_start(filename, line, "Contents don't match"); + logprintf(" file=\"%s\"\n", fn); + if (n > 0) { + hexdump(contents, buff, n, 0); + logprintf(" expected\n", fn); + hexdump(buff, contents, s, 0); + } else { + logprintf(" File empty, contents should be:\n"); + hexdump(buff, NULL, s, 0); + } + failure_finish(NULL); + free(contents); + return (0); +} + +/* Verify that a text file contains the specified lines, regardless of order */ +/* This could be more efficient if we sorted both sets of lines, etc, but + * since this is used only for testing and only ever deals with a dozen or so + * lines at a time, this relatively crude approach is just fine. */ +int +assertion_file_contains_lines_any_order(const char *file, int line, + const char *pathname, const char *lines[]) +{ + char *buff; + size_t buff_size; + size_t expected_count, actual_count, i, j; + char **expected = NULL; + char *p, **actual = NULL; + char c; + int expected_failure = 0, actual_failure = 0; + + assertion_count(file, line); + + buff = slurpfile(&buff_size, "%s", pathname); + if (buff == NULL) { + failure_start(pathname, line, "Can't read file: %s", pathname); + failure_finish(NULL); + return (0); + } + + /* Make a copy of the provided lines and count up the expected + * file size. */ + for (i = 0; lines[i] != NULL; ++i) { + } + expected_count = i; + if (expected_count) { + expected = malloc(sizeof(char *) * expected_count); + if (expected == NULL) { + failure_start(pathname, line, "Can't allocate memory"); + failure_finish(NULL); + free(expected); + return (0); + } + for (i = 0; lines[i] != NULL; ++i) { + expected[i] = strdup(lines[i]); + } + } + + /* Break the file into lines */ + actual_count = 0; + for (c = '\0', p = buff; p < buff + buff_size; ++p) { + if (*p == '\x0d' || *p == '\x0a') + *p = '\0'; + if (c == '\0' && *p != '\0') + ++actual_count; + c = *p; + } + if (actual_count) { + actual = calloc(sizeof(char *), actual_count); + if (actual == NULL) { + failure_start(pathname, line, "Can't allocate memory"); + failure_finish(NULL); + free(expected); + return (0); + } + for (j = 0, p = buff; p < buff + buff_size; + p += 1 + strlen(p)) { + if (*p != '\0') { + actual[j] = p; + ++j; + } + } + } + + /* Erase matching lines from both lists */ + for (i = 0; i < expected_count; ++i) { + if (expected[i] == NULL) + continue; + for (j = 0; j < actual_count; ++j) { + if (actual[j] == NULL) + continue; + if (strcmp(expected[i], actual[j]) == 0) { + free(expected[i]); + expected[i] = NULL; + actual[j] = NULL; + break; + } + } + } + + /* If there's anything left, it's a failure */ + for (i = 0; i < expected_count; ++i) { + if (expected[i] != NULL) + ++expected_failure; + } + for (j = 0; j < actual_count; ++j) { + if (actual[j] != NULL) + ++actual_failure; + } + if (expected_failure == 0 && actual_failure == 0) { + free(buff); + free(expected); + free(actual); + return (1); + } + failure_start(file, line, "File doesn't match: %s", pathname); + for (i = 0; i < expected_count; ++i) { + if (expected[i] != NULL) { + logprintf(" Expected but not present: %s\n", expected[i]); + free(expected[i]); + } + } + for (j = 0; j < actual_count; ++j) { + if (actual[j] != NULL) + logprintf(" Present but not expected: %s\n", actual[j]); + } + failure_finish(NULL); + free(buff); + free(expected); + free(actual); + return (0); +} + +/* Verify that a text file does not contains the specified strings */ +int +assertion_file_contains_no_invalid_strings(const char *file, int line, + const char *pathname, const char *strings[]) +{ + char *buff; + int i; + + buff = slurpfile(NULL, "%s", pathname); + if (buff == NULL) { + failure_start(file, line, "Can't read file: %s", pathname); + failure_finish(NULL); + return (0); + } + + for (i = 0; strings[i] != NULL; ++i) { + if (strstr(buff, strings[i]) != NULL) { + failure_start(file, line, "Invalid string in %s: %s", pathname, + strings[i]); + failure_finish(NULL); + free(buff); + return(0); + } + } + + free(buff); + return (0); +} + +/* Test that two paths point to the same file. */ +/* As a side-effect, asserts that both files exist. */ +static int +is_hardlink(const char *file, int line, + const char *path1, const char *path2) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; + int r; + + assertion_count(file, line); + r = my_GetFileInformationByName(path1, &bhfi1); + if (r == 0) { + failure_start(file, line, "File %s can't be inspected?", path1); + failure_finish(NULL); + return (0); + } + r = my_GetFileInformationByName(path2, &bhfi2); + if (r == 0) { + failure_start(file, line, "File %s can't be inspected?", path2); + failure_finish(NULL); + return (0); + } + return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber + && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh + && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); +#else + struct stat st1, st2; + int r; + + assertion_count(file, line); + r = lstat(path1, &st1); + if (r != 0) { + failure_start(file, line, "File should exist: %s", path1); + failure_finish(NULL); + return (0); + } + r = lstat(path2, &st2); + if (r != 0) { + failure_start(file, line, "File should exist: %s", path2); + failure_finish(NULL); + return (0); + } + return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); +#endif +} + +int +assertion_is_hardlink(const char *file, int line, + const char *path1, const char *path2) +{ + if (is_hardlink(file, line, path1, path2)) + return (1); + failure_start(file, line, + "Files %s and %s are not hardlinked", path1, path2); + failure_finish(NULL); + return (0); +} + +int +assertion_is_not_hardlink(const char *file, int line, + const char *path1, const char *path2) +{ + if (!is_hardlink(file, line, path1, path2)) + return (1); + failure_start(file, line, + "Files %s and %s should not be hardlinked", path1, path2); + failure_finish(NULL); + return (0); +} + +/* Verify a/b/mtime of 'pathname'. */ +/* If 'recent', verify that it's within last 10 seconds. */ +static int +assertion_file_time(const char *file, int line, + const char *pathname, long t, long nsec, char type, int recent) +{ + long long filet, filet_nsec; + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define EPOC_TIME (116444736000000000ULL) + FILETIME fxtime, fbirthtime, fatime, fmtime; + ULARGE_INTEGER wintm; + HANDLE h; + fxtime.dwLowDateTime = 0; + fxtime.dwHighDateTime = 0; + + assertion_count(file, line); + /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open + * a directory file. If not, CreateFile() will fail when + * the pathname is a directory. */ + h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h == INVALID_HANDLE_VALUE) { + failure_start(file, line, "Can't access %s\n", pathname); + failure_finish(NULL); + return (0); + } + r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); + switch (type) { + case 'a': fxtime = fatime; break; + case 'b': fxtime = fbirthtime; break; + case 'm': fxtime = fmtime; break; + } + CloseHandle(h); + if (r == 0) { + failure_start(file, line, "Can't GetFileTime %s\n", pathname); + failure_finish(NULL); + return (0); + } + wintm.LowPart = fxtime.dwLowDateTime; + wintm.HighPart = fxtime.dwHighDateTime; + filet = (wintm.QuadPart - EPOC_TIME) / 10000000; + filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; + nsec = (nsec / 100) * 100; /* Round the request */ +#else + struct stat st; + + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0) { + failure_start(file, line, "Can't stat %s\n", pathname); + failure_finish(NULL); + return (0); + } + switch (type) { + case 'a': filet = st.st_atime; break; + case 'm': filet = st.st_mtime; break; + case 'b': filet = 0; break; + default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); + exit(1); + } +#if defined(__FreeBSD__) + switch (type) { + case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; + case 'b': filet = st.st_birthtime; + /* FreeBSD filesystems that don't support birthtime + * (e.g., UFS1) always return -1 here. */ + if (filet == -1) { + return (1); + } + filet_nsec = st.st_birthtimespec.tv_nsec; break; + case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; + default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); + exit(1); + } + /* FreeBSD generally only stores to microsecond res, so round. */ + filet_nsec = (filet_nsec / 1000) * 1000; + nsec = (nsec / 1000) * 1000; +#else + filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ + if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ +#if defined(__HAIKU__) + if (type == 'a') return (1); /* Haiku doesn't have atime. */ +#endif +#endif +#endif + if (recent) { + /* Check that requested time is up-to-date. */ + time_t now = time(NULL); + if (filet < now - 10 || filet > now + 1) { + failure_start(file, line, + "File %s has %ctime %lld, %lld seconds ago\n", + pathname, type, filet, now - filet); + failure_finish(NULL); + return (0); + } + } else if (filet != t || filet_nsec != nsec) { + failure_start(file, line, + "File %s has %ctime %lld.%09lld, expected %lld.%09lld", + pathname, type, filet, filet_nsec, t, nsec); + failure_finish(NULL); + return (0); + } + return (1); +} + +/* Verify atime of 'pathname'. */ +int +assertion_file_atime(const char *file, int line, + const char *pathname, long t, long nsec) +{ + return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); +} + +/* Verify atime of 'pathname' is up-to-date. */ +int +assertion_file_atime_recent(const char *file, int line, const char *pathname) +{ + return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); +} + +/* Verify birthtime of 'pathname'. */ +int +assertion_file_birthtime(const char *file, int line, + const char *pathname, long t, long nsec) +{ + return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); +} + +/* Verify birthtime of 'pathname' is up-to-date. */ +int +assertion_file_birthtime_recent(const char *file, int line, + const char *pathname) +{ + return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); +} + +/* Verify mode of 'pathname'. */ +int +assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) +{ + int mode; + int r; + + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + failure_start(file, line, "assertFileMode not yet implemented for Windows"); + (void)mode; /* UNUSED */ + (void)r; /* UNUSED */ + (void)pathname; /* UNUSED */ + (void)expected_mode; /* UNUSED */ +#else + { + struct stat st; + r = lstat(pathname, &st); + mode = (int)(st.st_mode & 0777); + } + if (r == 0 && mode == expected_mode) + return (1); + failure_start(file, line, "File %s has mode %o, expected %o", + pathname, mode, expected_mode); +#endif + failure_finish(NULL); + return (0); +} + +/* Verify mtime of 'pathname'. */ +int +assertion_file_mtime(const char *file, int line, + const char *pathname, long t, long nsec) +{ + return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); +} + +/* Verify mtime of 'pathname' is up-to-date. */ +int +assertion_file_mtime_recent(const char *file, int line, const char *pathname) +{ + return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); +} + +/* Verify number of links to 'pathname'. */ +int +assertion_file_nlinks(const char *file, int line, + const char *pathname, int nlinks) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + BY_HANDLE_FILE_INFORMATION bhfi; + int r; + + assertion_count(file, line); + r = my_GetFileInformationByName(pathname, &bhfi); + if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) + return (1); + failure_start(file, line, "File %s has %d links, expected %d", + pathname, bhfi.nNumberOfLinks, nlinks); + failure_finish(NULL); + return (0); +#else + struct stat st; + int r; + + assertion_count(file, line); + r = lstat(pathname, &st); + if (r == 0 && (int)st.st_nlink == nlinks) + return (1); + failure_start(file, line, "File %s has %d links, expected %d", + pathname, st.st_nlink, nlinks); + failure_finish(NULL); + return (0); +#endif +} + +/* Verify size of 'pathname'. */ +int +assertion_file_size(const char *file, int line, const char *pathname, long size) +{ + int64_t filesize; + int r; + + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + { + BY_HANDLE_FILE_INFORMATION bhfi; + r = !my_GetFileInformationByName(pathname, &bhfi); + filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; + } +#else + { + struct stat st; + r = lstat(pathname, &st); + filesize = st.st_size; + } +#endif + if (r == 0 && filesize == size) + return (1); + failure_start(file, line, "File %s has size %ld, expected %ld", + pathname, (long)filesize, (long)size); + failure_finish(NULL); + return (0); +} + +/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ +int +assertion_is_dir(const char *file, int line, const char *pathname, int mode) +{ + struct stat st; + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)mode; /* UNUSED */ +#endif + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0) { + failure_start(file, line, "Dir should exist: %s", pathname); + failure_finish(NULL); + return (0); + } + if (!S_ISDIR(st.st_mode)) { + failure_start(file, line, "%s is not a dir", pathname); + failure_finish(NULL); + return (0); + } +#if !defined(_WIN32) || defined(__CYGWIN__) + /* Windows doesn't handle permissions the same way as POSIX, + * so just ignore the mode tests. */ + /* TODO: Can we do better here? */ + if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { + failure_start(file, line, "Dir %s has wrong mode", pathname); + logprintf(" Expected: 0%3o\n", mode); + logprintf(" Found: 0%3o\n", st.st_mode & 07777); + failure_finish(NULL); + return (0); + } +#endif + return (1); +} + +/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, + * verify that too. */ +int +assertion_is_reg(const char *file, int line, const char *pathname, int mode) +{ + struct stat st; + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)mode; /* UNUSED */ +#endif + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0 || !S_ISREG(st.st_mode)) { + failure_start(file, line, "File should exist: %s", pathname); + failure_finish(NULL); + return (0); + } +#if !defined(_WIN32) || defined(__CYGWIN__) + /* Windows doesn't handle permissions the same way as POSIX, + * so just ignore the mode tests. */ + /* TODO: Can we do better here? */ + if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { + failure_start(file, line, "File %s has wrong mode", pathname); + logprintf(" Expected: 0%3o\n", mode); + logprintf(" Found: 0%3o\n", st.st_mode & 07777); + failure_finish(NULL); + return (0); + } +#endif + return (1); +} + +/* Check whether 'pathname' is a symbolic link. If 'contents' is + * non-NULL, verify that the symlink has those contents. */ +static int +is_symlink(const char *file, int line, + const char *pathname, const char *contents) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)pathname; /* UNUSED */ + (void)contents; /* UNUSED */ + assertion_count(file, line); + /* Windows sort-of has real symlinks, but they're only usable + * by privileged users and are crippled even then, so there's + * really not much point in bothering with this. */ + return (0); +#else + char buff[300]; + struct stat st; + ssize_t linklen; + int r; + + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0) { + failure_start(file, line, + "Symlink should exist: %s", pathname); + failure_finish(NULL); + return (0); + } + if (!S_ISLNK(st.st_mode)) + return (0); + if (contents == NULL) + return (1); + linklen = readlink(pathname, buff, sizeof(buff)); + if (linklen < 0) { + failure_start(file, line, "Can't read symlink %s", pathname); + failure_finish(NULL); + return (0); + } + buff[linklen] = '\0'; + if (strcmp(buff, contents) != 0) + return (0); + return (1); +#endif +} + +/* Assert that path is a symlink that (optionally) contains contents. */ +int +assertion_is_symlink(const char *file, int line, + const char *path, const char *contents) +{ + if (is_symlink(file, line, path, contents)) + return (1); + if (contents) + failure_start(file, line, "File %s is not a symlink to %s", + path, contents); + else + failure_start(file, line, "File %s is not a symlink", path); + failure_finish(NULL); + return (0); +} + + +/* Create a directory and report any errors. */ +int +assertion_make_dir(const char *file, int line, const char *dirname, int mode) +{ + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)mode; /* UNUSED */ + if (0 == _mkdir(dirname)) + return (1); +#else + if (0 == mkdir(dirname, mode)) { + if (0 == chmod(dirname, mode)) { + assertion_file_mode(file, line, dirname, mode); + return (1); + } + } +#endif + failure_start(file, line, "Could not create directory %s", dirname); + failure_finish(NULL); + return(0); +} + +/* Create a file with the specified contents and report any failures. */ +int +assertion_make_file(const char *file, int line, + const char *path, int mode, int csize, const void *contents) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + /* TODO: Rework this to set file mode as well. */ + FILE *f; + (void)mode; /* UNUSED */ + assertion_count(file, line); + f = fopen(path, "wb"); + if (f == NULL) { + failure_start(file, line, "Could not create file %s", path); + failure_finish(NULL); + return (0); + } + if (contents != NULL) { + size_t wsize; + + if (csize < 0) + wsize = strlen(contents); + else + wsize = (size_t)csize; + if (wsize != fwrite(contents, 1, wsize, f)) { + fclose(f); + failure_start(file, line, + "Could not write file %s", path); + failure_finish(NULL); + return (0); + } + } + fclose(f); + return (1); +#else + int fd; + assertion_count(file, line); + fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); + if (fd < 0) { + failure_start(file, line, "Could not create %s", path); + failure_finish(NULL); + return (0); + } + if (0 != chmod(path, mode)) { + failure_start(file, line, "Could not chmod %s", path); + failure_finish(NULL); + close(fd); + return (0); + } + if (contents != NULL) { + ssize_t wsize; + + if (csize < 0) + wsize = (ssize_t)strlen(contents); + else + wsize = (ssize_t)csize; + if (wsize != write(fd, contents, wsize)) { + close(fd); + failure_start(file, line, + "Could not write to %s", path); + failure_finish(NULL); + close(fd); + return (0); + } + } + close(fd); + assertion_file_mode(file, line, path, mode); + return (1); +#endif +} + +/* Create a hardlink and report any failures. */ +int +assertion_make_hardlink(const char *file, int line, + const char *newpath, const char *linkto) +{ + int succeeded; + + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + succeeded = my_CreateHardLinkA(newpath, linkto); +#elif HAVE_LINK + succeeded = !link(linkto, newpath); +#else + succeeded = 0; +#endif + if (succeeded) + return (1); + failure_start(file, line, "Could not create hardlink"); + logprintf(" New link: %s\n", newpath); + logprintf(" Old name: %s\n", linkto); + failure_finish(NULL); + return(0); +} + +/* Create a symlink and report any failures. */ +int +assertion_make_symlink(const char *file, int line, + const char *newpath, const char *linkto) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + int targetIsDir = 0; /* TODO: Fix this */ + assertion_count(file, line); + if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) + return (1); +#elif HAVE_SYMLINK + assertion_count(file, line); + if (0 == symlink(linkto, newpath)) + return (1); +#endif + failure_start(file, line, "Could not create symlink"); + logprintf(" New link: %s\n", newpath); + logprintf(" Old name: %s\n", linkto); + failure_finish(NULL); + return(0); +} + +/* Set umask, report failures. */ +int +assertion_umask(const char *file, int line, int mask) +{ + assertion_count(file, line); + (void)file; /* UNUSED */ + (void)line; /* UNUSED */ + umask(mask); + return (1); +} + +/* Set times, report failures. */ +int +assertion_utimes(const char *file, int line, + const char *pathname, long at, long at_nsec, long mt, long mt_nsec) +{ + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + + (((nsec)/1000)*10)) + HANDLE h; + ULARGE_INTEGER wintm; + FILETIME fatime, fmtime; + FILETIME *pat, *pmt; + + assertion_count(file, line); + h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h == INVALID_HANDLE_VALUE) { + failure_start(file, line, "Can't access %s\n", pathname); + failure_finish(NULL); + return (0); + } + + if (at > 0 || at_nsec > 0) { + wintm.QuadPart = WINTIME(at, at_nsec); + fatime.dwLowDateTime = wintm.LowPart; + fatime.dwHighDateTime = wintm.HighPart; + pat = &fatime; + } else + pat = NULL; + if (mt > 0 || mt_nsec > 0) { + wintm.QuadPart = WINTIME(mt, mt_nsec); + fmtime.dwLowDateTime = wintm.LowPart; + fmtime.dwHighDateTime = wintm.HighPart; + pmt = &fmtime; + } else + pmt = NULL; + if (pat != NULL || pmt != NULL) + r = SetFileTime(h, NULL, pat, pmt); + else + r = 1; + CloseHandle(h); + if (r == 0) { + failure_start(file, line, "Can't SetFileTime %s\n", pathname); + failure_finish(NULL); + return (0); + } + return (1); +#else /* defined(_WIN32) && !defined(__CYGWIN__) */ + struct stat st; + struct timeval times[2]; + +#if !defined(__FreeBSD__) + mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ +#endif + if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) + return (1); + + r = lstat(pathname, &st); + if (r < 0) { + failure_start(file, line, "Can't stat %s\n", pathname); + failure_finish(NULL); + return (0); + } + + if (mt == 0 && mt_nsec == 0) { + mt = st.st_mtime; +#if defined(__FreeBSD__) + mt_nsec = st.st_mtimespec.tv_nsec; + /* FreeBSD generally only stores to microsecond res, so round. */ + mt_nsec = (mt_nsec / 1000) * 1000; +#endif + } + if (at == 0 && at_nsec == 0) { + at = st.st_atime; +#if defined(__FreeBSD__) + at_nsec = st.st_atimespec.tv_nsec; + /* FreeBSD generally only stores to microsecond res, so round. */ + at_nsec = (at_nsec / 1000) * 1000; +#endif + } + + times[1].tv_sec = mt; + times[1].tv_usec = mt_nsec / 1000; + + times[0].tv_sec = at; + times[0].tv_usec = at_nsec / 1000; + +#ifdef HAVE_LUTIMES + r = lutimes(pathname, times); +#else + r = utimes(pathname, times); +#endif + if (r < 0) { + failure_start(file, line, "Can't utimes %s\n", pathname); + failure_finish(NULL); + return (0); + } + return (1); +#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ +} + +/* Set nodump, report failures. */ +int +assertion_nodump(const char *file, int line, const char *pathname) +{ +#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) + int r; + + assertion_count(file, line); + r = chflags(pathname, UF_NODUMP); + if (r < 0) { + failure_start(file, line, "Can't set nodump %s\n", pathname); + failure_finish(NULL); + return (0); + } +#elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ + defined(FS_NODUMP_FL)) || \ + (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ + && defined(EXT2_NODUMP_FL)) + int fd, r, flags; + + assertion_count(file, line); + fd = open(pathname, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + failure_start(file, line, "Can't open %s\n", pathname); + failure_finish(NULL); + return (0); + } + r = ioctl(fd, +#ifdef FS_IOC_GETFLAGS + FS_IOC_GETFLAGS, +#else + EXT2_IOC_GETFLAGS, +#endif + &flags); + if (r < 0) { + failure_start(file, line, "Can't get flags %s\n", pathname); + failure_finish(NULL); + return (0); + } +#ifdef FS_NODUMP_FL + flags |= FS_NODUMP_FL; +#else + flags |= EXT2_NODUMP_FL; +#endif + + r = ioctl(fd, +#ifdef FS_IOC_SETFLAGS + FS_IOC_SETFLAGS, +#else + EXT2_IOC_SETFLAGS, +#endif + &flags); + if (r < 0) { + failure_start(file, line, "Can't set nodump %s\n", pathname); + failure_finish(NULL); + return (0); + } + close(fd); +#else + (void)pathname; /* UNUSED */ + assertion_count(file, line); +#endif + return (1); +} + +#ifdef PROGRAM +static void assert_version_id(char **qq, size_t *ss) +{ + char *q = *qq; + size_t s = *ss; + + /* Version number is a series of digits and periods. */ + while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { + ++q; + --s; + } + + if (q[0] == 'd' && q[1] == 'e' && q[2] == 'v') { + q += 3; + s -= 3; + } + + /* Skip a single trailing a,b,c, or d. */ + if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') + ++q; + + /* Version number terminated by space. */ + failure("No space after version: ``%s''", q); + assert(s > 1); + failure("No space after version: ``%s''", q); + assert(*q == ' '); + + ++q; --s; + + *qq = q; + *ss = s; +} + + +/* + * Check program version + */ +void assertVersion(const char *prog, const char *base) +{ + int r; + char *p, *q; + size_t s; + unsigned int prog_len = strlen(base); + + r = systemf("%s --version >version.stdout 2>version.stderr", prog); + if (r != 0) + r = systemf("%s -W version >version.stdout 2>version.stderr", + prog); + + failure("Unable to run either %s --version or %s -W version", + prog, prog); + if (!assert(r == 0)) + return; + + /* --version should generate nothing to stdout. */ + assertEmptyFile("version.stderr"); + + /* Verify format of version message. */ + q = p = slurpfile(&s, "version.stdout"); + + /* Version message should start with name of program, then space. */ + assert(s > prog_len + 1); + + failure("Version must start with '%s': ``%s''", base, p); + if (!assertEqualMem(q, base, prog_len)) { + free(p); + return; + } + + q += prog_len; s -= prog_len; + + assert(*q == ' '); + q++; s--; + + assert_version_id(&q, &s); + + /* Separator. */ + failure("No `-' between program name and versions: ``%s''", p); + assertEqualMem(q, "- ", 2); + q += 2; s -= 2; + + failure("Not long enough for libarchive version: ``%s''", p); + assert(s > 11); + + failure("Libarchive version must start with `libarchive': ``%s''", p); + assertEqualMem(q, "libarchive ", 11); + + q += 11; s -= 11; + + assert_version_id(&q, &s); + + /* Skip arbitrary third-party version numbers. */ + while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || + isalnum(*q))) { + ++q; + --s; + } + + /* All terminated by end-of-line. */ + assert(s >= 1); + + /* Skip an optional CR character (e.g., Windows) */ + failure("Version output must end with \\n or \\r\\n"); + + if (*q == '\r') { ++q; --s; } + assertEqualMem(q, "\n", 1); + + free(p); +} +#endif /* PROGRAM */ + +/* + * + * UTILITIES for use by tests. + * + */ + +/* + * Check whether platform supports symlinks. This is intended + * for tests to use in deciding whether to bother testing symlink + * support; if the platform doesn't support symlinks, there's no point + * in checking whether the program being tested can create them. + * + * Note that the first time this test is called, we actually go out to + * disk to create and verify a symlink. This is necessary because + * symlink support is actually a property of a particular filesystem + * and can thus vary between directories on a single system. After + * the first call, this returns the cached result from memory, so it's + * safe to call it as often as you wish. + */ +int +canSymlink(void) +{ + /* Remember the test result */ + static int value = 0, tested = 0; + if (tested) + return (value); + + ++tested; + assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); + /* Note: Cygwin has its own symlink() emulation that does not + * use the Win32 CreateSymbolicLink() function. */ +#if defined(_WIN32) && !defined(__CYGWIN__) + value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) + && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); +#elif HAVE_SYMLINK + value = (0 == symlink("canSymlink.0", "canSymlink.1")) + && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); +#endif + return (value); +} + +/* Platform-dependent options for hiding the output of a subcommand. */ +#if defined(_WIN32) && !defined(__CYGWIN__) +static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ +#else +static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ +#endif +/* + * Can this platform run the bzip2 program? + */ +int +canBzip2(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("bzip2 -d -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the grzip program? + */ +int +canGrzip(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("grzip -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the gzip program? + */ +int +canGzip(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("gzip -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the lrzip program? + */ +int +canRunCommand(const char *cmd) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("%s %s", cmd, redirectArgs) == 0) + value = 1; + } + return (value); +} + +int +canLrzip(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("lrzip -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the lz4 program? + */ +int +canLz4(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("lz4 -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the lzip program? + */ +int +canLzip(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("lzip -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the lzma program? + */ +int +canLzma(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("lzma -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the lzop program? + */ +int +canLzop(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("lzop -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the xz program? + */ +int +canXz(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("xz -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this filesystem handle nodump flags. + */ +#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) + +int +canNodump(void) +{ + const char *path = "cannodumptest"; + struct stat sb; + + assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); + if (chflags(path, UF_NODUMP) < 0) + return (0); + if (stat(path, &sb) < 0) + return (0); + if (sb.st_flags & UF_NODUMP) + return (1); + return (0); +} + +#elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) \ + && defined(FS_NODUMP_FL)) || \ + (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ + && defined(EXT2_NODUMP_FL)) +int +canNodump(void) +{ + const char *path = "cannodumptest"; + int fd, r, flags; + + assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); + fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return (0); + r = ioctl(fd, +#ifdef FS_IOC_GETFLAGS + FS_IOC_GETFLAGS, +#else + EXT2_IOC_GETFLAGS, +#endif + &flags); + if (r < 0) + return (0); +#ifdef FS_NODUMP_FL + flags |= FS_NODUMP_FL; +#else + flags |= EXT2_NODUMP_FL; +#endif + r = ioctl(fd, +#ifdef FS_IOC_SETFLAGS + FS_IOC_SETFLAGS, +#else + EXT2_IOC_SETFLAGS, +#endif + &flags); + if (r < 0) + return (0); + close(fd); + fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return (0); + r = ioctl(fd, +#ifdef FS_IOC_GETFLAGS + FS_IOC_GETFLAGS, +#else + EXT2_IOC_GETFLAGS, +#endif + &flags); + if (r < 0) + return (0); + close(fd); +#ifdef FS_NODUMP_FL + if (flags & FS_NODUMP_FL) +#else + if (flags & EXT2_NODUMP_FL) +#endif + return (1); + return (0); +} + +#else + +int +canNodump() +{ + return (0); +} + +#endif + +/* + * Sleep as needed; useful for verifying disk timestamp changes by + * ensuring that the wall-clock time has actually changed before we + * go back to re-read something from disk. + */ +void +sleepUntilAfter(time_t t) +{ + while (t >= time(NULL)) +#if defined(_WIN32) && !defined(__CYGWIN__) + Sleep(500); +#else + sleep(1); +#endif +} + +/* + * 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); + if (verbosity > VERBOSITY_FULL) + logprintf("Cmd: %s\n", buff); + 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; + FILE *f; + int r; + + va_start(ap, fmt); + vsprintf(filename, fmt, ap); + va_end(ap); + + f = fopen(filename, "rb"); + if (f == NULL) { + /* Note: No error; non-existent file is okay here. */ + return (NULL); + } + r = fstat(fileno(f), &st); + if (r != 0) { + logprintf("Can't stat file %s\n", filename); + fclose(f); + return (NULL); + } + p = malloc((size_t)st.st_size + 1); + if (p == NULL) { + logprintf("Can't allocate %ld bytes of memory to read file %s\n", + (long int)st.st_size, filename); + fclose(f); + return (NULL); + } + bytes_read = fread(p, 1, (size_t)st.st_size, f); + if (bytes_read < st.st_size) { + logprintf("Can't read file %s\n", filename); + fclose(f); + free(p); + return (NULL); + } + p[st.st_size] = '\0'; + if (sizep != NULL) + *sizep = (size_t)st.st_size; + fclose(f); + return (p); +} + +/* + * 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. + */ +void +dumpfile(const char *filename, void *data, size_t len) +{ + ssize_t bytes_written; + FILE *f; + + f = fopen(filename, "wb"); + if (f == NULL) { + logprintf("Can't open file %s for writing\n", filename); + return; + } + bytes_written = fwrite(data, 1, len, f); + if (bytes_written < (ssize_t)len) + logprintf("Can't write file %s\n", filename); + fclose(f); +} + +/* Read a uuencoded file from the reference directory, decode, and + * write the result into the current directory. */ +#define VALID_UUDECODE(c) (c >= 32 && c <= 96) +#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. */ + out = fopen(name, "wb"); + 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) { + assert(VALID_UUDECODE(p[0])); + assert(VALID_UUDECODE(p[1])); + n = UUDECODE(*p++) << 18; + n |= UUDECODE(*p++) << 12; + fputc(n >> 16, out); + --bytes; + } + if (bytes > 0) { + assert(VALID_UUDECODE(p[0])); + n |= UUDECODE(*p++) << 6; + fputc((n >> 8) & 0xFF, out); + --bytes; + } + if (bytes > 0) { + assert(VALID_UUDECODE(p[0])); + n |= UUDECODE(*p++); + fputc(n & 0xFF, out); + --bytes; + } + } + } + fclose(out); + fclose(in); +} + +void +copy_reference_file(const char *name) +{ + char buff[1024]; + FILE *in, *out; + size_t rbytes; + + sprintf(buff, "%s/%s", refdir, name); + in = fopen(buff, "rb"); + failure("Couldn't open reference file %s", buff); + assert(in != NULL); + if (in == NULL) + return; + /* Now, decode the rest and write it. */ + /* Not a lot of error checking here; the input better be right. */ + out = fopen(name, "wb"); + while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { + if (fwrite(buff, 1, rbytes, out) != rbytes) { + logprintf("Error: fwrite\n"); + break; + } + } + fclose(out); + fclose(in); +} + +int +is_LargeInode(const char *file) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + BY_HANDLE_FILE_INFORMATION bhfi; + int r; + + r = my_GetFileInformationByName(file, &bhfi); + if (r != 0) + return (0); + return (bhfi.nFileIndexHigh & 0x0000FFFFUL); +#else + struct stat st; + int64_t ino; + + if (stat(file, &st) < 0) + return (0); + ino = (int64_t)st.st_ino; + return (ino > 0xffffffff); +#endif +} + +void +extract_reference_files(const char **names) +{ + while (names && *names) + extract_reference_file(*names++); +} + +#ifndef PROGRAM +/* Set ACLs */ +int +assertion_entry_set_acls(const char *file, int line, struct archive_entry *ae, + struct archive_test_acl_t *acls, int n) +{ + int i, r, ret; + + assertion_count(file, line); + + ret = 0; + archive_entry_acl_clear(ae); + for (i = 0; i < n; i++) { + r = archive_entry_acl_add_entry(ae, + acls[i].type, acls[i].permset, acls[i].tag, + acls[i].qual, acls[i].name); + if (r != 0) { + ret = 1; + failure_start(file, line, "type=%#010x, ", + "permset=%#010x, tag=%d, qual=%d name=%s", + acls[i].type, acls[i].permset, acls[i].tag, + acls[i].qual, acls[i].name); + failure_finish(NULL); + } + } + + return (ret); +} + +static int +archive_test_acl_match(struct archive_test_acl_t *acl, int type, int permset, + int tag, int qual, const char *name) +{ + if (type != acl->type) + return (0); + if (permset != acl->permset) + return (0); + if (tag != acl->tag) + return (0); + if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) + return (1); + if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) + return (1); + if (tag == ARCHIVE_ENTRY_ACL_EVERYONE) + return (1); + if (tag == ARCHIVE_ENTRY_ACL_OTHER) + return (1); + if (qual != acl->qual) + return (0); + if (name == NULL) { + if (acl->name == NULL || acl->name[0] == '\0') + return (1); + return (0); + } + if (acl->name == NULL) { + if (name[0] == '\0') + return (1); + return (0); + } + return (0 == strcmp(name, acl->name)); +} + +/* Compare ACLs */ +int +assertion_entry_compare_acls(const char *file, int line, + struct archive_entry *ae, struct archive_test_acl_t *acls, int cnt, + int want_type, int mode) +{ + int *marker; + int i, r, n, ret; + int type, permset, tag, qual; + int matched; + const char *name; + + assertion_count(file, line); + + ret = 0; + n = 0; + marker = malloc(sizeof(marker[0]) * cnt); + + for (i = 0; i < cnt; i++) { + if ((acls[i].type & want_type) != 0) { + marker[n] = i; + n++; + } + } + + if (n == 0) { + failure_start(file, line, "No ACL's to compare, type mask: %d", + want_type); + return (1); + } + + while (0 == (r = archive_entry_acl_next(ae, want_type, + &type, &permset, &tag, &qual, &name))) { + for (i = 0, matched = 0; i < n && !matched; i++) { + if (archive_test_acl_match(&acls[marker[i]], type, + permset, tag, qual, name)) { + /* We found a match; remove it. */ + marker[i] = marker[n - 1]; + n--; + matched = 1; + } + } + if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS + && tag == ARCHIVE_ENTRY_ACL_USER_OBJ) { + if (!matched) { + failure_start(file, line, "No match for " + "user_obj perm"); + failure_finish(NULL); + ret = 1; + } + if ((permset << 6) != (mode & 0700)) { + failure_start(file, line, "USER_OBJ permset " + "(%02o) != user mode (%02o)", permset, + 07 & (mode >> 6)); + failure_finish(NULL); + ret = 1; + } + } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS + && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) { + if (!matched) { + failure_start(file, line, "No match for " + "group_obj perm"); + failure_finish(NULL); + ret = 1; + } + if ((permset << 3) != (mode & 0070)) { + failure_start(file, line, "GROUP_OBJ permset " + "(%02o) != group mode (%02o)", permset, + 07 & (mode >> 3)); + failure_finish(NULL); + ret = 1; + } + } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS + && tag == ARCHIVE_ENTRY_ACL_OTHER) { + if (!matched) { + failure_start(file, line, "No match for " + "other perm"); + failure_finish(NULL); + ret = 1; + } + if ((permset << 0) != (mode & 0007)) { + failure_start(file, line, "OTHER permset " + "(%02o) != other mode (%02o)", permset, + mode & 07); + failure_finish(NULL); + ret = 1; + } + } else if (matched != 1) { + failure_start(file, line, "Could not find match for " + "ACL (type=%#010x,permset=%#010x,tag=%d,qual=%d," + "name=``%s'')", type, permset, tag, qual, name); + failure_finish(NULL); + ret = 1; + } + } + if (r != ARCHIVE_EOF) { + failure_start(file, line, "Should not exit before EOF"); + failure_finish(NULL); + ret = 1; + } + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 && + (mode_t)(mode & 0777) != (archive_entry_mode(ae) & 0777)) { + failure_start(file, line, "Mode (%02o) and entry mode (%02o) " + "mismatch", mode, archive_entry_mode(ae)); + failure_finish(NULL); + ret = 1; + } + if (n != 0) { + failure_start(file, line, "Could not find match for ACL " + "(type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s'')", + acls[marker[0]].type, acls[marker[0]].permset, + acls[marker[0]].tag, acls[marker[0]].qual, + acls[marker[0]].name); + failure_finish(NULL); + ret = 1; + /* Number of ACLs not matched should == 0 */ + } + free(marker); + return (ret); +} +#endif /* !defined(PROGRAM) */ + +/* + * + * TEST management + * + */ + +/* + * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has + * a line like + * DEFINE_TEST(test_function) + * for each test. + */ + +/* Use "list.h" to declare all of the test functions. */ +#undef DEFINE_TEST +#define DEFINE_TEST(name) void name(void); +#include "list.h" + +/* Use "list.h" to create a list of all tests (functions and names). */ +#undef DEFINE_TEST +#define DEFINE_TEST(n) { n, #n, 0 }, +struct test_list_t tests[] = { + #include "list.h" +}; + +/* + * Summarize repeated failures in the just-completed test. + */ +static void +test_summarize(int failed, int skips_num) +{ + unsigned int i; + + switch (verbosity) { + case VERBOSITY_SUMMARY_ONLY: + printf(failed ? "E" : "."); + fflush(stdout); + break; + case VERBOSITY_PASSFAIL: + printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); + break; + } + + log_console = (verbosity == VERBOSITY_LIGHT_REPORT); + + for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { + if (failed_lines[i].count > 1 && !failed_lines[i].skip) + logprintf("%s:%d: Summary: Failed %d times\n", + failed_filename, i, failed_lines[i].count); + } + /* Clear the failure history for the next file. */ + failed_filename = NULL; + memset(failed_lines, 0, sizeof(failed_lines)); +} + +/* + * Actually run a single test, with appropriate setup and cleanup. + */ +static int +test_run(int i, const char *tmpdir) +{ + char workdir[1024]; + char logfilename[64]; + int failures_before = failures; + int skips_before = skips; + int oldumask; + + switch (verbosity) { + case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ + break; + case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ + printf("%3d: %-64s", i, tests[i].name); + fflush(stdout); + break; + default: /* Title of test, details will follow */ + printf("%3d: %s\n", i, tests[i].name); + } + + /* Chdir to the top-level work directory. */ + if (!assertChdir(tmpdir)) { + fprintf(stderr, + "ERROR: Can't chdir to top work dir %s\n", tmpdir); + exit(1); + } + /* Create a log file for this test. */ + sprintf(logfilename, "%s.log", tests[i].name); + logfile = fopen(logfilename, "w"); + fprintf(logfile, "%s\n\n", tests[i].name); + /* Chdir() to a work dir for this specific test. */ + snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); + testworkdir = workdir; + if (!assertMakeDir(testworkdir, 0755) + || !assertChdir(testworkdir)) { + fprintf(stderr, + "ERROR: Can't chdir to work dir %s\n", testworkdir); + exit(1); + } + /* Explicitly reset the locale before each test. */ + setlocale(LC_ALL, "C"); + /* Record the umask before we run the test. */ + umask(oldumask = umask(0)); + /* + * Run the actual test. + */ + (*tests[i].func)(); + /* + * Clean up and report afterwards. + */ + testworkdir = NULL; + /* Restore umask */ + umask(oldumask); + /* Reset locale. */ + setlocale(LC_ALL, "C"); + /* Reset directory. */ + if (!assertChdir(tmpdir)) { + fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", + tmpdir); + exit(1); + } + /* Report per-test summaries. */ + tests[i].failures = failures - failures_before; + test_summarize(tests[i].failures, skips - skips_before); + /* Close the per-test log file. */ + fclose(logfile); + logfile = NULL; + /* If there were no failures, we can remove the work dir and logfile. */ + if (tests[i].failures == 0) { + if (!keep_temp_files && assertChdir(tmpdir)) { +#if defined(_WIN32) && !defined(__CYGWIN__) + /* Make sure not to leave empty directories. + * Sometimes a processing of closing files used by tests + * is not done, then rmdir will be failed and it will + * leave a empty test directory. So we should wait a few + * seconds and retry rmdir. */ + int r, t; + for (t = 0; t < 10; t++) { + if (t > 0) + Sleep(1000); + r = systemf("rmdir /S /Q %s", tests[i].name); + if (r == 0) + break; + } + systemf("del %s", logfilename); +#else + systemf("rm -rf %s", tests[i].name); + systemf("rm %s", logfilename); +#endif + } + } + /* Return appropriate status. */ + return (tests[i].failures); +} + +/* + * + * + * MAIN and support routines. + * + * + */ + +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(" -u Keep running specifies tests until one fails.\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); +} + +static char * +get_refdir(const char *d) +{ + size_t tried_size, buff_size; + char *buff, *tried, *pwd = NULL, *p = NULL; + +#ifdef PATH_MAX + buff_size = PATH_MAX; +#else + buff_size = 8192; +#endif + buff = calloc(buff_size, 1); + if (buff == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + + /* Allocate a buffer to hold the various directories we checked. */ + tried_size = buff_size * 2; + tried = calloc(tried_size, 1); + if (tried == NULL) { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + + /* If a dir was specified, try that */ + if (d != NULL) { + pwd = NULL; + snprintf(buff, buff_size, "%s", d); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); + goto failure; + } + + /* Get the current dir. */ +#ifdef PATH_MAX + pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ +#else + pwd = getcwd(NULL, 0); +#endif + while (pwd[strlen(pwd) - 1] == '\n') + pwd[strlen(pwd) - 1] = '\0'; + + /* Look for a known file. */ + snprintf(buff, buff_size, "%s", pwd); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); + + snprintf(buff, buff_size, "%s/test", pwd); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); + +#if defined(LIBRARY) + snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); +#else + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); +#endif + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); + +#if defined(PROGRAM_ALIAS) + snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); +#endif + + if (memcmp(pwd, "/usr/obj", 8) == 0) { + snprintf(buff, buff_size, "%s", pwd + 8); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); + + snprintf(buff, buff_size, "%s/test", pwd + 8); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, tried_size - strlen(tried) - 1); + strncat(tried, "\n", tried_size - strlen(tried) - 1); + } + +failure: + printf("Unable to locate known reference file %s\n", KNOWNREF); + printf(" Checked following directories:\n%s\n", tried); + printf("Use -r option to specify full path to reference directory\n"); +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) + DebugBreak(); +#endif + exit(1); + +success: + free(p); + free(pwd); + free(tried); + + /* Copy result into a fresh buffer to reduce memory usage. */ + p = strdup(buff); + free(buff); + return p; +} + +int +main(int argc, char **argv) +{ + static const int limit = sizeof(tests) / sizeof(tests[0]); + int test_set[sizeof(tests) / sizeof(tests[0])]; + int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; + time_t now; + char *refdir_alloc = NULL; + const char *progname; + char **saved_argv; + const char *tmp, *option_arg, *p; + char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; + char tmpdir_timestamp[256]; + + (void)argc; /* UNUSED */ + + /* Get the current dir. */ +#ifdef PATH_MAX + pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ +#else + pwd = getcwd(NULL, 0); +#endif + while (pwd[strlen(pwd) - 1] == '\n') + pwd[strlen(pwd) - 1] = '\0'; + +#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) + /* To stop to run the default invalid parameter handler. */ + _set_invalid_parameter_handler(invalid_parameter_handler); + /* Disable annoying assertion message box. */ + _CrtSetReportMode(_CRT_ASSERT, 0); +#endif + + /* + * Name of this program, used to build root of our temp directory + * tree. + */ + progname = p = argv[0]; + if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) + { + fprintf(stderr, "ERROR: Out of memory."); + exit(1); + } + strcpy(testprogdir, progname); + while (*p != '\0') { + /* Support \ or / dir separators for Windows compat. */ + if (*p == '/' || *p == '\\') + { + progname = p + 1; + i = j; + } + ++p; + j++; + } + testprogdir[i] = '\0'; +#if defined(_WIN32) && !defined(__CYGWIN__) + if (testprogdir[0] != '/' && testprogdir[0] != '\\' && + !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || + (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && + testprogdir[1] == ':' && + (testprogdir[2] == '/' || testprogdir[2] == '\\'))) +#else + if (testprogdir[0] != '/') +#endif + { + /* Fixup path for relative directories. */ + if ((testprogdir = (char *)realloc(testprogdir, + strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) + { + fprintf(stderr, "ERROR: Out of memory."); + exit(1); + } + memmove(testprogdir + strlen(pwd) + 1, testprogdir, + strlen(testprogdir) + 1); + memcpy(testprogdir, pwd, strlen(pwd)); + testprogdir[strlen(pwd)] = '/'; + } + +#ifdef PROGRAM + /* Get the target program from environment, if available. */ + testprogfile = 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; + + /* Allow -v to be controlled through the environment. */ + if (getenv("_VERBOSITY_LEVEL") != NULL) + { + vlevel = getenv("_VERBOSITY_LEVEL"); + verbosity = atoi(vlevel); + if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) + { + /* Unsupported verbosity levels are silently ignored */ + vlevel = NULL; + verbosity = VERBOSITY_PASSFAIL; + } + } + + /* 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 + testprogfile = option_arg; +#else + fprintf(stderr, "-p option not permitted\n"); + usage(progname); +#endif + break; + case 'q': + if (!vlevel) + verbosity--; + break; + case 'r': + refdir = option_arg; + break; + case 'u': + until_failure++; + break; + case 'v': + if (!vlevel) + verbosity++; + break; + default: + fprintf(stderr, "Unrecognized option '%c'\n", + option); + usage(progname); + } + } + } + + /* + * Sanity-check that our options make sense. + */ +#ifdef PROGRAM + if (testprogfile == NULL) + { + if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + + strlen(PROGRAM) + 1)) == NULL) + { + fprintf(stderr, "ERROR: Out of memory."); + exit(1); + } + strcpy(tmp2, testprogdir); + strcat(tmp2, "/"); + strcat(tmp2, PROGRAM); + testprogfile = tmp2; + } + + { + char *testprg; +#if defined(_WIN32) && !defined(__CYGWIN__) + /* Command.com sometimes rejects '/' separators. */ + testprg = strdup(testprogfile); + for (i = 0; testprg[i] != '\0'; i++) { + if (testprg[i] == '/') + testprg[i] = '\\'; + } + testprogfile = testprg; +#endif + /* Quote the name that gets put into shell command lines. */ + testprg = malloc(strlen(testprogfile) + 3); + strcpy(testprg, "\""); + strcat(testprg, testprogfile); + strcat(testprg, "\""); + testprog = testprg; + } +#endif + +#if !defined(_WIN32) && defined(SIGPIPE) + { /* Ignore SIGPIPE signals */ + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGPIPE, &sa, NULL); + } +#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++) { + 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 (assertMakeDir(tmpdir,0755)) + break; + if (i >= 999) { + 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." + */ + refdir = refdir_alloc = get_refdir(refdir); + + /* + * Banner with basic information. + */ + printf("\n"); + printf("If tests fail or crash, details will be in:\n"); + printf(" %s\n", tmpdir); + printf("\n"); + if (verbosity > VERBOSITY_SUMMARY_ONLY) { + 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); + } else { + printf("Running "); + fflush(stdout); + } + + /* + * Run some or all of the individual tests. + */ + saved_argv = argv; + do { + argv = saved_argv; + do { + int test_num; + + test_num = get_test_set(test_set, limit, *argv, tests); + if (test_num < 0) { + printf("*** INVALID Test %s\n", *argv); + free(refdir_alloc); + free(testprogdir); + usage(progname); + return (1); + } + for (i = 0; i < test_num; i++) { + tests_run++; + if (test_run(test_set[i], tmpdir)) { + tests_failed++; + if (until_failure) + goto finish; + } + } + if (*argv != NULL) + argv++; + } while (*argv != NULL); + } while (until_failure); + +finish: + /* Must be freed after all tests run */ + free(tmp2); + free(testprogdir); + free(pwd); + + /* + * Report summary statistics. + */ + if (verbosity > VERBOSITY_SUMMARY_ONLY) { + printf("\n"); + printf("Totals:\n"); + printf(" Tests run: %8d\n", tests_run); + printf(" Tests failed: %8d\n", tests_failed); + printf(" Assertions checked:%8d\n", assertions); + printf(" Assertions failed: %8d\n", failures); + printf(" Skips reported: %8d\n", skips); + } + if (failures) { + printf("\n"); + printf("Failing tests:\n"); + for (i = 0; i < limit; ++i) { + if (tests[i].failures) + printf(" %d: %s (%d failures)\n", i, + tests[i].name, tests[i].failures); + } + printf("\n"); + printf("Details for failing tests: %s\n", tmpdir); + printf("\n"); + } else { + if (verbosity == VERBOSITY_SUMMARY_ONLY) + printf("\n"); + printf("%d tests passed, no failures\n", tests_run); + } + + free(refdir_alloc); + + /* If the final tmpdir is empty, we can remove it. */ + /* This should be the usual case when all tests succeed. */ + assertChdir(".."); + rmdir(tmpdir); + + return (tests_failed ? 1 : 0); +} Property changes on: vendor/libarchive/dist/test_utils/test_main.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property