diff options
205 files changed, 8698 insertions, 3572 deletions
diff --git a/.gitattributes b/.gitattributes index d959918..3829160 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,13 @@ .gitattributes export-ignore .hooks* export-ignore +# Custom attribute to mark sources as using our C code style. +[attr]our-c-style whitespace=tab-in-indent format.clang-format + +# Custom attribute to mark sources as generated. +# Do not perform whitespace checks. Do not format. +[attr]generated whitespace=-tab-in-indent,-indent-with-non-tab -format.clang-format + bootstrap crlf=input configure crlf=input *.[1-9] crlf=input @@ -16,10 +23,17 @@ configure crlf=input *.sln -crlf *.vcproj -crlf -*.c whitespace=tab-in-indent -*.h whitespace=tab-in-indent -*.cxx whitespace=tab-in-indent -*.txt whitespace=tab-in-indent -*.cmake whitespace=tab-in-indent +*.c our-c-style +*.cc our-c-style +*.cpp our-c-style +*.cu our-c-style +*.cxx our-c-style +*.h our-c-style +*.hh our-c-style +*.hpp our-c-style +*.hxx our-c-style +*.notcu our-c-style +*.cmake whitespace=tab-in-indent *.rst whitespace=tab-in-indent conflict-marker-size=79 +*.txt whitespace=tab-in-indent diff --git a/Auxiliary/cmake.m4 b/Auxiliary/cmake.m4 index 3ef4c16..7beff41 100644 --- a/Auxiliary/cmake.m4 +++ b/Auxiliary/cmake.m4 @@ -24,10 +24,10 @@ AC_ARG_VAR([$1][_LIBS], [linker flags for $1. This overrides the cmake output])d failed=false AC_MSG_CHECKING([for $1]) -if test -n "$1[]_$2[]FLAGS"; then +if test -z "${$1[]_$2[]FLAGS}"; then $1[]_$2[]FLAGS=`$CMAKE_BINARY --find-package "-DNAME=$1" "-DCOMPILER_ID=m4_default([$3], [GNU])" "-DLANGUAGE=$2" -DMODE=COMPILE $4` || failed=true fi -if test -n "$1[]_LIBS"; then +if test -z "${$1[]_LIBS}"; then $1[]_LIBS=`$CMAKE_BINARY --find-package "-DNAME=$1" "-DCOMPILER_ID=m4_default([$3], [GNU])" "-DLANGUAGE=$2" -DMODE=LINK $4` || failed=true fi diff --git a/CMakeLists.txt b/CMakeLists.txt index bec81a3..e613224 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -421,14 +421,15 @@ macro (CMAKE_BUILD_UTILITIES) if(NOT LIBLZMA_FOUND) message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBLZMA is ON but LibLZMA is not found!") endif() - set(LZMA_INCLUDE_DIR ${LIBLZMA_INCLUDE_DIRS}) - set(LZMA_LIBRARY ${LIBLZMA_LIBRARIES}) else() add_subdirectory(Utilities/cmliblzma) CMAKE_SET_TARGET_FOLDER(cmliblzma "Utilities/3rdParty") - set(LZMA_INCLUDE_DIR + set(LIBLZMA_HAS_AUTO_DECODER 1) + set(LIBLZMA_HAS_EASY_ENCODER 1) + set(LIBLZMA_HAS_LZMA_PRESET 1) + set(LIBLZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmliblzma/liblzma/api") - set(LZMA_LIBRARY cmliblzma) + set(LIBLZMA_LIBRARY cmliblzma) endif() endif() @@ -449,13 +450,14 @@ macro (CMAKE_BUILD_UTILITIES) add_definitions(-DLIBARCHIVE_STATIC) set(ENABLE_NETTLE OFF CACHE INTERNAL "Enable use of Nettle") set(ENABLE_OPENSSL ${CMAKE_USE_OPENSSL} CACHE INTERNAL "Enable use of OpenSSL") - set(ENABLE_LZMA ON CACHE INTERNAL "Enable the use of the system found LZMA library if found") - set(ENABLE_ZLIB ON CACHE INTERNAL "Enable the use of the system found ZLIB library if found") - set(ENABLE_BZip2 ON CACHE INTERNAL "Enable the use of the system found BZip2 library if found") - set(ENABLE_LIBXML2 OFF CACHE INTERNAL "Enable the use of the system found libxml2 library if found") - set(ENABLE_EXPAT ON CACHE INTERNAL "Enable the use of the system found EXPAT library if found") - set(ENABLE_PCREPOSIX OFF CACHE INTERNAL "Enable the use of the system found PCREPOSIX library if found") - set(ENABLE_LibGCC OFF CACHE INTERNAL "Enable the use of the system found LibGCC library if found") + set(ENABLE_LZMA ON CACHE INTERNAL "Enable the use of the system LZMA library if found") + set(ENABLE_LZO OFF CACHE INTERNAL "Enable the use of the system LZO library if found") + set(ENABLE_ZLIB ON CACHE INTERNAL "Enable the use of the system ZLIB library if found") + set(ENABLE_BZip2 ON CACHE INTERNAL "Enable the use of the system BZip2 library if found") + set(ENABLE_LIBXML2 OFF CACHE INTERNAL "Enable the use of the system libxml2 library if found") + set(ENABLE_EXPAT ON CACHE INTERNAL "Enable the use of the system EXPAT library if found") + set(ENABLE_PCREPOSIX OFF CACHE INTERNAL "Enable the use of the system PCREPOSIX library if found") + set(ENABLE_LibGCC OFF CACHE INTERNAL "Enable the use of the system LibGCC library if found") set(ENABLE_XATTR OFF CACHE INTERNAL "Enable extended attribute support") set(ENABLE_ACL OFF CACHE INTERNAL "Enable ACL support") set(ENABLE_ICONV OFF CACHE INTERNAL "Enable iconv support") diff --git a/Help/manual/cmake-language.7.rst b/Help/manual/cmake-language.7.rst index ba0b30f..27b5d30 100644 --- a/Help/manual/cmake-language.7.rst +++ b/Help/manual/cmake-language.7.rst @@ -329,11 +329,17 @@ For example: To support legacy CMake code, unquoted arguments may also contain double-quoted strings (``"..."``, possibly enclosing horizontal whitespace), and make-style variable references (``$(MAKEVAR)``). + Unescaped double-quotes must balance, may not appear at the beginning of an unquoted argument, and are treated as part of the content. For example, the unquoted arguments ``-Da="b c"``, ``-Da=$(v)``, and ``a" "b"c"d`` are each interpreted literally. + Make-style references are treated literally as part of the content + and do not undergo variable expansion. They are treated as part + of a single argument (rather than as separate ``$``, ``(``, + ``MAKEVAR``, and ``)`` arguments). + The above "unquoted_legacy" production represents such arguments. We do not recommend using legacy unquoted arguments in new code. Instead use a `Quoted Argument`_ or a `Bracket Argument`_ to diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 5fad10c..a3b6e6b 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -115,6 +115,7 @@ Properties on Targets /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG /prop_tgt/ARCHIVE_OUTPUT_NAME /prop_tgt/AUTOGEN_TARGET_DEPENDS + /prop_tgt/AUTOMOC_DEPEND_FILTERS /prop_tgt/AUTOMOC_MOC_OPTIONS /prop_tgt/AUTOMOC /prop_tgt/AUTOUIC diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst index 56d4ca7..3b9931e 100644 --- a/Help/manual/cmake-qt.7.rst +++ b/Help/manual/cmake-qt.7.rst @@ -63,26 +63,33 @@ If a ``Q_OBJECT`` or ``Q_GADGET`` macro is found in a header file, ``moc`` will be run on the file. The result will be put into a file named according to ``moc_<basename>.cpp``. If the macro is found in a C++ implementation file, the moc output will be put into a file named according to -``<basename>.moc``, following the Qt conventions. The ``moc file`` may be -included by the user in the C++ implementation file with a preprocessor -``#include``. If it is not so included, it will be added to a separate file -which is compiled into the target. +``<basename>.moc``, following the Qt conventions. The ``<basename>.moc`` must +be included by the user in the C++ implementation file with a preprocessor +``#include``. -The ``moc`` command line will consume the :prop_tgt:`COMPILE_DEFINITIONS` and -:prop_tgt:`INCLUDE_DIRECTORIES` target properties from the target it is being -invoked for, and for the appropriate build configuration. - -The generated ``moc_*.cpp`` and ``*.moc`` files are placed in the +Included ``moc_*.cpp`` and ``*.moc`` files will be generated in the ``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/include`` directory which is automatically added to the target's :prop_tgt:`INCLUDE_DIRECTORIES`. (This differs from CMake 3.7 and below; see their documentation for details.) +Not included ``moc_<basename>.cpp`` files will be generated in custom +folders to avoid name collisions and included in a separate +``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/moc_compilation.cpp`` +file which is compiled into the target. + +The ``moc`` command line will consume the :prop_tgt:`COMPILE_DEFINITIONS` and +:prop_tgt:`INCLUDE_DIRECTORIES` target properties from the target it is being +invoked for, and for the appropriate build configuration. + The :prop_tgt:`AUTOMOC` target property may be pre-set for all following targets by setting the :variable:`CMAKE_AUTOMOC` variable. The :prop_tgt:`AUTOMOC_MOC_OPTIONS` target property may be populated to set options to pass to ``moc``. The :variable:`CMAKE_AUTOMOC_MOC_OPTIONS` variable may be populated to pre-set the options for all following targets. +Additional ``moc`` dependency file names can be extracted from source code +by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. + Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`. diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 6112e65..259d87b 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -256,6 +256,7 @@ Variables that Control the Build /variable/CMAKE_ANDROID_STL_TYPE /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_AUTOMOC_DEPEND_FILTERS /variable/CMAKE_AUTOMOC_MOC_OPTIONS /variable/CMAKE_AUTOMOC /variable/CMAKE_AUTORCC diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index b42643f..b729c0b 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -8,31 +8,41 @@ preprocessor automatically, i.e. without having to use the :module:`QT4_WRAP_CPP() <FindQt4>` or QT5_WRAP_CPP() macro. Currently Qt4 and Qt5 are supported. -When this property is set ``ON``, CMake will scan the +When this property is set ``ON``, CMake will scan the header and source files at build time and invoke moc accordingly. -* If an ``#include`` statement like ``#include "moc_foo.cpp"`` is found, - the ``Q_OBJECT`` class declaration is expected in the header, and - ``moc`` is run on the header file. A ``moc_foo.cpp`` file will be - generated from the source's header into the - ``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/include`` - directory which is automatically added to the target's +* If an ``#include`` statement like ``#include "moc_<basename>.cpp"`` is found, + the ``Q_OBJECT`` or ``Q_GADGET`` macros are expected in an otherwise empty + line of the ``<basename>.h(xx)`` header file. ``moc`` is run on the header file to + generate ``moc_<basename>.cpp`` in the + ``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/include`` directory + which is automatically added to the target's :prop_tgt:`INCLUDE_DIRECTORIES`. This allows the compiler to find the - included ``moc_foo.cpp`` file regardless of the location the original source. - However, if multiple source files in different directories do this then their - generated moc files would collide. In this case a diagnostic will be issued. - -* If an ``#include`` statement like ``#include "foo.moc"`` is found, - then a ``Q_OBJECT`` is expected in the current source file and ``moc`` - is run on the file itself. Additionally, header files with the same - base name (like ``foo.h``) or ``_p`` appended to the base name (like - ``foo_p.h``) are parsed for ``Q_OBJECT`` macros, and if found, ``moc`` - is also executed on those files. ``AUTOMOC`` checks multiple header - alternative extensions, such as ``hpp``, ``hxx`` etc when searching - for headers. The resulting moc files, which are not included as shown - above in any of the source files are included in a generated - ``moc_compilation.cpp`` file, which is compiled as part of the - target. + included ``moc_<basename>.cpp`` file regardless of the location the + original source. + +* If an ``#include`` statement like ``#include "<basename>.moc"`` is found, + then ``Q_OBJECT`` or ``Q_GADGET`` macros are expected in the current source + file and ``moc`` is run on the source file itself. + +* Header files that are not included by an ``#include "moc_<basename>.cpp"`` + statement are nonetheless scanned for ``Q_OBJECT`` or ``Q_GADGET`` macros. + The resulting ``moc_<basename>.cpp`` files are generated in custom + directories and automatically included in the generated + ``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/moc_compilation.cpp`` file, + which is compiled as part of the target. The custom directories help to + avoid name collisions for moc files with the same ``<basename>``. + +* Additionally, header files with the same base name as a source file, + (like ``<basename>.h``) or ``_p`` appended to the base name (like + ``<basename>_p.h``), are parsed for ``Q_OBJECT`` or ``Q_GADGET`` macros, + and if found, ``moc`` is also executed on those files. + +* ``AUTOMOC`` always checks multiple header alternative extensions, + such as ``hpp``, ``hxx``, etc. when searching for headers. + +* ``AUTOMOC`` looks for the ``Q_PLUGIN_METADATA`` macro and reruns the + ``moc`` when the file addressed by the ``FILE`` argument of the macro changes. This property is initialized by the value of the :variable:`CMAKE_AUTOMOC` variable if it is set when a target is created. @@ -47,6 +57,9 @@ See the documentation for this variable for more details. The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the automoc targets together in an IDE, e.g. in MSVS. +Additional ``moc`` dependency file names can be extracted from source code +by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. + Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`. diff --git a/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst b/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst new file mode 100644 index 0000000..810cbf4 --- /dev/null +++ b/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst @@ -0,0 +1,45 @@ +AUTOMOC_DEPEND_FILTERS +---------------------- + +Filter definitions used by :prop_tgt:`AUTOMOC` to extract file names from +source code as additional dependencies for the ``moc`` file. + +This property is only used if the :prop_tgt:`AUTOMOC` property is ``ON`` +for this target. + +Filters are defined as ``KEYWORD;REGULAR_EXPRESSION`` pairs. First the file +content is searched for ``KEYWORD``. If it is found at least once, then file +names are extracted by successively searching for ``REGULAR_EXPRESSION`` and +taking the first match group. + +Consider a filter extracts the file name ``DEP`` from the content of a file +``FOO``. If ``DEP`` changes, then the ``moc`` file for ``FOO`` gets rebuilt. +The file ``DEP`` is searched for first in the vicinity +of ``FOO`` and afterwards in the target's :prop_tgt:`INCLUDE_DIRECTORIES`. + +By default :prop_tgt:`AUTOMOC_DEPEND_FILTERS` is initialized from +:variable:`CMAKE_AUTOMOC_DEPEND_FILTERS`, which is empty by default. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. + + +Example +------- + +Consider a file ``FOO.hpp`` holds a custom macro ``OBJ_JSON_FILE`` and we +want the ``moc`` file to depend on the macro`s file name argument:: + + class My_Class : public QObject + { + Q_OBJECT + OBJ_JSON_FILE ( "DEP.json" ) + ... + }; + +Then we might use :variable:`CMAKE_AUTOMOC_DEPEND_FILTERS` to +define a filter like this:: + + set(CMAKE_AUTOMOC_DEPEND_FILTERS + "OBJ_JSON_FILE" "[\n][ \t]*OBJ_JSON_FILE[ \t]*\\([ \t]*\"([^\"]+)\"" + ) diff --git a/Help/release/dev/Autogen_depends.rst b/Help/release/dev/Autogen_depends.rst new file mode 100644 index 0000000..c774386 --- /dev/null +++ b/Help/release/dev/Autogen_depends.rst @@ -0,0 +1,10 @@ +AutoGen depends +--------------- + +* Variable :variable:`CMAKE_AUTOMOC_DEPEND_FILTERS` was introduced to + allow :variable:`CMAKE_AUTOMOC` to extract additional dependency file names + for ``moc`` from the contents of source files. + +* The new target property :prop_tgt:`AUTOMOC_DEPEND_FILTERS` was introduced to + allow :prop_tgt:`AUTOMOC` to extract additional dependency file names + for ``moc`` from the contents of source files. diff --git a/Help/release/dev/Autogen_json.rst b/Help/release/dev/Autogen_json.rst new file mode 100644 index 0000000..73bbdf1 --- /dev/null +++ b/Help/release/dev/Autogen_json.rst @@ -0,0 +1,10 @@ +AutoGen json +------------ + +* When using :prop_tgt:`AUTOMOC`, CMake scans for the presence of the + ``Q_PLUGIN_METADATA`` macro and reruns moc when the file from the + macro's ``FILE`` argument changes. + +* When :prop_tgt:`AUTOMOC` detects an include statement of the form + ``#include "moc_<basename>.cpp"`` the respective header file is searched + for in the :prop_tgt:`INCLUDE_DIRECTORIES` of the target as well. diff --git a/Help/variable/CMAKE_AUTOMOC_DEPEND_FILTERS.rst b/Help/variable/CMAKE_AUTOMOC_DEPEND_FILTERS.rst new file mode 100644 index 0000000..5c3662d --- /dev/null +++ b/Help/variable/CMAKE_AUTOMOC_DEPEND_FILTERS.rst @@ -0,0 +1,12 @@ +CMAKE_AUTOMOC_DEPEND_FILTERS +---------------------------- + +Filter definitions used by :variable:`CMAKE_AUTOMOC` +to extract file names from source code as additional dependencies +for the ``moc`` file. + +This variable is used to initialize the :prop_tgt:`AUTOMOC_DEPEND_FILTERS` +property on all the targets. See that target property for additional +information. + +By default it is empty. diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 3fafaff..fc5024f 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -1,25 +1,32 @@ -set(AM_SOURCES @_moc_uic_sources@) -set(AM_HEADERS @_moc_uic_headers@) -set(AM_SKIP_MOC @_skip_moc@) -set(AM_SKIP_UIC @_skip_uic@) -set(AM_MOC_COMPILE_DEFINITIONS @_moc_compile_defs@) -set(AM_MOC_INCLUDES @_moc_incs@) -set(AM_MOC_OPTIONS @_moc_options@) -set(AM_MOC_RELAXED_MODE "@_moc_relaxed_mode@") -set(AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE "@CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE@") +# Target names +set(AM_TARGET_NAME @_autogen_target_name@) +set(AM_ORIGIN_TARGET_NAME @_origin_target_name@) +# Directories and files set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") -set(AM_QT_MOC_EXECUTABLE "@_qt_moc_executable@") -set(AM_QT_UIC_EXECUTABLE "@_qt_uic_executable@") -set(AM_QT_RCC_EXECUTABLE "@_qt_rcc_executable@") set(AM_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") set(AM_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") -set(AM_QT_VERSION_MAJOR "@_target_qt_version@") -set(AM_TARGET_NAME @_moc_target_name@) -set(AM_ORIGIN_TARGET_NAME @_origin_target_name@) +set(AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE "@CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE@") +set(AM_SOURCES @_sources@) +set(AM_HEADERS @_headers@) +# Qt environment +set(AM_QT_VERSION_MAJOR @_qt_version_major@) +set(AM_QT_MOC_EXECUTABLE @_qt_moc_executable@) +set(AM_QT_UIC_EXECUTABLE @_qt_uic_executable@) +set(AM_QT_RCC_EXECUTABLE @_qt_rcc_executable@) +# MOC settings +set(AM_MOC_SKIP @_moc_skip@) +set(AM_MOC_COMPILE_DEFINITIONS @_moc_compile_defs@) +set(AM_MOC_INCLUDES @_moc_incs@) +set(AM_MOC_OPTIONS @_moc_options@) +set(AM_MOC_RELAXED_MODE @_moc_relaxed_mode@) +set(AM_MOC_DEPEND_FILTERS @_moc_depend_filters@) +# UIC settings +set(AM_UIC_SKIP @_uic_skip@) set(AM_UIC_TARGET_OPTIONS @_uic_target_options@) set(AM_UIC_OPTIONS_FILES @_qt_uic_options_files@) set(AM_UIC_OPTIONS_OPTIONS @_qt_uic_options_options@) +# RCC settings set(AM_RCC_SOURCES @_rcc_files@ ) set(AM_RCC_INPUTS @_rcc_inputs@) set(AM_RCC_OPTIONS_FILES @_rcc_options_files@) diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 458c114..c3fd3c6 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -174,9 +174,16 @@ Create custom targets to build projects in external trees Install step options are: ``INSTALL_DIR <dir>`` - Installation prefix + Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder. + This does not actually configure the external project to install to + the given prefix. That must be done by passing appropriate arguments + to the external project configuration step, e.g. using ``<INSTALL_DIR>``. ``INSTALL_COMMAND <cmd>...`` - Command to drive install after build + Command to drive installation of the external project after it has been + built. This only happens at the *build* time of the calling project. + In order to install files from the external project alongside the + locally-built files, a separate local :command:`install` call must be + added to pick the files up from one of the external project trees. Test step options are: diff --git a/Modules/FindCUDA/select_compute_arch.cmake b/Modules/FindCUDA/select_compute_arch.cmake index a96a8ca..8fb44d8 100644 --- a/Modules/FindCUDA/select_compute_arch.cmake +++ b/Modules/FindCUDA/select_compute_arch.cmake @@ -112,6 +112,7 @@ function(CUDA_SELECT_NVCC_ARCH_FLAGS out_variable) list(REMOVE_DUPLICATES CUDA_ARCH_LIST) foreach(arch_name ${CUDA_ARCH_LIST}) set(arch_bin) + set(arch_ptx) set(add_ptx FALSE) # Check to see if we are compiling PTX if(arch_name MATCHES "(.*)\\+PTX$") diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index 2ee9439..f399836 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -48,6 +48,7 @@ function(_OPENMP_FLAG_CANDIDATES LANG) #GNU "-fopenmp" #Clang + "-fopenmp=libiomp5" "-fopenmp=libomp" #Microsoft Visual Studio "/openmp" diff --git a/Source/.gitattributes b/Source/.gitattributes index dbd6382..1cec8a3 100644 --- a/Source/.gitattributes +++ b/Source/.gitattributes @@ -1,10 +1,21 @@ -# Preserve indentation style in generated code. -cmCommandArgumentLexer.cxx whitespace=-tab-in-indent,-indent-with-non-tab -cmCommandArgumentLexer.h whitespace=-tab-in-indent,-indent-with-non-tab -cmDependsJavaLexer.cxx whitespace=-tab-in-indent,-indent-with-non-tab -cmDependsJavaLexer.h whitespace=-tab-in-indent,-indent-with-non-tab -cmExprLexer.cxx whitespace=-tab-in-indent,-indent-with-non-tab -cmExprLexer.h whitespace=-tab-in-indent,-indent-with-non-tab -cmFortranLexer.cxx whitespace=-tab-in-indent,-indent-with-non-tab -cmFortranLexer.h whitespace=-tab-in-indent,-indent-with-non-tab -cmListFileLexer.c whitespace=-tab-in-indent,-indent-with-non-tab +/cmCommandArgumentLexer.cxx generated +/cmCommandArgumentLexer.h generated +/cmCommandArgumentParser.cxx generated +/cmCommandArgumentParserTokens.h generated +/cmDependsJavaLexer.cxx generated +/cmDependsJavaLexer.h generated +/cmDependsJavaParser.cxx generated +/cmDependsJavaParserTokens.h generated +/cmExprLexer.cxx generated +/cmExprLexer.h generated +/cmExprParser.cxx generated +/cmExprParserTokens.h generated +/cmFortranLexer.cxx generated +/cmFortranLexer.h generated +/cmFortranParser.cxx generated +/cmFortranParserTokens.h generated +/cmListFileLexer.c generated + +# Do not format third-party sources. +/bindexplib.* -format.clang-format +/kwsys/** -format.clang-format diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 5e71120..12dc033 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 8) -set(CMake_VERSION_PATCH 20170221) +set(CMake_VERSION_PATCH 20170224) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx index fd67df9..ec5fc88 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.cxx +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -390,6 +390,8 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, bool remount_image = !cpack_package_icon.empty() || !cpack_dmg_ds_store_setup_script.empty(); + std::string temp_image_format = "UDZO"; + // Create 1 MB dummy padding file in staging area when we need to remount // image, so we have enough space for storing changes ... if (remount_image) { @@ -401,6 +403,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, return 0; } + temp_image_format = "UDRW"; } // Create a temporary read-write disk image ... @@ -413,7 +416,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, temp_image_command << " -ov"; temp_image_command << " -srcfolder \"" << staging.str() << "\""; temp_image_command << " -volname \"" << cpack_dmg_volume_name << "\""; - temp_image_command << " -format UDRW"; + temp_image_command << " -format " << temp_image_format; temp_image_command << " \"" << temp_image << "\""; if (!this->RunCommand(temp_image_command)) { @@ -632,29 +635,33 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, return 0; } - // convert to UDCO - std::string temp_udco = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); - temp_udco += "/temp-udco.dmg"; + if (temp_image_format != "UDZO") { + temp_image_format = "UDZO"; + // convert to UDZO to enable unflatten/flatten + std::string temp_udzo = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + temp_udzo += "/temp-udzo.dmg"; - std::ostringstream udco_image_command; - udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); - udco_image_command << " convert \"" << temp_image << "\""; - udco_image_command << " -format UDCO"; - udco_image_command << " -ov -o \"" << temp_udco << "\""; + std::ostringstream udco_image_command; + udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + udco_image_command << " convert \"" << temp_image << "\""; + udco_image_command << " -format UDZO"; + udco_image_command << " -ov -o \"" << temp_udzo << "\""; - if (!this->RunCommand(udco_image_command, &error)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error converting to UDCO dmg for adding SLA." - << std::endl - << error << std::endl); - return 0; + if (!this->RunCommand(udco_image_command, &error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error converting to UDCO dmg for adding SLA." + << std::endl + << error << std::endl); + return 0; + } + temp_image = temp_udzo; } // unflatten dmg std::ostringstream unflatten_command; unflatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); unflatten_command << " unflatten "; - unflatten_command << "\"" << temp_udco << "\""; + unflatten_command << "\"" << temp_image << "\""; if (!this->RunCommand(unflatten_command, &error)) { cmCPackLogger(cmCPackLog::LOG_ERROR, @@ -673,7 +680,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, } embed_sla_command << " \"" << sla_r << "\""; embed_sla_command << " -a -o "; - embed_sla_command << "\"" << temp_udco << "\""; + embed_sla_command << "\"" << temp_image << "\""; if (!this->RunCommand(embed_sla_command, &error)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error adding SLA." << std::endl @@ -686,7 +693,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, std::ostringstream flatten_command; flatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); flatten_command << " flatten "; - flatten_command << "\"" << temp_udco << "\""; + flatten_command << "\"" << temp_image << "\""; if (!this->RunCommand(flatten_command, &error)) { cmCPackLogger(cmCPackLog::LOG_ERROR, @@ -695,8 +702,6 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, << std::endl); return 0; } - - temp_image = temp_udco; } // Create the final compressed read-only disk image ... diff --git a/Source/CursesDialog/form/.gitattributes b/Source/CursesDialog/form/.gitattributes new file mode 100644 index 0000000..62d728c --- /dev/null +++ b/Source/CursesDialog/form/.gitattributes @@ -0,0 +1 @@ +* -format.clang-format diff --git a/Source/cmFilePathChecksum.cxx b/Source/cmFilePathChecksum.cxx index 3d8b695..62f52e5 100644 --- a/Source/cmFilePathChecksum.cxx +++ b/Source/cmFilePathChecksum.cxx @@ -45,7 +45,7 @@ void cmFilePathChecksum::setupParentDirs(const std::string& currentSrcDir, parentDirs[3].second = "ProjectBinary"; } -std::string cmFilePathChecksum::get(const std::string& filePath) +std::string cmFilePathChecksum::get(const std::string& filePath) const { std::string relPath; std::string relSeed; @@ -82,7 +82,7 @@ std::string cmFilePathChecksum::get(const std::string& filePath) } std::string cmFilePathChecksum::getPart(const std::string& filePath, - size_t length) + size_t length) const { return get(filePath).substr(0, length); } diff --git a/Source/cmFilePathChecksum.h b/Source/cmFilePathChecksum.h index df19053..59ca34c 100644 --- a/Source/cmFilePathChecksum.h +++ b/Source/cmFilePathChecksum.h @@ -47,13 +47,13 @@ public: /* @brief Calculates the path checksum for the parent directory of a file * */ - std::string get(const std::string& filePath); + std::string get(const std::string& filePath) const; /* @brief Same as get() but returns only the first length characters * */ std::string getPart(const std::string& filePath, - size_t length = partLengthDefault); + size_t length = partLengthDefault) const; private: /// Size of the parent directory list diff --git a/Source/cmGlobalVisualStudio15Generator.cxx b/Source/cmGlobalVisualStudio15Generator.cxx index d11ee7c..2312bc0 100644 --- a/Source/cmGlobalVisualStudio15Generator.cxx +++ b/Source/cmGlobalVisualStudio15Generator.cxx @@ -109,6 +109,18 @@ void cmGlobalVisualStudio15Generator::WriteSLNHeader(std::ostream& fout) } } +bool cmGlobalVisualStudio15Generator::InitializeWindows(cmMakefile* mf) +{ + // If the Win 8.1 SDK is installed then we can select a SDK matching + // the target Windows version. + if (this->IsWin81SDKInstalled()) { + return cmGlobalVisualStudio14Generator::InitializeWindows(mf); + } + // Otherwise we must choose a Win 10 SDK even if we are not targeting + // Windows 10. + return this->SelectWindows10SDK(mf, false); +} + bool cmGlobalVisualStudio15Generator::SelectWindowsStoreToolset( std::string& toolset) const { @@ -135,6 +147,28 @@ bool cmGlobalVisualStudio15Generator::IsWindowsStoreToolsetInstalled() const return vsSetupAPIHelper.IsWin10SDKInstalled(); } +bool cmGlobalVisualStudio15Generator::IsWin81SDKInstalled() const +{ + // Does the VS installer tool know about one? + if (vsSetupAPIHelper.IsWin81SDKInstalled()) { + return true; + } + + // Does the registry know about one (e.g. from VS 2015)? + std::string win81Root; + if (cmSystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" + "Windows Kits\\Installed Roots;KitsRoot81", + win81Root, cmSystemTools::KeyWOW64_32) || + cmSystemTools::ReadRegistryValue( + "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\" + "Windows Kits\\Installed Roots;KitsRoot81", + win81Root, cmSystemTools::KeyWOW64_32)) { + return true; + } + return false; +} + std::string cmGlobalVisualStudio15Generator::FindMSBuildCommand() { std::string msbuild; diff --git a/Source/cmGlobalVisualStudio15Generator.h b/Source/cmGlobalVisualStudio15Generator.h index 781b41e..f979b65 100644 --- a/Source/cmGlobalVisualStudio15Generator.h +++ b/Source/cmGlobalVisualStudio15Generator.h @@ -28,6 +28,7 @@ public: virtual const char* GetToolsVersion() { return "15.0"; } protected: + bool InitializeWindows(cmMakefile* mf) CM_OVERRIDE; virtual bool SelectWindowsStoreToolset(std::string& toolset) const; virtual const char* GetIDEVersion() { return "15.0"; } @@ -40,6 +41,9 @@ protected: // of the toolset is installed bool IsWindowsStoreToolsetInstalled() const; + // Check for a Win 8 SDK known to the registry or VS installer tool. + bool IsWin81SDKInstalled() const; + std::string FindMSBuildCommand() CM_OVERRIDE; std::string FindDevEnvCommand() CM_OVERRIDE; diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 8627cf2..5167205 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -423,14 +423,12 @@ void cmGlobalXCodeGenerator::AddExtraTargets( // Add XCODE depend helper std::string dir = root->GetCurrentBinaryDirectory(); cmCustomCommandLine makeHelper; - if (this->XcodeVersion < 50) { - makeHelper.push_back("make"); - makeHelper.push_back("-C"); - makeHelper.push_back(dir); - makeHelper.push_back("-f"); - makeHelper.push_back(this->CurrentXCodeHackMakefile); - makeHelper.push_back(""); // placeholder, see below - } + makeHelper.push_back("make"); + makeHelper.push_back("-C"); + makeHelper.push_back(dir); + makeHelper.push_back("-f"); + makeHelper.push_back(this->CurrentXCodeHackMakefile); + makeHelper.push_back(""); // placeholder, see below // Add ZERO_CHECK bool regenerate = !mf->IsOn("CMAKE_SUPPRESS_REGENERATION"); @@ -475,13 +473,12 @@ void cmGlobalXCodeGenerator::AddExtraTargets( // run the depend check makefile as a post build rule // this will make sure that when the next target is built // things are up-to-date - if (!makeHelper.empty() && - (target->GetType() == cmStateEnums::EXECUTABLE || - // Nope - no post-build for OBJECT_LIRBRARY - // target->GetType() == cmStateEnums::OBJECT_LIBRARY || - target->GetType() == cmStateEnums::STATIC_LIBRARY || - target->GetType() == cmStateEnums::SHARED_LIBRARY || - target->GetType() == cmStateEnums::MODULE_LIBRARY)) { + if (target->GetType() == cmStateEnums::OBJECT_LIBRARY || + (this->XcodeVersion < 50 && + (target->GetType() == cmStateEnums::EXECUTABLE || + target->GetType() == cmStateEnums::STATIC_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY))) { makeHelper[makeHelper.size() - 1] = // fill placeholder this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)"); cmCustomCommandLines commandLines; @@ -489,7 +486,8 @@ void cmGlobalXCodeGenerator::AddExtraTargets( std::vector<std::string> no_byproducts; lg->GetMakefile()->AddCustomCommandToTarget( target->GetName(), no_byproducts, no_depends, commandLines, - cmTarget::POST_BUILD, "Depend check for xcode", dir.c_str()); + cmTarget::POST_BUILD, "Depend check for xcode", dir.c_str(), true, + false, "", false, cmMakefile::AcceptObjectLibraryCommands); } if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY && @@ -3129,10 +3127,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( cmXCodeObject* t = *i; this->AddDependAndLinkInformation(t); } - if (this->XcodeVersion < 50) { - // now create xcode depend hack makefile - this->CreateXCodeDependHackTarget(targets); - } + this->CreateXCodeDependHackTarget(targets); // now add all targets to the root object cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST); for (std::vector<cmXCodeObject*>::iterator i = targets.begin(); @@ -3183,29 +3178,9 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( "default:\n" "\techo \"Do not invoke directly\"\n" "\n"; - makefileStream - << "# For each target create a dummy rule " - "so the target does not have to exist\n"; /* clang-format on */ - std::set<std::string> emitted; - for (std::vector<cmXCodeObject*>::iterator i = targets.begin(); - i != targets.end(); ++i) { - cmXCodeObject* target = *i; - std::map<std::string, cmXCodeObject::StringVec> const& deplibs = - target->GetDependLibraries(); - for (std::map<std::string, cmXCodeObject::StringVec>::const_iterator ci = - deplibs.begin(); - ci != deplibs.end(); ++ci) { - for (cmXCodeObject::StringVec::const_iterator d = ci->second.begin(); - d != ci->second.end(); ++d) { - if (emitted.insert(*d).second) { - makefileStream << this->ConvertToRelativeForMake(d->c_str()) - << ":\n"; - } - } - } - } - makefileStream << "\n\n"; + + std::set<std::string> dummyRules; // Write rules to help Xcode relink things at the right time. /* clang-format off */ @@ -3224,8 +3199,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( cmGeneratorTarget* gt = target->GetTarget(); if (gt->GetType() == cmStateEnums::EXECUTABLE || - // Nope - no post-build for OBJECT_LIRBRARY - // gt->GetType() == cmStateEnums::OBJECT_LIBRARY || + gt->GetType() == cmStateEnums::OBJECT_LIBRARY || gt->GetType() == cmStateEnums::STATIC_LIBRARY || gt->GetType() == cmStateEnums::SHARED_LIBRARY || gt->GetType() == cmStateEnums::MODULE_LIBRARY) { @@ -3235,6 +3209,7 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( } if (gt->GetType() == cmStateEnums::EXECUTABLE || + gt->GetType() == cmStateEnums::STATIC_LIBRARY || gt->GetType() == cmStateEnums::SHARED_LIBRARY || gt->GetType() == cmStateEnums::MODULE_LIBRARY) { std::string tfull = gt->GetFullPath(configName); @@ -3252,6 +3227,15 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( } } + std::vector<cmGeneratorTarget*> objlibs; + gt->GetObjectLibrariesCMP0026(objlibs); + for (std::vector<cmGeneratorTarget*>::const_iterator it = + objlibs.begin(); + it != objlibs.end(); ++it) { + makefileStream << this->PostBuildMakeTarget((*it)->GetName(), *ct) + << ": " << trel << "\n"; + } + // Create a rule for this target. makefileStream << trel << ":"; @@ -3262,10 +3246,28 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( std::vector<std::string> const& deplibs = x->second; for (std::vector<std::string>::const_iterator d = deplibs.begin(); d != deplibs.end(); ++d) { - makefileStream << "\\\n\t" - << this->ConvertToRelativeForMake(d->c_str()); + std::string file = this->ConvertToRelativeForMake(d->c_str()); + makefileStream << "\\\n\t" << file; + dummyRules.insert(file); } } + + for (std::vector<cmGeneratorTarget*>::const_iterator it = + objlibs.begin(); + it != objlibs.end(); ++it) { + + const std::string objLibName = (*it)->GetName(); + std::string d = this->GetObjectsNormalDirectory(this->CurrentProject, + configName, *it); + d += "lib"; + d += objLibName; + d += ".a"; + + std::string dependency = this->ConvertToRelativeForMake(d.c_str()); + makefileStream << "\\\n\t" << dependency; + dummyRules.insert(dependency); + } + // Write the action to remove the target if it is out of date. makefileStream << "\n"; makefileStream << "\t/bin/rm -f " @@ -3293,6 +3295,14 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( } } } + + makefileStream << "\n\n" + << "# For each target create a dummy rule" + << "so the target does not have to exist\n"; + for (std::set<std::string>::const_iterator it = dummyRules.begin(); + it != dummyRules.end(); ++it) { + makefileStream << *it << ":\n"; + } } void cmGlobalXCodeGenerator::OutputXCodeProject( diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index b3d7a04..c75d101 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -684,7 +684,8 @@ void cmMakefile::AddCustomCommandToTarget( const std::vector<std::string>& depends, const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, const char* comment, const char* workingDir, bool escapeOldStyle, - bool uses_terminal, const std::string& depfile, bool command_expand_lists) + bool uses_terminal, const std::string& depfile, bool command_expand_lists, + ObjectLibraryCommands objLibraryCommands) { // Find the target to which to add the custom command. cmTargets::iterator ti = this->Targets.find(target); @@ -724,7 +725,8 @@ void cmMakefile::AddCustomCommandToTarget( return; } - if (ti->second.GetType() == cmStateEnums::OBJECT_LIBRARY) { + if (objLibraryCommands == RejectObjectLibraryCommands && + ti->second.GetType() == cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Target \"" << target << "\" is an OBJECT library " diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 9d9e90a..4d5ce98 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -119,6 +119,13 @@ public: */ void FinalPass(); + /** How to handle custom commands for object libraries */ + enum ObjectLibraryCommands + { + RejectObjectLibraryCommands, + AcceptObjectLibraryCommands + }; + /** Add a custom command to the build. */ void AddCustomCommandToTarget( const std::string& target, const std::vector<std::string>& byproducts, @@ -126,7 +133,8 @@ public: const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, const char* comment, const char* workingDir, bool escapeOldStyle = true, bool uses_terminal = false, const std::string& depfile = "", - bool command_expand_lists = false); + bool command_expand_lists = false, + ObjectLibraryCommands objLibraryCommands = RejectObjectLibraryCommands); cmSourceFile* AddCustomCommandToOutput( const std::vector<std::string>& outputs, const std::vector<std::string>& byproducts, diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index 825eba0..de18265 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -22,7 +22,6 @@ #endif #include <algorithm> -#include <assert.h> #include <cmConfigure.h> #include <cmsys/FStream.hxx> #include <cmsys/RegularExpression.hxx> @@ -55,6 +54,13 @@ static std::string utilStripCR(std::string const& line) return line; } +static std::string GetSafeProperty(cmGeneratorTarget const* target, + const char* key) +{ + const char* tmp = target->GetProperty(key); + return std::string((tmp != CM_NULLPTR) ? tmp : ""); +} + static std::string GetAutogenTargetName(cmGeneratorTarget const* target) { std::string autogenTargetName = target->GetName(); @@ -98,19 +104,53 @@ static std::string GetQtMajorVersion(cmGeneratorTarget const* target) return qtMajorVersion; } +static void GetCompileDefinitionsAndDirectories( + cmGeneratorTarget const* target, const std::string& config, + std::string& incs, std::string& defs) +{ + cmLocalGenerator* localGen = target->GetLocalGenerator(); + { + std::vector<std::string> includeDirs; + // Get the include dirs for this target, without stripping the implicit + // include dirs off, see + // https://gitlab.kitware.com/cmake/cmake/issues/13667 + localGen->GetIncludeDirectories(includeDirs, target, "CXX", config, false); + incs = cmJoin(includeDirs, ";"); + } + { + std::set<std::string> defines; + localGen->AddCompileDefinitions(defines, target, config, "CXX"); + defs += cmJoin(defines, ";"); + } +} + +static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, + const std::string& value) +{ + makefile->AddDefinition(key, + cmOutputConverter::EscapeForCMake(value).c_str()); +} + +static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, + const std::vector<std::string>& values) +{ + makefile->AddDefinition( + key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str()); +} + static void SetupSourceFiles(cmGeneratorTarget const* target, std::vector<std::string>& mocUicSources, std::vector<std::string>& mocUicHeaders, - std::vector<std::string>& skipMocList, - std::vector<std::string>& skipUicList) + std::vector<std::string>& mocSkipList, + std::vector<std::string>& uicSkipList) { cmMakefile* makefile = target->Target->GetMakefile(); std::vector<cmSourceFile*> srcFiles; target->GetConfigCommonSourceFiles(srcFiles); - const bool targetMoc = target->GetPropertyAsBool("AUTOMOC"); - const bool targetUic = target->GetPropertyAsBool("AUTOUIC"); + const bool mocTarget = target->GetPropertyAsBool("AUTOMOC"); + const bool uicTarget = target->GetPropertyAsBool("AUTOUIC"); cmFilePathChecksum fpathCheckSum(makefile); for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); @@ -131,22 +171,22 @@ static void SetupSourceFiles(cmGeneratorTarget const* target, // Skip flags const bool skipAll = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOGEN")); - const bool skipMoc = + const bool mocSkip = skipAll || cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC")); - const bool skipUic = + const bool uicSkip = skipAll || cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOUIC")); // Add file name to skip lists. // Do this even when the file is not added to the sources/headers lists // because the file name may be extracted from an other file when // processing - if (skipMoc) { - skipMocList.push_back(absFile); + if (mocSkip) { + mocSkipList.push_back(absFile); } - if (skipUic) { - skipUicList.push_back(absFile); + if (uicSkip) { + uicSkipList.push_back(absFile); } - if ((targetMoc && !skipMoc) || (targetUic && !skipUic)) { + if ((mocTarget && !mocSkip) || (uicTarget && !uicSkip)) { // Add file name to sources or headers list switch (fileType) { case cmSystemTools::CXX_FILE_FORMAT: @@ -162,100 +202,89 @@ static void SetupSourceFiles(cmGeneratorTarget const* target, } } -static void GetCompileDefinitionsAndDirectories( - cmGeneratorTarget const* target, const std::string& config, - std::string& incs, std::string& defs) -{ - std::vector<std::string> includeDirs; - cmLocalGenerator* localGen = target->GetLocalGenerator(); - // Get the include dirs for this target, without stripping the implicit - // include dirs off, see https://gitlab.kitware.com/cmake/cmake/issues/13667 - localGen->GetIncludeDirectories(includeDirs, target, "CXX", config, false); - - incs = cmJoin(includeDirs, ";"); - - std::set<std::string> defines; - localGen->AddCompileDefinitions(defines, target, config, "CXX"); - - defs += cmJoin(defines, ";"); -} - static void MocSetupAutoTarget( cmGeneratorTarget const* target, const std::string& autogenTargetName, - std::vector<std::string> const& skipMoc, + const std::string& qtMajorVersion, + std::vector<std::string> const& mocSkipList, std::map<std::string, std::string>& configIncludes, std::map<std::string, std::string>& configDefines) { cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* makefile = target->Target->GetMakefile(); - const char* tmp = target->GetProperty("AUTOMOC_MOC_OPTIONS"); - std::string _moc_options = (tmp != CM_NULLPTR ? tmp : ""); - makefile->AddDefinition( - "_moc_options", cmOutputConverter::EscapeForCMake(_moc_options).c_str()); - makefile->AddDefinition( - "_skip_moc", - cmOutputConverter::EscapeForCMake(cmJoin(skipMoc, ";")).c_str()); - bool relaxedMode = makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE"); - makefile->AddDefinition("_moc_relaxed_mode", relaxedMode ? "TRUE" : "FALSE"); - - std::string _moc_incs; - std::string _moc_compile_defs; - std::vector<std::string> configs; - const std::string& config = makefile->GetConfigurations(configs); - GetCompileDefinitionsAndDirectories(target, config, _moc_incs, - _moc_compile_defs); + AddDefinitionEscaped(makefile, "_moc_options", + GetSafeProperty(target, "AUTOMOC_MOC_OPTIONS")); + AddDefinitionEscaped(makefile, "_moc_skip", mocSkipList); + AddDefinitionEscaped(makefile, "_moc_relaxed_mode", + makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE" + : "FALSE"); + AddDefinitionEscaped(makefile, "_moc_depend_filters", + GetSafeProperty(target, "AUTOMOC_DEPEND_FILTERS")); - makefile->AddDefinition( - "_moc_incs", cmOutputConverter::EscapeForCMake(_moc_incs).c_str()); - makefile->AddDefinition( - "_moc_compile_defs", - cmOutputConverter::EscapeForCMake(_moc_compile_defs).c_str()); - - for (std::vector<std::string>::const_iterator li = configs.begin(); - li != configs.end(); ++li) { - std::string config_moc_incs; - std::string config_moc_compile_defs; - GetCompileDefinitionsAndDirectories(target, *li, config_moc_incs, - config_moc_compile_defs); - if (config_moc_incs != _moc_incs) { - configIncludes[*li] = cmOutputConverter::EscapeForCMake(config_moc_incs); - if (_moc_incs.empty()) { - _moc_incs = config_moc_incs; + // Moc includes and compile definitions + { + std::string _moc_incs; + std::string _moc_compile_defs; + std::vector<std::string> configs; + { + const std::string& config = makefile->GetConfigurations(configs); + GetCompileDefinitionsAndDirectories(target, config, _moc_incs, + _moc_compile_defs); + AddDefinitionEscaped(makefile, "_moc_incs", _moc_incs); + AddDefinitionEscaped(makefile, "_moc_compile_defs", _moc_compile_defs); + } + for (std::vector<std::string>::const_iterator li = configs.begin(); + li != configs.end(); ++li) { + std::string config_moc_incs; + std::string config_moc_compile_defs; + GetCompileDefinitionsAndDirectories(target, *li, config_moc_incs, + config_moc_compile_defs); + if (config_moc_incs != _moc_incs) { + configIncludes[*li] = + cmOutputConverter::EscapeForCMake(config_moc_incs); + if (_moc_incs.empty()) { + _moc_incs = config_moc_incs; + } } - } - if (config_moc_compile_defs != _moc_compile_defs) { - configDefines[*li] = - cmOutputConverter::EscapeForCMake(config_moc_compile_defs); - if (_moc_compile_defs.empty()) { - _moc_compile_defs = config_moc_compile_defs; + if (config_moc_compile_defs != _moc_compile_defs) { + configDefines[*li] = + cmOutputConverter::EscapeForCMake(config_moc_compile_defs); + if (_moc_compile_defs.empty()) { + _moc_compile_defs = config_moc_compile_defs; + } } } } - const char* qtVersion = makefile->GetDefinition("_target_qt_version"); - if (strcmp(qtVersion, "5") == 0) { - cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc"); - if (!qt5Moc) { - cmSystemTools::Error("Qt5::moc target not found ", - autogenTargetName.c_str()); - return; + // Moc executable + { + std::string err; + const char* mocExec = CM_NULLPTR; + if (qtMajorVersion == "5") { + cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc"); + if (qt5Moc != CM_NULLPTR) { + mocExec = qt5Moc->ImportedGetLocation(""); + } else { + err = "Qt5::moc target not found " + autogenTargetName; + } + } else if (qtMajorVersion == "4") { + cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc"); + if (qt4Moc != CM_NULLPTR) { + mocExec = qt4Moc->ImportedGetLocation(""); + } else { + err = "Qt4::moc target not found " + autogenTargetName; + } + } else { + err = "The CMAKE_AUTOMOC feature supports only Qt 4 and Qt 5 "; + err += autogenTargetName; } - makefile->AddDefinition("_qt_moc_executable", - qt5Moc->ImportedGetLocation("")); - } else if (strcmp(qtVersion, "4") == 0) { - cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc"); - if (!qt4Moc) { - cmSystemTools::Error("Qt4::moc target not found ", - autogenTargetName.c_str()); - return; + // Add definition or error + if (err.empty()) { + AddDefinitionEscaped(makefile, "_qt_moc_executable", + mocExec ? mocExec : ""); + } else { + cmSystemTools::Error(err.c_str()); } - makefile->AddDefinition("_qt_moc_executable", - qt4Moc->ImportedGetLocation("")); - } else { - cmSystemTools::Error("The CMAKE_AUTOMOC feature supports only Qt 4 and " - "Qt 5 ", - autogenTargetName.c_str()); } } @@ -268,126 +297,126 @@ static void UicGetOpts(cmGeneratorTarget const* target, } static void UicSetupAutoTarget( - cmGeneratorTarget const* target, std::vector<std::string> const& skipUic, + cmGeneratorTarget const* target, const std::string& qtMajorVersion, + std::vector<std::string> const& uicSkipList, std::map<std::string, std::string>& configUicOptions) { cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* makefile = target->Target->GetMakefile(); - std::set<std::string> skipped; - skipped.insert(skipUic.begin(), skipUic.end()); + AddDefinitionEscaped(makefile, "_uic_skip", uicSkipList); - makefile->AddDefinition( - "_skip_uic", - cmOutputConverter::EscapeForCMake(cmJoin(skipUic, ";")).c_str()); - - std::vector<cmSourceFile*> uiFilesWithOptions = - makefile->GetQtUiFilesWithOptions(); - - const char* qtVersion = makefile->GetDefinition("_target_qt_version"); - - std::string _uic_opts; - std::vector<std::string> configs; - const std::string& config = makefile->GetConfigurations(configs); - UicGetOpts(target, config, _uic_opts); - - if (!_uic_opts.empty()) { - _uic_opts = cmOutputConverter::EscapeForCMake(_uic_opts); - makefile->AddDefinition("_uic_target_options", _uic_opts.c_str()); - } - for (std::vector<std::string>::const_iterator li = configs.begin(); - li != configs.end(); ++li) { - std::string config_uic_opts; - UicGetOpts(target, *li, config_uic_opts); - if (config_uic_opts != _uic_opts) { - configUicOptions[*li] = - cmOutputConverter::EscapeForCMake(config_uic_opts); - if (_uic_opts.empty()) { - _uic_opts = config_uic_opts; + // Uic target options + { + std::string _uic_opts; + std::vector<std::string> configs; + UicGetOpts(target, makefile->GetConfigurations(configs), _uic_opts); + + AddDefinitionEscaped(makefile, "_uic_target_options", _uic_opts); + + for (std::vector<std::string>::const_iterator li = configs.begin(); + li != configs.end(); ++li) { + std::string config_uic_opts; + UicGetOpts(target, *li, config_uic_opts); + if (config_uic_opts != _uic_opts) { + configUicOptions[*li] = + cmOutputConverter::EscapeForCMake(config_uic_opts); + if (_uic_opts.empty()) { + _uic_opts = config_uic_opts; + } } } } - - std::string uiFileFiles; - std::string uiFileOptions; - const char* sep = ""; - - for (std::vector<cmSourceFile*>::const_iterator fileIt = - uiFilesWithOptions.begin(); - fileIt != uiFilesWithOptions.end(); ++fileIt) { - cmSourceFile* sf = *fileIt; - std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - - if (!skipped.insert(absFile).second) { - continue; + // Uic files options + { + std::vector<std::string> uiFileFiles; + std::vector<std::string> uiFileOptions; + { + std::set<std::string> skipped; + skipped.insert(uicSkipList.begin(), uicSkipList.end()); + + const std::vector<cmSourceFile*> uiFilesWithOptions = + makefile->GetQtUiFilesWithOptions(); + for (std::vector<cmSourceFile*>::const_iterator fileIt = + uiFilesWithOptions.begin(); + fileIt != uiFilesWithOptions.end(); ++fileIt) { + cmSourceFile* sf = *fileIt; + const std::string absFile = + cmsys::SystemTools::GetRealPath(sf->GetFullPath()); + if (skipped.insert(absFile).second) { + // The file wasn't skipped + uiFileFiles.push_back(absFile); + { + std::string opts = sf->GetProperty("AUTOUIC_OPTIONS"); + cmSystemTools::ReplaceString(opts, ";", "@list_sep@"); + uiFileOptions.push_back(opts); + } + } + } } - uiFileFiles += sep; - uiFileFiles += absFile; - uiFileOptions += sep; - std::string opts = sf->GetProperty("AUTOUIC_OPTIONS"); - cmSystemTools::ReplaceString(opts, ";", "@list_sep@"); - uiFileOptions += opts; - sep = ";"; + AddDefinitionEscaped(makefile, "_qt_uic_options_files", uiFileFiles); + AddDefinitionEscaped(makefile, "_qt_uic_options_options", uiFileOptions); } - makefile->AddDefinition( - "_qt_uic_options_files", - cmOutputConverter::EscapeForCMake(uiFileFiles).c_str()); - makefile->AddDefinition( - "_qt_uic_options_options", - cmOutputConverter::EscapeForCMake(uiFileOptions).c_str()); - - std::string targetName = target->GetName(); - if (strcmp(qtVersion, "5") == 0) { - cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic"); - if (!qt5Uic) { - // Project does not use Qt5Widgets, but has AUTOUIC ON anyway + // Uic executable + { + std::string err; + const char* uicExec = CM_NULLPTR; + if (qtMajorVersion == "5") { + cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic"); + if (qt5Uic != CM_NULLPTR) { + uicExec = qt5Uic->ImportedGetLocation(""); + } else { + // Project does not use Qt5Widgets, but has AUTOUIC ON anyway + } + } else if (qtMajorVersion == "4") { + cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic"); + if (qt4Uic != CM_NULLPTR) { + uicExec = qt4Uic->ImportedGetLocation(""); + } else { + err = "Qt4::uic target not found " + target->GetName(); + } } else { - makefile->AddDefinition("_qt_uic_executable", - qt5Uic->ImportedGetLocation("")); + err = "The CMAKE_AUTOUIC feature supports only Qt 4 and Qt 5 "; + err += target->GetName(); } - } else if (strcmp(qtVersion, "4") == 0) { - cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic"); - if (!qt4Uic) { - cmSystemTools::Error("Qt4::uic target not found ", targetName.c_str()); - return; + // Add definition or error + if (err.empty()) { + AddDefinitionEscaped(makefile, "_qt_uic_executable", + uicExec ? uicExec : ""); + } else { + cmSystemTools::Error(err.c_str()); } - makefile->AddDefinition("_qt_uic_executable", - qt4Uic->ImportedGetLocation("")); - } else { - cmSystemTools::Error("The CMAKE_AUTOUIC feature supports only Qt 4 and " - "Qt 5 ", - targetName.c_str()); } } static std::string RccGetExecutable(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { + std::string rccExec; cmLocalGenerator* lg = target->GetLocalGenerator(); - - std::string const& targetName = target->GetName(); if (qtMajorVersion == "5") { cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc"); - if (!qt5Rcc) { - cmSystemTools::Error("Qt5::rcc target not found ", targetName.c_str()); - return std::string(); + if (qt5Rcc != CM_NULLPTR) { + rccExec = qt5Rcc->ImportedGetLocation(""); + } else { + cmSystemTools::Error("Qt5::rcc target not found ", + target->GetName().c_str()); } - return qt5Rcc->ImportedGetLocation(""); - } - if (qtMajorVersion == "4") { + } else if (qtMajorVersion == "4") { cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc"); - if (!qt4Rcc) { - cmSystemTools::Error("Qt4::rcc target not found ", targetName.c_str()); - return std::string(); + if (qt4Rcc != CM_NULLPTR) { + rccExec = qt4Rcc->ImportedGetLocation(""); + } else { + cmSystemTools::Error("Qt4::rcc target not found ", + target->GetName().c_str()); } - return qt4Rcc->ImportedGetLocation(""); + } else { + cmSystemTools::Error( + "The CMAKE_AUTORCC feature supports only Qt 4 and Qt 5 ", + target->GetName().c_str()); } - - cmSystemTools::Error("The CMAKE_AUTORCC feature supports only Qt 4 and " - "Qt 5 ", - targetName.c_str()); - return std::string(); + return rccExec; } static void RccMergeOptions(std::vector<std::string>& opts, @@ -397,26 +426,31 @@ static void RccMergeOptions(std::vector<std::string>& opts, static const char* valueOptions[] = { "name", "root", "compress", "threshold" }; std::vector<std::string> extraOpts; - for (std::vector<std::string>::const_iterator it = fileOpts.begin(); - it != fileOpts.end(); ++it) { + for (std::vector<std::string>::const_iterator fit = fileOpts.begin(); + fit != fileOpts.end(); ++fit) { std::vector<std::string>::iterator existingIt = - std::find(opts.begin(), opts.end(), *it); + std::find(opts.begin(), opts.end(), *fit); if (existingIt != opts.end()) { - const char* o = it->c_str(); - if (*o == '-') { - ++o; - } - if (isQt5 && *o == '-') { - ++o; + const char* optName = fit->c_str(); + if (*optName == '-') { + ++optName; + if (isQt5 && *optName == '-') { + ++optName; + } } - if (std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions), - cmStrCmp(*it)) != cmArrayEnd(valueOptions)) { - assert(existingIt + 1 != opts.end()); - *(existingIt + 1) = *(it + 1); - ++it; + // Test if this is a value option and change the existing value + if ((optName != fit->c_str()) && + std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions), + cmStrCmp(optName)) != cmArrayEnd(valueOptions)) { + const std::vector<std::string>::iterator existValueIt(existingIt + 1); + const std::vector<std::string>::const_iterator fileValueIt(fit + 1); + if ((existValueIt != opts.end()) && (fileValueIt != fileOpts.end())) { + *existValueIt = *fileValueIt; + ++fit; + } } } else { - extraOpts.push_back(*it); + extraOpts.push_back(*fit); } } opts.insert(opts.end(), extraOpts.begin(), extraOpts.end()); @@ -526,23 +560,20 @@ static bool RccListInputsQt4(cmSourceFile* sf, } cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); + cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); size_t offset = 0; while (fileMatchRegex.find(qrcContents.c_str() + offset)) { std::string qrcEntry = fileMatchRegex.match(1); - offset += qrcEntry.size(); - - cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); - fileReplaceRegex.find(qrcEntry); - std::string tag = fileReplaceRegex.match(1); - - qrcEntry = qrcEntry.substr(tag.size()); - + { + fileReplaceRegex.find(qrcEntry); + std::string tag = fileReplaceRegex.match(1); + qrcEntry = qrcEntry.substr(tag.size()); + } if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) { qrcEntry = sf->GetLocation().GetDirectory() + "/" + qrcEntry; } - depends.push_back(qrcEntry); } return true; @@ -563,89 +594,69 @@ static bool RccListInputs(const std::string& qtMajorVersion, cmSourceFile* sf, static void RccSetupAutoTarget(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { - std::string _rcc_files; - const char* sepRccFiles = ""; cmMakefile* makefile = target->Target->GetMakefile(); - - std::vector<cmSourceFile*> srcFiles; - target->GetConfigCommonSourceFiles(srcFiles); - - std::string qrcInputs; - const char* qrcInputsSep = ""; - - std::string rccFileFiles; - std::string rccFileOptions; - const char* optionSep = ""; - const bool qtMajorVersion5 = (qtMajorVersion == "5"); - - std::vector<std::string> rccOptions; + std::vector<std::string> _rcc_files; + std::vector<std::string> _rcc_inputs; + std::vector<std::string> rccFileFiles; + std::vector<std::string> rccFileOptions; + std::vector<std::string> rccOptionsTarget; if (const char* opts = target->GetProperty("AUTORCC_OPTIONS")) { - cmSystemTools::ExpandListArgument(opts, rccOptions); + cmSystemTools::ExpandListArgument(opts, rccOptionsTarget); } + std::vector<cmSourceFile*> srcFiles; + target->GetConfigCommonSourceFiles(srcFiles); for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; - std::string ext = sf->GetExtension(); - if (ext == "qrc") { - std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); + if (sf->GetExtension() == "qrc") { const bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOGEN")) || cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC")); - if (!skip) { - _rcc_files += sepRccFiles; - _rcc_files += absFile; - sepRccFiles = ";"; - - if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) { - std::vector<std::string> optsVec; - cmSystemTools::ExpandListArgument(prop, optsVec); - RccMergeOptions(rccOptions, optsVec, qtMajorVersion5); - } - - if (!rccOptions.empty()) { - rccFileFiles += optionSep; - rccFileFiles += absFile; - rccFileOptions += optionSep; - } - const char* listSep = ""; - for (std::vector<std::string>::const_iterator it = rccOptions.begin(); - it != rccOptions.end(); ++it) { - rccFileOptions += listSep; - rccFileOptions += *it; - listSep = "@list_sep@"; + const std::string absFile = + cmsys::SystemTools::GetRealPath(sf->GetFullPath()); + // qrc file + _rcc_files.push_back(absFile); + // qrc file entries + { + std::string entriesList; + if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { + std::vector<std::string> depends; + if (RccListInputs(qtMajorVersion, sf, target, depends)) { + entriesList = cmJoin(depends, "@list_sep@"); + } else { + return; + } + } + _rcc_inputs.push_back(entriesList); } - optionSep = ";"; - - std::string entriesList; - if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { - std::vector<std::string> depends; - if (RccListInputs(qtMajorVersion, sf, target, depends)) { - entriesList = cmJoin(depends, "@list_sep@"); - } else { - return; + // rcc options for this qrc file + { + // Merged target and file options + std::vector<std::string> rccOptions(rccOptionsTarget); + if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) { + std::vector<std::string> optsVec; + cmSystemTools::ExpandListArgument(prop, optsVec); + RccMergeOptions(rccOptions, optsVec, qtMajorVersion5); + } + // Only store non empty options lists + if (!rccOptions.empty()) { + rccFileFiles.push_back(absFile); + rccFileOptions.push_back(cmJoin(rccOptions, "@list_sep@")); } } - qrcInputs += qrcInputsSep; - qrcInputs += entriesList; - qrcInputsSep = ";"; } } } - makefile->AddDefinition( - "_rcc_inputs", cmOutputConverter::EscapeForCMake(qrcInputs).c_str()); - makefile->AddDefinition( - "_rcc_files", cmOutputConverter::EscapeForCMake(_rcc_files).c_str()); - makefile->AddDefinition( - "_rcc_options_files", - cmOutputConverter::EscapeForCMake(rccFileFiles).c_str()); - makefile->AddDefinition( - "_rcc_options_options", - cmOutputConverter::EscapeForCMake(rccFileOptions).c_str()); - makefile->AddDefinition("_qt_rcc_executable", - RccGetExecutable(target, qtMajorVersion).c_str()); + + AddDefinitionEscaped(makefile, "_rcc_files", _rcc_files); + AddDefinitionEscaped(makefile, "_rcc_inputs", _rcc_inputs); + AddDefinitionEscaped(makefile, "_rcc_options_files", rccFileFiles); + AddDefinitionEscaped(makefile, "_rcc_options_options", rccFileOptions); + AddDefinitionEscaped(makefile, "_qt_rcc_executable", + RccGetExecutable(target, qtMajorVersion)); } void cmQtAutoGeneratorInitializer::InitializeAutogenSources( @@ -872,47 +883,42 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( cmMakefile::ScopePushPop varScope(makefile); static_cast<void>(varScope); - // create a custom target for running generators at buildtime: - const std::string autogenTargetName = GetAutogenTargetName(target); - const std::string qtMajorVersion = GetQtMajorVersion(target); - - makefile->AddDefinition( - "_moc_target_name", - cmOutputConverter::EscapeForCMake(autogenTargetName).c_str()); - makefile->AddDefinition( - "_origin_target_name", - cmOutputConverter::EscapeForCMake(target->GetName()).c_str()); - makefile->AddDefinition("_target_qt_version", qtMajorVersion.c_str()); - - std::vector<std::string> mocUicSources; - std::vector<std::string> mocUicHeaders; - std::vector<std::string> skipMoc; - std::vector<std::string> skipUic; std::map<std::string, std::string> configMocIncludes; std::map<std::string, std::string> configMocDefines; std::map<std::string, std::string> configUicOptions; + { + // create a custom target for running generators at buildtime: + const std::string autogenTargetName = GetAutogenTargetName(target); + const std::string qtMajorVersion = GetQtMajorVersion(target); - if (target->GetPropertyAsBool("AUTOMOC") || - target->GetPropertyAsBool("AUTOUIC") || - target->GetPropertyAsBool("AUTORCC")) { - SetupSourceFiles(target, mocUicSources, mocUicHeaders, skipMoc, skipUic); - } - makefile->AddDefinition( - "_moc_uic_sources", - cmOutputConverter::EscapeForCMake(cmJoin(mocUicSources, ";")).c_str()); - makefile->AddDefinition( - "_moc_uic_headers", - cmOutputConverter::EscapeForCMake(cmJoin(mocUicHeaders, ";")).c_str()); + AddDefinitionEscaped(makefile, "_autogen_target_name", autogenTargetName); + AddDefinitionEscaped(makefile, "_origin_target_name", target->GetName()); + AddDefinitionEscaped(makefile, "_qt_version_major", qtMajorVersion); - if (target->GetPropertyAsBool("AUTOMOC")) { - MocSetupAutoTarget(target, autogenTargetName, skipMoc, configMocIncludes, - configMocDefines); - } - if (target->GetPropertyAsBool("AUTOUIC")) { - UicSetupAutoTarget(target, skipUic, configUicOptions); - } - if (target->GetPropertyAsBool("AUTORCC")) { - RccSetupAutoTarget(target, qtMajorVersion); + std::vector<std::string> _sources; + std::vector<std::string> _headers; + std::vector<std::string> mocSkipList; + std::vector<std::string> uicSkipList; + + if (target->GetPropertyAsBool("AUTOMOC") || + target->GetPropertyAsBool("AUTOUIC") || + target->GetPropertyAsBool("AUTORCC")) { + SetupSourceFiles(target, _sources, _headers, mocSkipList, uicSkipList); + } + AddDefinitionEscaped(makefile, "_sources", _sources); + AddDefinitionEscaped(makefile, "_headers", _headers); + + if (target->GetPropertyAsBool("AUTOMOC")) { + MocSetupAutoTarget(target, autogenTargetName, qtMajorVersion, + mocSkipList, configMocIncludes, configMocDefines); + } + if (target->GetPropertyAsBool("AUTOUIC")) { + UicSetupAutoTarget(target, qtMajorVersion, uicSkipList, + configUicOptions); + } + if (target->GetPropertyAsBool("AUTORCC")) { + RccSetupAutoTarget(target, qtMajorVersion); + } } // Generate config file @@ -924,7 +930,7 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), false, true, false); - // Append custom definitions to config file + // Append custom config definitions to info file if (!configMocDefines.empty() || !configMocIncludes.empty() || !configUicOptions.empty()) { @@ -946,33 +952,34 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( error += outputFile; error += " for writing."; cmSystemTools::Error(error.c_str()); - return; - } - if (!configMocDefines.empty()) { - for (std::map<std::string, std::string>::iterator - it = configMocDefines.begin(), - end = configMocDefines.end(); - it != end; ++it) { - infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " " - << it->second << ")\n"; + } else { + infoFile << "# Configuration specific options\n"; + if (!configMocDefines.empty()) { + for (std::map<std::string, std::string>::iterator + it = configMocDefines.begin(), + end = configMocDefines.end(); + it != end; ++it) { + infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " " + << it->second << ")\n"; + } } - } - if (!configMocIncludes.empty()) { - for (std::map<std::string, std::string>::iterator - it = configMocIncludes.begin(), - end = configMocIncludes.end(); - it != end; ++it) { - infoFile << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second - << ")\n"; + if (!configMocIncludes.empty()) { + for (std::map<std::string, std::string>::iterator + it = configMocIncludes.begin(), + end = configMocIncludes.end(); + it != end; ++it) { + infoFile << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second + << ")\n"; + } } - } - if (!configUicOptions.empty()) { - for (std::map<std::string, std::string>::iterator - it = configUicOptions.begin(), - end = configUicOptions.end(); - it != end; ++it) { - infoFile << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " " - << it->second << ")\n"; + if (!configUicOptions.empty()) { + for (std::map<std::string, std::string>::iterator + it = configUicOptions.begin(), + end = configUicOptions.end(); + it != end; ++it) { + infoFile << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " " + << it->second << ")\n"; + } } } } diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 32dd547..e7e456a 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -7,7 +7,7 @@ #include <cmConfigure.h> #include <cmsys/FStream.hxx> #include <cmsys/Terminal.h> -#include <iostream> +#include <list> #include <sstream> #include <stdlib.h> #include <string.h> @@ -30,12 +30,31 @@ // -- Static variables -static const char* MocOldSettingsKey = "AM_MOC_OLD_SETTINGS"; -static const char* UicOldSettingsKey = "AM_UIC_OLD_SETTINGS"; -static const char* RccOldSettingsKey = "AM_RCC_OLD_SETTINGS"; +static const char* SettingsKeyMoc = "AM_MOC_OLD_SETTINGS"; +static const char* SettingsKeyUic = "AM_UIC_OLD_SETTINGS"; +static const char* SettingsKeyRcc = "AM_RCC_OLD_SETTINGS"; // -- Static functions +/** + * @brief Returns a the string escaped and enclosed in quotes + */ +static std::string Quoted(const std::string& text) +{ + static const char* rep[18] = { "\\", "\\\\", "\"", "\\\"", "\a", "\\a", + "\b", "\\b", "\f", "\\f", "\n", "\\n", + "\r", "\\r", "\t", "\\t", "\v", "\\v" }; + + std::string res = text; + for (const char* const* it = cmArrayBegin(rep); it != cmArrayEnd(rep); + it += 2) { + cmSystemTools::ReplaceString(res, *it, *(it + 1)); + } + res = '"' + res; + res += '"'; + return res; +} + static std::string GetConfigDefinition(cmMakefile* makefile, const std::string& key, const std::string& config) @@ -52,7 +71,7 @@ static std::string GetConfigDefinition(cmMakefile* makefile, return makefile->GetSafeDefinition(key); } -static std::string OldSettingsFile(const std::string& targetDirectory) +static std::string SettingsFile(const std::string& targetDirectory) { std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); cmSystemTools::ConvertToUnixSlashes(filename); @@ -60,40 +79,19 @@ static std::string OldSettingsFile(const std::string& targetDirectory) return filename; } -static std::string FindMatchingHeader( - const std::string& absPath, const std::string& mocSubDir, - const std::string& basename, - const std::vector<std::string>& headerExtensions) +inline static bool SettingsMatch(cmMakefile* makefile, const char* key, + const std::string& value) { - std::string header; - for (std::vector<std::string>::const_iterator ext = headerExtensions.begin(); - ext != headerExtensions.end(); ++ext) { - std::string sourceFilePath = absPath + basename + "." + (*ext); - if (cmsys::SystemTools::FileExists(sourceFilePath.c_str())) { - header = sourceFilePath; - break; - } - // Try subdirectory instead - if (!mocSubDir.empty()) { - sourceFilePath = mocSubDir + basename + "." + (*ext); - if (cmsys::SystemTools::FileExists(sourceFilePath.c_str())) { - header = sourceFilePath; - break; - } - } - } - - return header; + return (value == makefile->GetSafeDefinition(key)); } -static std::string ExtractSubDir(const std::string& absPath, - const std::string& currentMoc) +static void SettingWrite(std::ostream& ostr, const char* key, + const std::string& value) { - std::string subDir; - if (currentMoc.find_first_of('/') != std::string::npos) { - subDir = absPath + cmsys::SystemTools::GetFilenamePath(currentMoc) + '/'; + if (!value.empty()) { + ostr << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value) + << ")\n"; } - return subDir; } static bool FileNameIsUnique(const std::string& filePath, @@ -141,13 +139,19 @@ static bool ListContains(const std::vector<std::string>& list, return (std::find(list.begin(), list.end(), entry) != list.end()); } -static std::string JoinOptions(const std::map<std::string, std::string>& opts) +static std::string JoinOptionsList(const std::vector<std::string>& opts) +{ + return cmOutputConverter::EscapeForCMake(cmJoin(opts, ";")); +} + +static std::string JoinOptionsMap( + const std::map<std::string, std::string>& opts) { std::string result; for (std::map<std::string, std::string>::const_iterator it = opts.begin(); it != opts.end(); ++it) { if (it != opts.begin()) { - result += "%%%"; + result += "@list_sep@"; } result += it->first; result += "==="; @@ -215,9 +219,9 @@ cmQtAutoGenerators::cmQtAutoGenerators() , RunMocFailed(false) , RunUicFailed(false) , RunRccFailed(false) - , GenerateMocAll(false) - , GenerateUicAll(false) - , GenerateRccAll(false) + , GenerateAllMoc(false) + , GenerateAllUic(false) + , GenerateAllRcc(false) { std::string colorEnv; @@ -230,9 +234,12 @@ cmQtAutoGenerators::cmQtAutoGenerators() } } + this->MacroFilters[0].first = "Q_OBJECT"; + this->MacroFilters[0].second.compile("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]"); + this->MacroFilters[1].first = "Q_GADGET"; + this->MacroFilters[1].second.compile("[\n][ \t]*Q_GADGET[^a-zA-Z0-9_]"); + // Precompile regular expressions - this->RegExpQObject.compile("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]"); - this->RegExpQGadget.compile("[\n][ \t]*Q_GADGET[^a-zA-Z0-9_]"); this->RegExpMocInclude.compile( "[\n][ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); @@ -256,23 +263,46 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory, CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&gg, snapshot)); gg.SetCurrentMakefile(mf.get()); - if (!this->ReadAutogenInfoFile(mf.get(), targetDirectory, config)) { - return false; - } - // Read old settings - this->OldSettingsReadFile(mf.get(), targetDirectory); - // Init and run - this->Init(); - if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") { - if (!this->RunAutogen(mf.get())) { - return false; + bool success = false; + if (this->ReadAutogenInfoFile(mf.get(), targetDirectory, config)) { + // Read old settings + this->SettingsFileRead(mf.get(), targetDirectory); + // Init and run + this->Init(mf.get()); + if (this->RunAutogen()) { + // Write current settings + if (this->SettingsFileWrite(targetDirectory)) { + success = true; + } } } - // Write latest settings - if (!this->OldSettingsWriteFile(targetDirectory)) { - return false; + return success; +} + +bool cmQtAutoGenerators::MocDependFilterPush(const std::string& key, + const std::string& regExp) +{ + bool success = false; + if (!key.empty()) { + if (!regExp.empty()) { + MocDependFilter filter; + filter.key = key; + if (filter.regExp.compile(regExp)) { + this->MocDependFilters.push_back(filter); + success = true; + } else { + this->LogError("AutoMoc: Error in AUTOMOC_DEPEND_FILTERS: Compiling " + "regular expression failed.\nKey: " + + Quoted(key) + "\nExp.: " + Quoted(regExp)); + } + } else { + this->LogError("AutoMoc: Error in AUTOMOC_DEPEND_FILTERS: Regular " + "expression is empty"); + } + } else { + this->LogError("AutoMoc: Error in AUTOMOC_DEPEND_FILTERS: Key is empty"); } - return true; + return success; } bool cmQtAutoGenerators::ReadAutogenInfoFile( @@ -284,9 +314,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( filename += "/AutogenInfo.cmake"; if (!makefile->ReadListFile(filename.c_str())) { - std::ostringstream err; - err << "AutoGen: error processing file: " << filename << std::endl; - this->LogError(err.str()); + this->LogError("AutoGen: Error processing file: " + filename); return false; } @@ -309,6 +337,13 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->QtMajorVersion = makefile->GetSafeDefinition("AM_Qt5Core_VERSION_MAJOR"); } + // Check Qt version + if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { + this->LogError("AutoGen: Error: Unsupported Qt version: " + + Quoted(this->QtMajorVersion)); + return false; + } + this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE"); this->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE"); this->RccExecutable = makefile->GetSafeDefinition("AM_QT_RCC_EXECUTABLE"); @@ -320,17 +355,44 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->Headers); // - Moc - cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_SKIP_MOC"), - this->SkipMoc); - this->MocCompileDefinitionsStr = - GetConfigDefinition(makefile, "AM_MOC_COMPILE_DEFINITIONS", config); - this->MocIncludesStr = - GetConfigDefinition(makefile, "AM_MOC_INCLUDES", config); - this->MocOptionsStr = makefile->GetSafeDefinition("AM_MOC_OPTIONS"); + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_MOC_SKIP"), + this->MocSkipList); + cmSystemTools::ExpandListArgument( + GetConfigDefinition(makefile, "AM_MOC_COMPILE_DEFINITIONS", config), + this->MocDefinitions); + cmSystemTools::ExpandListArgument( + GetConfigDefinition(makefile, "AM_MOC_INCLUDES", config), + this->MocIncludePaths); + cmSystemTools::ExpandListArgument( + makefile->GetSafeDefinition("AM_MOC_OPTIONS"), this->MocOptions); + { + std::vector<std::string> mocDependFilters; + cmSystemTools::ExpandListArgument( + makefile->GetSafeDefinition("AM_MOC_DEPEND_FILTERS"), mocDependFilters); + // Insert Q_PLUGIN_METADATA dependency filter + if (this->QtMajorVersion != "4") { + this->MocDependFilterPush("Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + } + // Insert user defined dependency filters + if ((mocDependFilters.size() % 2) == 0) { + for (std::vector<std::string>::const_iterator dit = + mocDependFilters.begin(); + dit != mocDependFilters.end(); dit += 2) { + if (!this->MocDependFilterPush(*dit, *(dit + 1))) { + return false; + } + } + } else { + this->LogError("AutoMoc: Error: AUTOMOC_DEPEND_FILTERS list size is not " + "a multiple of 2"); + } + } // - Uic - cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_SKIP_UIC"), - this->SkipUic); + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_UIC_SKIP"), + this->UicSkipList); cmSystemTools::ExpandListArgument( GetConfigDefinition(makefile, "AM_UIC_TARGET_OPTIONS", config), this->UicTargetOptions); @@ -342,10 +404,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( cmSystemTools::ExpandListArgument( makefile->GetSafeDefinition("AM_UIC_OPTIONS_OPTIONS"), uicOptionsVec); if (uicFilesVec.size() != uicOptionsVec.size()) { - std::ostringstream err; - err << "AutoGen: Error: Uic files/options lists size missmatch in: " - << filename << std::endl; - this->LogError(err.str()); + this->LogError( + "AutoGen: Error: Uic files/options lists size missmatch in: " + + filename); return false; } for (std::vector<std::string>::iterator fileIt = uicFilesVec.begin(), @@ -367,10 +428,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( cmSystemTools::ExpandListArgument( makefile->GetSafeDefinition("AM_RCC_OPTIONS_OPTIONS"), rccOptionsVec); if (rccFilesVec.size() != rccOptionsVec.size()) { - std::ostringstream err; - err << "AutoGen: Error: RCC files/options lists size missmatch in: " - << filename << std::endl; - this->LogError(err.str()); + this->LogError( + "AutoGen: Error: RCC files/options lists size missmatch in: " + + filename); return false; } for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(), @@ -390,10 +450,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( rccInputLists.resize(this->RccSources.size()); } if (this->RccSources.size() != rccInputLists.size()) { - std::ostringstream err; - err << "AutoGen: Error: RCC sources/inputs lists size missmatch in: " - << filename << std::endl; - this->LogError(err.str()); + this->LogError( + "AutoGen: Error: RCC sources/inputs lists size missmatch in: " + + filename); return false; } for (std::vector<std::string>::iterator fileIt = this->RccSources.begin(), @@ -414,265 +473,218 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( return true; } -std::string cmQtAutoGenerators::MocSettingsStringCompose() -{ - std::string res; - res += this->MocCompileDefinitionsStr; - res += " ~~~ "; - res += this->MocIncludesStr; - res += " ~~~ "; - res += this->MocOptionsStr; - res += " ~~~ "; - res += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; - res += " ~~~ "; - return res; -} - -std::string cmQtAutoGenerators::UicSettingsStringCompose() +void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile, + const std::string& targetDirectory) { - std::string res; - res += cmJoin(this->UicTargetOptions, "@osep@"); - res += " ~~~ "; - res += JoinOptions(this->UicOptions); - res += " ~~~ "; - return res; -} - -std::string cmQtAutoGenerators::RccSettingsStringCompose() -{ - std::string res; - res += JoinOptions(this->RccOptions); - res += " ~~~ "; - return res; -} - -void cmQtAutoGenerators::OldSettingsReadFile( - cmMakefile* makefile, const std::string& targetDirectory) -{ - if (!this->MocExecutable.empty() || !this->UicExecutable.empty() || - !this->RccExecutable.empty()) { - // Compose current settings strings - this->MocSettingsString = this->MocSettingsStringCompose(); - this->UicSettingsString = this->UicSettingsStringCompose(); - this->RccSettingsString = this->RccSettingsStringCompose(); + // Compose current settings strings + if (this->MocEnabled()) { + std::string& str = this->SettingsStringMoc; + str += JoinOptionsList(this->MocDefinitions); + str += " ~~~ "; + str += JoinOptionsList(this->MocIncludePaths); + str += " ~~~ "; + str += JoinOptionsList(this->MocOptions); + str += " ~~~ "; + str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; + str += " ~~~ "; + } + if (this->UicEnabled()) { + std::string& str = this->SettingsStringUic; + str += JoinOptionsList(this->UicTargetOptions); + str += " ~~~ "; + str += JoinOptionsMap(this->UicOptions); + str += " ~~~ "; + } + if (this->RccEnabled()) { + std::string& str = this->SettingsStringRcc; + str += JoinOptionsMap(this->RccOptions); + str += " ~~~ "; + } - // Read old settings - const std::string filename = OldSettingsFile(targetDirectory); - if (makefile->ReadListFile(filename.c_str())) { - if (!this->MocExecutable.empty()) { - const std::string sol = makefile->GetSafeDefinition(MocOldSettingsKey); - if (sol != this->MocSettingsString) { - this->GenerateMocAll = true; - } - } - if (!this->UicExecutable.empty()) { - const std::string sol = makefile->GetSafeDefinition(UicOldSettingsKey); - if (sol != this->UicSettingsString) { - this->GenerateUicAll = true; - } - } - if (!this->RccExecutable.empty()) { - const std::string sol = makefile->GetSafeDefinition(RccOldSettingsKey); - if (sol != this->RccSettingsString) { - this->GenerateRccAll = true; - } - } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->GenerateMocAll || this->GenerateUicAll || - this->GenerateRccAll) { - cmSystemTools::RemoveFile(filename); - } - } else { - // If the file could not be read re-generate everythiung. - this->GenerateMocAll = true; - this->GenerateUicAll = true; - this->GenerateRccAll = true; + // Read old settings + const std::string filename = SettingsFile(targetDirectory); + if (makefile->ReadListFile(filename.c_str())) { + if (!SettingsMatch(makefile, SettingsKeyMoc, this->SettingsStringMoc)) { + this->GenerateAllMoc = true; + } + if (!SettingsMatch(makefile, SettingsKeyUic, this->SettingsStringUic)) { + this->GenerateAllUic = true; + } + if (!SettingsMatch(makefile, SettingsKeyRcc, this->SettingsStringRcc)) { + this->GenerateAllRcc = true; + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (this->GenerateAllAny()) { + cmSystemTools::RemoveFile(filename); } + } else { + // If the file could not be read re-generate everythiung. + this->GenerateAllMoc = true; + this->GenerateAllUic = true; + this->GenerateAllRcc = true; } } -bool cmQtAutoGenerators::OldSettingsWriteFile( - const std::string& targetDirectory) +bool cmQtAutoGenerators::SettingsFileWrite(const std::string& targetDirectory) { bool success = true; // Only write if any setting changed - if (this->GenerateMocAll || this->GenerateUicAll || this->GenerateRccAll) { - const std::string filename = OldSettingsFile(targetDirectory); + if (this->GenerateAllAny()) { + const std::string filename = SettingsFile(targetDirectory); + if (this->Verbose) { + this->LogInfo("AutoGen: Writing settings file " + filename); + } cmsys::ofstream outfile; outfile.open(filename.c_str(), std::ios::trunc); if (outfile) { - if (!this->MocExecutable.empty()) { - outfile << "set(" << MocOldSettingsKey << " " - << cmOutputConverter::EscapeForCMake(this->MocSettingsString) - << ")\n"; - } - if (!this->UicExecutable.empty()) { - outfile << "set(" << UicOldSettingsKey << " " - << cmOutputConverter::EscapeForCMake(this->UicSettingsString) - << ")\n"; - } - if (!this->RccExecutable.empty()) { - outfile << "set(" << RccOldSettingsKey << " " - << cmOutputConverter::EscapeForCMake(this->RccSettingsString) - << ")\n"; - } + SettingWrite(outfile, SettingsKeyMoc, this->SettingsStringMoc); + SettingWrite(outfile, SettingsKeyUic, this->SettingsStringUic); + SettingWrite(outfile, SettingsKeyRcc, this->SettingsStringRcc); success = outfile.good(); outfile.close(); } else { success = false; // Remove old settings file to trigger full rebuild on next run cmSystemTools::RemoveFile(filename); - { - std::ostringstream err; - err << "AutoGen: Error: Writing old settings file failed: " << filename - << std::endl; - this->LogError(err.str()); - } + this->LogError("AutoGen: Error: Writing old settings file failed: " + + filename); } } return success; } -void cmQtAutoGenerators::Init() +void cmQtAutoGenerators::Init(cmMakefile* makefile) { this->AutogenBuildSubDir = this->AutogenTargetName; this->AutogenBuildSubDir += "/"; - this->OutMocCppFilenameRel = this->AutogenBuildSubDir; - this->OutMocCppFilenameRel += "moc_compilation.cpp"; + this->MocCppFilenameRel = this->AutogenBuildSubDir; + this->MocCppFilenameRel += "moc_compilation.cpp"; - this->OutMocCppFilenameAbs = - this->CurrentBinaryDir + this->OutMocCppFilenameRel; + this->MocCppFilenameAbs = this->CurrentBinaryDir + this->MocCppFilenameRel; // Init file path checksum generator fpathCheckSum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, this->ProjectBinaryDir); - std::vector<std::string> cdefList; - cmSystemTools::ExpandListArgument(this->MocCompileDefinitionsStr, cdefList); - for (std::vector<std::string>::const_iterator it = cdefList.begin(); - it != cdefList.end(); ++it) { - this->MocDefinitions.push_back("-D" + (*it)); - } - - cmSystemTools::ExpandListArgument(this->MocOptionsStr, this->MocOptions); - - std::vector<std::string> incPaths; - cmSystemTools::ExpandListArgument(this->MocIncludesStr, incPaths); - - std::set<std::string> frameworkPaths; - for (std::vector<std::string>::const_iterator it = incPaths.begin(); - it != incPaths.end(); ++it) { - const std::string& path = *it; - this->MocIncludes.push_back("-I" + path); - if (cmHasLiteralSuffix(path, ".framework/Headers")) { - // Go up twice to get to the framework root - std::vector<std::string> pathComponents; - cmsys::SystemTools::SplitPath(path, pathComponents); - std::string frameworkPath = cmsys::SystemTools::JoinPath( - pathComponents.begin(), pathComponents.end() - 2); - frameworkPaths.insert(frameworkPath); - } - } - - for (std::set<std::string>::const_iterator it = frameworkPaths.begin(); - it != frameworkPaths.end(); ++it) { - this->MocIncludes.push_back("-F"); - this->MocIncludes.push_back(*it); - } + // Acquire header extensions + this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); + // Sort include directories on demand if (this->IncludeProjectDirsBefore) { - const std::string binDir = "-I" + this->ProjectBinaryDir; - const std::string srcDir = "-I" + this->ProjectSourceDir; - - std::list<std::string> sortedMocIncludes; - std::list<std::string>::iterator it = this->MocIncludes.begin(); - while (it != this->MocIncludes.end()) { - if (cmsys::SystemTools::StringStartsWith(*it, binDir.c_str())) { - sortedMocIncludes.push_back(*it); - it = this->MocIncludes.erase(it); - } else { - ++it; + // Move strings to temporary list + std::list<std::string> includes; + includes.insert(includes.end(), this->MocIncludePaths.begin(), + this->MocIncludePaths.end()); + this->MocIncludePaths.clear(); + this->MocIncludePaths.reserve(includes.size()); + // Append project directories only + { + const char* movePaths[2] = { this->ProjectBinaryDir.c_str(), + this->ProjectSourceDir.c_str() }; + for (const char* const* mpit = cmArrayBegin(movePaths); + mpit != cmArrayEnd(movePaths); ++mpit) { + std::list<std::string>::iterator it = includes.begin(); + while (it != includes.end()) { + const std::string& path = *it; + if (cmsys::SystemTools::StringStartsWith(path, *mpit)) { + this->MocIncludePaths.push_back(path); + it = includes.erase(it); + } else { + ++it; + } + } } } - it = this->MocIncludes.begin(); - while (it != this->MocIncludes.end()) { - if (cmsys::SystemTools::StringStartsWith(*it, srcDir.c_str())) { - sortedMocIncludes.push_back(*it); - it = this->MocIncludes.erase(it); - } else { - ++it; + // Append remaining directories + this->MocIncludePaths.insert(this->MocIncludePaths.end(), includes.begin(), + includes.end()); + } + // Compose moc includes list + { + std::set<std::string> frameworkPaths; + for (std::vector<std::string>::const_iterator it = + this->MocIncludePaths.begin(); + it != this->MocIncludePaths.end(); ++it) { + const std::string& path = *it; + this->MocIncludes.push_back("-I" + path); + // Extract framework path + if (cmHasLiteralSuffix(path, ".framework/Headers")) { + // Go up twice to get to the framework root + std::vector<std::string> pathComponents; + cmsys::SystemTools::SplitPath(path, pathComponents); + std::string frameworkPath = cmsys::SystemTools::JoinPath( + pathComponents.begin(), pathComponents.end() - 2); + frameworkPaths.insert(frameworkPath); } } - sortedMocIncludes.insert(sortedMocIncludes.end(), - this->MocIncludes.begin(), - this->MocIncludes.end()); - this->MocIncludes = sortedMocIncludes; + // Append framework includes + for (std::set<std::string>::const_iterator it = frameworkPaths.begin(); + it != frameworkPaths.end(); ++it) { + this->MocIncludes.push_back("-F"); + this->MocIncludes.push_back(*it); + } } } -bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) +bool cmQtAutoGenerators::RunAutogen() { // the program goes through all .cpp files to see which moc files are // included. It is not really interesting how the moc file is named, but // what file the moc is created from. Once a moc is included the same moc // may not be included in the moc_compilation.cpp file anymore. OTOH if // there's a header containing Q_OBJECT where no corresponding moc file - // is included anywhere a moc_<filename>.cpp file is created and included in - // the moc_compilation.cpp file. + // is included anywhere a moc_<filename>.cpp file is created and included + // in the moc_compilation.cpp file. // key = moc source filepath, value = moc output filepath - std::map<std::string, std::string> includedMocs; - std::map<std::string, std::string> notIncludedMocs; - std::map<std::string, std::vector<std::string> > includedUis; + std::map<std::string, std::string> mocsIncluded; + std::map<std::string, std::string> mocsNotIncluded; + std::map<std::string, std::set<std::string> > mocDepends; + std::map<std::string, std::vector<std::string> > uisIncluded; // collects all headers which may need to be mocced - std::set<std::string> headerFilesMoc; - std::set<std::string> headerFilesUic; + std::set<std::string> mocHeaderFiles; + std::set<std::string> uicHeaderFiles; // Parse sources - { - const std::vector<std::string>& headerExtensions = - makefile->GetCMakeInstance()->GetHeaderExtensions(); - - for (std::vector<std::string>::const_iterator it = this->Sources.begin(); - it != this->Sources.end(); ++it) { - const std::string& absFilename = *it; - // Parse source file for MOC/UIC - if (!this->ParseSourceFile(absFilename, headerExtensions, includedMocs, - includedUis, this->MocRelaxedMode)) { - return false; - } - // Find additional headers - this->SearchHeadersForSourceFile(absFilename, headerExtensions, - headerFilesMoc, headerFilesUic); + for (std::vector<std::string>::const_iterator it = this->Sources.begin(); + it != this->Sources.end(); ++it) { + const std::string& absFilename = cmsys::SystemTools::GetRealPath(*it); + // Parse source file for MOC/UIC + if (!this->ParseSourceFile(absFilename, mocsIncluded, mocDepends, + uisIncluded, this->MocRelaxedMode)) { + return false; } + // Find additional headers + this->SearchHeadersForSourceFile(absFilename, mocHeaderFiles, + uicHeaderFiles); } // Parse headers for (std::vector<std::string>::const_iterator it = this->Headers.begin(); it != this->Headers.end(); ++it) { - const std::string& headerName = *it; - if (!this->MocSkipTest(headerName)) { - headerFilesMoc.insert(headerName); + const std::string& headerName = cmsys::SystemTools::GetRealPath(*it); + if (!this->MocSkip(headerName)) { + mocHeaderFiles.insert(headerName); } - if (!this->UicSkipTest(headerName)) { - headerFilesUic.insert(headerName); + if (!this->UicSkip(headerName)) { + uicHeaderFiles.insert(headerName); } } - this->ParseHeaders(headerFilesMoc, headerFilesUic, includedMocs, - notIncludedMocs, includedUis); + this->ParseHeaders(mocHeaderFiles, uicHeaderFiles, mocsIncluded, + mocsNotIncluded, mocDepends, uisIncluded); // Generate files - if (!this->MocGenerateAll(includedMocs, notIncludedMocs)) { + if (!this->MocGenerateAll(mocsIncluded, mocsNotIncluded, mocDepends)) { return false; } - if (!this->UicGenerateAll(includedUis)) { + if (!this->UicGenerateAll(uisIncluded)) { return false; } - if (!this->QrcGenerateAll()) { + if (!this->RccGenerateAll()) { return false; } @@ -683,35 +695,73 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) * @brief Tests if the C++ content requires moc processing * @return True if moc is required */ -bool cmQtAutoGenerators::MocRequired(const std::string& text, - std::string& macroName) +bool cmQtAutoGenerators::MocRequired(const std::string& contentText, + std::string* macroName) { - // Run a simple check before an expensive regular expression check - if (strstr(text.c_str(), "Q_OBJECT") != CM_NULLPTR) { - if (this->RegExpQObject.find(text)) { - macroName = "Q_OBJECT"; - return true; + for (unsigned int ii = 0; ii != cmArraySize(this->MacroFilters); ++ii) { + MacroFilter& filter = this->MacroFilters[ii]; + // Run a simple find string operation before the expensive + // regular expression check + if (contentText.find(filter.first) != std::string::npos) { + if (filter.second.find(contentText)) { + // Return macro name on demand + if (macroName != CM_NULLPTR) { + *macroName = filter.first; + } + return true; + } } } - if (strstr(text.c_str(), "Q_GADGET") != CM_NULLPTR) { - if (this->RegExpQGadget.find(text)) { - macroName = "Q_GADGET"; - return true; + return false; +} + +void cmQtAutoGenerators::MocFindDepends( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::set<std::string> >& mocDepends) +{ + for (std::vector<MocDependFilter>::iterator fit = + this->MocDependFilters.begin(); + fit != this->MocDependFilters.end(); ++fit) { + MocDependFilter& filter = *fit; + // Run a simple find string operation before the expensive + // regular expression check + if (contentText.find(filter.key) != std::string::npos) { + // Run regular expression check loop + const char* contentChars = contentText.c_str(); + while (filter.regExp.find(contentChars)) { + // Evaluate match + const std::string match = filter.regExp.match(1); + if (!match.empty()) { + // Find the dependency file + const std::string incFile = + this->FindIncludedFile(absFilename, match); + if (!incFile.empty()) { + mocDepends[absFilename].insert(incFile); + if (this->Verbose) { + this->LogInfo("AutoMoc: Found dependency:\n " + + Quoted(absFilename) + "\n " + Quoted(incFile)); + } + } else { + this->LogWarning("AutoMoc: Warning: " + Quoted(absFilename) + + "\n" + "Could not find dependency file " + + Quoted(match)); + } + } + contentChars += filter.regExp.end(); + } } } - return false; } /** * @brief Tests if the file should be ignored for moc scanning * @return True if the file should be ignored */ -bool cmQtAutoGenerators::MocSkipTest(const std::string& absFilename) +bool cmQtAutoGenerators::MocSkip(const std::string& absFilename) const { - // Test if moc scanning is enabled - if (!this->MocExecutable.empty()) { + if (this->MocEnabled()) { // Test if the file name is on the skip list - if (!ListContains(this->SkipMoc, absFilename)) { + if (!ListContains(this->MocSkipList, absFilename)) { return false; } } @@ -721,12 +771,11 @@ bool cmQtAutoGenerators::MocSkipTest(const std::string& absFilename) /** * @brief Tests if the file name is in the skip list */ -bool cmQtAutoGenerators::UicSkipTest(const std::string& absFilename) +bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const { - // Test if uic scanning is enabled - if (!this->UicExecutable.empty()) { + if (this->UicEnabled()) { // Test if the file name is on the skip list - if (!ListContains(this->SkipUic, absFilename)) { + if (!ListContains(this->UicSkipList, absFilename)) { return false; } } @@ -738,44 +787,40 @@ bool cmQtAutoGenerators::UicSkipTest(const std::string& absFilename) */ bool cmQtAutoGenerators::ParseSourceFile( const std::string& absFilename, - const std::vector<std::string>& headerExtensions, - std::map<std::string, std::string>& includedMocs, - std::map<std::string, std::vector<std::string> >& includedUis, bool relaxed) + std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, + std::map<std::string, std::vector<std::string> >& uisIncluded, bool relaxed) { bool success = true; - const std::string contentsString = ReadAll(absFilename); - if (contentsString.empty()) { - std::ostringstream err; - err << "AutoGen: Warning: " << absFilename << "\n" + const std::string contentText = ReadAll(absFilename); + if (contentText.empty()) { + std::ostringstream ost; + ost << "AutoGen: Warning: " << absFilename << "\n" << "The file is empty\n"; - this->LogWarning(err.str()); + this->LogWarning(ost.str()); } else { // Parse source contents for MOC - if (success && !this->MocSkipTest(absFilename)) { - success = this->ParseContentForMoc( - absFilename, contentsString, headerExtensions, includedMocs, relaxed); + if (success && !this->MocSkip(absFilename)) { + success = this->MocParseSourceContent(absFilename, contentText, + mocsIncluded, mocDepends, relaxed); } // Parse source contents for UIC - if (success && !this->UicSkipTest(absFilename)) { - this->ParseContentForUic(absFilename, contentsString, includedUis); + if (success && !this->UicSkip(absFilename)) { + this->UicParseContent(absFilename, contentText, uisIncluded); } } return success; } -void cmQtAutoGenerators::ParseContentForUic( - const std::string& absFilename, const std::string& contentsString, - std::map<std::string, std::vector<std::string> >& includedUis) +void cmQtAutoGenerators::UicParseContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::vector<std::string> >& uisIncluded) { - // Process if (this->Verbose) { - std::ostringstream err; - err << "AutoUic: Checking " << absFilename << "\n"; - this->LogInfo(err.str()); + this->LogInfo("AutoUic: Checking " + absFilename); } - const std::string realName = cmsys::SystemTools::GetRealPath(absFilename); - const char* contentChars = contentsString.c_str(); + const char* contentChars = contentText.c_str(); if (strstr(contentChars, "ui_") != CM_NULLPTR) { while (this->RegExpUicInclude.find(contentChars)) { const std::string currentUi = this->RegExpUicInclude.match(1); @@ -783,7 +828,7 @@ void cmQtAutoGenerators::ParseContentForUic( cmsys::SystemTools::GetFilenameWithoutLastExtension(currentUi); // basename should be the part of the ui filename used for // finding the correct header, so we need to remove the ui_ part - includedUis[realName].push_back(basename.substr(3)); + uisIncluded[absFilename].push_back(basename.substr(3)); contentChars += this->RegExpUicInclude.end(); } } @@ -792,79 +837,72 @@ void cmQtAutoGenerators::ParseContentForUic( /** * @return True on success */ -bool cmQtAutoGenerators::ParseContentForMoc( - const std::string& absFilename, const std::string& contentsString, - const std::vector<std::string>& headerExtensions, - std::map<std::string, std::string>& includedMocs, bool relaxed) +bool cmQtAutoGenerators::MocParseSourceContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, bool relaxed) { - // Process if (this->Verbose) { - std::ostringstream err; - err << "AutoMoc: Checking " << absFilename << "\n"; - this->LogInfo(err.str()); + this->LogInfo("AutoMoc: Checking " + absFilename); } const std::string scannedFileAbsPath = - cmsys::SystemTools::GetFilenamePath( - cmsys::SystemTools::GetRealPath(absFilename)) + - '/'; + cmsys::SystemTools::GetFilenamePath(absFilename) + '/'; const std::string scannedFileBasename = cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename); std::string macroName; - const bool requiresMoc = this->MocRequired(contentsString, macroName); + const bool requiresMoc = this->MocRequired(contentText, ¯oName); bool ownDotMocIncluded = false; - bool ownMocUnderscoreIncluded = false; - std::string ownMocUnderscoreFile; - std::string ownMocHeaderFile; + std::string ownMocUnderscoreInclude; + std::string ownMocUnderscoreHeader; // first a simple string check for "moc" is *much* faster than the regexp, // and if the string search already fails, we don't have to try the // expensive regexp - const char* contentChars = contentsString.c_str(); + const char* contentChars = contentText.c_str(); if (strstr(contentChars, "moc") != CM_NULLPTR) { // Iterate over all included moc files while (this->RegExpMocInclude.find(contentChars)) { - const std::string currentMoc = this->RegExpMocInclude.match(1); - // Basename of the current moc include - std::string basename = - cmsys::SystemTools::GetFilenameWithoutLastExtension(currentMoc); + const std::string incString = this->RegExpMocInclude.match(1); + // Basename of the moc include + const std::string incBasename = + cmsys::SystemTools::GetFilenameWithoutLastExtension(incString); + std::string incSubDir; + if (incString.find_first_of('/') != std::string::npos) { + incSubDir = cmsys::SystemTools::GetFilenamePath(incString); + incSubDir += '/'; + } // If the moc include is of the moc_foo.cpp style we expect // the Q_OBJECT class declaration in a header file. // If the moc include is of the foo.moc style we need to look for // a Q_OBJECT macro in the current source file, if it contains the // macro we generate the moc file from the source file. - if (cmHasLiteralPrefix(basename, "moc_")) { + if (cmHasLiteralPrefix(incBasename, "moc_")) { // Include: moc_FOO.cxx - // basename should be the part of the moc filename used for - // finding the correct header, so we need to remove the moc_ part - basename = basename.substr(4); - const std::string mocSubDir = - ExtractSubDir(scannedFileAbsPath, currentMoc); - const std::string headerToMoc = FindMatchingHeader( - scannedFileAbsPath, mocSubDir, basename, headerExtensions); - + // Remove the moc_ part + const std::string incRealBasename = incBasename.substr(4); + const std::string headerToMoc = + this->FindMocHeader(scannedFileAbsPath, incRealBasename, incSubDir); if (!headerToMoc.empty()) { - includedMocs[headerToMoc] = currentMoc; - if (relaxed && (basename == scannedFileBasename)) { - ownMocUnderscoreIncluded = true; - ownMocUnderscoreFile = currentMoc; - ownMocHeaderFile = headerToMoc; + // Register moc job + mocsIncluded[headerToMoc] = incString; + this->MocFindDepends(headerToMoc, contentText, mocDepends); + // Store meta information for relaxed mode + if (relaxed && (incRealBasename == scannedFileBasename)) { + ownMocUnderscoreInclude = incString; + ownMocUnderscoreHeader = headerToMoc; } } else { - std::ostringstream err; - err << "AutoMoc: Error: " << absFilename << "\n" - << "The file includes the moc file \"" << currentMoc - << "\", but could not find header \"" << basename << '{' - << JoinExts(headerExtensions) << "}\" "; - if (mocSubDir.empty()) { - err << "in " << scannedFileAbsPath << "\n"; - } else { - err << "neither in " << scannedFileAbsPath << " nor in " - << mocSubDir << "\n"; - } - this->LogError(err.str()); + std::ostringstream ost; + ost << "AutoMoc: Error: " << absFilename << "\n" + << "The file includes the moc file " << Quoted(incString) + << ", but could not find header " + << Quoted(incRealBasename + "{" + + JoinExts(this->HeaderExtensions) + "}"); + ; + this->LogError(ost.str()); return false; } } else { @@ -872,71 +910,81 @@ bool cmQtAutoGenerators::ParseContentForMoc( std::string fileToMoc; if (relaxed) { // Mode: Relaxed - if (!requiresMoc || basename != scannedFileBasename) { - const std::string mocSubDir = - ExtractSubDir(scannedFileAbsPath, currentMoc); - const std::string headerToMoc = FindMatchingHeader( - scannedFileAbsPath, mocSubDir, basename, headerExtensions); + if (requiresMoc && (incBasename == scannedFileBasename)) { + // Include self + fileToMoc = absFilename; + ownDotMocIncluded = true; + } else { + // In relaxed mode try to find a header instead but issue a warning + const std::string headerToMoc = + this->FindMocHeader(scannedFileAbsPath, incBasename, incSubDir); if (!headerToMoc.empty()) { // This is for KDE4 compatibility: fileToMoc = headerToMoc; - if (!requiresMoc && basename == scannedFileBasename) { - std::ostringstream err; - err << "AutoMoc: Warning: " << absFilename << "\n" - << "The file includes the moc file \"" << currentMoc - << "\", but does not contain a " << macroName - << " macro. Running moc on " - << "\"" << headerToMoc << "\" ! Include \"moc_" << basename - << ".cpp\" for a compatibility with " - "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"; - this->LogWarning(err.str()); + if (!requiresMoc && (incBasename == scannedFileBasename)) { + std::ostringstream ost; + ost << "AutoMoc: Warning: " << Quoted(absFilename) << "\n" + << "The file includes the moc file " << Quoted(incString) + << ", but does not contain a Q_OBJECT or Q_GADGET macro.\n" + << "Running moc on " << Quoted(headerToMoc) << "!\n" + << "Include " << Quoted("moc_" + incBasename + ".cpp") + << " for a compatibility with strict mode (see " + "CMAKE_AUTOMOC_RELAXED_MODE).\n"; + this->LogWarning(ost.str()); } else { - std::ostringstream err; - err << "AutoMoc: Warning: " << absFilename << "\n" - << "The file includes the moc file \"" << currentMoc - << "\" instead of \"moc_" << basename - << ".cpp\". Running moc on " - << "\"" << headerToMoc << "\" ! Include \"moc_" << basename - << ".cpp\" for compatibility with " - "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"; - this->LogWarning(err.str()); + std::ostringstream ost; + ost << "AutoMoc: Warning: " << Quoted(absFilename) << "\n" + << "The file includes the moc file " << Quoted(incString) + << " instead of " << Quoted("moc_" + incBasename + ".cpp") + << ".\n" + << "Running moc on " << Quoted(headerToMoc) << "!\n" + << "Include " << Quoted("moc_" + incBasename + ".cpp") + << " for compatibility with strict mode (see " + "CMAKE_AUTOMOC_RELAXED_MODE).\n"; + this->LogWarning(ost.str()); } } else { - std::ostringstream err; - err << "AutoMoc: Error: " << absFilename << "\n" - << "The file includes the moc file \"" << currentMoc - << "\", which seems to be the moc file from a different " + std::ostringstream ost; + ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n" + << "The file includes the moc file " << Quoted(incString) + << ". which seems to be the moc file from a different " "source file. CMake also could not find a matching " - "header.\n"; - this->LogError(err.str()); + "header."; + this->LogError(ost.str()); return false; } - } else { - // Include self - fileToMoc = absFilename; - ownDotMocIncluded = true; } } else { // Mode: Strict - if (basename == scannedFileBasename) { + if (incBasename == scannedFileBasename) { // Include self fileToMoc = absFilename; ownDotMocIncluded = true; + // Accept but issue a warning if moc isn't required + if (!requiresMoc) { + std::ostringstream ost; + ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n" + << "The file includes the moc file " << Quoted(incString) + << ", but does not contain a Q_OBJECT or Q_GADGET " + "macro."; + this->LogWarning(ost.str()); + } } else { // Don't allow FOO.moc include other than self in strict mode - std::ostringstream err; - err << "AutoMoc: Error: " << absFilename << "\n" - << "The file includes the moc file \"" << currentMoc - << "\", which seems to be the moc file from a different " - "source file. This is not supported. Include \"" - << scannedFileBasename - << ".moc\" to run moc on this source file.\n"; - this->LogError(err.str()); + std::ostringstream ost; + ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n" + << "The file includes the moc file " << Quoted(incString) + << ", which seems to be the moc file from a different " + "source file. This is not supported. Include " + << Quoted(scannedFileBasename + ".moc") + << " to run moc on this source file."; + this->LogError(ost.str()); return false; } } if (!fileToMoc.empty()) { - includedMocs[fileToMoc] = currentMoc; + mocsIncluded[fileToMoc] = incString; + this->MocFindDepends(fileToMoc, contentText, mocDepends); } } // Forward content pointer @@ -944,36 +992,40 @@ bool cmQtAutoGenerators::ParseContentForMoc( } } - // In this case, check whether the scanned file itself contains a Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. if (requiresMoc && !ownDotMocIncluded) { - if (relaxed && ownMocUnderscoreIncluded) { + // In this case, check whether the scanned file itself contains a Q_OBJECT. + // If this is the case, the moc_foo.cpp should probably be generated from + // foo.cpp instead of foo.h, because otherwise it won't build. + // But warn, since this is not how it is supposed to be used. + if (relaxed && !ownMocUnderscoreInclude.empty()) { // This is for KDE4 compatibility: - std::ostringstream err; - err << "AutoMoc: Warning: " << absFilename << "\n" + std::ostringstream ost; + ost << "AutoMoc: Warning: " << Quoted(absFilename) << "\n" << "The file contains a " << macroName << " macro, but does not include " - << "\"" << scannedFileBasename << ".moc\", but instead includes " - << "\"" << ownMocUnderscoreFile << "\". Running moc on " - << "\"" << absFilename << "\" ! Better include \"" - << scannedFileBasename - << ".moc\" for compatibility with " - "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"; - this->LogWarning(err.str()); + << Quoted(scannedFileBasename + ".moc") << ", but instead includes " + << Quoted(ownMocUnderscoreInclude) << ".\n" + << "Running moc on " << Quoted(absFilename) << "!\n" + << "Better include " << Quoted(scannedFileBasename + ".moc") + << " for compatibility with strict mode (see " + "CMAKE_AUTOMOC_RELAXED_MODE)."; + this->LogWarning(ost.str()); // Use scanned source file instead of scanned header file as moc source - includedMocs[absFilename] = ownMocUnderscoreFile; - includedMocs.erase(ownMocHeaderFile); + mocsIncluded[absFilename] = ownMocUnderscoreInclude; + this->MocFindDepends(absFilename, contentText, mocDepends); + // Remove + mocsIncluded.erase(ownMocUnderscoreHeader); } else { // Otherwise always error out since it will not compile: - std::ostringstream err; - err << "AutoMoc: Error: " << absFilename << "\n" + std::ostringstream ost; + ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n" << "The file contains a " << macroName << " macro, but does not include " - << "\"" << scannedFileBasename << ".moc\" !\n"; - this->LogError(err.str()); + << Quoted(scannedFileBasename + ".moc") << "!\n" + << "Consider adding the include or enabling SKIP_AUTOMOC for this " + "file."; + this->LogError(ost.str()); return false; } } @@ -981,45 +1033,48 @@ bool cmQtAutoGenerators::ParseContentForMoc( return true; } -void cmQtAutoGenerators::SearchHeadersForSourceFile( - const std::string& absFilename, - const std::vector<std::string>& headerExtensions, - std::set<std::string>& absHeadersMoc, std::set<std::string>& absHeadersUic) +void cmQtAutoGenerators::MocParseHeaderContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::string>& mocsNotIncluded, + std::map<std::string, std::set<std::string> >& mocDepends) { - // search for header files and private header files we may need to moc: - std::string basepath = cmsys::SystemTools::GetFilenamePath( - cmsys::SystemTools::GetRealPath(absFilename)); - basepath += '/'; - basepath += cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename); - - // Search for regular header - for (std::vector<std::string>::const_iterator ext = headerExtensions.begin(); - ext != headerExtensions.end(); ++ext) { - const std::string headerName = basepath + "." + (*ext); - if (cmsys::SystemTools::FileExists(headerName.c_str())) { - // Moc headers - if (!this->MocSkipTest(absFilename) && !this->MocSkipTest(headerName)) { - absHeadersMoc.insert(headerName); - } - // Uic headers - if (!this->UicSkipTest(absFilename) && !this->UicSkipTest(headerName)) { - absHeadersUic.insert(headerName); - } - break; - } + // Log + if (this->Verbose) { + this->LogInfo("AutoMoc: Checking " + absFilename); } - // Search for private header - for (std::vector<std::string>::const_iterator ext = headerExtensions.begin(); - ext != headerExtensions.end(); ++ext) { - const std::string headerName = basepath + "_p." + (*ext); - if (cmsys::SystemTools::FileExists(headerName.c_str())) { + if (this->MocRequired(contentText)) { + // Register moc job + mocsNotIncluded[absFilename] = + this->ChecksumedPath(absFilename, "moc_", ".cpp"); + this->MocFindDepends(absFilename, contentText, mocDepends); + } +} + +void cmQtAutoGenerators::SearchHeadersForSourceFile( + const std::string& absFilename, std::set<std::string>& mocHeaderFiles, + std::set<std::string>& uicHeaderFiles) const +{ + std::string basepaths[2]; + { + std::string bpath = cmsys::SystemTools::GetFilenamePath(absFilename); + bpath += '/'; + bpath += cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename); + // search for default header files and private header files + basepaths[0] = bpath; + basepaths[1] = bpath + "_p"; + } + + for (const std::string* bpit = cmArrayBegin(basepaths); + bpit != cmArrayEnd(basepaths); ++bpit) { + std::string headerName; + if (this->FindHeader(headerName, *bpit)) { // Moc headers - if (!this->MocSkipTest(absFilename) && !this->MocSkipTest(headerName)) { - absHeadersMoc.insert(headerName); + if (!this->MocSkip(absFilename) && !this->MocSkip(headerName)) { + mocHeaderFiles.insert(headerName); } // Uic headers - if (!this->UicSkipTest(absFilename) && !this->UicSkipTest(headerName)) { - absHeadersUic.insert(headerName); + if (!this->UicSkip(absFilename) && !this->UicSkip(headerName)) { + uicHeaderFiles.insert(headerName); } break; } @@ -1027,98 +1082,88 @@ void cmQtAutoGenerators::SearchHeadersForSourceFile( } void cmQtAutoGenerators::ParseHeaders( - const std::set<std::string>& absHeadersMoc, - const std::set<std::string>& absHeadersUic, - const std::map<std::string, std::string>& includedMocs, - std::map<std::string, std::string>& notIncludedMocs, - std::map<std::string, std::vector<std::string> >& includedUis) + const std::set<std::string>& mocHeaderFiles, + const std::set<std::string>& uicHeaderFiles, + const std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::string>& mocsNotIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, + std::map<std::string, std::vector<std::string> >& uisIncluded) { // Merged header files list to read files only once std::set<std::string> headerFiles; - headerFiles.insert(absHeadersMoc.begin(), absHeadersMoc.end()); - headerFiles.insert(absHeadersUic.begin(), absHeadersUic.end()); + headerFiles.insert(mocHeaderFiles.begin(), mocHeaderFiles.end()); + headerFiles.insert(uicHeaderFiles.begin(), uicHeaderFiles.end()); for (std::set<std::string>::const_iterator hIt = headerFiles.begin(); hIt != headerFiles.end(); ++hIt) { const std::string& headerName = *hIt; - const std::string contents = ReadAll(headerName); + const std::string contentText = ReadAll(headerName); // Parse header content for MOC - if ((absHeadersMoc.find(headerName) != absHeadersMoc.end()) && - (includedMocs.find(headerName) == includedMocs.end())) { - // Process - if (this->Verbose) { - std::ostringstream err; - err << "AutoMoc: Checking " << headerName << "\n"; - this->LogInfo(err.str()); - } - std::string macroName; - if (this->MocRequired(contents, macroName)) { - notIncludedMocs[headerName] = fpathCheckSum.getPart(headerName) + - "/moc_" + - cmsys::SystemTools::GetFilenameWithoutLastExtension(headerName) + - ".cpp"; - } + if ((mocHeaderFiles.find(headerName) != mocHeaderFiles.end()) && + (mocsIncluded.find(headerName) == mocsIncluded.end())) { + this->MocParseHeaderContent(headerName, contentText, mocsNotIncluded, + mocDepends); } // Parse header content for UIC - if (absHeadersUic.find(headerName) != absHeadersUic.end()) { - this->ParseContentForUic(headerName, contents, includedUis); + if (uicHeaderFiles.find(headerName) != uicHeaderFiles.end()) { + this->UicParseContent(headerName, contentText, uisIncluded); } } } bool cmQtAutoGenerators::MocGenerateAll( - const std::map<std::string, std::string>& includedMocs, - const std::map<std::string, std::string>& notIncludedMocs) + const std::map<std::string, std::string>& mocsIncluded, + const std::map<std::string, std::string>& mocsNotIncluded, + const std::map<std::string, std::set<std::string> >& mocDepends) { - if (this->MocExecutable.empty()) { + if (!this->MocEnabled()) { return true; } + bool mocCompFileGenerated = false; + bool mocCompChanged = false; + // look for name collisions { std::multimap<std::string, std::string> collisions; // Test merged map of included and notIncluded - std::map<std::string, std::string> mergedMocs(includedMocs); - mergedMocs.insert(notIncludedMocs.begin(), notIncludedMocs.end()); + std::map<std::string, std::string> mergedMocs(mocsIncluded); + mergedMocs.insert(mocsNotIncluded.begin(), mocsNotIncluded.end()); if (this->NameCollisionTest(mergedMocs, collisions)) { - std::ostringstream err; - err << "AutoMoc: Error: " + std::ostringstream ost; + ost << "AutoMoc: Error: " "The same moc file will be generated " - "from different sources." - << std::endl - << "To avoid this error either" << std::endl - << "- rename the source files or" << std::endl - << "- do not include the (moc_NAME.cpp|NAME.moc) file" << std::endl; - this->LogErrorNameCollision(err.str(), collisions); + "from different sources.\n" + "To avoid this error either\n" + "- rename the source files or\n" + "- do not include the (moc_NAME.cpp|NAME.moc) file"; + this->LogErrorNameCollision(ost.str(), collisions); return false; } } - - // generate moc files that are included by source files. + // Generate moc files that are included by source files. { - const std::string subDirPrefix = "include/"; + const std::string subDir = "include/"; for (std::map<std::string, std::string>::const_iterator it = - includedMocs.begin(); - it != includedMocs.end(); ++it) { - if (!this->MocGenerateFile(it->first, it->second, subDirPrefix)) { + mocsIncluded.begin(); + it != mocsIncluded.end(); ++it) { + if (!this->MocGenerateFile(it->first, it->second, subDir, mocDepends)) { if (this->RunMocFailed) { return false; } } } } - - // generate moc files that are _not_ included by source files. - bool automocCppChanged = false; + // Generate moc files that are _not_ included by source files. { - const std::string subDirPrefix; + const std::string subDir; for (std::map<std::string, std::string>::const_iterator it = - notIncludedMocs.begin(); - it != notIncludedMocs.end(); ++it) { - if (this->MocGenerateFile(it->first, it->second, subDirPrefix)) { - automocCppChanged = true; + mocsNotIncluded.begin(); + it != mocsNotIncluded.end(); ++it) { + if (this->MocGenerateFile(it->first, it->second, subDir, mocDepends)) { + mocCompFileGenerated = true; } else { if (this->RunMocFailed) { return false; @@ -1130,138 +1175,158 @@ bool cmQtAutoGenerators::MocGenerateAll( // Compose moc_compilation.cpp content std::string automocSource; { - std::ostringstream outStream; - outStream << "/* This file is autogenerated, do not edit*/\n"; - if (notIncludedMocs.empty()) { + std::ostringstream ost; + ost << "/* This file is autogenerated, do not edit*/\n"; + if (mocsNotIncluded.empty()) { // Dummy content - outStream << "enum some_compilers { need_more_than_nothing };\n"; + ost << "enum some_compilers { need_more_than_nothing };\n"; } else { // Valid content for (std::map<std::string, std::string>::const_iterator it = - notIncludedMocs.begin(); - it != notIncludedMocs.end(); ++it) { - outStream << "#include \"" << it->second << "\"\n"; - } - } - outStream.flush(); - automocSource = outStream.str(); - } - - // Check if we even need to update moc_compilation.cpp - if (!automocCppChanged) { - // compare contents of the moc_compilation.cpp file - const std::string oldContents = ReadAll(this->OutMocCppFilenameAbs); - if (oldContents == automocSource) { - // nothing changed: don't touch the moc_compilation.cpp file - if (this->Verbose) { - std::ostringstream err; - err << "AutoMoc: " << this->OutMocCppFilenameRel << " still up to date" - << std::endl; - this->LogInfo(err.str()); + mocsNotIncluded.begin(); + it != mocsNotIncluded.end(); ++it) { + ost << "#include \"" << it->second << "\"\n"; } - return true; } + automocSource = ost.str(); } - // Actually write moc_compilation.cpp + // Check if the content of moc_compilation.cpp changed { - std::string msg = "Generating MOC compilation "; - msg += this->OutMocCppFilenameRel; - this->LogBold(msg); + const std::string oldContents = ReadAll(this->MocCppFilenameAbs); + mocCompChanged = (oldContents != automocSource); } - // Make sure the parent directory exists - bool success = this->MakeParentDirectory(this->OutMocCppFilenameAbs); - if (success) { - cmsys::ofstream outfile; - outfile.open(this->OutMocCppFilenameAbs.c_str(), std::ios::trunc); - if (!outfile) { - success = false; - std::ostringstream err; - err << "AutoMoc: error opening " << this->OutMocCppFilenameAbs << "\n"; - this->LogError(err.str()); - } else { - outfile << automocSource; - // Check for write errors - if (!outfile.good()) { + + bool success = true; + if (mocCompChanged) { + // Actually write moc_compilation.cpp + this->LogBold("Generating MOC compilation " + this->MocCppFilenameRel); + + // Make sure the parent directory exists + success = this->MakeParentDirectory(this->MocCppFilenameAbs); + if (success) { + cmsys::ofstream outfile; + outfile.open(this->MocCppFilenameAbs.c_str(), std::ios::trunc); + if (!outfile) { success = false; - std::ostringstream err; - err << "AutoMoc: error writing " << this->OutMocCppFilenameAbs << "\n"; - this->LogError(err.str()); + this->LogError("AutoMoc: Error opening " + this->MocCppFilenameAbs); + } else { + outfile << automocSource; + // Check for write errors + if (!outfile.good()) { + success = false; + this->LogError("AutoMoc: Error writing " + this->MocCppFilenameAbs); + } } } + } else if (mocCompFileGenerated) { + // Only touch moc_compilation.cpp + if (this->Verbose) { + this->LogInfo("Touching MOC compilation " + this->MocCppFilenameRel); + } + cmSystemTools::Touch(this->MocCppFilenameAbs, false); } + return success; } /** * @return True if a moc file was created. False may indicate an error. */ -bool cmQtAutoGenerators::MocGenerateFile(const std::string& sourceFile, - const std::string& mocFileName, - const std::string& subDirPrefix) +bool cmQtAutoGenerators::MocGenerateFile( + const std::string& sourceFile, const std::string& mocFileName, + const std::string& subDirPrefix, + const std::map<std::string, std::set<std::string> >& mocDepends) { + bool mocGenerated = false; + bool generateMoc = this->GenerateAllMoc; + const std::string mocFileRel = this->AutogenBuildSubDir + subDirPrefix + mocFileName; const std::string mocFileAbs = this->CurrentBinaryDir + mocFileRel; - bool generateMoc = this->GenerateMocAll; - // Test if the source file is newer that the build file if (!generateMoc) { + // Test if the source file is newer that the build file generateMoc = FileAbsentOrOlder(mocFileAbs, sourceFile); + if (!generateMoc) { + // Test if a dependency file changed + std::map<std::string, std::set<std::string> >::const_iterator dit = + mocDepends.find(sourceFile); + if (dit != mocDepends.end()) { + for (std::set<std::string>::const_iterator fit = dit->second.begin(); + fit != dit->second.end(); ++fit) { + if (FileAbsentOrOlder(mocFileAbs, *fit)) { + generateMoc = true; + break; + } + } + } + } } if (generateMoc) { // Log this->LogBold("Generating MOC source " + mocFileRel); // Make sure the parent directory exists - if (!this->MakeParentDirectory(mocFileAbs)) { - this->RunMocFailed = true; - return false; - } - - std::vector<std::string> command; - command.push_back(this->MocExecutable); - command.insert(command.end(), this->MocIncludes.begin(), - this->MocIncludes.end()); - command.insert(command.end(), this->MocDefinitions.begin(), - this->MocDefinitions.end()); - command.insert(command.end(), this->MocOptions.begin(), - this->MocOptions.end()); + if (this->MakeParentDirectory(mocFileAbs)) { + // Compose moc command + std::vector<std::string> cmd; + cmd.push_back(this->MocExecutable); + cmd.insert(cmd.end(), this->MocIncludes.begin(), + this->MocIncludes.end()); + // Add definitions + for (std::vector<std::string>::const_iterator it = + this->MocDefinitions.begin(); + it != this->MocDefinitions.end(); ++it) { + cmd.push_back("-D" + (*it)); + } + cmd.insert(cmd.end(), this->MocOptions.begin(), this->MocOptions.end()); #ifdef _WIN32 - command.push_back("-DWIN32"); + cmd.push_back("-DWIN32"); #endif - command.push_back("-o"); - command.push_back(mocFileAbs); - command.push_back(sourceFile); + cmd.push_back("-o"); + cmd.push_back(mocFileAbs); + cmd.push_back(sourceFile); - if (this->Verbose) { - this->LogCommand(command); - } + // Log moc command + if (this->Verbose) { + this->LogCommand(cmd); + } - std::string output; - int retVal = 0; - bool result = - cmSystemTools::RunSingleCommand(command, &output, &output, &retVal); - if (!result || retVal) { - { - std::ostringstream err; - err << "AutoMoc: Error: moc process for " << mocFileRel << " failed:\n" - << output << std::endl; - this->LogError(err.str()); + // Execute moc command + bool res = false; + int retVal = 0; + std::string output; + res = cmSystemTools::RunSingleCommand(cmd, &output, &output, &retVal); + + if (!res || (retVal != 0)) { + // Command failed + { + std::ostringstream ost; + ost << "AutoMoc: Error: moc process failed for\n"; + ost << Quoted(mocFileRel) << "\n"; + ost << "AutoMoc: Command:\n" << cmJoin(cmd, " ") << "\n"; + ost << "AutoMoc: Command output:\n" << output << "\n"; + this->LogError(ost.str()); + } + cmSystemTools::RemoveFile(mocFileAbs); + this->RunMocFailed = true; + } else { + // Success + mocGenerated = true; } - cmSystemTools::RemoveFile(mocFileAbs); + } else { + // Parent directory creation failed this->RunMocFailed = true; - return false; } - return true; } - return false; + return mocGenerated; } bool cmQtAutoGenerators::UicGenerateAll( - const std::map<std::string, std::vector<std::string> >& includedUis) + const std::map<std::string, std::vector<std::string> >& uisIncluded) { - if (this->UicExecutable.empty()) { + if (!this->UicEnabled()) { return true; } @@ -1269,8 +1334,8 @@ bool cmQtAutoGenerators::UicGenerateAll( std::map<std::string, std::map<std::string, std::string> > uiGenMap; std::map<std::string, std::string> testMap; for (std::map<std::string, std::vector<std::string> >::const_iterator it = - includedUis.begin(); - it != includedUis.end(); ++it) { + uisIncluded.begin(); + it != uisIncluded.end(); ++it) { // source file path std::string sourcePath = cmsys::SystemTools::GetFilenamePath(it->first); sourcePath += '/'; @@ -1291,12 +1356,11 @@ bool cmQtAutoGenerators::UicGenerateAll( { std::multimap<std::string, std::string> collisions; if (this->NameCollisionTest(testMap, collisions)) { - std::ostringstream err; - err << "AutoUic: Error: The same ui_NAME.h file will be generated " - "from different sources." - << std::endl - << "To avoid this error rename the source files." << std::endl; - this->LogErrorNameCollision(err.str(), collisions); + std::ostringstream ost; + ost << "AutoUic: Error: The same ui_NAME.h file will be generated " + "from different sources.\n" + "To avoid this error rename the source files.\n"; + this->LogErrorNameCollision(ost.str(), collisions); return false; } } @@ -1328,13 +1392,15 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, const std::string& uiInputFile, const std::string& uiOutputFile) { + bool uicGenerated = false; + bool generateUic = this->GenerateAllUic; + const std::string uicFileRel = this->AutogenBuildSubDir + "include/" + uiOutputFile; const std::string uicFileAbs = this->CurrentBinaryDir + uicFileRel; - bool generateUic = this->GenerateUicAll; - // Test if the source file is newer that the build file if (!generateUic) { + // Test if the source file is newer that the build file generateUic = FileAbsentOrOlder(uicFileAbs, uiInputFile); } if (generateUic) { @@ -1342,55 +1408,64 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, this->LogBold("Generating UIC header " + uicFileRel); // Make sure the parent directory exists - if (!this->MakeParentDirectory(uicFileAbs)) { - this->RunUicFailed = true; - return false; - } - - std::vector<std::string> command; - command.push_back(this->UicExecutable); - - std::vector<std::string> opts = this->UicTargetOptions; - std::map<std::string, std::string>::const_iterator optionIt = - this->UicOptions.find(uiInputFile); - if (optionIt != this->UicOptions.end()) { - std::vector<std::string> fileOpts; - cmSystemTools::ExpandListArgument(optionIt->second, fileOpts); - UicMergeOptions(opts, fileOpts, this->QtMajorVersion == "5"); - } - command.insert(command.end(), opts.begin(), opts.end()); + if (this->MakeParentDirectory(uicFileAbs)) { + // Compose uic command + std::vector<std::string> cmd; + cmd.push_back(this->UicExecutable); + { + std::vector<std::string> opts = this->UicTargetOptions; + std::map<std::string, std::string>::const_iterator optionIt = + this->UicOptions.find(uiInputFile); + if (optionIt != this->UicOptions.end()) { + std::vector<std::string> fileOpts; + cmSystemTools::ExpandListArgument(optionIt->second, fileOpts); + UicMergeOptions(opts, fileOpts, (this->QtMajorVersion == "5")); + } + cmd.insert(cmd.end(), opts.begin(), opts.end()); + } + cmd.push_back("-o"); + cmd.push_back(uicFileAbs); + cmd.push_back(uiInputFile); - command.push_back("-o"); - command.push_back(uicFileAbs); - command.push_back(uiInputFile); + // Log command + if (this->Verbose) { + this->LogCommand(cmd); + } - if (this->Verbose) { - this->LogCommand(command); - } - std::string output; - int retVal = 0; - bool result = - cmSystemTools::RunSingleCommand(command, &output, &output, &retVal); - if (!result || retVal) { - { - std::ostringstream err; - err << "AutoUic: Error: uic process for " << uicFileRel - << " needed by\n \"" << realName << "\"\nfailed:\n" - << output << std::endl; - this->LogError(err.str()); + // Execute command + bool res = false; + int retVal = 0; + std::string output; + res = cmSystemTools::RunSingleCommand(cmd, &output, &output, &retVal); + + if (!res || (retVal != 0)) { + // Command failed + { + std::ostringstream ost; + ost << "AutoUic: Error: uic process failed for\n"; + ost << Quoted(uicFileRel) << " needed by\n"; + ost << Quoted(realName) << "\n"; + ost << "AutoUic: Command:\n" << cmJoin(cmd, " ") << "\n"; + ost << "AutoUic: Command output:\n" << output << "\n"; + this->LogError(ost.str()); + } + cmSystemTools::RemoveFile(uicFileAbs); + this->RunUicFailed = true; + } else { + // Success + uicGenerated = true; } - cmSystemTools::RemoveFile(uicFileAbs); + } else { + // Parent directory creation failed this->RunUicFailed = true; - return false; } - return true; } - return false; + return uicGenerated; } -bool cmQtAutoGenerators::QrcGenerateAll() +bool cmQtAutoGenerators::RccGenerateAll() { - if (this->RccExecutable.empty()) { + if (!this->RccEnabled()) { return true; } @@ -1400,9 +1475,8 @@ bool cmQtAutoGenerators::QrcGenerateAll() si != this->RccSources.end(); ++si) { const std::string ext = cmsys::SystemTools::GetFilenameLastExtension(*si); if (ext == ".qrc") { - qrcGenMap[*si] = this->AutogenBuildSubDir + fpathCheckSum.getPart(*si) + - "/qrc_" + cmsys::SystemTools::GetFilenameWithoutLastExtension(*si) + - ".cpp"; + qrcGenMap[*si] = + this->AutogenBuildSubDir + this->ChecksumedPath(*si, "qrc_", ".cpp"); } } @@ -1410,12 +1484,11 @@ bool cmQtAutoGenerators::QrcGenerateAll() { std::multimap<std::string, std::string> collisions; if (this->NameCollisionTest(qrcGenMap, collisions)) { - std::ostringstream err; - err << "AutoRcc: Error: The same qrc_NAME.cpp file" - " will be generated from different sources." - << std::endl - << "To avoid this error rename the source .qrc files." << std::endl; - this->LogErrorNameCollision(err.str(), collisions); + std::ostringstream ost; + ost << "AutoRcc: Error: The same qrc_NAME.cpp file" + " will be generated from different sources.\n" + "To avoid this error rename the source .qrc files.\n"; + this->LogErrorNameCollision(ost.str(), collisions); return false; } } @@ -1425,7 +1498,7 @@ bool cmQtAutoGenerators::QrcGenerateAll() qrcGenMap.begin(); si != qrcGenMap.end(); ++si) { bool unique = FileNameIsUnique(si->first, qrcGenMap); - if (!this->QrcGenerateFile(si->first, si->second, unique)) { + if (!this->RccGenerateFile(si->first, si->second, unique)) { if (this->RunRccFailed) { return false; } @@ -1437,146 +1510,166 @@ bool cmQtAutoGenerators::QrcGenerateAll() /** * @return True if a rcc file was created. False may indicate an error. */ -bool cmQtAutoGenerators::QrcGenerateFile(const std::string& qrcInputFile, - const std::string& qrcOutputFile, +bool cmQtAutoGenerators::RccGenerateFile(const std::string& rccInputFile, + const std::string& rccOutputFile, bool unique_n) { - std::string symbolName = - cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcInputFile); - if (!unique_n) { - symbolName += "_"; - symbolName += fpathCheckSum.getPart(qrcInputFile); - } - // Replace '-' with '_'. The former is valid for - // file names but not for symbol names. - std::replace(symbolName.begin(), symbolName.end(), '-', '_'); - - const std::string qrcBuildFile = this->CurrentBinaryDir + qrcOutputFile; - - bool generateQrc = this->GenerateRccAll; - // Test if the resources list file is newer than build file - if (!generateQrc) { - generateQrc = FileAbsentOrOlder(qrcBuildFile, qrcInputFile); - } - // Test if any resource file is newer than the build file - if (!generateQrc) { - const std::vector<std::string>& files = this->RccInputs[qrcInputFile]; - for (std::vector<std::string>::const_iterator it = files.begin(); - it != files.end(); ++it) { - if (FileAbsentOrOlder(qrcBuildFile, *it)) { - generateQrc = true; - break; + bool rccGenerated = false; + bool generateRcc = this->GenerateAllRcc; + + const std::string rccBuildFile = this->CurrentBinaryDir + rccOutputFile; + + if (!generateRcc) { + // Test if the resources list file is newer than build file + generateRcc = FileAbsentOrOlder(rccBuildFile, rccInputFile); + if (!generateRcc) { + // Test if any resource file is newer than the build file + const std::vector<std::string>& files = this->RccInputs[rccInputFile]; + for (std::vector<std::string>::const_iterator it = files.begin(); + it != files.end(); ++it) { + if (FileAbsentOrOlder(rccBuildFile, *it)) { + generateRcc = true; + break; + } } } } - if (generateQrc) { - { - std::string msg = "Generating RCC source "; - msg += qrcOutputFile; - this->LogBold(msg); - } + if (generateRcc) { + // Log + this->LogBold("Generating RCC source " + rccOutputFile); // Make sure the parent directory exists - if (!this->MakeParentDirectory(qrcOutputFile)) { - this->RunRccFailed = true; - return false; - } - - std::vector<std::string> command; - command.push_back(this->RccExecutable); - { - std::map<std::string, std::string>::const_iterator optionIt = - this->RccOptions.find(qrcInputFile); - if (optionIt != this->RccOptions.end()) { - cmSystemTools::ExpandListArgument(optionIt->second, command); + if (this->MakeParentDirectory(rccBuildFile)) { + // Compose symbol name + std::string symbolName = + cmsys::SystemTools::GetFilenameWithoutLastExtension(rccInputFile); + if (!unique_n) { + symbolName += "_"; + symbolName += fpathCheckSum.getPart(rccInputFile); } - } - command.push_back("-name"); - command.push_back(symbolName); - command.push_back("-o"); - command.push_back(qrcBuildFile); - command.push_back(qrcInputFile); + // Replace '-' with '_'. The former is valid for + // file names but not for symbol names. + std::replace(symbolName.begin(), symbolName.end(), '-', '_'); - if (this->Verbose) { - this->LogCommand(command); - } - std::string output; - int retVal = 0; - bool result = - cmSystemTools::RunSingleCommand(command, &output, &output, &retVal); - if (!result || retVal) { + // Compose rcc command + std::vector<std::string> cmd; + cmd.push_back(this->RccExecutable); { - std::ostringstream err; - err << "AutoRcc: Error: rcc process for " << qrcOutputFile - << " failed:\n" - << output << std::endl; - this->LogError(err.str()); + std::map<std::string, std::string>::const_iterator optionIt = + this->RccOptions.find(rccInputFile); + if (optionIt != this->RccOptions.end()) { + cmSystemTools::ExpandListArgument(optionIt->second, cmd); + } + } + cmd.push_back("-name"); + cmd.push_back(symbolName); + cmd.push_back("-o"); + cmd.push_back(rccBuildFile); + cmd.push_back(rccInputFile); + + // Log command + if (this->Verbose) { + this->LogCommand(cmd); + } + + // Execute command + bool res = false; + int retVal = 0; + std::string output; + res = cmSystemTools::RunSingleCommand(cmd, &output, &output, &retVal); + if (!res || (retVal != 0)) { + // Command failed + { + std::ostringstream ost; + ost << "AutoRcc: Error: rcc process failed for\n"; + ost << Quoted(rccOutputFile) << "\n"; + ost << "AutoRcc: Command:\n" << cmJoin(cmd, " ") << "\n"; + ost << "AutoRcc: Command output:\n" << output << "\n"; + this->LogError(ost.str()); + } + cmSystemTools::RemoveFile(rccBuildFile); + this->RunRccFailed = true; + } else { + // Success + rccGenerated = true; } - cmSystemTools::RemoveFile(qrcBuildFile); + } else { + // Parent directory creation failed this->RunRccFailed = true; - return false; } - return true; } - return false; + return rccGenerated; } void cmQtAutoGenerators::LogErrorNameCollision( const std::string& message, - const std::multimap<std::string, std::string>& collisions) + const std::multimap<std::string, std::string>& collisions) const { typedef std::multimap<std::string, std::string>::const_iterator Iter; - std::ostringstream err; + std::ostringstream ost; // Add message - err << message; + if (!message.empty()) { + ost << message; + if (message[message.size() - 1] != '\n') { + ost << '\n'; + } + } // Append collision list for (Iter it = collisions.begin(); it != collisions.end(); ++it) { - err << it->first << " : " << it->second << std::endl; + ost << it->first << " : " << it->second << '\n'; } - this->LogError(err.str()); + this->LogError(ost.str()); } -void cmQtAutoGenerators::LogBold(const std::string& message) +void cmQtAutoGenerators::LogBold(const std::string& message) const { cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | cmsysTerminal_Color_ForegroundBold, message.c_str(), true, this->ColorOutput); } -void cmQtAutoGenerators::LogInfo(const std::string& message) +void cmQtAutoGenerators::LogInfo(const std::string& message) const { - std::cout << message.c_str(); + std::string msg(message); + if (!msg.empty()) { + if (msg[msg.size() - 1] != '\n') { + msg.push_back('\n'); + } + cmSystemTools::Stdout(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerators::LogWarning(const std::string& message) +void cmQtAutoGenerators::LogWarning(const std::string& message) const { - std::ostringstream ostr; - ostr << message << "\n"; - std::cout << message.c_str(); + std::string msg(message); + if (!msg.empty()) { + if (msg[msg.size() - 1] != '\n') { + msg.push_back('\n'); + } + // Append empty line + msg.push_back('\n'); + cmSystemTools::Stdout(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerators::LogError(const std::string& message) +void cmQtAutoGenerators::LogError(const std::string& message) const { - std::ostringstream ostr; - ostr << message << "\n\n"; - std::cerr << ostr.str(); + std::string msg(message); + if (!msg.empty()) { + if (msg[msg.size() - 1] != '\n') { + msg.push_back('\n'); + } + // Append empty line + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command) +void cmQtAutoGenerators::LogCommand( + const std::vector<std::string>& command) const { - std::ostringstream sbuf; - for (std::vector<std::string>::const_iterator cmdIt = command.begin(); - cmdIt != command.end(); ++cmdIt) { - if (cmdIt != command.begin()) { - sbuf << " "; - } - sbuf << *cmdIt; - } - if (!sbuf.str().empty()) { - sbuf << std::endl; - this->LogInfo(sbuf.str()); - } + this->LogInfo(cmJoin(command, " ")); } /** @@ -1585,7 +1678,7 @@ void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command) */ bool cmQtAutoGenerators::NameCollisionTest( const std::map<std::string, std::string>& genFiles, - std::multimap<std::string, std::string>& collisions) + std::multimap<std::string, std::string>& collisions) const { typedef std::map<std::string, std::string>::const_iterator Iter; typedef std::map<std::string, std::string>::value_type VType; @@ -1610,19 +1703,131 @@ bool cmQtAutoGenerators::NameCollisionTest( } /** + * @brief Generates a file path based on the checksum of the source file path + * @return The path + */ +std::string cmQtAutoGenerators::ChecksumedPath(const std::string& sourceFile, + const char* basePrefix, + const char* baseSuffix) const +{ + std::string res = fpathCheckSum.getPart(sourceFile); + res += "/"; + res += basePrefix; + res += cmsys::SystemTools::GetFilenameWithoutLastExtension(sourceFile); + res += baseSuffix; + return res; +} + +/** + * @brief Tries to find the header file to the given file base path by + * appending different header extensions + * @return True on success + */ +bool cmQtAutoGenerators::FindHeader(std::string& header, + const std::string& testBasePath) const +{ + for (std::vector<std::string>::const_iterator ext = + this->HeaderExtensions.begin(); + ext != this->HeaderExtensions.end(); ++ext) { + std::string testFilePath(testBasePath); + testFilePath += '.'; + testFilePath += (*ext); + if (cmsys::SystemTools::FileExists(testFilePath.c_str())) { + header = testFilePath; + return true; + } + } + return false; +} + +bool cmQtAutoGenerators::FindHeaderGlobal( + std::string& header, const std::string& testBasePath) const +{ + for (std::vector<std::string>::const_iterator iit = + this->MocIncludePaths.begin(); + iit != this->MocIncludePaths.end(); ++iit) { + const std::string fullPath = ((*iit) + '/' + testBasePath); + if (FindHeader(header, fullPath)) { + return true; + } + } + return false; +} + +std::string cmQtAutoGenerators::FindMocHeader(const std::string& basePath, + const std::string& baseName, + const std::string& subDir) const +{ + std::string header; + do { + if (!subDir.empty()) { + if (this->FindHeader(header, basePath + subDir + baseName)) { + break; + } + } + if (this->FindHeader(header, basePath + baseName)) { + break; + } + // Try include directories + if (this->FindHeaderGlobal(header, subDir + baseName)) { + break; + } + } while (false); + // Sanitize + if (!header.empty()) { + header = cmsys::SystemTools::GetRealPath(header); + } + return header; +} + +std::string cmQtAutoGenerators::FindIncludedFile( + const std::string& sourceFile, const std::string& includeString) const +{ + // Search in vicinity of the source + { + std::string testPath = cmSystemTools::GetFilenamePath(sourceFile); + testPath += '/'; + testPath += includeString; + if (cmsys::SystemTools::FileExists(testPath.c_str())) { + return cmsys::SystemTools::GetRealPath(testPath); + } + } + // Search globally + return FindInIncludeDirectories(includeString); +} + +/** + * @brief Tries to find a file in the include directories + * @return True on success + */ +std::string cmQtAutoGenerators::FindInIncludeDirectories( + const std::string& includeString) const +{ + std::string res; + for (std::vector<std::string>::const_iterator iit = + this->MocIncludePaths.begin(); + iit != this->MocIncludePaths.end(); ++iit) { + const std::string fullPath = ((*iit) + '/' + includeString); + if (cmsys::SystemTools::FileExists(fullPath.c_str())) { + res = cmsys::SystemTools::GetRealPath(fullPath); + break; + } + } + return res; +} + +/** * @brief Generates the parent directory of the given file on demand * @return True on success */ -bool cmQtAutoGenerators::MakeParentDirectory(const std::string& filename) +bool cmQtAutoGenerators::MakeParentDirectory(const std::string& filename) const { bool success = true; const std::string dirName = cmSystemTools::GetFilenamePath(filename); if (!dirName.empty()) { success = cmsys::SystemTools::MakeDirectory(dirName); if (!success) { - std::ostringstream err; - err << "AutoGen: Directory creation failed: " << dirName << std::endl; - this->LogError(err.str()); + this->LogError("AutoGen: Error: Directory creation failed: " + dirName); } } return success; diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index 7891eb9..e4b7f60 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -7,10 +7,10 @@ #include <cmFilePathChecksum.h> #include <cmsys/RegularExpression.hxx> -#include <list> #include <map> #include <set> #include <string> +#include <utility> #include <vector> class cmMakefile; @@ -22,64 +22,93 @@ public: bool Run(const std::string& targetDirectory, const std::string& config); private: + // - Types + + /// @brief Used to extract additional dependencies from content text + struct MocDependFilter + { + std::string key; + cmsys::RegularExpression regExp; + }; + typedef std::pair<std::string, cmsys::RegularExpression> MacroFilter; + // - Configuration + bool MocDependFilterPush(const std::string& key, const std::string& regExp); bool ReadAutogenInfoFile(cmMakefile* makefile, const std::string& targetDirectory, const std::string& config); - std::string MocSettingsStringCompose(); - std::string UicSettingsStringCompose(); - std::string RccSettingsStringCompose(); - void OldSettingsReadFile(cmMakefile* makefile, - const std::string& targetDirectory); - bool OldSettingsWriteFile(const std::string& targetDirectory); + bool MocEnabled() const { return !this->MocExecutable.empty(); } + bool UicEnabled() const { return !this->UicExecutable.empty(); } + bool RccEnabled() const { return !this->RccExecutable.empty(); } + + // - Settings file + void SettingsFileRead(cmMakefile* makefile, + const std::string& targetDirectory); + bool SettingsFileWrite(const std::string& targetDirectory); + + bool GenerateAllAny() const + { + return (this->GenerateAllMoc || this->GenerateAllRcc || + this->GenerateAllUic); + } // - Init and run - void Init(); - bool RunAutogen(cmMakefile* makefile); + void Init(cmMakefile* makefile); + bool RunAutogen(); // - Content analysis - bool MocRequired(const std::string& text, std::string& macroName); - bool MocSkipTest(const std::string& absFilename); - bool UicSkipTest(const std::string& absFilename); + bool MocRequired(const std::string& contentText, + std::string* macroName = CM_NULLPTR); + void MocFindDepends( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::set<std::string> >& mocDepends); + + bool MocSkip(const std::string& absFilename) const; + bool UicSkip(const std::string& absFilename) const; bool ParseSourceFile( const std::string& absFilename, - const std::vector<std::string>& headerExtensions, - std::map<std::string, std::string>& includedMocs, + std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, std::map<std::string, std::vector<std::string> >& includedUis, bool relaxed); - void SearchHeadersForSourceFile( - const std::string& absFilename, - const std::vector<std::string>& headerExtensions, - std::set<std::string>& absHeadersMoc, - std::set<std::string>& absHeadersUic); + void SearchHeadersForSourceFile(const std::string& absFilename, + std::set<std::string>& mocHeaderFiles, + std::set<std::string>& uicHeaderFiles) const; void ParseHeaders( - const std::set<std::string>& absHeadersMoc, - const std::set<std::string>& absHeadersUic, - const std::map<std::string, std::string>& includedMocs, - std::map<std::string, std::string>& notIncludedMocs, + const std::set<std::string>& mocHeaderFiles, + const std::set<std::string>& uicHeaderFiles, + const std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::string>& mocsNotIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, std::map<std::string, std::vector<std::string> >& includedUis); - void ParseContentForUic( - const std::string& fileName, const std::string& contentsString, + void UicParseContent( + const std::string& fileName, const std::string& contentText, std::map<std::string, std::vector<std::string> >& includedUis); - bool ParseContentForMoc(const std::string& absFilename, - const std::string& contentsString, - const std::vector<std::string>& headerExtensions, - std::map<std::string, std::string>& includedMocs, - bool relaxed); + bool MocParseSourceContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, bool relaxed); + + void MocParseHeaderContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::string>& mocsNotIncluded, + std::map<std::string, std::set<std::string> >& mocDepends); // - Moc file generation bool MocGenerateAll( - const std::map<std::string, std::string>& includedMocs, - const std::map<std::string, std::string>& notIncludedMocs); - bool MocGenerateFile(const std::string& sourceFile, - const std::string& mocFileName, - const std::string& subDirPrefix); + const std::map<std::string, std::string>& mocsIncluded, + const std::map<std::string, std::string>& mocsNotIncluded, + const std::map<std::string, std::set<std::string> >& mocDepends); + bool MocGenerateFile( + const std::string& sourceFile, const std::string& mocFileName, + const std::string& subDirPrefix, + const std::map<std::string, std::set<std::string> >& mocDepends); // - Uic file generation bool UicGenerateAll( @@ -88,25 +117,39 @@ private: const std::string& uiInputFile, const std::string& uiOutputFile); - // - Qrc file generation - bool QrcGenerateAll(); - bool QrcGenerateFile(const std::string& qrcInputFile, + // - Rcc file generation + bool RccGenerateAll(); + bool RccGenerateFile(const std::string& qrcInputFile, const std::string& qrcOutputFile, bool unique_n); // - Logging void LogErrorNameCollision( const std::string& message, - const std::multimap<std::string, std::string>& collisions); - void LogBold(const std::string& message); - void LogInfo(const std::string& message); - void LogWarning(const std::string& message); - void LogError(const std::string& message); - void LogCommand(const std::vector<std::string>& command); + const std::multimap<std::string, std::string>& collisions) const; + void LogBold(const std::string& message) const; + void LogInfo(const std::string& message) const; + void LogWarning(const std::string& message) const; + void LogError(const std::string& message) const; + void LogCommand(const std::vector<std::string>& command) const; // - Utility - bool NameCollisionTest(const std::map<std::string, std::string>& genFiles, - std::multimap<std::string, std::string>& collisions); - bool MakeParentDirectory(const std::string& filename); + bool NameCollisionTest( + const std::map<std::string, std::string>& genFiles, + std::multimap<std::string, std::string>& collisions) const; + std::string ChecksumedPath(const std::string& sourceFile, + const char* basePrefix, + const char* baseSuffix) const; + bool MakeParentDirectory(const std::string& filename) const; + + bool FindHeader(std::string& header, const std::string& testBasePath) const; + bool FindHeaderGlobal(std::string& header, + const std::string& testBasePath) const; + std::string FindMocHeader(const std::string& basePath, + const std::string& baseName, + const std::string& subDir) const; + std::string FindIncludedFile(const std::string& sourceFile, + const std::string& includeString) const; + std::string FindInIncludeDirectories(const std::string& includeString) const; // - Target names std::string OriginTargetName; @@ -125,31 +168,31 @@ private: // - File lists std::vector<std::string> Sources; std::vector<std::string> Headers; + // - Settings + std::string SettingsStringMoc; + std::string SettingsStringUic; + std::string SettingsStringRcc; // - Moc - std::vector<std::string> SkipMoc; - std::string MocCompileDefinitionsStr; - std::string MocIncludesStr; - std::string MocOptionsStr; - std::string OutMocCppFilenameRel; - std::string OutMocCppFilenameAbs; - std::list<std::string> MocIncludes; - std::list<std::string> MocDefinitions; + std::string MocCppFilenameRel; + std::string MocCppFilenameAbs; + std::vector<std::string> MocSkipList; + std::vector<std::string> MocIncludePaths; + std::vector<std::string> MocIncludes; + std::vector<std::string> MocDefinitions; std::vector<std::string> MocOptions; - std::string MocSettingsString; + std::vector<MocDependFilter> MocDependFilters; // - Uic - std::vector<std::string> SkipUic; + std::vector<std::string> UicSkipList; std::vector<std::string> UicTargetOptions; std::map<std::string, std::string> UicOptions; - std::string UicSettingsString; // - Rcc std::vector<std::string> RccSources; std::map<std::string, std::string> RccOptions; std::map<std::string, std::vector<std::string> > RccInputs; - std::string RccSettingsString; // - Utility cmFilePathChecksum fpathCheckSum; - cmsys::RegularExpression RegExpQObject; - cmsys::RegularExpression RegExpQGadget; + std::vector<std::string> HeaderExtensions; + MacroFilter MacroFilters[2]; cmsys::RegularExpression RegExpMocInclude; cmsys::RegularExpression RegExpUicInclude; // - Flags @@ -159,9 +202,9 @@ private: bool RunMocFailed; bool RunUicFailed; bool RunRccFailed; - bool GenerateMocAll; - bool GenerateUicAll; - bool GenerateRccAll; + bool GenerateAllMoc; + bool GenerateAllUic; + bool GenerateAllRcc; bool MocRelaxedMode; }; diff --git a/Source/cmServerConnection.h b/Source/cmServerConnection.h index 3efe28d..9357831 100644 --- a/Source/cmServerConnection.h +++ b/Source/cmServerConnection.h @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include <cmConfigure.h> // IWYU pragma: keep + #include <string> #include <vector> diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index ad3d604..0f3d91b 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -245,6 +245,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("AUTOMOC", CM_NULLPTR); this->SetPropertyDefault("AUTOUIC", CM_NULLPTR); this->SetPropertyDefault("AUTORCC", CM_NULLPTR); + this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", CM_NULLPTR); this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", CM_NULLPTR); this->SetPropertyDefault("AUTOUIC_OPTIONS", CM_NULLPTR); this->SetPropertyDefault("AUTORCC_OPTIONS", CM_NULLPTR); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index e3853ed..eac48ee 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -131,6 +131,7 @@ void cmVisualStudio10TargetGenerator::WritePlatformConfigTag( } stream->fill(' '); stream->width(indentLevel * 2); + (*stream) << ""; // applies indentation (*stream) << "<" << tag << " Condition=\""; (*stream) << "'$(Configuration)|$(Platform)'=='"; (*stream) << config << "|" << this->Platform; diff --git a/Tests/CompileFeatures/.gitattributes b/Tests/CompileFeatures/.gitattributes new file mode 100644 index 0000000..83da28d --- /dev/null +++ b/Tests/CompileFeatures/.gitattributes @@ -0,0 +1,2 @@ +# Do not format a source containing C++11 '>>' syntax as C++98. +cxx_right_angle_brackets.cpp -format.clang-format diff --git a/Tests/PositionIndependentTargets/.gitattributes b/Tests/PositionIndependentTargets/.gitattributes new file mode 100644 index 0000000..ed36631 --- /dev/null +++ b/Tests/PositionIndependentTargets/.gitattributes @@ -0,0 +1,2 @@ +# Do not format a source where we want a long line preserved. +pic_test.h -format.clang-format diff --git a/Tests/QtAutogen/CMakeLists.txt b/Tests/QtAutogen/CMakeLists.txt index 4b90ad8..257c187 100644 --- a/Tests/QtAutogen/CMakeLists.txt +++ b/Tests/QtAutogen/CMakeLists.txt @@ -46,26 +46,26 @@ endif() get_property(QT_COMPILE_FEATURES TARGET ${QT_QTCORE_TARGET} PROPERTY INTERFACE_COMPILE_FEATURES) -# -- Test: AUTORCC +# -- Test # RCC only add_executable(rccOnly rccOnly.cpp rccOnlyRes.qrc) set_property(TARGET rccOnly PROPERTY AUTORCC ON) target_link_libraries(rccOnly ${QT_QTCORE_TARGET}) -# -- Test: AUTORCC +# -- Test # RCC empty add_executable(rccEmpty rccEmpty.cpp rccEmptyRes.qrc) set_property(TARGET rccEmpty PROPERTY AUTORCC ON) target_link_libraries(rccEmpty ${QT_QTCORE_TARGET}) -# -- Test: AUTOUIC +# -- Test # UIC only qtx_wrap_cpp(uicOnlyMoc uicOnlySource/uiconly.h) add_executable(uicOnly uicOnlySource/uiconly.cpp ${uicOnlyMoc}) set_property(TARGET uicOnly PROPERTY AUTOUIC ON) target_link_libraries(uicOnly ${QT_LIBRARIES}) -# -- Test: AUTOMOC, AUTORCC +# -- Test # Add not_generated_file.qrc to the source list to get the file-level # dependency, but don't generate a c++ file from it. Disable the AUTORCC # feature for this target. This tests that qrc files in the sources don't @@ -80,13 +80,14 @@ set_target_properties(no_link_language PROPERTIES AUTOMOC TRUE) target_compile_features(no_link_language PRIVATE ${QT_COMPILE_FEATURES}) target_compile_features(empty PRIVATE ${QT_COMPILE_FEATURES}) -# -- Test: AUTORCC +# -- Test # When a file listed in a .qrc file changes the target must be rebuilt try_compile(RCC_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/autorcc_depends" "${CMAKE_CURRENT_SOURCE_DIR}/autorcc_depends" autorcc_depends - CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" + "-DQT_TEST_VERSION=${QT_TEST_VERSION}" "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" OUTPUT_VARIABLE output ) @@ -115,13 +116,14 @@ if (NOT file1_step1 GREATER file1_before) message(SEND_ERROR "file1 (${qrc_file1}) should have changed in the first step!") endif() -# -- Test: AUTOMOC +# -- Test # Ensure a repeated build succeeds when a header containing a QObject changes try_compile(MOC_RERUN "${CMAKE_CURRENT_BINARY_DIR}/automoc_rerun" "${CMAKE_CURRENT_SOURCE_DIR}/automoc_rerun" automoc_rerun - CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" + "-DQT_TEST_VERSION=${QT_TEST_VERSION}" "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" OUTPUT_VARIABLE output ) @@ -139,7 +141,7 @@ if (automoc_rerun_result) message(SEND_ERROR "Second build of automoc_rerun failed.") endif() -# -- Test: AUTOMOC, SKIP_AUTOMOC +# -- Test # Test for SKIP_AUTOMOC and SKIP_AUTOGEN on an AUTOMOC enabled target qtx_wrap_cpp(skipMocWrapMoc skipSource/qItemA.hpp @@ -161,7 +163,7 @@ set_property(TARGET skipMocB PROPERTY AUTOMOC ON) set_property(TARGET skipMocB PROPERTY AUTOUIC ON) target_link_libraries(skipMocB ${QT_LIBRARIES}) -# -- Test: AUTOUIC, SKIP_AUTOUIC +# -- Test # Test for SKIP_AUTOUIC and SKIP_AUTOGEN on an AUTOUIC enabled target set(skipUicSources skipUic.cpp @@ -181,7 +183,7 @@ set_property(TARGET skipUicB PROPERTY AUTOUIC ON) set_property(TARGET skipUicB PROPERTY AUTOMOC ON) target_link_libraries(skipUicB ${QT_LIBRARIES}) -# -- Test: AUTORCC, SKIP_AUTORCC +# -- Test # Test for SKIP_AUTORCC and SKIP_AUTOGEN on an AUTORCC enabled target set(skipRccSources skipRcc.cpp @@ -202,10 +204,87 @@ set_property(TARGET skipRccB PROPERTY AUTOUIC ON) set_property(TARGET skipRccB PROPERTY AUTOMOC ON) target_link_libraries(skipRccB ${QT_LIBRARIES}) -# -- Test: AUTOMOC AUTORCC +# -- Test # Source files with the same basename in different subdirectories add_subdirectory(sameName) -# -- Test: AUTOMOC AUTORCC AUTOUIC +# -- Test +# Tests various include moc patterns +add_subdirectory(mocIncludeStrict) + +# -- Test +# Tests various include moc patterns +add_subdirectory(mocIncludeRelaxed) + +# -- Test +# Tests Q_PLUGIN_METADATA json file change detection +if (NOT QT_TEST_VERSION STREQUAL 4) + try_compile(MOC_PLUGIN + "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin" + "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin" + mocPlugin + CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" + OUTPUT_VARIABLE output + ) + if (NOT MOC_PLUGIN) + message(SEND_ERROR "Initial build of mocPlugin failed. Output: ${output}") + endif() + + set(timeformat "%Y%j%H%M%S") + set(mocPluginBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin") + find_library(style_a_file "PluginStyleA" "${mocPluginBinDir}") + find_library(style_b_file "PluginStyleB" "${mocPluginBinDir}") + find_library(style_c_file "PluginStyleC" "${mocPluginBinDir}") + find_library(style_d_file "PluginStyleD" "${mocPluginBinDir}") + + file(TIMESTAMP "${style_a_file}" style_a_before "${timeformat}") + file(TIMESTAMP "${style_b_file}" style_b_before "${timeformat}") + file(TIMESTAMP "${style_c_file}" style_c_before "${timeformat}") + file(TIMESTAMP "${style_d_file}" style_d_before "${timeformat}") + + # Ensure that the timestamp will change and touch the json files + execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) + execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/StyleC.json") + execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/sub/StyleD.json") + execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPluginBinDir}") + + file(TIMESTAMP "${style_a_file}" style_a_after "${timeformat}") + file(TIMESTAMP "${style_b_file}" style_b_after "${timeformat}") + file(TIMESTAMP "${style_c_file}" style_c_after "${timeformat}") + file(TIMESTAMP "${style_d_file}" style_d_after "${timeformat}") + + if (style_a_after GREATER style_a_before) + message(SEND_ERROR "file (${style_a_file}) should not have changed!") + endif() + if (style_b_after GREATER style_b_before) + message(SEND_ERROR "file (${style_b_file}) should not have changed!") + endif() + if (NOT style_c_after GREATER style_c_before) + message(SEND_ERROR "file (${style_c_file}) should have changed!") + endif() + if (NOT style_d_after GREATER style_d_before) + message(SEND_ERROR "file (${style_d_file}) should have changed!") + endif() + + # Test custom macro + file(TIMESTAMP "${style_c_file}" style_c_before "${timeformat}") + file(TIMESTAMP "${style_d_file}" style_d_before "${timeformat}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) + execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/StyleC_Custom.json") + execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/sub/StyleD_Custom.json") + execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPluginBinDir}") + file(TIMESTAMP "${style_c_file}" style_c_after "${timeformat}") + file(TIMESTAMP "${style_d_file}" style_d_after "${timeformat}") + if (NOT style_c_after GREATER style_c_before) + message(SEND_ERROR "file (${style_c_file}) should have changed!") + endif() + if (NOT style_d_after GREATER style_d_before) + message(SEND_ERROR "file (${style_d_file}) should have changed!") + endif() + +endif() + +# -- Test # Complex test case add_subdirectory(complex) diff --git a/Tests/QtAutogen/mocInclude/ObjA.cpp b/Tests/QtAutogen/mocInclude/ObjA.cpp new file mode 100644 index 0000000..1b0311d --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjA.cpp @@ -0,0 +1,24 @@ +#include "ObjA.hpp" + +class SubObjA : public QObject +{ + Q_OBJECT + +public: + SubObjA() {} + ~SubObjA() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjA::aSlot() +{ +} + +void ObjA::go() +{ + SubObjA subObj; +} + +#include "ObjA.moc" diff --git a/Tests/QtAutogen/mocInclude/ObjA.hpp b/Tests/QtAutogen/mocInclude/ObjA.hpp new file mode 100644 index 0000000..281e90d --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjA.hpp @@ -0,0 +1,13 @@ +#ifndef OBJA_HPP +#define OBJA_HPP + +#include <QObject> + +class ObjA : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; + +#endif diff --git a/Tests/QtAutogen/mocInclude/ObjB.cpp b/Tests/QtAutogen/mocInclude/ObjB.cpp new file mode 100644 index 0000000..5ff315d --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjB.cpp @@ -0,0 +1,25 @@ +#include "ObjB.hpp" + +class SubObjB : public QObject +{ + Q_OBJECT + +public: + SubObjB() {} + ~SubObjB() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjB::aSlot() +{ +} + +void ObjB::go() +{ + SubObjB subObj; +} + +#include "ObjB.moc" +#include "moc_ObjB.cpp" diff --git a/Tests/QtAutogen/mocInclude/ObjB.hpp b/Tests/QtAutogen/mocInclude/ObjB.hpp new file mode 100644 index 0000000..94f3d49 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjB.hpp @@ -0,0 +1,13 @@ +#ifndef OBJB_HPP +#define OBJB_HPP + +#include <QObject> + +class ObjB : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; + +#endif diff --git a/Tests/QtAutogen/mocInclude/ObjC.cpp b/Tests/QtAutogen/mocInclude/ObjC.cpp new file mode 100644 index 0000000..8ca34cb --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjC.cpp @@ -0,0 +1,26 @@ +#include "ObjC.hpp" + +class SubObjC : public QObject +{ + Q_OBJECT + +public: + SubObjC() {} + ~SubObjC() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjC::aSlot() +{ +} + +void ObjC::go() +{ + SubObjC subObj; +} + +#include "ObjC.moc" +// Not the own header +#include "moc_ObjD.cpp" diff --git a/Tests/QtAutogen/mocInclude/ObjC.hpp b/Tests/QtAutogen/mocInclude/ObjC.hpp new file mode 100644 index 0000000..a8e98eb --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjC.hpp @@ -0,0 +1,13 @@ +#ifndef OBJC_HPP +#define OBJC_HPP + +#include <QObject> + +class ObjC : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; + +#endif diff --git a/Tests/QtAutogen/mocInclude/ObjD.cpp b/Tests/QtAutogen/mocInclude/ObjD.cpp new file mode 100644 index 0000000..c18aec1 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjD.cpp @@ -0,0 +1,26 @@ +#include "ObjD.hpp" + +class SubObjD : public QObject +{ + Q_OBJECT + +public: + SubObjD() {} + ~SubObjD() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjD::aSlot() +{ +} + +void ObjD::go() +{ + SubObjD subObj; +} + +#include "ObjD.moc" +// Header in subdirectory +#include "subA/moc_SubObjA.cpp" diff --git a/Tests/QtAutogen/mocInclude/ObjD.hpp b/Tests/QtAutogen/mocInclude/ObjD.hpp new file mode 100644 index 0000000..b6ee098 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/ObjD.hpp @@ -0,0 +1,13 @@ +#ifndef OBJD_HPP +#define OBJD_HPP + +#include <QObject> + +class ObjD : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; + +#endif diff --git a/Tests/QtAutogen/mocInclude/subA/SubObjA.cpp b/Tests/QtAutogen/mocInclude/subA/SubObjA.cpp new file mode 100644 index 0000000..a05f6e3 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/subA/SubObjA.cpp @@ -0,0 +1,27 @@ +#include "SubObjA.hpp" + +namespace subA { + +class SubObjA : public QObject +{ + Q_OBJECT + +public: + SubObjA() {} + ~SubObjA() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjA::aSlot() +{ +} + +void ObjA::go() +{ + SubObjA subObj; +} +} + +#include "SubObjA.moc" diff --git a/Tests/QtAutogen/mocInclude/subA/SubObjA.hpp b/Tests/QtAutogen/mocInclude/subA/SubObjA.hpp new file mode 100644 index 0000000..31a18b6 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/subA/SubObjA.hpp @@ -0,0 +1,16 @@ +#ifndef SUBOBJA_HPP +#define SUBOBJA_HPP + +#include <QObject> + +namespace subA { + +class ObjA : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/mocInclude/subB/SubObjB.cpp b/Tests/QtAutogen/mocInclude/subB/SubObjB.cpp new file mode 100644 index 0000000..1e77639 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/subB/SubObjB.cpp @@ -0,0 +1,27 @@ +#include "SubObjB.hpp" + +namespace subB { + +class SubObjB : public QObject +{ + Q_OBJECT + +public: + SubObjB() {} + ~SubObjB() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjB::aSlot() +{ +} + +void ObjB::go() +{ + SubObjB subObj; +} +} + +#include "SubObjB.moc" diff --git a/Tests/QtAutogen/mocInclude/subB/SubObjB.hpp b/Tests/QtAutogen/mocInclude/subB/SubObjB.hpp new file mode 100644 index 0000000..3f29fa2 --- /dev/null +++ b/Tests/QtAutogen/mocInclude/subB/SubObjB.hpp @@ -0,0 +1,16 @@ +#ifndef SUBOBJB_HPP +#define SUBOBJB_HPP + +#include <QObject> + +namespace subB { + +class ObjB : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/mocInclude/subC/SubObjC.cpp b/Tests/QtAutogen/mocInclude/subC/SubObjC.cpp new file mode 100644 index 0000000..c2d94ef --- /dev/null +++ b/Tests/QtAutogen/mocInclude/subC/SubObjC.cpp @@ -0,0 +1,27 @@ +#include "SubObjC.hpp" + +namespace subC { + +class SubObjC : public QObject +{ + Q_OBJECT + +public: + SubObjC() {} + ~SubObjC() {} + + Q_SLOT + void aSlot(); +}; + +void SubObjC::aSlot() +{ +} + +void ObjC::go() +{ + SubObjC subObj; +} +} + +#include "SubObjC.moc" diff --git a/Tests/QtAutogen/mocInclude/subC/SubObjC.hpp b/Tests/QtAutogen/mocInclude/subC/SubObjC.hpp new file mode 100644 index 0000000..dc251fd --- /dev/null +++ b/Tests/QtAutogen/mocInclude/subC/SubObjC.hpp @@ -0,0 +1,16 @@ +#ifndef SUBOBJC_HPP +#define SUBOBJC_HPP + +#include <QObject> + +namespace subC { + +class ObjC : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/mocIncludeRelaxed/CMakeLists.txt b/Tests/QtAutogen/mocIncludeRelaxed/CMakeLists.txt new file mode 100644 index 0000000..6a0829d --- /dev/null +++ b/Tests/QtAutogen/mocIncludeRelaxed/CMakeLists.txt @@ -0,0 +1,18 @@ +# Test moc include patterns + +set(CMAKE_AUTOMOC_RELAXED_MODE TRUE) + +include_directories("../mocInclude") + +add_executable(mocIncludeRelaxed + ../mocInclude/ObjA.cpp + ../mocInclude/ObjB.cpp + ../mocInclude/ObjC.cpp + ../mocInclude/ObjD.cpp + ../mocInclude/subA/SubObjA.cpp + ../mocInclude/subB/SubObjB.cpp + ../mocInclude/subC/SubObjC.cpp + main.cpp +) +target_link_libraries(mocIncludeRelaxed ${QT_LIBRARIES}) +set_target_properties(mocIncludeRelaxed PROPERTIES AUTOMOC ON) diff --git a/Tests/QtAutogen/mocIncludeRelaxed/main.cpp b/Tests/QtAutogen/mocIncludeRelaxed/main.cpp new file mode 100644 index 0000000..142d59e --- /dev/null +++ b/Tests/QtAutogen/mocIncludeRelaxed/main.cpp @@ -0,0 +1,14 @@ +#include "ObjA.hpp" +#include "ObjB.hpp" +#include "ObjC.hpp" + +int main(int argv, char** args) +{ + ObjA objA; + ObjB objB; + ObjC objC; + return 0; +} + +// Header in global subdirectory +#include "subB/moc_SubObjB.cpp" diff --git a/Tests/QtAutogen/mocIncludeStrict/CMakeLists.txt b/Tests/QtAutogen/mocIncludeStrict/CMakeLists.txt new file mode 100644 index 0000000..22e93a8 --- /dev/null +++ b/Tests/QtAutogen/mocIncludeStrict/CMakeLists.txt @@ -0,0 +1,18 @@ +# Test moc include patterns + +set(CMAKE_AUTOMOC_RELAXED_MODE FALSE) + +include_directories("../mocInclude") + +add_executable(mocIncludeStrict + ../mocInclude/ObjA.cpp + ../mocInclude/ObjB.cpp + ../mocInclude/ObjC.cpp + ../mocInclude/ObjD.cpp + ../mocInclude/subA/SubObjA.cpp + ../mocInclude/subB/SubObjB.cpp + ../mocInclude/subC/SubObjC.cpp + main.cpp +) +target_link_libraries(mocIncludeStrict ${QT_LIBRARIES}) +set_target_properties(mocIncludeStrict PROPERTIES AUTOMOC ON) diff --git a/Tests/QtAutogen/mocIncludeStrict/main.cpp b/Tests/QtAutogen/mocIncludeStrict/main.cpp new file mode 100644 index 0000000..142d59e --- /dev/null +++ b/Tests/QtAutogen/mocIncludeStrict/main.cpp @@ -0,0 +1,14 @@ +#include "ObjA.hpp" +#include "ObjB.hpp" +#include "ObjC.hpp" + +int main(int argv, char** args) +{ + ObjA objA; + ObjB objB; + ObjC objC; + return 0; +} + +// Header in global subdirectory +#include "subB/moc_SubObjB.cpp" diff --git a/Tests/QtAutogen/mocPlugin/CMakeLists.txt b/Tests/QtAutogen/mocPlugin/CMakeLists.txt new file mode 100644 index 0000000..b72b05d --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.8) + +set(CMAKE_AUTOMOC_DEPEND_FILTERS + "A_CUSTOM_MACRO" + "[\n][ \t]*A_CUSTOM_MACRO[ \t\r\n]*\\([^,]+,[ \t\r\n]*\"([^\"]+)\"" + ) + +if (NOT QT_TEST_VERSION STREQUAL 5) + message(SEND_ERROR "Invalid Qt version specified.") +endif() +find_package(Qt5Widgets REQUIRED) + +if(Qt5_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC) + add_definitions(${CMAKE_CXX_COMPILE_OPTIONS_PIC}) +endif() + +configure_file(jsonIn/StyleC.json jsonFiles/StyleC.json) +configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json) +configure_file(jsonIn/StyleC.json jsonFiles/StyleC_Custom.json) +configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD_Custom.json) + +# Enable automoc +set(CMAKE_AUTOMOC TRUE) + +include_directories("${CMAKE_CURRENT_BINARY_DIR}/jsonFiles") +link_libraries(Qt5::Widgets) + +add_library(PluginStyleA MODULE StyleA.cpp) +add_library(PluginStyleB MODULE StyleB.cpp) +add_library(PluginStyleC MODULE StyleC.cpp) +add_library(PluginStyleD MODULE StyleD.cpp) +add_library(PluginStyleE MODULE StyleE.cpp) diff --git a/Tests/QtAutogen/mocPlugin/StyleA.cpp b/Tests/QtAutogen/mocPlugin/StyleA.cpp new file mode 100644 index 0000000..b5e8753 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleA.cpp @@ -0,0 +1,6 @@ +#include "StyleA.hpp" + +QStyle* StyleA::create(const QString& key) +{ + return 0; +} diff --git a/Tests/QtAutogen/mocPlugin/StyleA.hpp b/Tests/QtAutogen/mocPlugin/StyleA.hpp new file mode 100644 index 0000000..1b6154d --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleA.hpp @@ -0,0 +1,17 @@ +#ifndef STYLEA_HPP +#define STYLEA_HPP + +#include "StyleCommon.hpp" +#include <QStylePlugin> + +class StyleA : public QStylePlugin +{ + Q_OBJECT + // Json file in local directory + Q_PLUGIN_METADATA(IID "org.styles.A" FILE "StyleA.json") + A_CUSTOM_MACRO(SomeArg, "StyleA_Custom.json", AnotherArg) +public: + QStyle* create(const QString& key); +}; + +#endif diff --git a/Tests/QtAutogen/mocPlugin/StyleA.json b/Tests/QtAutogen/mocPlugin/StyleA.json new file mode 100644 index 0000000..cc33953 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleA.json @@ -0,0 +1 @@ +{ "Keys": [ "Rocket", "Starbuster" ] } diff --git a/Tests/QtAutogen/mocPlugin/StyleA_Custom.json b/Tests/QtAutogen/mocPlugin/StyleA_Custom.json new file mode 100644 index 0000000..cc33953 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleA_Custom.json @@ -0,0 +1 @@ +{ "Keys": [ "Rocket", "Starbuster" ] } diff --git a/Tests/QtAutogen/mocPlugin/StyleB.cpp b/Tests/QtAutogen/mocPlugin/StyleB.cpp new file mode 100644 index 0000000..17d4400 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleB.cpp @@ -0,0 +1,6 @@ +#include "StyleB.hpp" + +QStyle* StyleB::create(const QString& key) +{ + return 0; +} diff --git a/Tests/QtAutogen/mocPlugin/StyleB.hpp b/Tests/QtAutogen/mocPlugin/StyleB.hpp new file mode 100644 index 0000000..163c9b2 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleB.hpp @@ -0,0 +1,17 @@ +#ifndef STYLEB_HPP +#define STYLEB_HPP + +#include "StyleCommon.hpp" +#include <QStylePlugin> + +class StyleB : public QStylePlugin +{ + Q_OBJECT + // Json file in local subdirectory + Q_PLUGIN_METADATA(IID "org.styles.B" FILE "jsonIn/StyleB.json") + A_CUSTOM_MACRO(SomeArg, "jsonIn/StyleB_Custom.json", AnotherArg) +public: + QStyle* create(const QString& key); +}; + +#endif diff --git a/Tests/QtAutogen/mocPlugin/StyleC.cpp b/Tests/QtAutogen/mocPlugin/StyleC.cpp new file mode 100644 index 0000000..37e7564 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleC.cpp @@ -0,0 +1,6 @@ +#include "StyleC.hpp" + +QStyle* StyleC::create(const QString& key) +{ + return 0; +} diff --git a/Tests/QtAutogen/mocPlugin/StyleC.hpp b/Tests/QtAutogen/mocPlugin/StyleC.hpp new file mode 100644 index 0000000..52a887a --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleC.hpp @@ -0,0 +1,17 @@ +#ifndef STYLEC_HPP +#define STYLEC_HPP + +#include "StyleCommon.hpp" +#include <QStylePlugin> + +class StyleC : public QStylePlugin +{ + Q_OBJECT + // Json file in global root directory + Q_PLUGIN_METADATA(IID "org.styles.C" FILE "StyleC.json") + A_CUSTOM_MACRO(SomeArg, "StyleC_Custom.json", AnotherArg) +public: + QStyle* create(const QString& key); +}; + +#endif diff --git a/Tests/QtAutogen/mocPlugin/StyleCommon.hpp b/Tests/QtAutogen/mocPlugin/StyleCommon.hpp new file mode 100644 index 0000000..f1a7ec6 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleCommon.hpp @@ -0,0 +1,7 @@ +#ifndef STYLECOMMON_HPP +#define STYLECOMMON_HPP + +// Empty test macro definition +#define A_CUSTOM_MACRO(name, jsonFile, pluginRegistrations) + +#endif diff --git a/Tests/QtAutogen/mocPlugin/StyleD.cpp b/Tests/QtAutogen/mocPlugin/StyleD.cpp new file mode 100644 index 0000000..7e4b121 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleD.cpp @@ -0,0 +1,6 @@ +#include "StyleD.hpp" + +QStyle* StyleD::create(const QString& key) +{ + return 0; +} diff --git a/Tests/QtAutogen/mocPlugin/StyleD.hpp b/Tests/QtAutogen/mocPlugin/StyleD.hpp new file mode 100644 index 0000000..df8a439 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleD.hpp @@ -0,0 +1,17 @@ +#ifndef STYLED_HPP +#define STYLED_HPP + +#include "StyleCommon.hpp" +#include <QStylePlugin> + +class StyleD : public QStylePlugin +{ + Q_OBJECT + // Json file in global sub director + Q_PLUGIN_METADATA(IID "org.styles.D" FILE "sub/StyleD.json") + A_CUSTOM_MACRO(SomeArg, "sub/StyleD_Custom.json", AnotherArg) +public: + QStyle* create(const QString& key); +}; + +#endif diff --git a/Tests/QtAutogen/mocPlugin/StyleE.cpp b/Tests/QtAutogen/mocPlugin/StyleE.cpp new file mode 100644 index 0000000..8fc9a7f --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleE.cpp @@ -0,0 +1,6 @@ +#include "StyleE.hpp" + +QStyle* StyleE::create(const QString& key) +{ + return 0; +} diff --git a/Tests/QtAutogen/mocPlugin/StyleE.hpp b/Tests/QtAutogen/mocPlugin/StyleE.hpp new file mode 100644 index 0000000..e7915a8 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/StyleE.hpp @@ -0,0 +1,17 @@ +#ifndef STYLEE_HPP +#define STYLEE_HPP + +#include "StyleCommon.hpp" +#include <QStylePlugin> + +class StyleE : public QStylePlugin +{ + Q_OBJECT + // No Json file + Q_PLUGIN_METADATA(IID "org.styles.E") + A_CUSTOM_MACRO(SomeArg, InvalidFileArg, AnotherArg) +public: + QStyle* create(const QString& key); +}; + +#endif diff --git a/Tests/QtAutogen/mocPlugin/jsonIn/StyleB.json b/Tests/QtAutogen/mocPlugin/jsonIn/StyleB.json new file mode 100644 index 0000000..129cac4 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/jsonIn/StyleB.json @@ -0,0 +1 @@ +{ "Keys": [ "Rocket", "StarbusterB" ] } diff --git a/Tests/QtAutogen/mocPlugin/jsonIn/StyleB_Custom.json b/Tests/QtAutogen/mocPlugin/jsonIn/StyleB_Custom.json new file mode 100644 index 0000000..129cac4 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/jsonIn/StyleB_Custom.json @@ -0,0 +1 @@ +{ "Keys": [ "Rocket", "StarbusterB" ] } diff --git a/Tests/QtAutogen/mocPlugin/jsonIn/StyleC.json b/Tests/QtAutogen/mocPlugin/jsonIn/StyleC.json new file mode 100644 index 0000000..bf17580 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/jsonIn/StyleC.json @@ -0,0 +1 @@ +{ "Keys": [ "Rocket", "StarbusterC" ] } diff --git a/Tests/QtAutogen/mocPlugin/jsonIn/StyleD.json b/Tests/QtAutogen/mocPlugin/jsonIn/StyleD.json new file mode 100644 index 0000000..f7fcc19 --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/jsonIn/StyleD.json @@ -0,0 +1 @@ +{ "Keys": [ "Rocket", "StarbusterD" ] } diff --git a/Tests/QtAutogen/mocPlugin/main.cpp b/Tests/QtAutogen/mocPlugin/main.cpp new file mode 100644 index 0000000..3ba2ddc --- /dev/null +++ b/Tests/QtAutogen/mocPlugin/main.cpp @@ -0,0 +1,6 @@ +#include "StyleA.hpp" + +int main(int argv, char** args) +{ + return 0; +} diff --git a/Tests/QtAutogen/sameName/CMakeLists.txt b/Tests/QtAutogen/sameName/CMakeLists.txt index 9e47a3e..4d2dcd9 100644 --- a/Tests/QtAutogen/sameName/CMakeLists.txt +++ b/Tests/QtAutogen/sameName/CMakeLists.txt @@ -18,3 +18,14 @@ add_executable(sameName ) target_link_libraries(sameName ${QT_LIBRARIES}) set_target_properties(sameName PROPERTIES AUTOMOC TRUE AUTORCC TRUE) + +# Set different compression levels +if (QT_TEST_VERSION STREQUAL 4) + set(rccCompress "-compress") +else() + set(rccCompress "--compress") +endif() +set_target_properties(sameName PROPERTIES AUTORCC_OPTIONS "${rccCompress};0" ) +set_source_files_properties(aaa/data.qrc PROPERTIES AUTORCC_OPTIONS "${rccCompress};1" ) +set_source_files_properties(bbb/data.qrc PROPERTIES AUTORCC_OPTIONS "${rccCompress};2" ) +set_source_files_properties(ccc/data.qrc PROPERTIES AUTORCC_OPTIONS "${rccCompress};3" ) diff --git a/Tests/RunCMake/CommandLine/cmake_depends/.gitattributes b/Tests/RunCMake/CommandLine/cmake_depends/.gitattributes new file mode 100644 index 0000000..9c22288 --- /dev/null +++ b/Tests/RunCMake/CommandLine/cmake_depends/.gitattributes @@ -0,0 +1,2 @@ +# Do not format a source encoded in UTF-16. +test_UTF-16LE.h -format.clang-format diff --git a/Tests/RunCMake/GenerateExportHeader/reference/.gitattributes b/Tests/RunCMake/GenerateExportHeader/reference/.gitattributes new file mode 100644 index 0000000..883a7f1 --- /dev/null +++ b/Tests/RunCMake/GenerateExportHeader/reference/.gitattributes @@ -0,0 +1,2 @@ +# Exclude reference content from formatting. +* -format.clang-format diff --git a/Tests/RunCMake/ObjectLibrary/Dependencies.cmake b/Tests/RunCMake/ObjectLibrary/Dependencies.cmake new file mode 100644 index 0000000..4e3db5f --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/Dependencies.cmake @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.7) + +project(Dependencies) + +add_library(myobj OBJECT ${CMAKE_BINARY_DIR}/depends_obj.cpp) +add_library(mylib STATIC $<TARGET_OBJECTS:myobj> depends_lib.cpp) +add_executable(myexe depends_main.cpp) +target_link_libraries(myexe mylib) + +enable_testing() +add_test(NAME myexe COMMAND $<TARGET_FILE:myexe>) diff --git a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake index 42973f8..9291218 100644 --- a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake +++ b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake @@ -16,3 +16,22 @@ run_cmake(ObjWithObj) run_cmake(PostBuild) run_cmake(PreBuild) run_cmake(PreLink) + +function(run_Dependencies) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Dependencies-build) + set(RunCMake_TEST_NO_CLEAN 1) + + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + run_cmake_command(Dependencies-build ${CMAKE_COMMAND} -E copy ${RunCMake_SOURCE_DIR}/depends_obj1.cpp ${RunCMake_TEST_BINARY_DIR}/depends_obj.cpp) + run_cmake(Dependencies) + run_cmake_command(Dependencies-build ${CMAKE_COMMAND} --build .) + run_cmake_command(Dependencies-build ${CMAKE_COMMAND} -E copy ${RunCMake_SOURCE_DIR}/depends_obj0.cpp ${RunCMake_TEST_BINARY_DIR}/depends_obj.cpp) + run_cmake_command(Dependencies-build ${CMAKE_COMMAND} -E sleep 1) + run_cmake_command(Dependencies-build ${CMAKE_COMMAND} -E touch_nocreate ${RunCMake_TEST_BINARY_DIR}/depends_obj.cpp) + run_cmake_command(Dependencies-build ${CMAKE_COMMAND} --build .) + run_cmake_command(Dependencies-build ${CMAKE_CTEST_COMMAND} -C Debug) +endfunction() + +run_Dependencies() diff --git a/Tests/RunCMake/ObjectLibrary/depends_lib.cpp b/Tests/RunCMake/ObjectLibrary/depends_lib.cpp new file mode 100644 index 0000000..540b536 --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/depends_lib.cpp @@ -0,0 +1,7 @@ +#include <stdlib.h> +int myobj_foo(); + +void mylib_foo() +{ + exit(myobj_foo()); +} diff --git a/Tests/RunCMake/ObjectLibrary/depends_main.cpp b/Tests/RunCMake/ObjectLibrary/depends_main.cpp new file mode 100644 index 0000000..a9d323d --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/depends_main.cpp @@ -0,0 +1,7 @@ +void mylib_foo(); + +int main() +{ + mylib_foo(); + return 0; +} diff --git a/Tests/RunCMake/ObjectLibrary/depends_obj0.cpp b/Tests/RunCMake/ObjectLibrary/depends_obj0.cpp new file mode 100644 index 0000000..73a17e7 --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/depends_obj0.cpp @@ -0,0 +1,4 @@ +int myobj_foo() +{ + return 0; +} diff --git a/Tests/RunCMake/ObjectLibrary/depends_obj1.cpp b/Tests/RunCMake/ObjectLibrary/depends_obj1.cpp new file mode 100644 index 0000000..f62ef70 --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/depends_obj1.cpp @@ -0,0 +1,4 @@ +int myobj_foo() +{ + return 1; +} diff --git a/Utilities/.gitattributes b/Utilities/.gitattributes index c6345eb..bd97802 100644 --- a/Utilities/.gitattributes +++ b/Utilities/.gitattributes @@ -1,3 +1,7 @@ /Git export-ignore /GitSetup export-ignore SetupForDevelopment.sh export-ignore + +# Do not format third-party sources. +/KWIML/** -format.clang-format +/cm*/** -format.clang-format diff --git a/Utilities/Scripts/clang-format.bash b/Utilities/Scripts/clang-format.bash index 2b36ac5..edcda77 100755 --- a/Utilities/Scripts/clang-format.bash +++ b/Utilities/Scripts/clang-format.bash @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2015-2016 Kitware, Inc. +# Copyright 2015-2017 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -106,34 +106,12 @@ case "$mode" in *) die "invalid mode: $mode" ;; esac -# Filter sources to which our style should apply. -$git_ls -z -- '*.c' '*.cc' '*.cpp' '*.cxx' '*.h' '*.hh' '*.hpp' '*.hxx' '*.cu' '*.notcu' | +# List files as selected above. +$git_ls | - # Exclude lexer/parser generator input and output. - egrep -z -v '^Source/cmCommandArgumentLexer\.' | - egrep -z -v '^Source/cmCommandArgumentParser(\.y|\.cxx|Tokens\.h)' | - egrep -z -v '^Source/cmDependsJavaLexer\.' | - egrep -z -v '^Source/cmDependsJavaParser(\.y|\.cxx|Tokens\.h)' | - egrep -z -v '^Source/cmExprLexer\.' | - egrep -z -v '^Source/cmExprParser(\.y|\.cxx|Tokens\.h)' | - egrep -z -v '^Source/cmFortranLexer\.' | - egrep -z -v '^Source/cmFortranParser(\.y|\.cxx|Tokens\.h)' | - egrep -z -v '^Source/cmListFileLexer(\.in\.l|\.c)' | - - # Exclude third-party sources. - egrep -z -v '^Source/bindexplib' | - egrep -z -v '^Source/(kwsys|CursesDialog/form)/' | - egrep -z -v '^Utilities/(KW|cm).*/' | - - # Exclude reference content. - egrep -z -v '^Tests/RunCMake/GenerateExportHeader/reference/' | - - # Exclude manually-formatted sources (e.g. with long lines). - egrep -z -v '^Tests/PositionIndependentTargets/pic_test.h' | - egrep -z -v '^Tests/CompileFeatures/cxx_right_angle_brackets.cpp' | - - # Exclude sources with encoding not suported by clang-format. - egrep -z -v '^Tests/RunCMake/CommandLine/cmake_depends/test_UTF-16LE.h' | + # Select sources with our attribute. + git check-attr --stdin format.clang-format | + sed -n '/: format\.clang-format: set$/ {s/:[^:]*:[^:]*$//p}' | # Update sources in-place. - xargs -0 "$clang_format" -i + xargs -d '\n' "$clang_format" -i diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash index 3b42269..6c4eeb0 100755 --- a/Utilities/Scripts/update-libarchive.bash +++ b/Utilities/Scripts/update-libarchive.bash @@ -8,7 +8,7 @@ readonly name="LibArchive" readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>" readonly subtree="Utilities/cmlibarchive" readonly repo="https://github.com/libarchive/libarchive.git" -readonly tag="master" +readonly tag="v3.3.0" readonly shortlog=false readonly paths=" CMakeLists.txt diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt index 09bef51..56811b6 100644 --- a/Utilities/cmlibarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/CMakeLists.txt @@ -71,13 +71,15 @@ include(CTest) OPTION(ENABLE_NETTLE "Enable use of Nettle" ON) OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON) -OPTION(ENABLE_LZMA "Enable the use of the system found LZMA library if found" ON) -OPTION(ENABLE_ZLIB "Enable the use of the system found ZLIB library if found" ON) -OPTION(ENABLE_BZip2 "Enable the use of the system found BZip2 library if found" ON) -OPTION(ENABLE_LIBXML2 "Enable the use of the system found libxml2 library if found" ON) -OPTION(ENABLE_EXPAT "Enable the use of the system found EXPAT library if found" ON) -OPTION(ENABLE_PCREPOSIX "Enable the use of the system found PCREPOSIX library if found" ON) -OPTION(ENABLE_LibGCC "Enable the use of the system found LibGCC library if found" 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) @@ -107,7 +109,7 @@ INCLUDE(CheckHeaderDirent) INCLUDE(CheckIncludeFile) INCLUDE(CheckIncludeFiles) INCLUDE(CheckLibraryExists) -INCLUDE(CheckStructMember) +INCLUDE(CheckStructHasMember) INCLUDE(CheckSymbolExists) INCLUDE(CheckTypeExists) INCLUDE(CheckTypeSize) @@ -285,21 +287,20 @@ MARK_AS_ADVANCED(CLEAR BZIP2_LIBRARIES) # Find LZMA # IF(ENABLE_LZMA) - FIND_PACKAGE(LZMA) + FIND_PACKAGE(LibLZMA) ELSE() - SET(LZMA_FOUND FALSE) # Override cached value - SET(LZMADEC_FOUND FALSE) # Override cached value + SET(LIBZMA_FOUND FALSE) # Override cached value ENDIF() -IF(LZMA_FOUND) +IF(LIBLZMA_FOUND) SET(HAVE_LIBLZMA 1) SET(HAVE_LZMA_H 1) - INCLUDE_DIRECTORIES(${LZMA_INCLUDE_DIR}) - LIST(APPEND ADDITIONAL_LIBS ${LZMA_LIBRARIES}) + INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS}) + LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES}) IF(CMAKE_USE_SYSTEM_LIBLZMA) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( - "${LZMA_INCLUDE_DIR}" "${LZMA_LIBRARIES}" + "${LIBLZMA_INCLUDE_DIRS}" "${LIBLZMA_LIBRARIES}" COMPILES "#include <lzma.h>\nint main() {return (int)lzma_version_number(); }" "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC") @@ -309,27 +310,26 @@ IF(LZMA_FOUND) ELSE() ADD_DEFINITIONS(-DLZMA_API_STATIC) ENDIF() -ELSEIF(LZMADEC_FOUND) - SET(HAVE_LIBLZMADEC 1) - SET(HAVE_LZMADEC_H 1) - INCLUDE_DIRECTORIES(${LZMADEC_INCLUDE_DIR}) - LIST(APPEND ADDITIONAL_LIBS ${LZMADEC_LIBRARIES}) -ELSE(LZMA_FOUND) +ELSE(LIBLZMA_FOUND) # LZMA not found and will not be used. -ENDIF(LZMA_FOUND) +ENDIF(LIBLZMA_FOUND) IF(0) # CMake does not need LZO2 support in libarchive # # Find LZO2 # -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) +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) @@ -459,7 +459,7 @@ IF(ENABLE_CNG) ELSE(ENABLE_CNG) UNSET(HAVE_BCRYPT_H CACHE) ENDIF(ENABLE_CNG) -# Following files need windwos.h, so we should test it after windows.h test. +# 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) @@ -1005,6 +1005,14 @@ CHECK_FUNCTION_EXISTS(wmemmove HAVE_WMEMMOVE) CMAKE_POP_CHECK_STATE() # Restore the state of the variables +CHECK_C_SOURCE_COMPILES( + "#include <sys/types.h>\n#include <sys/mount.h>\nint main(void) { struct vfsconf v; return sizeof(v);}" + HAVE_STRUCT_VFSCONF) + +CHECK_C_SOURCE_COMPILES( + "#include <sys/types.h>\n#include <sys/mount.h>\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( @@ -1030,6 +1038,10 @@ CHECK_C_SOURCE_COMPILES( "#include <sys/sysmacros.h>\nint main() { return major(256); }" MAJOR_IN_SYSMACROS) +CHECK_C_SOURCE_COMPILES( + "#include <lzma.h>\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) @@ -1063,47 +1075,47 @@ CHECK_SYMBOL_EXISTS(SSIZE_MAX "limits.h" HAVE_DECL_SSIZE_MAX) # Check struct members # # Check for tm_gmtoff in struct tm -CHECK_STRUCT_MEMBER("struct tm" tm_gmtoff +CHECK_STRUCT_HAS_MEMBER("struct tm" tm_gmtoff "time.h" HAVE_STRUCT_TM_TM_GMTOFF) -CHECK_STRUCT_MEMBER("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_MEMBER("struct statfs" f_namemax +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_MEMBER("struct stat" st_birthtime +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_MEMBER("struct stat" st_birthtimespec.tv_nsec +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_MEMBER("struct stat" st_mtimespec.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_MEMBER("struct stat" st_mtim.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_MEMBER("struct stat" st_mtime_n +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_n "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIME_N) -CHECK_STRUCT_MEMBER("struct stat" st_umtime +CHECK_STRUCT_HAS_MEMBER("struct stat" st_umtime "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_UMTIME) -CHECK_STRUCT_MEMBER("struct stat" st_mtime_usec +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_MEMBER("struct stat" st_blksize +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_MEMBER("struct stat" st_flags +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_MEMBER("struct statvfs" f_iosize + CHECK_STRUCT_HAS_MEMBER("struct statvfs" f_iosize "sys/types.h;sys/statvfs.h" HAVE_STRUCT_STATVFS_F_IOSIZE) ENDIF() # # -CHECK_STRUCT_MEMBER("struct tm" tm_sec +CHECK_STRUCT_HAS_MEMBER("struct tm" tm_sec "sys/types.h;sys/time.h;time.h" TIME_WITH_SYS_TIME) # @@ -1323,16 +1335,36 @@ IF(ENABLE_ACL) # 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 <sys/types.h> +#include <sys/acl.h> +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. @@ -1347,7 +1379,15 @@ ELSE(ENABLE_ACL) 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) # diff --git a/Utilities/cmlibarchive/build/cmake/CheckStructMember.cmake b/Utilities/cmlibarchive/build/cmake/CheckStructMember.cmake deleted file mode 100644 index 05ddb3a..0000000 --- a/Utilities/cmlibarchive/build/cmake/CheckStructMember.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# - Check if the given struct or class has the specified member variable -# CHECK_STRUCT_MEMBER (STRUCT MEMBER HEADER VARIABLE) -# -# STRUCT - the name of the struct or class you are interested in -# MEMBER - the member which existence you want to check -# HEADER - the header(s) where the prototype should be declared -# VARIABLE - variable to store the result -# -# The following variables may be set before calling this macro to -# modify the way the check is run: -# -# CMAKE_REQUIRED_FLAGS = string of compile command line flags -# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) -# CMAKE_REQUIRED_INCLUDES = list of include directories - -# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org> -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - - -INCLUDE(CheckCSourceCompiles) - -MACRO (CHECK_STRUCT_MEMBER _STRUCT _MEMBER _HEADER _RESULT) - SET(_INCLUDE_FILES) - FOREACH (it ${_HEADER}) - SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n") - ENDFOREACH (it) - - SET(_CHECK_STRUCT_MEMBER_SOURCE_CODE " -${_INCLUDE_FILES} -int main() -{ - static ${_STRUCT} tmp; - if (sizeof(tmp.${_MEMBER})) - return 0; - return 0; -} -") - CHECK_C_SOURCE_COMPILES("${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT}) - -ENDMACRO (CHECK_STRUCT_MEMBER) - diff --git a/Utilities/cmlibarchive/build/cmake/FindLZMA.cmake b/Utilities/cmlibarchive/build/cmake/FindLZMA.cmake deleted file mode 100644 index 0b46b2c..0000000 --- a/Utilities/cmlibarchive/build/cmake/FindLZMA.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# - Find lzma and lzmadec -# Find the native LZMA includes and library -# -# LZMA_INCLUDE_DIR - where to find lzma.h, etc. -# LZMA_LIBRARIES - List of libraries when using liblzma. -# LZMA_FOUND - True if liblzma found. -# LZMADEC_INCLUDE_DIR - where to find lzmadec.h, etc. -# LZMADEC_LIBRARIES - List of libraries when using liblzmadec. -# LZMADEC_FOUND - True if liblzmadec found. - -IF (LZMA_INCLUDE_DIR) - # Already in cache, be silent - SET(LZMA_FIND_QUIETLY TRUE) -ENDIF (LZMA_INCLUDE_DIR) - -FIND_PATH(LZMA_INCLUDE_DIR lzma.h) -FIND_LIBRARY(LZMA_LIBRARY NAMES lzma liblzma) - -# handle the QUIETLY and REQUIRED arguments and set LZMA_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMA DEFAULT_MSG LZMA_LIBRARY LZMA_INCLUDE_DIR) - -IF(LZMA_FOUND) - SET( LZMA_LIBRARIES ${LZMA_LIBRARY} ) -ELSE(LZMA_FOUND) - SET( LZMA_LIBRARIES ) - - IF (LZMADEC_INCLUDE_DIR) - # Already in cache, be silent - SET(LZMADEC_FIND_QUIETLY TRUE) - ENDIF (LZMADEC_INCLUDE_DIR) - - FIND_PATH(LZMADEC_INCLUDE_DIR lzmadec.h) - FIND_LIBRARY(LZMADEC_LIBRARY NAMES lzmadec ) - - # handle the QUIETLY and REQUIRED arguments and set LZMADEC_FOUND to TRUE if - # all listed variables are TRUE - INCLUDE(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMADEC DEFAULT_MSG LZMADEC_LIBRARY - LZMADEC_INCLUDE_DIR) - - IF(LZMADEC_FOUND) - SET( LZMADEC_LIBRARIES ${LZMADEC_LIBRARY} ) - ELSE(LZMADEC_FOUND) - SET( LZMADEC_LIBRARIES ) - ENDIF(LZMADEC_FOUND) -ENDIF(LZMA_FOUND) diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in index 9843875..fc248b3 100644 --- a/Utilities/cmlibarchive/build/cmake/config.h.in +++ b/Utilities/cmlibarchive/build/cmake/config.h.in @@ -296,6 +296,9 @@ typedef uint64_t uintmax_t; /* 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 @@ -326,6 +329,12 @@ typedef uint64_t uintmax_t; /* 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 @@ -727,6 +736,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the <lzma.h> 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 <lzo/lzo1x.h> header file. */ #cmakedefine HAVE_LZO_LZO1X_H 1 @@ -929,6 +941,12 @@ typedef uint64_t uintmax_t; /* 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 diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version index f293156..a000b30 100644 --- a/Utilities/cmlibarchive/build/version +++ b/Utilities/cmlibarchive/build/version @@ -1 +1 @@ -3002001 +3003000 diff --git a/Utilities/cmlibarchive/libarchive/CMakeLists.txt b/Utilities/cmlibarchive/libarchive/CMakeLists.txt index eaa7b20..d412c80 100644 --- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt @@ -168,15 +168,33 @@ SET(libarchive_MANS archive_entry_time.3 archive_read.3 archive_read_add_passphrase.3 + archive_read_data.3 archive_read_disk.3 + archive_read_extract.3 + archive_read_filter.3 + archive_read_format.3 + archive_read_free.3 + archive_read_header.3 + archive_read_new.3 + archive_read_open.3 archive_read_set_options.3 archive_util.3 archive_write.3 + archive_write_blocksize.3 + archive_write_data.3 archive_write_disk.3 + archive_write_filter.3 + archive_write_finish_entry.3 + archive_write_format.3 + archive_write_free.3 + archive_write_header.3 + archive_write_new.3 + archive_write_open.3 archive_write_set_options.3 archive_write_set_passphrase.3 cpio.5 libarchive.3 + libarchive_changes.3 libarchive_internals.3 libarchive-formats.5 mtree.5 diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h index 3b5104b..040f151 100644 --- a/Utilities/cmlibarchive/libarchive/archive.h +++ b/Utilities/cmlibarchive/libarchive/archive.h @@ -36,7 +36,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3002001 +#define ARCHIVE_VERSION_NUMBER 3003000 #include <sys/stat.h> #include <stddef.h> /* for wchar_t */ @@ -152,7 +152,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.2.1" +#define ARCHIVE_VERSION_ONLY_STRING "3.3.0" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -370,7 +370,7 @@ typedef const char *archive_passphrase_callback(struct archive *, * 4) Repeatedly call archive_read_next_header to get information about * successive archive entries. Call archive_read_data to extract * data for entries of interest. - * 5) Call archive_read_finish to end processing. + * 5) Call archive_read_free to end processing. */ __LA_DECL struct archive *archive_read_new(void); @@ -559,7 +559,7 @@ __LA_DECL la_int64_t archive_read_header_position(struct archive *); * 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 uncapable of encryption support. + * 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. @@ -981,12 +981,12 @@ __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 travesal be restored. */ +/* 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 travesal be restored. +/* 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. */ @@ -1121,7 +1121,7 @@ __LA_DECL int archive_match_time_excluded(struct archive *, /* * Flags to tell a matching type of time stamps. These are used for - * following functinos. + * following functions. */ /* Time flag: mtime to be tested. */ #define ARCHIVE_MATCH_MTIME (0x0100) @@ -1141,7 +1141,7 @@ __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 particluar file. */ +/* 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 *, diff --git a/Utilities/cmlibarchive/libarchive/archive_acl.c b/Utilities/cmlibarchive/libarchive/archive_acl.c index bf4b610..b8b6b63 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl.c +++ b/Utilities/cmlibarchive/libarchive/archive_acl.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle + * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,25 +56,77 @@ static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc); +static int archive_acl_text_want_type(struct archive_acl *acl, int flags); +static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type, + int flags, int wide, struct archive *a, + struct archive_string_conv *sc); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); +static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, + int *result); +static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, + int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); -static int prefix_w(const wchar_t *start, const wchar_t *end, - const wchar_t *test); -static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id); +static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, + int tag, int flags, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); static int isint(const char *start, const char *end, int *result); static int ismode(const char *start, const char *end, int *result); +static int is_nfs4_flags(const char *start, const char *end, + int *result); +static int is_nfs4_perms(const char *start, const char *end, + int *result); static void next_field(const char **p, const char **start, const char **end, char *sep); -static int prefix_c(const char *start, const char *end, - const char *test); -static void append_entry(char **p, const char *prefix, int tag, - const char *name, int perm, int id); +static void append_entry(char **p, const char *prefix, int type, + int tag, int flags, const char *name, int perm, int id); static void append_id(char **p, int id); +static const struct { + const int perm; + const char c; + const wchar_t wc; +} nfsv4_acl_perm_map[] = { + { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r', + L'r' }, + { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w', + L'w' }, + { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' }, + { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, + 'p', L'p' }, + { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' }, + { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' }, + { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' }, + { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' }, + { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' }, + { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' }, + { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' }, + { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' }, + { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' }, + { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' } +}; + +static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) / + sizeof(nfsv4_acl_perm_map[0])); + +static const struct { + const int perm; + const char c; + const wchar_t wc; +} nfsv4_acl_flag_map[] = { + { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' }, + { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' }, + { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' }, + { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' }, + { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' }, + { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' }, + { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' } +}; + +static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) / + sizeof(nfsv4_acl_flag_map[0])); + void archive_acl_clear(struct archive_acl *acl) { @@ -94,6 +147,7 @@ archive_acl_clear(struct archive_acl *acl) acl->acl_text = NULL; } acl->acl_p = NULL; + acl->acl_types = 0; acl->acl_state = 0; /* Not counting. */ } @@ -279,23 +333,31 @@ acl_new_entry(struct archive_acl *acl, acl->acl_text = NULL; } - /* If there's a matching entry already in the list, overwrite it. */ + /* + * If there's a matching entry already in the list, overwrite it. + * NFSv4 entries may be repeated and are not overwritten. + * + * TODO: compare names of no id is provided (needs more rework) + */ ap = acl->acl_head; aq = NULL; while (ap != NULL) { - if (ap->type == type && ap->tag == tag && ap->id == id) { - ap->permset = permset; - return (ap); + if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) && + ap->type == type && ap->tag == tag && ap->id == id) { + if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && + tag != ARCHIVE_ENTRY_ACL_GROUP)) { + ap->permset = permset; + return (ap); + } } aq = ap; ap = ap->next; } /* Add a new entry to the end of the list. */ - ap = (struct archive_acl_entry *)malloc(sizeof(*ap)); + ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap)); if (ap == NULL) return (NULL); - memset(ap, 0, sizeof(*ap)); if (aq == NULL) acl->acl_head = ap; else @@ -331,6 +393,15 @@ archive_acl_count(struct archive_acl *acl, int want_type) } /* + * Return a bitmask of stored ACL types in an ACL list + */ +int +archive_acl_types(struct archive_acl *acl) +{ + return (acl->acl_types); +} + +/* * 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. @@ -366,8 +437,8 @@ archive_acl_reset(struct archive_acl *acl, int want_type) * standard permissions and include them in the returned list. */ int -archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type, - int *permset, int *tag, int *id, const char **name) +archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, + int *type, int *permset, int *tag, int *id, const char **name) { *name = NULL; *id = -1; @@ -432,130 +503,273 @@ archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int } /* - * Generate a text version of the ACL. The flags parameter controls - * the style of the generated ACL. + * Determine what type of ACL do we want */ -const wchar_t * -archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags) +static int +archive_acl_text_want_type(struct archive_acl *acl, int flags) { - int count; - size_t length; - const wchar_t *wname; - const wchar_t *prefix; - wchar_t separator; - struct archive_acl_entry *ap; - int id, r; - wchar_t *wp; + int want_type; - if (acl->acl_text_w != NULL) { - free (acl->acl_text_w); - acl->acl_text_w = NULL; + /* Check if ACL is NFSv4 */ + if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + /* NFSv4 should never mix with POSIX.1e */ + if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) + return (0); + else + return (ARCHIVE_ENTRY_ACL_TYPE_NFS4); } - separator = L','; + /* Now deal with POSIX.1e ACLs */ + + want_type = 0; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) + want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) + want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + + /* By default we want both access and default ACLs */ + if (want_type == 0) + return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E); + + return (want_type); +} + +/* + * Calculate ACL text string length + */ +static ssize_t +archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, + int wide, struct archive *a, struct archive_string_conv *sc) { + struct archive_acl_entry *ap; + const char *name; + const wchar_t *wname; + int count, idlen, tmp, r; + ssize_t length; + size_t len; + count = 0; length = 0; - ap = acl->acl_head; - while (ap != NULL) { - if ((ap->type & flags) != 0) { - count++; - if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && - (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) - length += 8; /* "default:" */ - length += 5; /* tag name */ - length += 1; /* colon */ - r = archive_mstring_get_wcs(a, &ap->name, &wname); - if (r == 0 && wname != NULL) - length += wcslen(wname); - else if (r < 0 && errno == ENOMEM) - return (NULL); - else - length += sizeof(uid_t) * 3 + 1; - length ++; /* colon */ + for (ap = acl->acl_head; ap != NULL; ap = ap->next) { + if ((ap->type & want_type) == 0) + continue; + /* + * Filemode-mapping ACL entries are stored exclusively in + * ap->mode so they should not be in the list + */ + if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) + && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ + || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ + || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) + continue; + count++; + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 + && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) + length += 8; /* "default:" */ + switch (ap->tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + length += 6; /* "owner@" */ + break; + } + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_USER: + case ARCHIVE_ENTRY_ACL_MASK: + length += 4; /* "user", "mask" */ + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + length += 6; /* "group@" */ + break; + } + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_GROUP: + case ARCHIVE_ENTRY_ACL_OTHER: + length += 5; /* "group", "other" */ + break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + length += 9; /* "everyone@" */ + break; + } + length += 1; /* colon after tag */ + if (ap->tag == ARCHIVE_ENTRY_ACL_USER || + ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { + if (wide) { + r = archive_mstring_get_wcs(a, &ap->name, + &wname); + if (r == 0 && wname != NULL) + length += wcslen(wname); + else if (r < 0 && errno == ENOMEM) + return (0); + else + length += sizeof(uid_t) * 3 + 1; + } else { + r = archive_mstring_get_mbs_l(&ap->name, &name, + &len, sc); + if (r != 0) + return (0); + if (len > 0 && name != NULL) + length += len; + else + length += sizeof(uid_t) * 3 + 1; + } + length += 1; /* colon after user or group name */ + } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) + length += 1; /* 2nd colon empty user,group or other */ + + if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) + && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) + && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER + || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) { + /* Solaris has no colon after other: and mask: */ + length = length - 1; + } + + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* rwxpdDaARWcCos:fdinSFI:deny */ + length += 27; + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0) + length += 1; /* allow, alarm, audit */ + } else length += 3; /* rwx */ + + if ((ap->tag == ARCHIVE_ENTRY_ACL_USER || + ap->tag == ARCHIVE_ENTRY_ACL_GROUP) && + (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) { length += 1; /* colon */ - length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; - length ++; /* newline */ + /* ID digit count */ + idlen = 1; + tmp = ap->id; + while (tmp > 9) { + tmp = tmp / 10; + idlen++; + } + length += idlen; } - ap = ap->next; + length ++; /* entry separator */ } - if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { - length += 10; /* "user::rwx\n" */ - length += 11; /* "group::rwx\n" */ - length += 11; /* "other::rwx\n" */ - } + /* Add filemode-mapping access entries to the length */ + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) { + /* "user::rwx\ngroup::rwx\nother:rwx\n" */ + length += 31; + } else { + /* "user::rwx\ngroup::rwx\nother::rwx\n" */ + length += 32; + } + } else if (count == 0) + return (0); + + /* The terminating character is included in count */ + return (length); +} + +/* + * Generate a wide text version of the ACL. The flags parameter controls + * the type and style of the generated ACL. + */ +wchar_t * +archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, + struct archive *a) +{ + int count; + ssize_t length; + size_t len; + const wchar_t *wname; + const wchar_t *prefix; + wchar_t separator; + struct archive_acl_entry *ap; + int id, r, want_type; + wchar_t *wp, *ws; + + want_type = archive_acl_text_want_type(acl, flags); + + /* Both NFSv4 and POSIX.1 types found */ + if (want_type == 0) + return (NULL); + + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) + flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; + + length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL); - if (count == 0) + if (length == 0) return (NULL); + if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) + separator = L','; + else + separator = L'\n'; + /* Now, allocate the string and actually populate it. */ - wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); - if (wp == NULL) + wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t)); + if (wp == NULL) { + if (errno == ENOMEM) + __archive_errx(1, "No memory"); return (NULL); + } count = 0; - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, + + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); - *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, + *wp++ = separator; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); - *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, + *wp++ = separator; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); count += 3; - - ap = acl->acl_head; - while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - r = archive_mstring_get_wcs(a, &ap->name, &wname); - if (r == 0) { - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry_w(&wp, NULL, ap->tag, wname, - ap->permset, id); - count++; - } else if (r < 0 && errno == ENOMEM) - return (NULL); - } - ap = ap->next; - } } - - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) + for (ap = acl->acl_head; ap != NULL; ap = ap->next) { + if ((ap->type & want_type) == 0) + continue; + /* + * Filemode-mapping ACL entries are stored exclusively in + * ap->mode so they should not be in the list + */ + if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) + && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ + || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ + || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) + continue; + if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && + (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) prefix = L"default:"; else prefix = NULL; - ap = acl->acl_head; - count = 0; - while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - r = archive_mstring_get_wcs(a, &ap->name, &wname); - if (r == 0) { - if (count > 0) - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry_w(&wp, prefix, ap->tag, - wname, ap->permset, id); - count ++; - } else if (r < 0 && errno == ENOMEM) - return (NULL); - } - ap = ap->next; - } + r = archive_mstring_get_wcs(a, &ap->name, &wname); + if (r == 0) { + if (count > 0) + *wp++ = separator; + if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + id = ap->id; + else + id = -1; + append_entry_w(&wp, prefix, ap->type, ap->tag, flags, + wname, ap->permset, id); + count++; + } else if (r < 0 && errno == ENOMEM) + return (NULL); } - return (acl->acl_text_w); -} + /* Add terminating character */ + *wp++ = L'\0'; + + len = wcslen(ws); + if ((ssize_t)len > (length - 1)) + __archive_errx(1, "Buffer overrun"); + + if (text_len != NULL) + *text_len = len; + + return (ws); +} static void append_id_w(wchar_t **wp, int id) @@ -568,9 +782,11 @@ append_id_w(wchar_t **wp, int id) } static void -append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id) +append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, + int tag, int flags, const wchar_t *wname, int perm, int id) { + int i; + if (prefix != NULL) { wcscpy(*wp, prefix); *wp += wcslen(*wp); @@ -579,6 +795,10 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + wcscpy(*wp, L"owner@"); + break; + } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); @@ -586,6 +806,10 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + wcscpy(*wp, L"group@"); + break; + } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: wcscpy(*wp, L"group"); @@ -600,153 +824,184 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, wname = NULL; id = -1; break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + wcscpy(*wp, L"everyone@"); + wname = NULL; + id = -1; + break; } *wp += wcslen(*wp); *(*wp)++ = L':'; - if (wname != NULL) { - wcscpy(*wp, wname); + if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || + tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + if (wname != NULL) { + wcscpy(*wp, wname); + *wp += wcslen(*wp); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id_w(wp, id); + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) + id = -1; + } + /* Solaris style has no second colon after other and mask */ + if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) + || (tag != ARCHIVE_ENTRY_ACL_OTHER + && tag != ARCHIVE_ENTRY_ACL_MASK)) + *(*wp)++ = L':'; + } + if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + /* POSIX.1e ACL perms */ + *(*wp)++ = (perm & 0444) ? L'r' : L'-'; + *(*wp)++ = (perm & 0222) ? L'w' : L'-'; + *(*wp)++ = (perm & 0111) ? L'x' : L'-'; + } else { + /* NFSv4 ACL perms */ + for (i = 0; i < nfsv4_acl_perm_map_size; i++) { + if (perm & nfsv4_acl_perm_map[i].perm) + *(*wp)++ = nfsv4_acl_perm_map[i].wc; + else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) + *(*wp)++ = L'-'; + } + *(*wp)++ = L':'; + for (i = 0; i < nfsv4_acl_flag_map_size; i++) { + if (perm & nfsv4_acl_flag_map[i].perm) + *(*wp)++ = nfsv4_acl_flag_map[i].wc; + else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) + *(*wp)++ = L'-'; + } + *(*wp)++ = L':'; + switch (type) { + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + wcscpy(*wp, L"allow"); + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + wcscpy(*wp, L"deny"); + break; + case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: + wcscpy(*wp, L"audit"); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ALARM: + wcscpy(*wp, L"alarm"); + break; + default: + break; + } *wp += wcslen(*wp); - } else if (tag == ARCHIVE_ENTRY_ACL_USER - || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id_w(wp, id); - id = -1; } - *(*wp)++ = L':'; - *(*wp)++ = (perm & 0444) ? L'r' : L'-'; - *(*wp)++ = (perm & 0222) ? L'w' : L'-'; - *(*wp)++ = (perm & 0111) ? L'x' : L'-'; if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); } - **wp = L'\0'; } -int -archive_acl_text_l(struct archive_acl *acl, int flags, - const char **acl_text, size_t *acl_text_len, +/* + * Generate a text version of the ACL. The flags parameter controls + * the type and style of the generated ACL. + */ +char * +archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive_string_conv *sc) { int count; - size_t length; + ssize_t length; + size_t len; const char *name; const char *prefix; char separator; struct archive_acl_entry *ap; - size_t len; - int id, r; - char *p; + int id, r, want_type; + char *p, *s; - if (acl->acl_text != NULL) { - free (acl->acl_text); - acl->acl_text = NULL; - } + want_type = archive_acl_text_want_type(acl, flags); - *acl_text = NULL; - if (acl_text_len != NULL) - *acl_text_len = 0; - separator = ','; - count = 0; - length = 0; - ap = acl->acl_head; - while (ap != NULL) { - if ((ap->type & flags) != 0) { - count++; - if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && - (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) - length += 8; /* "default:" */ - length += 5; /* tag name */ - length += 1; /* colon */ - r = archive_mstring_get_mbs_l( - &ap->name, &name, &len, sc); - if (r != 0) - return (-1); - if (len > 0 && name != NULL) - length += len; - else - length += sizeof(uid_t) * 3 + 1; - length ++; /* colon */ - length += 3; /* rwx */ - length += 1; /* colon */ - length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; - length ++; /* newline */ - } - ap = ap->next; - } + /* Both NFSv4 and POSIX.1 types found */ + if (want_type == 0) + return (NULL); - if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { - length += 10; /* "user::rwx\n" */ - length += 11; /* "group::rwx\n" */ - length += 11; /* "other::rwx\n" */ - } + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) + flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; - if (count == 0) - return (0); + length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); + + if (length == 0) + return (NULL); + + if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) + separator = ','; + else + separator = '\n'; /* Now, allocate the string and actually populate it. */ - p = acl->acl_text = (char *)malloc(length); - if (p == NULL) - return (-1); + p = s = (char *)malloc(length * sizeof(char)); + if (p == NULL) { + if (errno == ENOMEM) + __archive_errx(1, "No memory"); + return (NULL); + } count = 0; - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, + + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); - *p++ = ','; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, + *p++ = separator; + append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); - *p++ = ','; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, + *p++ = separator; + append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); count += 3; - - for (ap = acl->acl_head; ap != NULL; ap = ap->next) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) - continue; - r = archive_mstring_get_mbs_l( - &ap->name, &name, &len, sc); - if (r != 0) - return (-1); - *p++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry(&p, NULL, ap->tag, name, - ap->permset, id); - count++; - } } - - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) + for (ap = acl->acl_head; ap != NULL; ap = ap->next) { + if ((ap->type & want_type) == 0) + continue; + /* + * Filemode-mapping ACL entries are stored exclusively in + * ap->mode so they should not be in the list + */ + if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) + && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ + || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ + || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) + continue; + if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && + (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) prefix = "default:"; else prefix = NULL; - count = 0; - for (ap = acl->acl_head; ap != NULL; ap = ap->next) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) - continue; - r = archive_mstring_get_mbs_l( - &ap->name, &name, &len, sc); - if (r != 0) - return (-1); - if (count > 0) - *p++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry(&p, prefix, ap->tag, - name, ap->permset, id); - count ++; + r = archive_mstring_get_mbs_l( + &ap->name, &name, &len, sc); + if (r != 0) + return (NULL); + if (count > 0) + *p++ = separator; + if (name == NULL || + (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { + id = ap->id; + } else { + id = -1; } + append_entry(&p, prefix, ap->type, ap->tag, flags, name, + ap->permset, id); + count++; } - *acl_text = acl->acl_text; - if (acl_text_len != NULL) - *acl_text_len = strlen(acl->acl_text); - return (0); + /* Add terminating character */ + *p++ = '\0'; + + len = strlen(s); + + if ((ssize_t)len > (length - 1)) + __archive_errx(1, "Buffer overrun"); + + if (text_len != NULL) + *text_len = len; + + return (s); } static void @@ -760,9 +1015,11 @@ append_id(char **p, int id) } static void -append_entry(char **p, const char *prefix, int tag, - const char *name, int perm, int id) +append_entry(char **p, const char *prefix, int type, + int tag, int flags, const char *name, int perm, int id) { + int i; + if (prefix != NULL) { strcpy(*p, prefix); *p += strlen(*p); @@ -771,6 +1028,10 @@ append_entry(char **p, const char *prefix, int tag, case ARCHIVE_ENTRY_ACL_USER_OBJ: name = NULL; id = -1; + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + strcpy(*p, "owner@"); + break; + } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: strcpy(*p, "user"); @@ -778,6 +1039,10 @@ append_entry(char **p, const char *prefix, int tag, case ARCHIVE_ENTRY_ACL_GROUP_OBJ: name = NULL; id = -1; + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + strcpy(*p, "group@"); + break; + } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: strcpy(*p, "group"); @@ -792,48 +1057,120 @@ append_entry(char **p, const char *prefix, int tag, name = NULL; id = -1; break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + strcpy(*p, "everyone@"); + name = NULL; + id = -1; + break; } *p += strlen(*p); *(*p)++ = ':'; - if (name != NULL) { - strcpy(*p, name); + if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || + tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + if (name != NULL) { + strcpy(*p, name); + *p += strlen(*p); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id(p, id); + if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) + id = -1; + } + /* Solaris style has no second colon after other and mask */ + if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) + || (tag != ARCHIVE_ENTRY_ACL_OTHER + && tag != ARCHIVE_ENTRY_ACL_MASK)) + *(*p)++ = ':'; + } + if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + /* POSIX.1e ACL perms */ + *(*p)++ = (perm & 0444) ? 'r' : '-'; + *(*p)++ = (perm & 0222) ? 'w' : '-'; + *(*p)++ = (perm & 0111) ? 'x' : '-'; + } else { + /* NFSv4 ACL perms */ + for (i = 0; i < nfsv4_acl_perm_map_size; i++) { + if (perm & nfsv4_acl_perm_map[i].perm) + *(*p)++ = nfsv4_acl_perm_map[i].c; + else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) + *(*p)++ = '-'; + } + *(*p)++ = ':'; + for (i = 0; i < nfsv4_acl_flag_map_size; i++) { + if (perm & nfsv4_acl_flag_map[i].perm) + *(*p)++ = nfsv4_acl_flag_map[i].c; + else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) + *(*p)++ = '-'; + } + *(*p)++ = ':'; + switch (type) { + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + strcpy(*p, "allow"); + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + strcpy(*p, "deny"); + break; + case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: + strcpy(*p, "audit"); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ALARM: + strcpy(*p, "alarm"); + break; + } *p += strlen(*p); - } else if (tag == ARCHIVE_ENTRY_ACL_USER - || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id(p, id); - id = -1; } - *(*p)++ = ':'; - *(*p)++ = (perm & 0444) ? 'r' : '-'; - *(*p)++ = (perm & 0222) ? 'w' : '-'; - *(*p)++ = (perm & 0111) ? 'x' : '-'; if (id != -1) { *(*p)++ = ':'; append_id(p, id); } - **p = '\0'; } /* - * Parse a textual ACL. This automatically recognizes and supports - * extensions described above. The 'type' argument is used to - * indicate the type that should be used for any entries not - * explicitly marked as "default:". + * Parse a wide ACL text string. + * + * The want_type argument may be one of the following: + * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT + * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL + * + * POSIX.1e ACL entries prefixed with "default:" are treated as + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 */ int -archive_acl_parse_w(struct archive_acl *acl, - const wchar_t *text, int default_type) +archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, + int want_type) { struct { const wchar_t *start; const wchar_t *end; - } field[4], name; + } field[6], name; + + const wchar_t *s, *st; - int fields, n; - int type, tag, permset, id; + int numfields, fields, n, r, sol, ret; + int type, types, tag, permset, id; + size_t len; wchar_t sep; - while (text != NULL && *text != L'\0') { + ret = ARCHIVE_OK; + types = 0; + + switch (want_type) { + case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: + want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + numfields = 5; + break; + case ARCHIVE_ENTRY_ACL_TYPE_NFS4: + numfields = 6; + break; + default: + return (ARCHIVE_FATAL); + } + + while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. @@ -842,7 +1179,7 @@ archive_acl_parse_w(struct archive_acl *acl, do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); - if (fields < 4) { + if (fields < numfields) { field[fields].start = start; field[fields].end = end; } @@ -850,78 +1187,210 @@ archive_acl_parse_w(struct archive_acl *acl, } while (sep == L':'); /* Set remaining fields to blank. */ - for (n = fields; n < 4; ++n) + for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; - /* Check for a numeric ID in field 1 or 3. */ - id = -1; - isint_w(field[1].start, field[1].end, &id); - /* Field 3 is optional. */ - if (id == -1 && fields > 3) - isint_w(field[3].start, field[3].end, &id); - - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. - */ - if (field[0].end - field[0].start > 7 - && wmemcmp(field[0].start, L"default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - field[0].start += 7; - } else - type = default_type; + if (field[0].start != NULL && *(field[0].start) == L'#') { + /* Comment, skip entry */ + continue; + } + n = 0; + sol = 0; + id = -1; + permset = 0; name.start = name.end = NULL; - if (prefix_w(field[0].start, field[0].end, L"user")) { - if (!ismode_w(field[2].start, field[2].end, &permset)) - return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_USER; - name = field[1]; + + if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* POSIX.1e ACLs */ + /* + * Default keyword "default:user::rwx" + * if found, we have one more field + * + * We also support old Solaris extension: + * "defaultuser::rwx" is the default ACL corresponding + * to "user::rwx", etc. valid only for first field + */ + s = field[0].start; + len = field[0].end - field[0].start; + if (*s == L'd' && (len == 1 || (len >= 7 + && wmemcmp((s + 1), L"efault", 6) == 0))) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + if (len > 7) + field[0].start += 7; + else + n = 1; } else - tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (prefix_w(field[0].start, field[0].end, L"group")) { - if (!ismode_w(field[2].start, field[2].end, &permset)) - return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_GROUP; + type = want_type; + + /* Check for a numeric ID in field n+1 or n+3. */ + isint_w(field[n + 1].start, field[n + 1].end, &id); + /* Field n+3 is optional. */ + if (id == -1 && fields > n+3) + isint_w(field[n + 3].start, field[n + 3].end, + &id); + + tag = 0; + s = field[n].start; + st = field[n].start + 1; + len = field[n].end - field[n].start; + + switch (*s) { + case L'u': + if (len == 1 || (len == 4 + && wmemcmp(st, L"ser", 3) == 0)) + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + break; + case L'g': + if (len == 1 || (len == 5 + && wmemcmp(st, L"roup", 4) == 0)) + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case L'o': + if (len == 1 || (len == 5 + && wmemcmp(st, L"ther", 4) == 0)) + tag = ARCHIVE_ENTRY_ACL_OTHER; + break; + case L'm': + if (len == 1 || (len == 4 + && wmemcmp(st, L"ask", 3) == 0)) + tag = ARCHIVE_ENTRY_ACL_MASK; + break; + default: + break; + } + + switch (tag) { + case ARCHIVE_ENTRY_ACL_OTHER: + case ARCHIVE_ENTRY_ACL_MASK: + if (fields == (n + 2) + && field[n + 1].start < field[n + 1].end + && ismode_w(field[n + 1].start, + field[n + 1].end, &permset)) { + /* This is Solaris-style "other:rwx" */ + sol = 1; + } else if (fields == (n + 3) && + field[n + 1].start < field[n + 1].end) { + /* Invalid mask or other field */ + ret = ARCHIVE_WARN; + continue; + } + break; + case ARCHIVE_ENTRY_ACL_USER_OBJ: + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + if (id != -1 || + field[n + 1].start < field[n + 1].end) { + name = field[n + 1]; + if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) + tag = ARCHIVE_ENTRY_ACL_USER; + else + tag = ARCHIVE_ENTRY_ACL_GROUP; + } + break; + default: + /* Invalid tag, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + + /* + * Without "default:" we expect mode in field 2 + * Exception: Solaris other and mask fields + */ + if (permset == 0 && !ismode_w(field[n + 2 - sol].start, + field[n + 2 - sol].end, &permset)) { + /* Invalid mode, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + } else { + /* NFS4 ACLs */ + s = field[0].start; + len = field[0].end - field[0].start; + tag = 0; + + switch (len) { + case 4: + if (wmemcmp(s, L"user", 4) == 0) + tag = ARCHIVE_ENTRY_ACL_USER; + break; + case 5: + if (wmemcmp(s, L"group", 5) == 0) + tag = ARCHIVE_ENTRY_ACL_GROUP; + break; + case 6: + if (wmemcmp(s, L"owner@", 6) == 0) + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + else if (wmemcmp(s, L"group@", len) == 0) + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case 9: + if (wmemcmp(s, L"everyone@", 9) == 0) + tag = ARCHIVE_ENTRY_ACL_EVERYONE; + default: + break; + } + + if (tag == 0) { + /* Invalid tag, skip entry */ + ret = ARCHIVE_WARN; + continue; + } else if (tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + n = 1; name = field[1]; + isint_w(name.start, name.end, &id); } else - tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (prefix_w(field[0].start, field[0].end, L"other")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode_w(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "other:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode_w(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "other::rwx" */ - } else - return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_OTHER; - } else if (prefix_w(field[0].start, field[0].end, L"mask")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode_w(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "mask:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode_w(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "mask::rwx" */ - } else - return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_MASK; - } else - return (ARCHIVE_WARN); + n = 0; + + if (!is_nfs4_perms_w(field[1 + n].start, + field[1 + n].end, &permset)) { + /* Invalid NFSv4 perms, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + if (!is_nfs4_flags_w(field[2 + n].start, + field[2 + n].end, &permset)) { + /* Invalid NFSv4 flags, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + s = field[3 + n].start; + len = field[3 + n].end - field[3 + n].start; + type = 0; + if (len == 4) { + if (wmemcmp(s, L"deny", 4) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_DENY; + } else if (len == 5) { + if (wmemcmp(s, L"allow", 5) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; + else if (wmemcmp(s, L"audit", 5) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; + else if (wmemcmp(s, L"alarm", 5) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; + } + if (type == 0) { + /* Invalid entry type, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + isint_w(field[4 + n].start, field[4 + n].end, &id); + } /* Add entry to the internal list. */ - archive_acl_add_entry_w_len(acl, type, permset, + r = archive_acl_add_entry_w_len(acl, type, permset, tag, id, name.start, name.end - name.start); + if (r < ARCHIVE_WARN) + return (r); + if (r != ARCHIVE_OK) + ret = ARCHIVE_WARN; + types |= type; } - return (ARCHIVE_OK); + + /* Reset ACL */ + archive_acl_reset(acl, types); + + return (ret); } /* @@ -967,16 +1436,122 @@ ismode_w(const wchar_t *start, const wchar_t *end, int *permset) *permset = 0; while (p < end) { switch (*p++) { - case 'r': case 'R': + case L'r': case L'R': *permset |= ARCHIVE_ENTRY_ACL_READ; break; - case 'w': case 'W': + case L'w': case L'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE; break; - case 'x': case 'X': + case L'x': case L'X': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; - case '-': + case L'-': + break; + default: + return (0); + } + } + return (1); +} + +/* + * Parse a string as a NFS4 ACL permission field. + * Returns true if the string is non-empty and consists only of NFS4 ACL + * permission characters, false otherwise + */ +static int +is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) +{ + const wchar_t *p = start; + + while (p < end) { + switch (*p++) { + case L'r': + *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; + break; + case L'w': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; + break; + case L'x': + *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; + break; + case L'p': + *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; + break; + case L'D': + *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; + break; + case L'd': + *permset |= ARCHIVE_ENTRY_ACL_DELETE; + break; + case L'a': + *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; + break; + case L'A': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; + break; + case L'R': + *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; + break; + case L'W': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; + break; + case L'c': + *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; + break; + case L'C': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; + break; + case L'o': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; + break; + case L's': + *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; + break; + case L'-': + break; + default: + return(0); + } + } + return (1); +} + +/* + * Parse a string as a NFS4 ACL flags field. + * Returns true if the string is non-empty and consists only of NFS4 ACL + * flag characters, false otherwise + */ +static int +is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) +{ + const wchar_t *p = start; + + while (p < end) { + switch(*p++) { + case L'f': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; + break; + case L'd': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; + break; + case L'i': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; + break; + case L'n': + *permset |= + ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; + break; + case L'S': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; + break; + case L'F': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; + break; + case L'I': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; + break; + case L'-': break; default: return (0); @@ -1023,46 +1598,48 @@ next_field_w(const wchar_t **wp, const wchar_t **start, } /* - * Return true if the characters [start...end) are a prefix of 'test'. - * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. - */ -static int -prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) -{ - if (start == end) - return (0); - - if (*start++ != *test++) - return (0); - - while (start < end && *start++ == *test++) - ; - - if (start < end) - return (0); - - return (1); -} - -/* - * Parse a textual ACL. This automatically recognizes and supports - * extensions described above. The 'type' argument is used to - * indicate the type that should be used for any entries not - * explicitly marked as "default:". + * Parse an ACL text string. + * + * The want_type argument may be one of the following: + * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT + * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL + * + * POSIX.1e ACL entries prefixed with "default:" are treated as + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 */ int -archive_acl_parse_l(struct archive_acl *acl, - const char *text, int default_type, struct archive_string_conv *sc) +archive_acl_from_text_l(struct archive_acl *acl, const char *text, + int want_type, struct archive_string_conv *sc) { struct { const char *start; const char *end; - } field[4], name; + } field[6], name; - int fields, n, r, ret = ARCHIVE_OK; - int type, tag, permset, id; + const char *s, *st; + int numfields, fields, n, r, sol, ret; + int type, types, tag, permset, id; + size_t len; char sep; + switch (want_type) { + case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: + want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + numfields = 5; + break; + case ARCHIVE_ENTRY_ACL_TYPE_NFS4: + numfields = 6; + break; + default: + return (ARCHIVE_FATAL); + } + + ret = ARCHIVE_OK; + types = 0; + while (text != NULL && *text != '\0') { /* * Parse the fields out of the next entry, @@ -1072,7 +1649,7 @@ archive_acl_parse_l(struct archive_acl *acl, do { const char *start, *end; next_field(&text, &start, &end, &sep); - if (fields < 4) { + if (fields < numfields) { field[fields].start = start; field[fields].end = end; } @@ -1080,72 +1657,197 @@ archive_acl_parse_l(struct archive_acl *acl, } while (sep == ':'); /* Set remaining fields to blank. */ - for (n = fields; n < 4; ++n) + for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; - /* Check for a numeric ID in field 1 or 3. */ - id = -1; - isint(field[1].start, field[1].end, &id); - /* Field 3 is optional. */ - if (id == -1 && fields > 3) - isint(field[3].start, field[3].end, &id); - - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. - */ - if (field[0].end - field[0].start > 7 - && memcmp(field[0].start, "default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - field[0].start += 7; - } else - type = default_type; + if (field[0].start != NULL && *(field[0].start) == '#') { + /* Comment, skip entry */ + continue; + } + n = 0; + sol = 0; + id = -1; + permset = 0; name.start = name.end = NULL; - if (prefix_c(field[0].start, field[0].end, "user")) { - if (!ismode(field[2].start, field[2].end, &permset)) - return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_USER; - name = field[1]; + + if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* POSIX.1e ACLs */ + /* + * Default keyword "default:user::rwx" + * if found, we have one more field + * + * We also support old Solaris extension: + * "defaultuser::rwx" is the default ACL corresponding + * to "user::rwx", etc. valid only for first field + */ + s = field[0].start; + len = field[0].end - field[0].start; + if (*s == 'd' && (len == 1 || (len >= 7 + && memcmp((s + 1), "efault", 6) == 0))) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + if (len > 7) + field[0].start += 7; + else + n = 1; } else - tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (prefix_c(field[0].start, field[0].end, "group")) { - if (!ismode(field[2].start, field[2].end, &permset)) - return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_GROUP; + type = want_type; + + /* Check for a numeric ID in field n+1 or n+3. */ + isint(field[n + 1].start, field[n + 1].end, &id); + /* Field n+3 is optional. */ + if (id == -1 && fields > (n + 3)) + isint(field[n + 3].start, field[n + 3].end, + &id); + + tag = 0; + s = field[n].start; + st = field[n].start + 1; + len = field[n].end - field[n].start; + + switch (*s) { + case 'u': + if (len == 1 || (len == 4 + && memcmp(st, "ser", 3) == 0)) + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + break; + case 'g': + if (len == 1 || (len == 5 + && memcmp(st, "roup", 4) == 0)) + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case 'o': + if (len == 1 || (len == 5 + && memcmp(st, "ther", 4) == 0)) + tag = ARCHIVE_ENTRY_ACL_OTHER; + break; + case 'm': + if (len == 1 || (len == 4 + && memcmp(st, "ask", 3) == 0)) + tag = ARCHIVE_ENTRY_ACL_MASK; + break; + default: + break; + } + + switch (tag) { + case ARCHIVE_ENTRY_ACL_OTHER: + case ARCHIVE_ENTRY_ACL_MASK: + if (fields == (n + 2) + && field[n + 1].start < field[n + 1].end + && ismode(field[n + 1].start, + field[n + 1].end, &permset)) { + /* This is Solaris-style "other:rwx" */ + sol = 1; + } else if (fields == (n + 3) && + field[n + 1].start < field[n + 1].end) { + /* Invalid mask or other field */ + ret = ARCHIVE_WARN; + continue; + } + break; + case ARCHIVE_ENTRY_ACL_USER_OBJ: + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + if (id != -1 || + field[n + 1].start < field[n + 1].end) { + name = field[n + 1]; + if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) + tag = ARCHIVE_ENTRY_ACL_USER; + else + tag = ARCHIVE_ENTRY_ACL_GROUP; + } + break; + default: + /* Invalid tag, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + + /* + * Without "default:" we expect mode in field 3 + * Exception: Solaris other and mask fields + */ + if (permset == 0 && !ismode(field[n + 2 - sol].start, + field[n + 2 - sol].end, &permset)) { + /* Invalid mode, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + } else { + /* NFS4 ACLs */ + s = field[0].start; + len = field[0].end - field[0].start; + tag = 0; + + switch (len) { + case 4: + if (memcmp(s, "user", 4) == 0) + tag = ARCHIVE_ENTRY_ACL_USER; + break; + case 5: + if (memcmp(s, "group", 5) == 0) + tag = ARCHIVE_ENTRY_ACL_GROUP; + break; + case 6: + if (memcmp(s, "owner@", 6) == 0) + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + else if (memcmp(s, "group@", 6) == 0) + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case 9: + if (memcmp(s, "everyone@", 9) == 0) + tag = ARCHIVE_ENTRY_ACL_EVERYONE; + break; + default: + break; + } + + if (tag == 0) { + /* Invalid tag, skip entry */ + ret = ARCHIVE_WARN; + continue; + } else if (tag == ARCHIVE_ENTRY_ACL_USER || + tag == ARCHIVE_ENTRY_ACL_GROUP) { + n = 1; name = field[1]; + isint(name.start, name.end, &id); } else - tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (prefix_c(field[0].start, field[0].end, "other")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "other:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "other::rwx" */ - } else - return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_OTHER; - } else if (prefix_c(field[0].start, field[0].end, "mask")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "mask:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "mask::rwx" */ - } else - return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_MASK; - } else - return (ARCHIVE_WARN); + n = 0; + + if (!is_nfs4_perms(field[1 + n].start, + field[1 + n].end, &permset)) { + /* Invalid NFSv4 perms, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + if (!is_nfs4_flags(field[2 + n].start, + field[2 + n].end, &permset)) { + /* Invalid NFSv4 flags, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + s = field[3 + n].start; + len = field[3 + n].end - field[3 + n].start; + type = 0; + if (len == 4) { + if (memcmp(s, "deny", 4) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_DENY; + } else if (len == 5) { + if (memcmp(s, "allow", 5) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; + else if (memcmp(s, "audit", 5) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; + else if (memcmp(s, "alarm", 5) == 0) + type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; + } + if (type == 0) { + /* Invalid entry type, skip entry */ + ret = ARCHIVE_WARN; + continue; + } + isint(field[4 + n].start, field[4 + n].end, + &id); + } /* Add entry to the internal list. */ r = archive_acl_add_entry_len_l(acl, type, permset, @@ -1154,7 +1856,12 @@ archive_acl_parse_l(struct archive_acl *acl, return (r); if (r != ARCHIVE_OK) ret = ARCHIVE_WARN; + types |= type; } + + /* Reset ACL */ + archive_acl_reset(acl, types); + return (ret); } @@ -1220,6 +1927,112 @@ ismode(const char *start, const char *end, int *permset) } /* + * Parse a string as a NFS4 ACL permission field. + * Returns true if the string is non-empty and consists only of NFS4 ACL + * permission characters, false otherwise + */ +static int +is_nfs4_perms(const char *start, const char *end, int *permset) +{ + const char *p = start; + + while (p < end) { + switch (*p++) { + case 'r': + *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; + break; + case 'w': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; + break; + case 'x': + *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; + break; + case 'p': + *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; + break; + case 'D': + *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; + break; + case 'd': + *permset |= ARCHIVE_ENTRY_ACL_DELETE; + break; + case 'a': + *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; + break; + case 'A': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; + break; + case 'R': + *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; + break; + case 'W': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; + break; + case 'c': + *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; + break; + case 'C': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; + break; + case 'o': + *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; + break; + case 's': + *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; + break; + case '-': + break; + default: + return(0); + } + } + return (1); +} + +/* + * Parse a string as a NFS4 ACL flags field. + * Returns true if the string is non-empty and consists only of NFS4 ACL + * flag characters, false otherwise + */ +static int +is_nfs4_flags(const char *start, const char *end, int *permset) +{ + const char *p = start; + + while (p < end) { + switch(*p++) { + case 'f': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; + break; + case 'd': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; + break; + case 'i': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; + break; + case 'n': + *permset |= + ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; + break; + case 'S': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; + break; + case 'F': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; + break; + case 'I': + *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; + break; + case '-': + break; + default: + return (0); + } + } + return (1); +} + +/* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last @@ -1254,25 +2067,3 @@ next_field(const char **p, const char **start, if (**p != '\0') (*p)++; } - -/* - * Return true if the characters [start...end) are a prefix of 'test'. - * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. - */ -static int -prefix_c(const char *start, const char *end, const char *test) -{ - if (start == end) - return (0); - - if (*start++ != *test++) - return (0); - - while (start < end && *start++ == *test++) - ; - - if (start < end) - return (0); - - return (1); -} diff --git a/Utilities/cmlibarchive/libarchive/archive_acl_private.h b/Utilities/cmlibarchive/libarchive/archive_acl_private.h index 1421adb..ef0b023 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_acl_private.h @@ -56,6 +56,7 @@ struct archive_acl { void archive_acl_clear(struct archive_acl *); void archive_acl_copy(struct archive_acl *, struct archive_acl *); int archive_acl_count(struct archive_acl *, int); +int archive_acl_types(struct archive_acl *); int archive_acl_reset(struct archive_acl *, int); int archive_acl_next(struct archive *, struct archive_acl *, int, int *, int *, int *, int *, const char **); @@ -66,22 +67,17 @@ int archive_acl_add_entry_w_len(struct archive_acl *, int archive_acl_add_entry_len(struct archive_acl *, int, int, int, int, const char *, size_t); -const wchar_t *archive_acl_text_w(struct archive *, struct archive_acl *, int); -int archive_acl_text_l(struct archive_acl *, int, const char **, size_t *, +wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int, + struct archive *); +char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int, struct archive_string_conv *); /* - * Private ACL parser. This is private because it handles some - * very weird formats that clients should not be messing with. - * Clients should only deal with their platform-native formats. - * Because of the need to support many formats cleanly, new arguments - * are likely to get added on a regular basis. Clients who try to use - * this interface are likely to be surprised when it changes. + * ACL text parser. */ -int archive_acl_parse_w(struct archive_acl *, - const wchar_t *, int /* type */); -int archive_acl_parse_l(struct archive_acl *, - const char *, int /* type */, - struct archive_string_conv *); +int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */, + int /* type */); +int archive_acl_from_text_l(struct archive_acl *, const char * /* text */, + int /* type */, struct archive_string_conv *); #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor.c b/Utilities/cmlibarchive/libarchive/archive_cryptor.c index 2a51dfe..ced52fd 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor.c +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor.c @@ -302,7 +302,8 @@ aes_ctr_release(archive_crypto_ctx *ctx) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { - ctx->ctx = EVP_CIPHER_CTX_new(); + if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) + return -1; switch (key_len) { case 16: ctx->type = EVP_aes_128_ecb(); break; diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.c b/Utilities/cmlibarchive/libarchive/archive_entry.c index 4ac1966..d4f061b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry.c @@ -1,5 +1,6 @@ /*- * 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 @@ -248,10 +249,9 @@ archive_entry_new2(struct archive *a) { struct archive_entry *entry; - entry = (struct archive_entry *)malloc(sizeof(*entry)); + entry = (struct archive_entry *)calloc(1, sizeof(*entry)); if (entry == NULL) return (NULL); - memset(entry, 0, sizeof(*entry)); entry->archive = a; return (entry); } @@ -1442,6 +1442,15 @@ archive_entry_acl_add_entry_w(struct archive_entry *entry, } /* + * 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 @@ -1478,34 +1487,121 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, } /* - * Generate a text version of the ACL. The flags parameter controls + * 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) { - const wchar_t *r; - r = archive_acl_text_w(entry->archive, &entry->acl, flags); - if (r == NULL && errno == ENOMEM) - __archive_errx(1, "No memory"); - return (r); + 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) { - const char *p; - if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0 - && errno == ENOMEM) - __archive_errx(1, "No memory"); - return (p); + 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) { - return (archive_acl_text_l(&entry->acl, flags, acl_text, len, 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); } /* diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h index 81cd425..c331117 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry.h @@ -1,5 +1,6 @@ /*- * 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 @@ -29,7 +30,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3002001 +#define ARCHIVE_VERSION_NUMBER 3003000 /* * Note: archive_entry.h is for use outside of libarchive; the @@ -65,6 +66,27 @@ typedef int64_t la_int64_t; # 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 <unistd.h> /* 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' */ @@ -104,6 +126,9 @@ typedef int64_t la_int64_t; # define __LA_DECL #endif +/* CMake uses some deprecated APIs to build with old libarchive versions. */ +#define __LA_DEPRECATED + #ifdef __cplusplus extern "C" { #endif @@ -420,6 +445,7 @@ __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const voi /* * 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 @@ -433,15 +459,16 @@ __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const voi | 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_FAILED_ACCESS \ + | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) /* We need to be able to specify combinations of these. */ -#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256 /* POSIX.1e only */ -#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512 /* POSIX.1e only */ -#define ARCHIVE_ENTRY_ACL_TYPE_ALLOW 1024 /* NFS4 only */ -#define ARCHIVE_ENTRY_ACL_TYPE_DENY 2048 /* NFS4 only */ -#define ARCHIVE_ENTRY_ACL_TYPE_AUDIT 4096 /* NFS4 only */ -#define ARCHIVE_ENTRY_ACL_TYPE_ALARM 8192 /* NFS4 only */ +#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 \ @@ -492,21 +519,51 @@ __LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type * 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_TYPE_NFS4 - Include NFS4 entries. - * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in - * each ACL entry. ('star' introduced this for POSIX.1e, this flag - * also applies to NFS4.) * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each - * default ACL entry, as used in old Solaris ACLs. + * 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 1024 -#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 +#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 *, + la_ssize_t * /* len */, int /* flags */); +__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *, + 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 */); + int /* flags */) __LA_DEPRECATED; __LA_DECL const char *archive_entry_acl_text(struct archive_entry *, - int /* flags */); + 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 */); diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 b/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 index 5aff996..c5115f7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 @@ -1,4 +1,5 @@ .\" Copyright (c) 2010 Joerg Sonnenberger +.\" Copyright (c) 2016 Martin Matuska .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 2, 2012 +.Dd February 15, 2017 .Dt ARCHIVE_ENTRY_ACL 3 .Os .Sh NAME @@ -30,10 +31,14 @@ .Nm archive_entry_acl_add_entry_w , .Nm archive_entry_acl_clear , .Nm archive_entry_acl_count , +.Nm archive_entry_acl_from_text , +.Nm archive_entry_acl_from_text_w, .Nm archive_entry_acl_next , .Nm archive_entry_acl_next_w , .Nm archive_entry_acl_reset , -.Nm archive_entry_acl_text_w +.Nm archive_entry_acl_to_text , +.Nm archive_entry_acl_to_text_w , +.Nm archive_entry_acl_types .Nd functions for manipulating Access Control Lists in archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -62,6 +67,18 @@ Streaming Archive Library (libarchive, -larchive) .Ft int .Fn archive_entry_acl_count "struct archive_entry *a" "int type" .Ft int +.Fo archive_entry_acl_from_text +.Fa "struct archive_entry *a" +.Fa "const char *text" +.Fa "int type" +.Fc +.Ft int +.Fo archive_entry_acl_from_text_w +.Fa "struct archive_entry *a" +.Fa "const wchar_t *text" +.Fa "int type" +.Fc +.Ft int .Fo archive_entry_acl_next .Fa "struct archive_entry *a" .Fa "int type" @@ -83,31 +100,48 @@ Streaming Archive Library (libarchive, -larchive) .Fc .Ft int .Fn archive_entry_acl_reset "struct archive_entry *a" "int type" -.Ft const wchar_t * -.Fn archive_entry_acl_text_w "struct archive_entry *a" "int flags" +.Ft char * +.Fo archive_entry_acl_to_text +.Fa "struct archive_entry *a" +.Fa "ssize_t *len_p" +.Fa "int flags" +.Fc +.Ft wchar_t * +.Fo archive_entry_acl_to_text_w +.Fa "struct archive_entry *a" +.Fa "ssize_t *len_p" +.Fa "int flags" +.Fc +.Ft int +.Fn archive_entry_acl_types "struct archive_entry *a" .\" enum? .Sh DESCRIPTION -An -.Dq Access Control List -is a generalisation of the classic Unix permission system. +The +.Dq Access Control Lists (ACLs) +extend the standard Unix perssion model. The ACL interface of .Nm libarchive -is derived from the POSIX.1e draft, but restricted to simplify dealing -with practical implementations in various Operating Systems and archive formats. -.Pp -An ACL consists of a number of independent entries. +supports both POSIX.1e and NFSv4 style ACLs. Use of ACLs is restricted by +various levels of ACL support in operating systems, file systems and archive +formats. +.Ss POSIX.1e Access Control Lists +A POSIX.1e ACL consists of a number of independent entries. Each entry specifies the permission set as bitmask of basic permissions. -Valid permissions are: +Valid permissions in the +.Fa permset +are: .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_EXECUTE" -.It Dv ARCHIVE_ENTRY_ACL_EXECUTE -.It Dv ARCHIVE_ENTRY_ACL_WRITE -.It Dv ARCHIVE_ENTRY_ACL_READ +.It Dv ARCHIVE_ENTRY_ACL_READ ( Sy r ) +.It Dv ARCHIVE_ENTRY_ACL_WRITE ( Sy w ) +.It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x ) .El The permissions correspond to the normal Unix permissions. .Pp -The tag specifies the principal to which the permission applies. +The +.Fa tag +specifies the principal to which the permission applies. Valid values are: -.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ" +.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ" .It Dv ARCHIVE_ENTRY_ACL_USER The user specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ @@ -119,8 +153,9 @@ The group who owns the file. .It Dv ARCHIVE_ENTRY_ACL_MASK The maximum permissions to be obtained via group permissions. .It Dv ARCHIVE_ENTRY_ACL_OTHER -Any principal who doesn't have a user or group entry. +Any principal who is not file owner or a member of the owning group. .El +.Pp The principals .Dv ARCHIVE_ENTRY_ACL_USER_OBJ , .Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ @@ -129,19 +164,123 @@ and are equivalent to user, group and other in the classic Unix permission model and specify non-extended ACL entries. .Pp -All files have an access ACL +All files with have an access ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS . This specifies the permissions required for access to the file itself. Directories have an additional ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT , which controls the initial access ACL for newly created directory entries. +.Ss NFSv4 Access Control Lists +A NFSv4 ACL consists of multiple individual entries called Access Control +Entries (ACEs). +.Pp +There are four possible types of a NFSv4 ACE: +.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYE_ALLOW" +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW +Allow principal to perform actions requiring given permissions. +.It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY +Prevent principal from performing actions requiring given permissions. +.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT +Log access attempts by principal which require given permissions. +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM +Trigger a system alarm on access attempts by principal which require given +permissions. +.El +.Pp +The +.Fa tag +specifies the principal to which the permission applies. +Valid values are: +.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ" +.It Dv ARCHIVE_ENTRY_ACL_USER +The user specified by the name field. +.It Dv ARCHIVE_ENTRY_ACL_USER_OBJ +The owner of the file. +.It Dv ARCHIVE_ENTRY_ACL_GROUP +The group specied by the name field. +.It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ +The group who owns the file. +.It Dv ARCHIVE_ENTRY_ACL_EVERYONE +Any principal who is not file owner or a member of the owning group. +.El +.Pp +Entries with the +.Dv ARCHIVE_ENTRY_ACL_USER +or +.Dv ARCHIVE_ENTRY_ACL_GROUP +tag store the user and group name in the +.Fa name +string and optionally the user or group ID in the +.Fa qualifier +integer. +.Pp +NFSv4 ACE permissions and flags are stored in the same +.Fa permset +bitfield. Some permissions share the same constant and permission character but +have different effect on directories than on files. The following ACE +permissions are supported: +.Bl -tag -offset indent -compact -width ARCHIV +.It Dv ARCHIVE_ENTRY_ACL_READ_DATA ( Sy r ) +Read data (file). +.It Dv ARCHIVE_ENTRY_ACL_LIST_DIRECTORY ( Sy r ) +List entries (directory). +.It ARCHIVE_ENTRY_ACL_WRITE_DATA ( Sy w ) +Write data (file). +.It ARCHIVE_ENTRY_ACL_ADD_FILE ( Sy w ) +Create files (directory). +.It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x ) +Execute file or change into a directory. +.It Dv ARCHIVE_ENTRY_ACL_APPEND_DATA ( Sy p ) +Append data (file). +.It Dv ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY ( Sy p ) +Create subdirectories (directory). +.It Dv ARCHIVE_ENTRY_ACL_DELETE_CHILD ( Sy D ) +Remove files and subdirectories inside a directory. +.It Dv ARCHIVE_ENTRY_ACL_DELETE ( Sy d ) +Remove file or directory. +.It Dv ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES ( Sy a ) +Read file or directory attributes. +.It Dv ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES ( Sy A ) +Write file or directory attributes. +.It Dv ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS ( Sy R ) +Read named file or directory attributes. +.It Dv ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS ( Sy W ) +Write named file or directory attributes. +.It Dv ARCHIVE_ENTRY_ACL_READ_ACL ( Sy c ) +Read file or directory ACL. +.It Dv ARCHIVE_ENTRY_ACL_WRITE_ACL ( Sy C ) +Write file or directory ACL. +.It Dv ARCHIVE_ENTRY_ACL_WRITE_OWNER ( Sy o ) +Change owner of a file or directory. +.It Dv ARCHIVE_ENTRY_ACL_SYNCHRONIZE ( Sy s ) +Use synchronous I/O. +.El .Pp +The following NFSv4 ACL inheritance flags are supported: +.Bl -tag -offset indent -compact -width ARCHIV +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT ( Sy f ) +Inherit parent directory ACE to files. +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT ( Sy d ) +Inherit parent directory ACE to subdirectories. +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY ( Sy i ) +Only inherit, do not apply the permission on the directory itself. +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n ) +Do not propagate inherit flags. Only first-level entries inherit ACLs. +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S ) +Trigger alarm or audit on succesful access. +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F ) +Trigger alarm or audit on failed access. +.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERITED ( Sy I ) +Mark that ACE was inherited. +.El +.Ss Functions .Fn archive_entry_acl_add_entry and .Fn archive_entry_acl_add_entry_w add a single ACL entry. For the access ACL and non-extended principals, the classic Unix permissions -are updated. +are updated. An archive enry cannot contain both POSIX.1e and NFSv4 ACL +entries. .Pp .Fn archive_entry_acl_clear removes all ACL entries and resets the enumeration pointer. @@ -150,14 +289,58 @@ removes all ACL entries and resets the enumeration pointer. counts the ACL entries that have the given type mask. .Fa type can be the bitwise-or of -.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS -and -.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT . -If +.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT" +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS +.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT +.El +for POSIX.1e ACLs and +.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_ALLOW" +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW +.It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY +.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM +.El +for NFSv4 ACLs. For POSIX.1e ACLs if .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS is included and at least one extended ACL entry is found, the three non-extened ACLs are added. .Pp +.Fn archive_entry_acl_from_text +and +.Fn archive_entry_acl_from_text_w +add new +.Pq or merge with existing +ACL entries from +.Pq wide +text. The argument +.Fa type +may take one of the following values: +.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT" +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS +.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT +.It Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4 +.El +Supports all formats that can be created with +.Fn archive_entry_acl_to_text +or respective +.Fn archive_entry_acl_to_text_w . +Existing ACL entries are preserved. To get a clean new ACL from text +.Fn archive_entry_acl_clear +must be called first. Entries prefixed with +.Dq default: +are treated as +.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT +unless +.Fa type +is +.Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4 . +Invalid entries, non-parseable ACL entries and entries beginning with +the +.Sq # +character +.Pq comments +are skipped. +.Pp .Fn archive_entry_acl_next and .Fn archive_entry_acl_next_w @@ -179,29 +362,81 @@ or set using Otherwise, the function returns the same value as .Fn archive_entry_acl_count . .Pp -.Fn archive_entry_acl_text_w -converts the ACL entries for the given type mask into a wide string. -In addition to the normal type flags, -.Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID +.Fn archive_entry_acl_to_text and -.Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT -can be specified to further customize the result. -The returned long string is valid until the next call to -.Fn archive_entry_acl_clear , -.Fn archive_entry_acl_add_entry , -.Fn archive_entry_acl_add_entry_w +.Fn archive_entry_acl_to_text_w +convert the ACL entries for the given type into a +.Pq wide +string of ACL entries separated by newline. If the the pointer +.Fa len_p +is not NULL, then the function shall return the length of the string +.Pq not including the NULL terminator +in the location pointed to by +.Fa len_p . +The +.Fa flag +argument is a bitwise-or. +.Pp +The following flags are effective only on POSIX.1e ACL: +.Bl -tag -offset indent -compact -width ARCHIV +.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS +Output access ACLs. +.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT +Output POSIX.1e default ACLs. +.It Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT +Prefix each default ACL entry with the word +.Dq default: . +.It Dv ARCHIVE_ENTRY_ACL_STYLE_SOLARIS +The mask and other ACLs don not contain a double colon. +.El +.Pp +The following flags are effecive only on NFSv4 ACL: +.Bl -tag -offset indent -compact -width ARCHIV +.It Dv ARCHIVE_ENTRY_ACL_STYLE_COMPACT +Do not output minus characters for unset permissions and flags in NFSv4 ACL +permission and flag fields. +.El +.Pp +The following flags are effective on both POSIX.1e and NFSv4 ACL: +.Bl -tag -offset indent -compact -width ARCHIV +.It Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID +Add an additional colon-separated field containing the user or group id. +.It Dv ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA +Separate ACL entries with comma instead of newline. +.El +.Pp +If the archive entry contains NFSv4 ACLs, all types of NFSv4 ACLs are returned. +It the entry contains POSIX.1e ACLs and none of the flags +.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS or -.Fn archive_entry_acl_text_w . +.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT +are specified, both access and default entries are returned and default entries +are prefixed with +.Dq default: . +.Pp +.Fn archive_entry_acl_types +get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4 +ACL entries cannot be mixed, this function is a very efficient way to detect if +an ACL already contains POSIX.1e or NFSv4 ACL entries. .Sh RETURN VALUES .Fn archive_entry_acl_count and .Fn archive_entry_acl_reset returns the number of ACL entries that match the given type mask. -If the type mask includes +For POSIX.1e ACLS if the type mask includes .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS and at least one extended ACL entry exists, the three classic Unix permissions are counted. .Pp +.Fn archive_entry_acl_from_text +and +.Fn archive_entry_acl_from_text_w +return +.Dv ARCHIVE_OK +if all entries were successfully parsed and +.Dv ARCHIVE_WARN +if one or more entries were invalid or non-parseable. +.Pp .Fn archive_entry_acl_next and .Fn archive_entry_acl_next_w @@ -216,20 +451,16 @@ if .Fn archive_entry_acl_reset has not been called first. .Pp -.Fn archive_entry_text_w -returns a wide string representation of the ACL entrise matching the -given type mask. -The returned long string is valid until the next call to -.Fn archive_entry_acl_clear , -.Fn archive_entry_acl_add_entry , -.Fn archive_entry_acl_add_entry_w -or -.Fn archive_entry_acl_text_w . +.Fn archive_entry_acl_to_text +returns a string representing the ACL entries matching the given type and +flags on success or NULL on error. +.Pp +.Fn archive_entry_acl_to_text_w +returns a wide string representing the ACL entries matching the given type +and flags on success or NULL on error. +.Pp +.Fn archive_entry_acl_types +returns a bitmask of ACL entry types or 0 if archive entry has no ACL entries. .Sh SEE ALSO -.Xr archive_entry 3 -.Xr libarchive 3 , -.Sh BUGS -.Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID -and -.Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT -are not documented. +.Xr archive_entry 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_locale.h b/Utilities/cmlibarchive/libarchive/archive_entry_locale.h index 02e024a..44550c5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_locale.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry_locale.h @@ -63,9 +63,13 @@ int _archive_entry_uname_l(struct archive_entry *, const char **, size_t *, struct archive_string_conv *); #define archive_entry_acl_text_l _archive_entry_acl_text_l int _archive_entry_acl_text_l(struct archive_entry *, int, - const char **, size_t *, struct archive_string_conv *); - - +const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED; +#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l +char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int, + struct archive_string_conv *); +#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l +int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text, + int type, struct archive_string_conv *); #define archive_entry_copy_gname_l _archive_entry_copy_gname_l int _archive_entry_copy_gname_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c b/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c index 16cb3f7..af2517a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c @@ -80,7 +80,7 @@ archive_entry_strmode(struct archive_entry *entry) if (mode & 0001) bp[9] = 't'; else bp[9] = 'T'; } - if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)) + if (archive_entry_acl_types(entry) != 0) bp[10] = '+'; return (bp); diff --git a/Utilities/cmlibarchive/libarchive/archive_match.c b/Utilities/cmlibarchive/libarchive/archive_match.c index 4c41bad..be72066 100644 --- a/Utilities/cmlibarchive/libarchive/archive_match.c +++ b/Utilities/cmlibarchive/libarchive/archive_match.c @@ -471,7 +471,7 @@ archive_match_path_excluded(struct archive *_a, } /* - * Utilty functions to get statistic information for inclusion patterns. + * Utility functions to get statistic information for inclusion patterns. */ int archive_match_path_unmatched_inclusions(struct archive *_a) @@ -655,7 +655,7 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist, } } - /* If something error happend, report it immediately. */ + /* If an error occurred, report it immediately. */ if (r < ARCHIVE_OK) { archive_copy_error(&(a->archive), ar); archive_read_free(ar); @@ -1270,7 +1270,7 @@ set_timefilter_pathname_wcs(struct archive_match *a, int timetype, #endif /* _WIN32 && !__CYGWIN__ */ /* - * Call back funtions for archive_rb. + * Call back functions for archive_rb. */ static int cmp_node_mbs(const struct archive_rb_node *n1, @@ -1405,7 +1405,7 @@ add_entry(struct archive_match *a, int flag, &(a->exclusion_tree), pathname); /* - * We always overwrite comparison condision. + * We always overwrite comparison condition. * If you do not want to overwrite it, you should not * call archive_match_exclude_entry(). We cannot know * what behavior you really expect since overwriting @@ -1481,7 +1481,7 @@ time_excluded(struct archive_match *a, struct archive_entry *entry) if (nsec == a->older_ctime_nsec && (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL) == 0) - return (1); /* Eeual, skip it. */ + return (1); /* Equal, skip it. */ } } if (a->newer_mtime_filter) { @@ -1513,7 +1513,7 @@ time_excluded(struct archive_match *a, struct archive_entry *entry) } } - /* If there is no excluson list, include the file. */ + /* If there is no exclusion list, include the file. */ if (a->exclusion_entry_list.count == 0) return (0); @@ -1700,7 +1700,7 @@ add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id) break; } - /* Add oowner id. */ + /* Add owner id. */ if (i == ids->count) ids->ids[ids->count++] = id; else if (ids->ids[i] != id) { diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h index 0e97e27..43a3ccc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h @@ -33,10 +33,7 @@ #include <string.h> /* memset */ static inline EVP_MD_CTX *EVP_MD_CTX_new(void) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)malloc(sizeof(EVP_MD_CTX)); - if (ctx != NULL) { - memset(ctx, 0, sizeof(*ctx)); - } + EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); return ctx; } diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h index d4ae0d1..2deeb5f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h @@ -33,11 +33,7 @@ #include <string.h> /* memset */ static inline HMAC_CTX *HMAC_CTX_new(void) { - HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof(HMAC_CTX)); - if (ctx != NULL) { - memset(ctx, 0, sizeof(*ctx)); - HMAC_CTX_init(ctx); - } + HMAC_CTX *ctx = (HMAC_CTX *)calloc(1, sizeof(HMAC_CTX)); return ctx; } diff --git a/Utilities/cmlibarchive/libarchive/archive_options.c b/Utilities/cmlibarchive/libarchive/archive_options.c index dbf3e80..6496025 100644 --- a/Utilities/cmlibarchive/libarchive/archive_options.c +++ b/Utilities/cmlibarchive/libarchive/archive_options.c @@ -26,6 +26,10 @@ #include "archive_platform.h" __FBSDID("$FreeBSD$"); +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + #include "archive_options_private.h" static const char * @@ -105,8 +109,11 @@ _archive_set_options(struct archive *a, const char *options, if (options == NULL || options[0] == '\0') return ARCHIVE_OK; - data = (char *)malloc(strlen(options) + 1); - strcpy(data, options); + if ((data = strdup(options)) == NULL) { + archive_set_error(a, + ENOMEM, "Out of memory adding file to list"); + return (ARCHIVE_FATAL); + } s = (const char *)data; do { diff --git a/Utilities/cmlibarchive/libarchive/archive_platform.h b/Utilities/cmlibarchive/libarchive/archive_platform.h index 08a384c..47d144b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform.h @@ -152,8 +152,25 @@ * 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 && HAVE_ACL_USER +#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 <sys/acl.h>, 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 /* @@ -164,6 +181,15 @@ #define CAN_RESTORE_METADATA_FD #endif +/* + * glibc 2.24 deprecates readdir_r + */ +#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24)) +#define USE_READDIR_R 1 +#else +#undef USE_READDIR_R +#endif + /* Set up defaults for internal error codes. */ #ifndef ARCHIVE_ERRNO_FILE_FORMAT #if HAVE_EFTYPE diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h b/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h index 3a6b9eb..06c99e8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h @@ -19,7 +19,7 @@ If you need the compatibility with original PPMd var.H, you can use external Ran #define PPMD7_MAX_ORDER 64 #define PPMD7_MIN_MEM_SIZE (1 << 11) -#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3) +#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 12 * 3) struct CPpmd7_Context_; diff --git a/Utilities/cmlibarchive/libarchive/archive_random.c b/Utilities/cmlibarchive/libarchive/archive_random.c index a20b9b1..357f973 100644 --- a/Utilities/cmlibarchive/libarchive/archive_random.c +++ b/Utilities/cmlibarchive/libarchive/archive_random.c @@ -80,7 +80,7 @@ archive_random(void *buf, size_t nbytes) success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - if (!success && GetLastError() == NTE_BAD_KEYSET) { + if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) { success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET); } diff --git a/Utilities/cmlibarchive/libarchive/archive_rb.c b/Utilities/cmlibarchive/libarchive/archive_rb.c index 5b5da20..cf58ac3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_rb.c +++ b/Utilities/cmlibarchive/libarchive/archive_rb.c @@ -312,7 +312,7 @@ __archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt, father = RB_FATHER(self); if (RB_BLACK_P(father)) { /* - * If our greatgrandpa is black, we're done. + * If our great-grandpa is black, we're done. */ return; } diff --git a/Utilities/cmlibarchive/libarchive/archive_read.c b/Utilities/cmlibarchive/libarchive/archive_read.c index 0bbacc8..d1feceb 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read.c +++ b/Utilities/cmlibarchive/libarchive/archive_read.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:2 static int choose_filters(struct archive_read *); static int choose_format(struct archive_read *); +static int close_filters(struct archive_read *); static struct archive_vtable *archive_read_vtable(void); static int64_t _archive_filter_bytes(struct archive *, int); static int _archive_filter_code(struct archive *, int); @@ -528,7 +529,7 @@ archive_read_open1(struct archive *_a) { slot = choose_format(a); if (slot < 0) { - __archive_read_close_filters(a); + close_filters(a); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } @@ -582,7 +583,6 @@ choose_filters(struct archive_read *a) /* Verify the filter by asking it for some data. */ __archive_read_filter_ahead(a->filter, 1, &avail); if (avail < 0) { - __archive_read_close_filters(a); __archive_read_free_filters(a); return (ARCHIVE_FATAL); } @@ -601,7 +601,6 @@ choose_filters(struct archive_read *a) a->filter = filter; r = (best_bidder->init)(a->filter); if (r != ARCHIVE_OK) { - __archive_read_close_filters(a); __archive_read_free_filters(a); return (ARCHIVE_FATAL); } @@ -765,7 +764,7 @@ archive_read_header_position(struct archive *_a) * 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 uncapable of encryption support. + * 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. @@ -986,8 +985,8 @@ _archive_read_data_block(struct archive *_a, return (a->format->read_data)(a, buff, size, offset); } -int -__archive_read_close_filters(struct archive_read *a) +static int +close_filters(struct archive_read *a) { struct archive_read_filter *f = a->filter; int r = ARCHIVE_OK; @@ -1010,6 +1009,9 @@ __archive_read_close_filters(struct archive_read *a) void __archive_read_free_filters(struct archive_read *a) { + /* Make sure filters are closed and their buffers are freed */ + close_filters(a); + while (a->filter != NULL) { struct archive_read_filter *t = a->filter->upstream; free(a->filter); @@ -1052,7 +1054,7 @@ _archive_read_close(struct archive *_a) /* TODO: Clean up the formatters. */ /* Release the filter objects. */ - r1 = __archive_read_close_filters(a); + r1 = close_filters(a); if (r1 < r) r = r1; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c index f67f1eb..cf821b5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c @@ -125,7 +125,7 @@ void __archive_read_reset_passphrase(struct archive_read *a) { - a->passphrases.candiate = -1; + a->passphrases.candidate = -1; } /* @@ -137,31 +137,31 @@ __archive_read_next_passphrase(struct archive_read *a) struct archive_read_passphrase *p; const char *passphrase; - if (a->passphrases.candiate < 0) { + if (a->passphrases.candidate < 0) { /* Count out how many passphrases we have. */ int cnt = 0; for (p = a->passphrases.first; p != NULL; p = p->next) cnt++; - a->passphrases.candiate = cnt; + a->passphrases.candidate = cnt; p = a->passphrases.first; - } else if (a->passphrases.candiate > 1) { + } else if (a->passphrases.candidate > 1) { /* Rotate a passphrase list. */ - a->passphrases.candiate--; + a->passphrases.candidate--; p = remove_passphrases_from_head(a); add_passphrase_to_tail(a, p); - /* Pick a new passphrase candiate up. */ + /* Pick a new passphrase candidate up. */ p = a->passphrases.first; - } else if (a->passphrases.candiate == 1) { - /* This case is that all cadiates failed to decryption. */ - a->passphrases.candiate = 0; + } else if (a->passphrases.candidate == 1) { + /* This case is that all candidates failed to decrypt. */ + a->passphrases.candidate = 0; if (a->passphrases.first->next != NULL) { /* Rotate a passphrase list. */ p = remove_passphrases_from_head(a); add_passphrase_to_tail(a, p); } p = NULL; - } else /* There is no passphrase candaite. */ + } else /* There is no passphrase candidate. */ p = NULL; if (p != NULL) @@ -177,7 +177,7 @@ __archive_read_next_passphrase(struct archive_read *a) if (p == NULL) return (NULL); insert_passphrase_to_head(a, p); - a->passphrases.candiate = 1; + a->passphrases.candidate = 1; } } else passphrase = NULL; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c b/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c index 3a0d4d6..5e4d163 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c @@ -133,7 +133,6 @@ archive_read_append_filter(struct archive *_a, int code) a->filter = filter; r2 = (bidder->init)(a->filter); if (r2 != ARCHIVE_OK) { - __archive_read_close_filters(a); __archive_read_free_filters(a); return (ARCHIVE_FATAL); } @@ -191,7 +190,6 @@ archive_read_append_filter_program_signature(struct archive *_a, a->filter = filter; r = (bidder->init)(a->filter); if (r != ARCHIVE_OK) { - __archive_read_close_filters(a); __archive_read_free_filters(a); return (ARCHIVE_FATAL); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk.3 b/Utilities/cmlibarchive/libarchive/archive_read_disk.3 index 525dc59..2a5c130 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 2, 2012 +.Dd December 30, 2016 .Dt ARCHIVE_READ_DISK 3 .Os .Sh NAME @@ -54,9 +54,9 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_read_disk_set_symlink_physical "struct archive *" .Ft int .Fn archive_read_disk_set_symlink_hybrid "struct archive *" -.Ft int +.Ft const char * .Fn archive_read_disk_gname "struct archive *" "gid_t" -.Ft int +.Ft const char * .Fn archive_read_disk_uname "struct archive *" "uid_t" .Ft int .Fo archive_read_disk_set_gname_lookup diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c index 74fe353..591a212 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c @@ -1,6 +1,7 @@ /*- * 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 @@ -37,6 +38,11 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #ifdef HAVE_SYS_ACL_H #include <sys/acl.h> #endif +#ifdef HAVE_DARWIN_ACL +#include <membership.h> +#include <grp.h> +#include <pwd.h> +#endif #ifdef HAVE_SYS_EXTATTR_H #include <sys/extattr.h> #endif @@ -117,6 +123,15 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #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 *, @@ -125,6 +140,10 @@ 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, @@ -302,20 +321,17 @@ setup_mac_metadata(struct archive_read_disk *a, 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) { @@ -400,34 +416,87 @@ setup_mac_metadata(struct archive_read_disk *a, } #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 -#ifdef HAVE_POSIX_ACL +#if HAVE_POSIX_ACL || HAVE_NFS4_ACL static int translate_acl(struct archive_read_disk *a, - struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); + 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; - acl_t acl; -#if HAVE_ACL_IS_TRIVIAL_NP - int r; +#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 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); + } + } archive_entry_acl_clear(entry); -#ifdef ACL_TYPE_NFS4 - /* Try NFS4 ACL first. */ + 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, ACL_TYPE_NFS4); + acl = acl_get_link_np(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) @@ -436,21 +505,62 @@ setup_acls(struct archive_read_disk *a, acl = NULL; #endif else - acl = acl_get_file(accpath, ACL_TYPE_NFS4); -#if HAVE_ACL_IS_TRIVIAL_NP +#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. */ - acl_is_trivial_np(acl, &r); - if (r) { - acl_free(acl); - acl = NULL; - } + 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) { - translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); + r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); acl_free(acl); - return (ARCHIVE_OK); - } + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "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_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) @@ -467,88 +577,609 @@ setup_acls(struct archive_read_disk *a, #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) { - translate_acl(a, entry, acl, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + 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"); + 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) { - translate_acl(a, entry, acl, + 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"); + return (r); + } } } +#endif /* HAVE_POSIX_ACL */ return (ARCHIVE_OK); } /* - * Translate system ACL into libarchive internal structure. + * Translate system ACL permissions into libarchive internal structure */ - -static struct { - int archive_perm; - int platform_perm; +static const struct { + const int archive_perm; + const int platform_perm; } acl_perm_map[] = { - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, - {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, -#ifdef ACL_TYPE_NFS4 - {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} +#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 */ }; -#ifdef ACL_TYPE_NFS4 -static struct { - int archive_inherit; - int platform_inherit; +#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[] = { - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, +#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_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 +#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; -#ifdef ACL_TYPE_NFS4 +#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; - int brand, r; #endif acl_entry_t acl_entry; acl_permset_t acl_permset; int i, entry_acl_type; - int s, ae_id, ae_tag, ae_perm; + int r, s, ae_id, ae_tag, ae_perm; +#if !HAVE_DARWIN_ACL + void *q; +#endif const char *ae_name; - -#ifdef ACL_TYPE_NFS4 +#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. - acl_get_brand_np(acl, &brand); + 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) { @@ -556,40 +1187,67 @@ translate_acl(struct archive_read_disk *a, case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: break; default: - // XXX set warning message? - return ARCHIVE_FAILED; + 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) { - // XXX set warning message? - return ARCHIVE_FAILED; + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid ACL entry type for NFSv4 ACL"); + return (ARCHIVE_WARN); } break; default: - // XXX set warning message? - return ARCHIVE_FAILED; - break; + 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); - while (s == 1) { + 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; - acl_get_tag_type(acl_entry, &acl_tag); + 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: - ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); - ae_name = archive_read_disk_uname(&a->archive, ae_id); + 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: - ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); - ae_name = archive_read_disk_gname(&a->archive, ae_id); + 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: @@ -604,24 +1262,52 @@ translate_acl(struct archive_read_disk *a, case ACL_OTHER: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; break; -#ifdef ACL_TYPE_NFS4 +#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; } - // XXX acl type maps to allow/deny/audit/YYYY bits - // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for - // non-NFSv4 ACLs +#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; -#ifdef ACL_TYPE_NFS4 - r = acl_get_entry_type_np(acl_entry, &acl_type); - if (r == 0) { +#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; @@ -635,29 +1321,54 @@ translate_acl(struct archive_read_disk *a, 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); } - } - - /* - * Libarchive stores "flag" (NFSv4 inheritance bits) - * in the ae_perm bitmap. - */ - acl_get_flagset_np(acl_entry, &acl_flagset); - for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { - if (acl_get_flag_np(acl_flagset, - acl_inherit_map[i].platform_inherit)) - ae_perm |= acl_inherit_map[i].archive_inherit; +#endif /* HAVE_ACL_TYPE_NFS4 */ - } -#endif + /* + * 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 */ - acl_get_permset(acl_entry, &acl_permset); + 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. */ - if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm)) + 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; } @@ -666,10 +1377,18 @@ translate_acl(struct archive_read_disk *a, 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); } -#else +#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) @@ -679,7 +1398,7 @@ setup_acls(struct archive_read_disk *a, (void)fd; /* UNUSED */ return (ARCHIVE_OK); } -#endif +#endif /* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */ #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \ HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \ @@ -699,15 +1418,10 @@ setup_acls(struct archive_read_disk *a, 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) @@ -772,21 +1486,23 @@ setup_xattrs(struct archive_read_disk *a, 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); } } @@ -849,7 +1565,7 @@ setup_xattrs(struct archive_read_disk *a, 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); @@ -870,19 +1586,16 @@ setup_xattrs(struct archive_read_disk *a, */ 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); @@ -932,21 +1645,23 @@ setup_xattrs(struct archive_read_disk *a, 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); } } @@ -996,7 +1711,7 @@ setup_xattrs(struct archive_read_disk *a, 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; } @@ -1024,7 +1739,7 @@ setup_xattrs(struct archive_read_disk *a, #if defined(HAVE_LINUX_FIEMAP_H) /* - * Linux sparse interface. + * 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 @@ -1039,7 +1754,7 @@ setup_xattrs(struct archive_read_disk *a, */ static int -setup_sparse(struct archive_read_disk *a, +setup_sparse_fiemap(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[4096]; @@ -1090,8 +1805,8 @@ setup_sparse(struct archive_read_disk *a, if (r < 0) { /* When something error happens, it is better we * should return ARCHIVE_OK because an earlier - * version(<2.6.28) cannot perfom FS_IOC_FIEMAP. */ - goto exit_setup_sparse; + * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ + goto exit_setup_sparse_fiemap; } if (fm->fm_mapped_extents == 0) { if (iters == 0) { @@ -1126,14 +1841,24 @@ setup_sparse(struct archive_read_disk *a, } else break; } -exit_setup_sparse: +exit_setup_sparse_fiemap: return (exit_sts); } -#elif defined(SEEK_HOLE) && defined(SEEK_DATA) && defined(_PC_MIN_HOLE_SIZE) +#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) /* - * FreeBSD and Solaris sparse interface. + * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) */ static int @@ -1141,8 +1866,8 @@ setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int64_t size; - off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */ - off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */ + off_t initial_off; + off_t off_s, off_e; int exit_sts = ARCHIVE_OK; int check_fully_sparse = 0; @@ -1168,8 +1893,10 @@ setup_sparse(struct archive_read_disk *a, } 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); @@ -1180,8 +1907,10 @@ setup_sparse(struct archive_read_disk *a, 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, @@ -1192,6 +1921,19 @@ setup_sparse(struct archive_read_disk *a, 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) { @@ -1223,7 +1965,7 @@ setup_sparse(struct archive_read_disk *a, goto exit_setup_sparse; } if (off_s == 0 && off_e == size) - break;/* This is not spase. */ + break;/* This is not sparse. */ archive_entry_sparse_add_entry(entry, off_s, off_e - off_s); off_s = off_e; @@ -1241,7 +1983,7 @@ exit_setup_sparse: return (exit_sts); } -#else +#elif !defined(HAVE_LINUX_FIEMAP_H) /* * Generic (stub) sparse support. diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c index 22a1f14..b893704 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c @@ -165,7 +165,7 @@ struct filesystem { int synthetic; int remote; int noatime; -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) size_t name_max; #endif long incr_xfer_size; @@ -200,7 +200,7 @@ struct tree { DIR *d; #define INVALID_DIR_HANDLE NULL struct dirent *de; -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) struct dirent *dirent; size_t dirent_allocated; #endif @@ -244,7 +244,7 @@ struct tree { int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; - int allocated_filesytem; + int allocated_filesystem; int entry_fd; int entry_eof; @@ -675,7 +675,7 @@ setup_suitable_read_buffer(struct archive_read_disk *a) asize = cf->min_xfer_size; /* Increase a buffer size up to 64K bytes in - * a proper incremant size. */ + * a proper increment size. */ while (asize < 1024*64) asize += incr; /* Take a margin to adjust to the filesystem @@ -938,7 +938,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, - "Faild : %s", archive_error_string(a->matching)); + "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1026,7 +1026,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, - * especially, invokng a callback. */ + * 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); @@ -1041,7 +1041,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, - "Faild : %s", archive_error_string(a->matching)); + "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1067,7 +1067,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, - "Faild : %s", archive_error_string(a->matching)); + "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1382,7 +1382,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { - /* There is the filesytem ID we've already generated. */ + /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); @@ -1390,10 +1390,10 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) } /* - * This is the new filesytem which we have to generate a new ID for. + * 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_filesytem) { + if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; @@ -1406,7 +1406,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; - t->allocated_filesytem = s; + t->allocated_filesystem = s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); @@ -1504,7 +1504,20 @@ 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) @@ -1579,7 +1592,7 @@ setup_current_filesystem(struct archive_read_disk *a) #endif t->current_filesystem->noatime = 0; -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) /* Set maximum filename length. */ #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; @@ -1602,7 +1615,7 @@ setup_current_filesystem(struct archive_read_disk *a) else t->current_filesystem->name_max = nm; #endif -#endif /* HAVE_READDIR_R */ +#endif /* USE_READDIR_R */ return (ARCHIVE_OK); } @@ -1643,7 +1656,7 @@ setup_current_filesystem(struct archive_read_disk *a) archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { - /* Usuall come here unless NetBSD supports _PC_REC_XFER_ALIGN + /* 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; @@ -1804,7 +1817,7 @@ setup_current_filesystem(struct archive_read_disk *a) #endif t->current_filesystem->noatime = 0; -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namelen; #endif @@ -1888,7 +1901,7 @@ setup_current_filesystem(struct archive_read_disk *a) #endif t->current_filesystem->noatime = 0; -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; #endif @@ -1905,7 +1918,7 @@ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; -#if defined(_PC_NAME_MAX) && defined(HAVE_READDIR_R) +#if defined(_PC_NAME_MAX) && defined(USE_READDIR_R) long nm; #endif t->current_filesystem->synthetic = -1;/* Not supported */ @@ -1917,7 +1930,7 @@ setup_current_filesystem(struct archive_read_disk *a) t->current_filesystem->min_xfer_size = -1; t->current_filesystem->incr_xfer_size = -1; -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) if (tree_current_is_symblic_link_target(t)) { @@ -1931,7 +1944,7 @@ setup_current_filesystem(struct archive_read_disk *a) if (nm == -1) # endif /* _PC_NAME_MAX */ /* - * Some sysmtes (HP-UX or others?) incorrectly defined + * Some systems (HP-UX or others?) incorrectly defined * NAME_MAX macro to be a smaller value. */ # if defined(NAME_MAX) && NAME_MAX >= 255 @@ -1945,7 +1958,7 @@ setup_current_filesystem(struct archive_read_disk *a) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ -#endif /* HAVE_READDIR_R */ +#endif /* USE_READDIR_R */ return (ARCHIVE_OK); } @@ -2050,8 +2063,7 @@ tree_push(struct tree *t, const char *path, int filesystem_id, { struct tree_entry *te; - te = malloc(sizeof(*te)); - memset(te, 0, sizeof(*te)); + te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) @@ -2109,9 +2121,8 @@ tree_open(const char *path, int symlink_mode, int restore_time) { struct tree *t; - if ((t = malloc(sizeof(*t))) == NULL) + if ((t = calloc(1, sizeof(*t))) == NULL) return (NULL); - memset(t, 0, sizeof(*t)); archive_string_init(&t->path); archive_string_ensure(&t->path, 31); t->initial_symlink_mode = symlink_mode; @@ -2353,7 +2364,7 @@ tree_dir_next_posix(struct tree *t) size_t namelen; if (t->d == NULL) { -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) size_t dirent_size; #endif @@ -2374,7 +2385,7 @@ tree_dir_next_posix(struct tree *t) t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } -#if defined(HAVE_READDIR_R) +#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) { @@ -2391,11 +2402,11 @@ tree_dir_next_posix(struct tree *t) } t->dirent_allocated = dirent_size; } -#endif /* HAVE_READDIR_R */ +#endif /* USE_READDIR_R */ } for (;;) { errno = 0; -#if defined(HAVE_READDIR_R) +#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 @@ -2647,7 +2658,7 @@ tree_free(struct tree *t) if (t == NULL) return; archive_string_free(&t->path); -#if defined(HAVE_READDIR_R) +#if defined(USE_READDIR_R) free(t->dirent); #endif free(t->sparse_list); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c index d6b2d55..c7fd247 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c @@ -232,6 +232,7 @@ static const char * lookup_uname_helper(struct name_cache *cache, id_t id) { struct passwd *result; + (void)cache; /* UNUSED */ result = getpwuid((uid_t)id); @@ -298,6 +299,7 @@ static const char * lookup_gname_helper(struct name_cache *cache, id_t id) { struct group *result; + (void)cache; /* UNUSED */ result = getgrgid((gid_t)id); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c index 566d264..27b75e3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c @@ -168,7 +168,7 @@ struct tree { int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; - int allocated_filesytem; + int allocated_filesystem; HANDLE entry_fh; int entry_eof; @@ -389,10 +389,9 @@ archive_read_disk_new(void) { struct archive_read_disk *a; - a = (struct archive_read_disk *)malloc(sizeof(*a)); + a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); - memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); @@ -803,7 +802,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, - "Faild : %s", archive_error_string(a->matching)); + "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { @@ -863,7 +862,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, - * especially, invokng a callback. */ + * especially, invoking a callback. */ t->restore_time.lastWriteTime = st->ftLastWriteTime; t->restore_time.lastAccessTime = st->ftLastAccessTime; t->restore_time.filetype = archive_entry_filetype(entry); @@ -875,7 +874,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, - "Faild : %s", archive_error_string(a->matching)); + "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { @@ -901,7 +900,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, - "Faild : %s", archive_error_string(a->matching)); + "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1261,7 +1260,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { - /* There is the filesytem ID we've already generated. */ + /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); @@ -1269,10 +1268,10 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) } /* - * There is a new filesytem, we generate a new ID for. + * There is a new filesystem, we generate a new ID for. */ fid = t->max_filesystem_id++; - if (t->max_filesystem_id > t->allocated_filesytem) { + if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; @@ -1285,7 +1284,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; - t->allocated_filesytem = (int)s; + t->allocated_filesystem = (int)s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); @@ -1402,7 +1401,7 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype) return (0); - /* Close a file descritor. + /* Close a file descriptor. * It will not be used for SetFileTime() because it has been opened * by a read only mode. */ @@ -1437,8 +1436,7 @@ tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path, { struct tree_entry *te; - te = malloc(sizeof(*te)); - memset(te, 0, sizeof(*te)); + te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) @@ -1507,8 +1505,7 @@ tree_open(const wchar_t *path, int symlink_mode, int restore_time) { struct tree *t; - t = malloc(sizeof(*t)); - memset(t, 0, sizeof(*t)); + t = calloc(1, sizeof(*t)); archive_string_init(&(t->full_path)); archive_string_init(&t->path); archive_wstring_ensure(&t->path, 15); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_extract2.c b/Utilities/cmlibarchive/libarchive/archive_read_extract2.c index 7b2c126..4febd8c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_extract2.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_extract2.c @@ -52,12 +52,11 @@ struct archive_read_extract * __archive_read_get_extract(struct archive_read *a) { if (a->extract == NULL) { - a->extract = (struct archive_read_extract *)malloc(sizeof(*a->extract)); + a->extract = (struct archive_read_extract *)calloc(1, sizeof(*a->extract)); if (a->extract == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't extract"); return (NULL); } - memset(a->extract, 0, sizeof(*a->extract)); a->cleanup_archive_extract = archive_read_extract_cleanup; } return (a->extract); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c index 5611aa8..86635e2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c @@ -222,7 +222,7 @@ file_open(struct archive *a, void *client_data) void *buffer; const char *filename = NULL; const wchar_t *wfilename = NULL; - int fd; + int fd = -1; int is_disk_like = 0; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */ @@ -277,7 +277,7 @@ file_open(struct archive *a, void *client_data) #else archive_set_error(a, ARCHIVE_ERRNO_MISC, "Unexpedted operation in archive_read_open_filename"); - return (ARCHIVE_FATAL); + goto fail; #endif } if (fstat(fd, &st) != 0) { @@ -287,7 +287,7 @@ file_open(struct archive *a, void *client_data) else archive_set_error(a, errno, "Can't stat '%s'", filename); - return (ARCHIVE_FATAL); + goto fail; } /* @@ -356,11 +356,9 @@ file_open(struct archive *a, void *client_data) mine->block_size = new_block_size; } buffer = malloc(mine->block_size); - if (mine == NULL || buffer == NULL) { + if (buffer == NULL) { archive_set_error(a, ENOMEM, "No memory"); - free(mine); - free(buffer); - return (ARCHIVE_FATAL); + goto fail; } mine->buffer = buffer; mine->fd = fd; @@ -372,6 +370,14 @@ file_open(struct archive *a, void *client_data) mine->use_lseek = 1; return (ARCHIVE_OK); +fail: + /* + * Don't close file descriptors not opened or ones pointing referring + * to `FNT_STDIN`. + */ + if (fd != -1 && fd != 0) + close(fd); + return (ARCHIVE_FATAL); } static ssize_t diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c b/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c index ff935a7..311be47 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c @@ -70,12 +70,11 @@ archive_read_open_memory2(struct archive *a, const void *buff, { struct read_memory_data *mine; - mine = (struct read_memory_data *)malloc(sizeof(*mine)); + mine = (struct read_memory_data *)calloc(1, sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } - memset(mine, 0, sizeof(*mine)); mine->start = mine->p = (const unsigned char *)buff; mine->end = mine->start + size; mine->read_size = read_size; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_private.h b/Utilities/cmlibarchive/libarchive/archive_read_private.h index 9b61a53..78546dc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_read_private.h @@ -221,7 +221,7 @@ struct archive_read { struct { struct archive_read_passphrase *first; struct archive_read_passphrase **last; - int candiate; + int candidate; archive_passphrase_callback *callback; void *client_data; } passphrases; @@ -252,7 +252,6 @@ int64_t __archive_read_consume(struct archive_read *, int64_t); int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); int __archive_read_program(struct archive_read_filter *, const char *); void __archive_read_free_filters(struct archive_read *); -int __archive_read_close_filters(struct archive_read *); struct archive_read_extract *__archive_read_get_extract(struct archive_read *); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c index e877917..663e2d3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c @@ -180,7 +180,7 @@ lz4_reader_bid(struct archive_read_filter_bidder *self, return (0); bits_checked += 8; BD = buffer[5]; - /* A block maximum size shuld be more than 3. */ + /* A block maximum size should be more than 3. */ if (((BD & 0x70) >> 4) < 4) return (0); /* Reserved bits must be "0". */ @@ -417,7 +417,7 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) /* Reserved bits must be zero. */ if (bd & 0x8f) goto malformed_error; - /* Get a maxinum block size. */ + /* Get a maximum block size. */ switch (read_buf[1] >> 4) { case 4: /* 64 KB */ state->flags.block_maximum_size = 64 * 1024; @@ -595,7 +595,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) #endif } - /* Check if an error happend in decompression process. */ + /* Check if an error occurred in the decompression process. */ if (uncompressed_size < 0) { archive_set_error(&(self->archive->archive), ARCHIVE_ERRNO_MISC, "lz4 decompression failed"); @@ -627,7 +627,7 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p) if (state->stage == SELECT_STREAM) { state->stage = READ_DEFAULT_STREAM; - /* First, read a desciprtor. */ + /* First, read a descriptor. */ if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK) return (ret); state->stage = READ_DEFAULT_BLOCK; @@ -706,6 +706,11 @@ lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p) /* Make sure we have a whole block. */ read_buf = __archive_read_filter_ahead(self->upstream, 4 + compressed, NULL); + if (read_buf == NULL) { + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, "truncated lz4 input"); + return (ARCHIVE_FATAL); + } ret = LZ4_decompress_safe(read_buf + 4, state->out_block, compressed, (int)state->out_block_size); if (ret < 0) { diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c index 23c18c7..4356b82 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c @@ -436,7 +436,7 @@ lzop_filter_read(struct archive_read_filter *self, const void **p) } /* - * Drive lzo uncompresison. + * Drive lzo uncompression. */ out_size = (lzo_uint)state->uncompressed_size; r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c index 66dc2f4..b8bf128 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c @@ -430,6 +430,7 @@ __archive_read_program(struct archive_read_filter *self, const char *cmd) &state->child_stdout); if (child == -1) { free(state->out_buf); + archive_string_free(&state->description); free(state); archive_set_error(&self->archive->archive, EINVAL, "Can't initialize filter; unable to run program \"%s\"", @@ -441,6 +442,7 @@ __archive_read_program(struct archive_read_filter *self, const char *cmd) if (state->child == NULL) { child_stop(self, state); free(state->out_buf); + archive_string_free(&state->description); free(state); archive_set_error(&self->archive->archive, EINVAL, "Can't initialize filter; unable to run program \"%s\"", diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c index 787a619..6412979 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c @@ -312,6 +312,7 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self, avail -= len; if (l == 6) { + /* "begin " */ if (!uuchar[*b]) return (0); /* Get a length of decoded bytes. */ @@ -319,30 +320,14 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self, if (l > 45) /* Normally, maximum length is 45(character 'M'). */ return (0); - while (l && len-nl > 0) { - if (l > 0) { - if (!uuchar[*b++]) - return (0); - if (!uuchar[*b++]) - return (0); - len -= 2; - --l; - } - if (l > 0) { - if (!uuchar[*b++]) - return (0); - --len; - --l; - } - if (l > 0) { - if (!uuchar[*b++]) - return (0); - --len; - --l; - } + if (l > len - nl) + return (0); /* Line too short. */ + while (l) { + if (!uuchar[*b++]) + return (0); + --len; + --l; } - if (len-nl < 0) - return (0); if (len-nl == 1 && (uuchar[*b] || /* Check sum. */ (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */ @@ -352,8 +337,8 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self, b += nl; if (avail && uuchar[*b]) return (firstline+30); - } - if (l == 13) { + } else if (l == 13) { + /* "begin-base64 " */ while (len-nl > 0) { if (!base64[*b++]) return (0); @@ -510,6 +495,13 @@ read_more: } llen = len; if ((nl == 0) && (uudecode->state != ST_UUEND)) { + if (total == 0 && ravail <= 0) { + /* There is nothing more to read, fail */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Missing format data"); + return (ARCHIVE_FATAL); + } /* * Save remaining data which does not contain * NL('\n','\r'). @@ -566,7 +558,7 @@ read_more: "Insufficient compressed data"); return (ARCHIVE_FATAL); } - /* Get length of undecoded bytes of curent line. */ + /* Get length of undecoded bytes of current line. */ l = UUDECODE(*b++); body--; if (l > body) { diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c index 8f48273..a188186 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c @@ -43,8 +43,6 @@ __FBSDID("$FreeBSD$"); #endif #if HAVE_LZMA_H #include <cm_lzma.h> -#elif HAVE_LZMADEC_H -#include <lzmadec.h> #endif #include "archive.h" @@ -82,19 +80,6 @@ static ssize_t xz_filter_read(struct archive_read_filter *, const void **); static int xz_filter_close(struct archive_read_filter *); static int xz_lzma_bidder_init(struct archive_read_filter *); -#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC - -struct private_data { - lzmadec_stream stream; - unsigned char *out_block; - size_t out_block_size; - int64_t total_out; - char eof; /* True = found end of compressed data. */ -}; - -/* Lzma-only filter */ -static ssize_t lzma_filter_read(struct archive_read_filter *, const void **); -static int lzma_filter_close(struct archive_read_filter *); #endif /* @@ -178,8 +163,6 @@ archive_read_support_filter_lzma(struct archive *_a) bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); -#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC - return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external lzma program for lzma decompression"); @@ -310,7 +293,7 @@ lzma_bidder_bid(struct archive_read_filter_bidder *self, /* Second through fifth bytes are dictionary size, stored in * little-endian order. The minimum dictionary size is * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option - * -d12 and the maxinam dictionary size is 1 << 27(128MiB) + * -d12 and the maximum dictionary size is 1 << 27(128MiB) * which the one uses with option -d27. * NOTE: A comment of LZMA SDK source code says this dictionary * range is from 1 << 12 to 1 << 30. */ @@ -601,9 +584,7 @@ lzip_init(struct archive_read_filter *self) return (ARCHIVE_FATAL); } ret = lzma_raw_decoder(&(state->stream), filters); -#if LZMA_VERSION < 50010000 free(filters[0].options); -#endif if (ret != LZMA_OK) { set_error(self, ret); return (ARCHIVE_FATAL); @@ -763,175 +744,6 @@ xz_filter_close(struct archive_read_filter *self) #else -#if HAVE_LZMADEC_H && HAVE_LIBLZMADEC - -/* - * If we have the older liblzmadec library, then we can handle - * LZMA streams but not XZ streams. - */ - -/* - * Setup the callbacks. - */ -static int -lzma_bidder_init(struct archive_read_filter *self) -{ - static const size_t out_block_size = 64 * 1024; - void *out_block; - struct private_data *state; - ssize_t ret, avail_in; - - self->code = ARCHIVE_FILTER_LZMA; - self->name = "lzma"; - - state = (struct private_data *)calloc(sizeof(*state), 1); - out_block = (unsigned char *)malloc(out_block_size); - if (state == NULL || out_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, - "Can't allocate data for lzma decompression"); - free(out_block); - free(state); - return (ARCHIVE_FATAL); - } - - self->data = state; - state->out_block_size = out_block_size; - state->out_block = out_block; - self->read = lzma_filter_read; - self->skip = NULL; /* not supported */ - self->close = lzma_filter_close; - - /* Prime the lzma library with 18 bytes of input. */ - state->stream.next_in = (unsigned char *)(uintptr_t) - __archive_read_filter_ahead(self->upstream, 18, &avail_in); - if (state->stream.next_in == NULL) - return (ARCHIVE_FATAL); - state->stream.avail_in = avail_in; - state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; - - /* Initialize compression library. */ - ret = lzmadec_init(&(state->stream)); - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); - if (ret == LZMADEC_OK) - return (ARCHIVE_OK); - - /* Library setup failed: Clean up. */ - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing lzma library"); - - /* Override the error message if we know what really went wrong. */ - switch (ret) { - case LZMADEC_HEADER_ERROR: - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid header"); - break; - case LZMADEC_MEM_ERROR: - archive_set_error(&self->archive->archive, ENOMEM, - "Internal error initializing compression library: " - "out of memory"); - break; - } - - free(state->out_block); - free(state); - self->data = NULL; - return (ARCHIVE_FATAL); -} - -/* - * Return the next block of decompressed data. - */ -static ssize_t -lzma_filter_read(struct archive_read_filter *self, const void **p) -{ - struct private_data *state; - size_t decompressed; - ssize_t avail_in, ret; - - state = (struct private_data *)self->data; - - /* Empty our output buffer. */ - state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; - - /* Try to fill the output buffer. */ - while (state->stream.avail_out > 0 && !state->eof) { - state->stream.next_in = (unsigned char *)(uintptr_t) - __archive_read_filter_ahead(self->upstream, 1, &avail_in); - if (state->stream.next_in == NULL && avail_in < 0) { - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "truncated lzma input"); - return (ARCHIVE_FATAL); - } - state->stream.avail_in = avail_in; - - /* Decompress as much as we can in one pass. */ - ret = lzmadec_decode(&(state->stream), avail_in == 0); - switch (ret) { - case LZMADEC_STREAM_END: /* Found end of stream. */ - state->eof = 1; - /* FALL THROUGH */ - case LZMADEC_OK: /* Decompressor made some progress. */ - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); - break; - case LZMADEC_BUF_ERROR: /* Insufficient input data? */ - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "Insufficient compressed data"); - return (ARCHIVE_FATAL); - default: - /* Return an error. */ - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "Lzma decompression failed"); - return (ARCHIVE_FATAL); - } - } - - decompressed = state->stream.next_out - state->out_block; - state->total_out += decompressed; - if (decompressed == 0) - *p = NULL; - else - *p = state->out_block; - return (decompressed); -} - -/* - * Clean up the decompressor. - */ -static int -lzma_filter_close(struct archive_read_filter *self) -{ - struct private_data *state; - int ret; - - state = (struct private_data *)self->data; - ret = ARCHIVE_OK; - switch (lzmadec_end(&(state->stream))) { - case LZMADEC_OK: - break; - default: - archive_set_error(&(self->archive->archive), - ARCHIVE_ERRNO_MISC, - "Failed to clean up %s compressor", - self->archive->archive.compression_name); - ret = ARCHIVE_FATAL; - } - - free(state->out_block); - free(state); - return (ret); -} - -#else - /* * * If we have no suitable library on this system, we can't actually do @@ -953,9 +765,6 @@ lzma_bidder_init(struct archive_read_filter *self) return (r); } -#endif /* HAVE_LZMADEC_H */ - - static int xz_bidder_init(struct archive_read_filter *self) { @@ -984,5 +793,4 @@ lzip_bidder_init(struct archive_read_filter *self) return (r); } - #endif /* HAVE_LZMA_H */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c index a33ea4f..96774f4 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c @@ -213,7 +213,7 @@ struct _7zip { int header_is_encoded; uint64_t header_bytes_remaining; unsigned long header_crc32; - /* Header offset to check that reading pointes of the file contens + /* Header offset to check that reading points of the file contents * will not exceed the header. */ uint64_t header_offset; /* Base offset of the archive file for a seek in case reading SFX. */ @@ -263,22 +263,22 @@ struct _7zip { /* * Decompressor controllers. */ - /* Decording LZMA1 and LZMA2 data. */ + /* Decoding LZMA1 and LZMA2 data. */ #ifdef HAVE_LZMA_H lzma_stream lzstream; int lzstream_valid; #endif - /* Decording bzip2 data. */ + /* Decoding bzip2 data. */ #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream bzstream; int bzstream_valid; #endif - /* Decording deflate data. */ + /* Decoding deflate data. */ #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; #endif - /* Decording PPMd data. */ + /* Decoding PPMd data. */ int ppmd7_stat; CPpmd7 ppmd7_context; CPpmd7z_RangeDec range_dec; @@ -552,7 +552,7 @@ skip_sfx(struct archive_read *a, ssize_t bytes_avail) /* * If bytes_avail > SFX_MIN_ADDR we do not have to call * __archive_read_seek() at this time since we have - * alredy had enough data. + * already had enough data. */ if (bytes_avail > SFX_MIN_ADDR) __archive_read_consume(a, SFX_MIN_ADDR); @@ -760,7 +760,7 @@ archive_read_format_7zip_read_header(struct archive_read *a, symsize += size; } if (symsize == 0) { - /* If there is no synname, handle it as a regular + /* If there is no symname, handle it as a regular * file. */ zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFREG; @@ -1056,10 +1056,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, #endif { lzma_options_delta delta_opt; - lzma_filter filters[LZMA_FILTERS_MAX]; -#if LZMA_VERSION < 50010000 - lzma_filter *ff; -#endif + lzma_filter filters[LZMA_FILTERS_MAX], *ff; int fi = 0; if (zip->lzstream_valid) { @@ -1144,9 +1141,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, else filters[fi].id = LZMA_FILTER_LZMA1; filters[fi].options = NULL; -#if LZMA_VERSION < 50010000 ff = &filters[fi]; -#endif r = lzma_properties_decode(&filters[fi], NULL, coder1->properties, (size_t)coder1->propertiesSize); if (r != LZMA_OK) { @@ -1158,9 +1153,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, filters[fi].id = LZMA_VLI_UNKNOWN; filters[fi].options = NULL; r = lzma_raw_decoder(&(zip->lzstream), filters); -#if LZMA_VERSION < 50010000 free(ff->options); -#endif if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); @@ -2431,6 +2424,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, switch (type) { case kEmptyStream: + if (h->emptyStreamBools != NULL) + return (-1); h->emptyStreamBools = calloc((size_t)zip->numFiles, sizeof(*h->emptyStreamBools)); if (h->emptyStreamBools == NULL) @@ -2451,6 +2446,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, return (-1); break; } + if (h->emptyFileBools != NULL) + return (-1); h->emptyFileBools = calloc(empty_streams, sizeof(*h->emptyFileBools)); if (h->emptyFileBools == NULL) @@ -2465,6 +2462,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, return (-1); break; } + if (h->antiBools != NULL) + return (-1); h->antiBools = calloc(empty_streams, sizeof(*h->antiBools)); if (h->antiBools == NULL) @@ -2491,6 +2490,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if ((ll & 1) || ll < zip->numFiles * 4) return (-1); + if (zip->entry_names != NULL) + return (-1); zip->entry_names = malloc(ll); if (zip->entry_names == NULL) return (-1); @@ -2543,6 +2544,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if ((p = header_bytes(a, 2)) == NULL) return (-1); allAreDefined = *p; + if (h->attrBools != NULL) + return (-1); h->attrBools = calloc((size_t)zip->numFiles, sizeof(*h->attrBools)); if (h->attrBools == NULL) @@ -3285,7 +3288,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, return (r); /* - * Skip the bytes we alrady has skipped in skip_stream(). + * Skip the bytes we already has skipped in skip_stream(). */ while (skip_bytes) { ssize_t skipped; @@ -3503,7 +3506,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, return (ARCHIVE_FATAL); } - /* Allocate memory for the decorded data of a sub + /* Allocate memory for the decoded data of a sub * stream. */ b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { @@ -3588,7 +3591,7 @@ skip_stream(struct archive_read *a, size_t skip_bytes) if (zip->folder_index == 0) { /* * Optimization for a list mode. - * Avoid unncecessary decoding operations. + * Avoid unnecessary decoding operations. */ zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes += skip_bytes; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c index 4b5b66b..b6b9fc3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c @@ -104,13 +104,12 @@ archive_read_support_format_ar(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_ar"); - ar = (struct ar *)malloc(sizeof(*ar)); + ar = (struct ar *)calloc(1, sizeof(*ar)); if (ar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); return (ARCHIVE_FATAL); } - memset(ar, 0, sizeof(*ar)); ar->strtab = NULL; r = __archive_read_register_format(a, @@ -260,7 +259,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, archive_entry_set_filetype(entry, AE_IFREG); /* Get the size of the filename table. */ number = ar_atol10(h + AR_size_offset, AR_size_size); - if (number > SIZE_MAX) { + if (number > SIZE_MAX || number > 1024 * 1024 * 1024) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filename table too large"); return (ARCHIVE_FATAL); @@ -316,7 +315,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, * If we can't look up the real name, warn and return * the entry with the wrong name. */ - if (ar->strtab == NULL || number > ar->strtab_size) { + if (ar->strtab == NULL || number >= ar->strtab_size) { archive_set_error(&a->archive, EINVAL, "Can't find long filename for GNU/SVR4 archive entry"); archive_entry_copy_pathname(entry, filename); @@ -342,16 +341,19 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, /* Parse the size of the name, adjust the file size. */ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3); - bsd_name_length = (size_t)number; - /* Guard against the filename + trailing NUL - * overflowing a size_t and against the filename size - * being larger than the entire entry. */ - if (number > (uint64_t)(bsd_name_length + 1) - || (int64_t)bsd_name_length > ar->entry_bytes_remaining) { + /* Sanity check the filename length: + * = Must be <= SIZE_MAX - 1 + * = Must be <= 1MB + * = Cannot be bigger than the entire entry + */ + if (number > SIZE_MAX - 1 + || number > 1024 * 1024 + || (int64_t)number > ar->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad input file size"); return (ARCHIVE_FATAL); } + bsd_name_length = (size_t)number; ar->entry_bytes_remaining -= bsd_name_length; /* Adjust file size reported to client. */ archive_entry_set_size(entry, ar->entry_bytes_remaining); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c index 6356963..20eb157 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c @@ -67,7 +67,7 @@ struct lzx_dec { /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; - /* Translation reversal for x86 proccessor CALL byte sequence(E8). + /* Translation reversal for x86 processor CALL byte sequence(E8). * This is used for LZX only. */ uint32_t translation_size; char translation; @@ -645,12 +645,13 @@ cab_read_header(struct archive_read *a) cab = (struct cab *)(a->format->data); if (cab->found_header == 0 && p[0] == 'M' && p[1] == 'Z') { - /* This is an executable? Must be self-extracting... */ + /* This is an executable? Must be self-extracting... */ err = cab_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); - if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL) + /* Re-read header after processing the SFX. */ + if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); } @@ -1494,6 +1495,8 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */ if (mszip > 0) { + if (bytes_avail <= 0) + goto nomszip; if (bytes_avail <= mszip) { if (mszip == 2) { if (cab->stream.next_in[0] != 0x43) @@ -1554,7 +1557,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) /* * Note: I suspect there is a bug in makecab.exe because, in rare * case, compressed bytes are still remaining regardless we have - * gotten all uncompressed bytes, which size is recoded in CFDATA, + * gotten all uncompressed bytes, which size is recorded in CFDATA, * as much as we need, and we have to use the garbage so as to * correctly compute the sum of CFDATA accordingly. */ @@ -1741,7 +1744,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) } /* - * Translation reversal of x86 proccessor CALL byte sequence(E8). + * Translation reversal of x86 processor CALL byte sequence(E8). */ lzx_translation(&cab->xstrm, cab->uncompressed_buffer, cfdata->uncompressed_size, @@ -2270,7 +2273,7 @@ static int lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) { /* - * x86 proccessor family can read misaligned data without an access error. + * x86 processor family can read misaligned data without an access error. */ int n = CACHE_BITS - br->cache_avail; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c index b09db0e..ffd4a85 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c @@ -326,7 +326,7 @@ archive_read_format_cpio_options(struct archive_read *a, cpio = (struct cpio *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { - /* Handle filnames as libarchive 2.x */ + /* Handle filenames as libarchive 2.x */ cpio->init_default_conversion = (val != NULL)?1:0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { @@ -356,7 +356,7 @@ archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; - const void *h; + const void *h, *hl; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; @@ -406,11 +406,11 @@ archive_read_format_cpio_read_header(struct archive_read *a, "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); return (ARCHIVE_FATAL); } - h = __archive_read_ahead(a, + hl = __archive_read_ahead(a, (size_t)cpio->entry_bytes_remaining, NULL); - if (h == NULL) + if (hl == NULL) return (ARCHIVE_FATAL); - if (archive_entry_copy_symlink_l(entry, (const char *)h, + if (archive_entry_copy_symlink_l(entry, (const char *)hl, (size_t)cpio->entry_bytes_remaining, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, @@ -434,7 +434,8 @@ archive_read_format_cpio_read_header(struct archive_read *a, * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ - if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) { + if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", + 11) == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); @@ -814,8 +815,8 @@ header_odc(struct archive_read *a, struct cpio *cpio, * NOTE: if a filename suffix is ".z", it is the file gziped by afio. * it would be nice that we can show uncompressed file size and we can * uncompressed file contents automatically, unfortunately we have nothing - * to get a uncompressed file size while reading each header. it means - * we also cannot uncompressed file contens under the our framework. + * to get a uncompressed file size while reading each header. It means + * we also cannot uncompress file contents under our framework. */ static int header_afiol(struct archive_read *a, struct cpio *cpio, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c index c19614a..3541330 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c @@ -322,7 +322,7 @@ struct iso9660 { struct archive_string pathname; char seenRockridge; /* Set true if RR extensions are used. */ - char seenSUSP; /* Set true if SUSP is beging used. */ + char seenSUSP; /* Set true if SUSP is being used. */ char seenJoliet; unsigned char suspOffset; @@ -374,7 +374,7 @@ struct iso9660 { size_t utf16be_path_len; unsigned char *utf16be_previous_path; size_t utf16be_previous_path_len; - /* Null buufer used in bidder to improve its performance. */ + /* Null buffer used in bidder to improve its performance. */ unsigned char null[2048]; }; @@ -1199,7 +1199,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a, archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_utf16be == NULL) - /* Coundn't allocate memory */ + /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } if (iso9660->utf16be_path == NULL) { @@ -1864,7 +1864,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, if ((file->utf16be_name = malloc(name_len)) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file name"); - return (NULL); + goto fail; } memcpy(file->utf16be_name, p, name_len); file->utf16be_bytes = name_len; @@ -1943,10 +1943,8 @@ parse_file_info(struct archive_read *a, struct file_info *parent, file->symlink_continues = 0; rr_start += iso9660->suspOffset; r = parse_rockridge(a, file, rr_start, rr_end); - if (r != ARCHIVE_OK) { - free(file); - return (NULL); - } + if (r != ARCHIVE_OK) + goto fail; /* * A file size of symbolic link files in ISO images * made by makefs is not zero and its location is @@ -1990,7 +1988,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE"); - return (NULL); + goto fail; } /* * Sanity check: file does not have "CL" extension. @@ -1999,7 +1997,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE and CL"); - return (NULL); + goto fail; } /* * Sanity check: The file type must be a directory. @@ -2008,7 +2006,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE"); - return (NULL); + goto fail; } } else if (parent != NULL && parent->rr_moved) file->rr_moved_has_re_only = 0; @@ -2022,7 +2020,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); - return (NULL); + goto fail; } /* * Sanity check: The file type must be a regular file. @@ -2031,7 +2029,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); - return (NULL); + goto fail; } parent->subdirs++; /* Overwrite an offset and a number of this "CL" entry @@ -2049,7 +2047,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); - return (NULL); + goto fail; } } if (file->cl_offset == file->offset || @@ -2057,7 +2055,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); - return (NULL); + goto fail; } } } @@ -2088,6 +2086,10 @@ parse_file_info(struct archive_read *a, struct file_info *parent, #endif register_file(iso9660, file); return (file); +fail: + archive_string_free(&file->name); + free(file); + return (NULL); } static int @@ -2407,7 +2409,7 @@ read_CE(struct archive_read *a, struct iso9660 *iso9660) return (ARCHIVE_FATAL); } while (heap->cnt && heap->reqs[0].offset == iso9660->current_position); - /* NOTE: Do not move this consume's code to fron of + /* NOTE: Do not move this consume's code to front of * do-while loop. Registration of nested CE extension * might cause error because of current position. */ __archive_read_consume(a, step); @@ -2729,7 +2731,7 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, if (file == NULL) { /* * If directory entries all which are descendant of - * rr_moved are stil remaning, expose their. + * rr_moved are still remaining, expose their. */ if (iso9660->re_files.first != NULL && iso9660->rr_moved != NULL && @@ -2852,7 +2854,7 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, empty_files.last = &empty_files.first; /* Collect files which has the same file serial number. * Peek pending_files so that file which number is different - * is not put bak. */ + * is not put back. */ while (iso9660->pending_files.used > 0 && (iso9660->pending_files.files[0]->number == -1 || iso9660->pending_files.files[0]->number == number)) { @@ -2860,7 +2862,7 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, /* This file has the same offset * but it's wrong offset which empty files * and symlink files have. - * NOTE: This wrong offse was recorded by + * NOTE: This wrong offset was recorded by * old mkisofs utility. If ISO images is * created by latest mkisofs, this does not * happen. diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c index eff02d8..ebd7f2c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c @@ -924,6 +924,9 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha) /* Get a real compressed file size. */ lha->compsize -= extdsize - 2; + if (lha->compsize < 0) + goto invalid; /* Invalid compressed file size */ + if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); @@ -1711,14 +1714,18 @@ lha_crc16(uint16_t crc, const void *pp, size_t len) */ for (;len >= 8; len -= 8) { /* This if statement expects compiler optimization will - * remove the stament which will not be executed. */ + * remove the statement which will not be executed. */ +#undef bswap16 #ifndef __has_builtin # define __has_builtin(x) 0 #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ # define bswap16(x) _byteswap_ushort(x) -#elif (defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8) \ - || (defined(__clang__) && __has_builtin(__builtin_bswap16)) +#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4) +/* GCC 4.8 and later has __builtin_bswap16() */ +# define bswap16(x) __builtin_bswap16(x) +#elif defined(__clang__) && __has_builtin(__builtin_bswap16) +/* All clang versions have __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #else # define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c index 8c3be9a..4231ff5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c @@ -75,6 +75,8 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #define MTREE_HAS_OPTIONAL 0x0800 #define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ +#define MTREE_HASHTABLE_SIZE 1024 + struct mtree_option { struct mtree_option *next; char *value; @@ -86,6 +88,8 @@ struct mtree_entry { char *name; char full; char used; + unsigned int name_hash; + struct mtree_entry *hashtable_next; }; struct mtree { @@ -98,6 +102,7 @@ struct mtree { const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; + struct mtree_entry *entry_hashtable[MTREE_HASHTABLE_SIZE]; struct archive_string current_dir; struct archive_string contents_name; @@ -110,6 +115,7 @@ struct mtree { static int bid_keycmp(const char *, const char *, ssize_t); static int cleanup(struct archive_read *); static int detect_form(struct archive_read *, int *); +static unsigned int hash(const char *); static int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); @@ -223,13 +229,12 @@ archive_read_support_format_mtree(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_mtree"); - mtree = (struct mtree *)malloc(sizeof(*mtree)); + mtree = (struct mtree *)calloc(1, sizeof(*mtree)); if (mtree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } - memset(mtree, 0, sizeof(*mtree)); mtree->fd = -1; r = __archive_read_register_format(a, mtree, "mtree", @@ -301,6 +306,15 @@ get_line_size(const char *b, ssize_t avail, ssize_t *nlsize) return (avail); } +/* + * <---------------- ravail ---------------------> + * <-- diff ------> <--- avail -----------------> + * <---- len -----------> + * | Previous lines | line being parsed nl extra | + * ^ + * b + * + */ static ssize_t next_line(struct archive_read *a, const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl) @@ -339,7 +353,7 @@ next_line(struct archive_read *a, *b += diff; *avail -= diff; tested = len;/* Skip some bytes we already determinated. */ - len = get_line_size(*b, *avail, nl); + len = get_line_size(*b + len, *avail - len, nl); if (len >= 0) len += tested; } @@ -701,13 +715,13 @@ detect_form(struct archive_read *a, int *is_form_d) } } else break; - } else if (strncmp(p, "/set", 4) == 0) { + } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (bid_keyword_list(p+4, len-4, 0, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; - } else if (strncmp(p, "/unset", 6) == 0) { + } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) break; /* This line continues. */ @@ -853,11 +867,12 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_option **global, const char *line, ssize_t line_len, struct mtree_entry **last_entry, int is_form_d) { - struct mtree_entry *entry; + struct mtree_entry *entry, *ht_iter; struct mtree_option *iter; const char *next, *eq, *name, *end; size_t name_len, len; int r, i; + unsigned int ht_idx; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); @@ -868,6 +883,8 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, entry->name = NULL; entry->used = 0; entry->full = 0; + entry->name_hash = 0; + entry->hashtable_next = NULL; /* Add this entry to list. */ if (*last_entry == NULL) @@ -920,6 +937,16 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, memcpy(entry->name, name, name_len); entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); + entry->name_hash = hash(entry->name); + + ht_idx = entry->name_hash % MTREE_HASHTABLE_SIZE; + if ((ht_iter = mtree->entry_hashtable[ht_idx]) != NULL) { + while (ht_iter->hashtable_next) + ht_iter = ht_iter->hashtable_next; + ht_iter->hashtable_next = entry; + } else { + mtree->entry_hashtable[ht_idx] = entry; + } for (iter = *global; iter != NULL; iter = iter->next) { r = add_option(a, &entry->options, iter->value, @@ -992,11 +1019,11 @@ read_mtree(struct archive_read *a, struct mtree *mtree) if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); - } else if (strncmp(p, "/set", 4) == 0) { + } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (p[4] != ' ' && p[4] != '\t') break; r = process_global_set(a, &global, p); - } else if (strncmp(p, "/unset", 6) == 0) { + } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (p[6] != ' ' && p[6] != '\t') break; r = process_global_unset(a, &global, p); @@ -1113,9 +1140,10 @@ parse_file(struct archive_read *a, struct archive_entry *entry, * with pathname canonicalization, which is a very * tricky subject.) */ - for (mp = mentry->next; mp != NULL; mp = mp->next) { + for (mp = mentry->hashtable_next; mp != NULL; mp = mp->hashtable_next) { if (mp->full && !mp->used - && strcmp(mentry->name, mp->name) == 0) { + && mentry->name_hash == mp->name_hash + && strcmp(mentry->name, mp->name) == 0) { /* Later lines override earlier ones. */ mp->used = 1; r1 = parse_line(a, entry, mtree, mp, @@ -1580,8 +1608,11 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, if (*val == '.') { ++val; ns = (long)mtree_atol10(&val); - } else - ns = 0; + if (ns < 0) + ns = 0; + else if (ns > 999999999) + ns = 999999999; + } if (m > my_time_t_max) m = my_time_t_max; else if (m < my_time_t_min) @@ -1991,3 +2022,19 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, find_off = u - mtree->line.s; } } + +static unsigned int +hash(const char *p) +{ + /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, + as used by ELF for hashing function names. */ + unsigned g, h = 0; + while (*p != '\0') { + h = (h << 4) + *p++; + if ((g = h & 0xF0000000) != 0) { + h ^= g >> 24; + h &= 0x0FFFFFFF; + } + } + return h; +} diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c index fc8153f..658d49d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c @@ -647,13 +647,12 @@ archive_read_support_format_rar(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar"); - rar = (struct rar *)malloc(sizeof(*rar)); + rar = (struct rar *)calloc(sizeof(*rar), 1); if (rar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data"); return (ARCHIVE_FATAL); } - memset(rar, 0, sizeof(*rar)); /* * Until enough data has been read, we cannot tell about @@ -907,7 +906,7 @@ archive_read_format_rar_read_header(struct archive_read *a, sizeof(rar->reserved2)); } - /* Main header is password encrytped, so we cannot read any + /* Main header is password encrypted, so we cannot read any file names or any other info about files from the header. */ if (rar->main_flags & MHD_PASSWORD) { diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c index b0521a6..bd7f13d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c @@ -1,6 +1,7 @@ /*- * 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 @@ -136,6 +137,7 @@ struct tar { 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; @@ -202,9 +204,14 @@ 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); + 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 *, char *attr); + 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 *); @@ -293,6 +300,57 @@ archive_read_format_tar_cleanup(struct archive_read *a) 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) @@ -345,23 +403,19 @@ archive_read_format_tar_bid(struct archive_read *a, int best_bid) return (0); bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ - /* Sanity check: Look at first byte of mode field. */ - switch (255 & (unsigned)header->mode[0]) { - case 0: case 255: - /* Base-256 value: No further verification possible! */ - break; - case ' ': /* Not recommended, but not illegal, either. */ - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - /* Octal Value. */ - /* TODO: Check format of remainder of this field. */ - break; - default: - /* Not a valid mode; bail out here. */ - return (0); + /* + * 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; } - /* TODO: Sanity test uid/gid/size/mtime/rdevmajor/rdevminor fields. */ return (bid); } @@ -375,7 +429,7 @@ archive_read_format_tar_options(struct archive_read *a, tar = (struct tar *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { - /* Handle UTF-8 filnames as libarchive 2.x */ + /* 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); @@ -793,9 +847,9 @@ tar_read_header(struct archive_read *a, struct tar *tar, 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, @@ -890,7 +944,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, { const struct archive_entry_header_ustar *header; size_t size; - int err; + int err, acl_type; int64_t type; char *acl, *p; @@ -935,11 +989,12 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, switch ((int)type & ~0777777) { case 01000000: /* POSIX.1e ACL */ + acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; break; case 03000000: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Solaris NFSv4 ACLs not supported"); - return (ARCHIVE_WARN); + /* 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)", @@ -968,8 +1023,8 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); } archive_strncpy(&(tar->localname), acl, p - acl); - err = archive_acl_parse_l(archive_entry_acl(entry), - tar->localname.s, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, tar->sconv_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, @@ -1128,8 +1183,15 @@ header_common(struct archive_read *a, struct tar *tar, if (tar->entry_bytes_remaining < 0) { tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Tar entry has negative size?"); - err = ARCHIVE_WARN; + "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); @@ -1264,6 +1326,14 @@ header_common(struct archive_read *a, struct tar *tar, * 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 @@ -1415,7 +1485,7 @@ header_pax_extensions(struct archive_read *a, struct tar *tar, * 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.s); + err2 = pax_header(a, tar, entry, &tar->pax_header); err = err_combine(err, err2); tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); @@ -1496,16 +1566,17 @@ header_ustar(struct archive_read *a, struct tar *tar, */ static int pax_header(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, char *attr) + struct archive_entry *entry, struct archive_string *in_as) { - size_t attr_length, l, line_length; + 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 = strlen(attr); + attr_length = in_as->length; tar->pax_hdrcharset_binary = 0; archive_string_empty(&(tar->entry_gname)); archive_string_empty(&(tar->entry_linkpath)); @@ -1570,11 +1641,13 @@ pax_header(struct archive_read *a, struct tar *tar, } *p = '\0'; - /* Identify null-terminated 'value' portion. */ 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); + err2 = pax_attribute(a, tar, entry, key, value, value_length); if (err2 == ARCHIVE_FATAL) return (err2); err = err_combine(err, err2); @@ -1695,6 +1768,66 @@ pax_attribute_xattr(struct archive_entry *entry, 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. @@ -1710,7 +1843,7 @@ pax_attribute_xattr(struct archive_entry *entry, */ static int pax_attribute(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const char *key, const char *value) + struct archive_entry *entry, const char *key, const char *value, size_t value_length) { int64_t s; long n; @@ -1721,6 +1854,14 @@ pax_attribute(struct archive_read *a, struct tar *tar, * 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; @@ -1803,53 +1944,20 @@ pax_attribute(struct archive_read *a, struct tar *tar, case 'S': /* We support some keys used by the "star" archiver */ if (strcmp(key, "SCHILY.acl.access") == 0) { - 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_parse_l(archive_entry_acl(entry), - value, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, - tar->sconv_acl); - if (r != ARCHIVE_OK) { - err = r; - if (err == ARCHIVE_FATAL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for " - "SCHILY.acl.access"); - return (err); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Parse error: SCHILY.acl.access"); - } + 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) { - 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_parse_l(archive_entry_acl(entry), - value, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, - tar->sconv_acl); - if (r != ARCHIVE_OK) { - err = r; - if (err == ARCHIVE_FATAL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for " - "SCHILY.acl.default"); - return (err); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Parse error: SCHILY.acl.default"); - } + 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))); @@ -1870,6 +1978,9 @@ pax_attribute(struct archive_read *a, struct tar *tar, } 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); @@ -2116,12 +2227,11 @@ gnu_add_sparse_entry(struct archive_read *a, struct tar *tar, { struct sparse_block *p; - p = (struct sparse_block *)malloc(sizeof(*p)); + p = (struct sparse_block *)calloc(1, sizeof(*p)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - memset(p, 0, sizeof(*p)); if (tar->sparse_last != NULL) tar->sparse_last->next = p; else @@ -2377,6 +2487,9 @@ gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed) 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)); @@ -2472,7 +2585,7 @@ tar_atol_base_n(const char *p, size_t char_cnt, int 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 evaulated. + * due to the way the && operator is evaluated. */ while (char_cnt != 0 && (*p == ' ' || *p == '\t')) { p++; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c index 46a59ea..c3e86c0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c @@ -88,7 +88,7 @@ typedef enum { WT_RVIS, /* conversion, unsupported */ WT_CONV, - /* continutation, unsupported at the moment */ + /* continuation, unsupported at the moment */ WT_CONT, /* invalid type */ LAST_WT @@ -134,8 +134,8 @@ 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) { @@ -146,12 +146,11 @@ archive_read_support_format_warc(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_warc"); - if ((w = malloc(sizeof(*w))) == NULL) { + if ((w = calloc(1, sizeof(*w))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } - memset(w, 0, sizeof(*w)); r = __archive_read_register_format( a, w, "warc", @@ -199,8 +198,8 @@ _warc_bid(struct archive_read *a, int best_bid) /* otherwise snarf the record's version number */ ver = _warc_rdver(hdr, nrd); - if (ver == 0U || ver > 10000U) { - /* oh oh oh, best not to wager ... */ + if (ver < 1200U || ver > 10000U) { + /* we only support WARC 0.12 to 1.0 */ return -1; } @@ -255,23 +254,32 @@ start_over: &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); return (ARCHIVE_FATAL); - } else if ((ver = _warc_rdver(buf, eoh - buf)) > 10000U) { - /* nawww, I wish they promised backward compatibility - * anyhoo, in their infinite wisdom the 28500 guys might - * come up with something we can't possibly handle so - * best end things here */ + } + 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, - "Unsupported record version"); + "Invalid record version"); return (ARCHIVE_FATAL); - } else if ((cntlen = _warc_rdlen(buf, eoh - buf)) < 0) { + } 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); - } else if ((rtime = _warc_rdrtm(buf, eoh - buf)) == (time_t)-1) { + } + 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( @@ -285,7 +293,7 @@ start_over: if (ver != w->pver) { /* stringify this entry's version */ archive_string_sprintf(&w->sver, - "WARC/%u.%u", ver / 10000, ver % 10000); + "WARC/%u.%u", ver / 10000, (ver % 10000) / 100); /* remember the version */ w->pver = ver; } @@ -318,7 +326,7 @@ start_over: } memcpy(w->pool.str, fnam.str, fnam.len); w->pool.str[fnam.len] = '\0'; - /* let noone else know about the pool, it's a secret, shhh */ + /* let no one else know about the pool, it's a secret, shhh */ fnam.str = w->pool.str; /* snarf mtime or deduce from rtime @@ -523,6 +531,10 @@ time_from_tm(struct tm *t) #endif } +static int la_isblank(int c) { + return c == ' ' || c == '\t'; +} + static time_t xstrpisotime(const char *s, char **endptr) { @@ -535,7 +547,8 @@ xstrpisotime(const char *s, char **endptr) /* as a courtesy to our callers, and since this is a non-standard * routine, we skip leading whitespace */ - for (; isspace(*s); s++); + while (la_isblank((unsigned char)*s)) + ++s; /* read year */ if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') { @@ -562,7 +575,7 @@ xstrpisotime(const char *s, char **endptr) goto out; } - /* massage TM to fulfill some of POSIX' contraints */ + /* massage TM to fulfill some of POSIX' constraints */ tm.tm_year -= 1900; tm.tm_mon--; @@ -577,51 +590,41 @@ out: } static unsigned int -_warc_rdver(const char buf[10], size_t bsz) +_warc_rdver(const char *buf, size_t bsz) { static const char magic[] = "WARC/"; - unsigned int ver; + unsigned int ver = 0U; + unsigned int end = 0U; - (void)bsz; /* UNUSED */ - - if (memcmp(buf, magic, sizeof(magic) - 1U) != 0) { - /* nope */ - return 99999U; + 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; - /* most common case gets a quick-check here */ - if (memcmp(buf, "1.0\r\n", 5U) == 0) { - ver = 10000U; - } else { - switch (*buf) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - if (buf[1U] == '.') { - char *on; - - /* set up major version */ - ver = (buf[0U] - '0') * 10000U; - /* minor version, anyone? */ - ver += (strtol(buf + 2U, &on, 10)) * 100U; - /* don't parse anything else */ - if (on > buf + 2U) { - break; - } - } - /* FALLTHROUGH */ - case '9': - default: - /* just make the version ridiculously high */ - ver = 999999U; - break; + + 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 + */ + if (ver >= 1200U) { + if (memcmp(buf + 3U + end, "\r\n", 2U) != 0) + ver = 0U; + } else if (ver < 1200U) { + if (!la_isblank(*(buf + 3U + end))) + ver = 0U; } } return ver; @@ -631,32 +634,27 @@ static unsigned int _warc_rdtyp(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Type:"; - const char *const eob = buf + bsz; - const char *val; + 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 */ - for (val += sizeof(_key) - 1U; val < eob && isspace(*val); val++); - - if (val + 8U > eob) { - ; - } else if (memcmp(val, "resource", 8U) == 0) { - return WT_RSRC; - } else if (memcmp(val, "warcinfo", 8U) == 0) { - return WT_INFO; - } else if (memcmp(val, "metadata", 8U) == 0) { - return WT_META; - } else if (memcmp(val, "request", 7U) == 0) { - return WT_REQ; - } else if (memcmp(val, "response", 8U) == 0) { - return WT_RSP; - } else if (memcmp(val, "conversi", 8U) == 0) { - return WT_CONV; - } else if (memcmp(val, "continua", 8U) == 0) { - return WT_CONT; + while (val < eol && la_isblank((unsigned char)*val)) + ++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; } @@ -665,10 +663,7 @@ static warc_string_t _warc_rduri(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Target-URI:"; - const char *const eob = buf + bsz; - const char *val; - const char *uri; - const char *eol; + const char *val, *uri, *eol, *p; warc_string_t res = {0U, NULL}; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { @@ -676,23 +671,33 @@ _warc_rduri(const char *buf, size_t bsz) return res; } /* overread whitespace */ - for (val += sizeof(_key) - 1U; val < eob && isspace(*val); val++); + val += sizeof(_key) - 1U; + if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { + /* no end of line */ + return res; + } + + while (val < eol && la_isblank((unsigned char)*val)) + ++val; /* overread URL designators */ - if ((uri = xmemmem(val, eob - val, "://", 3U)) == NULL) { + if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) { /* not touching that! */ return res; - } else if ((eol = memchr(uri, '\n', eob - uri)) == NULL) { - /* no end of line? :O */ - return res; } - /* massage uri to point to after :// */ + /* 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; - /* also massage eol to point to the first whitespace - * after the last non-whitespace character before - * the end of the line */ - for (; eol > uri && isspace(eol[-1]); eol--); /* now then, inspect the URI */ if (memcmp(val, "file", 4U) == 0) { @@ -715,7 +720,7 @@ static ssize_t _warc_rdlen(const char *buf, size_t bsz) { static const char _key[] = "\r\nContent-Length:"; - const char *val; + const char *val, *eol; char *on = NULL; long int len; @@ -723,14 +728,24 @@ _warc_rdlen(const char *buf, size_t bsz) /* no bother */ return -1; } - - /* strtol kindly overreads whitespace for us, so use that */ 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 && la_isblank(*val)) + val++; + /* there must be at least one digit */ + if (!isdigit(*val)) + return -1; len = strtol(val, &on, 10); - if (on == NULL || !isspace(*on)) { - /* hm, can we trust that number? Best not. */ + if (on != eol) { + /* line must end here */ return -1; } + return (size_t)len; } @@ -738,7 +753,7 @@ static time_t _warc_rdrtm(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Date:"; - const char *val; + const char *val, *eol; char *on = NULL; time_t res; @@ -746,13 +761,17 @@ _warc_rdrtm(const char *buf, size_t bsz) /* 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 */ - val += sizeof(_key) - 1U; res = xstrpisotime(val, &on); - if (on == NULL || !isspace(*on)) { - /* hm, can we trust that number? Best not. */ - return (time_t)-1; + if (on != eol) { + /* line must end here */ + return -1; } return res; } @@ -761,7 +780,7 @@ static time_t _warc_rdmtm(const char *buf, size_t bsz) { static const char _key[] = "\r\nLast-Modified:"; - const char *val; + const char *val, *eol; char *on = NULL; time_t res; @@ -769,13 +788,17 @@ _warc_rdmtm(const char *buf, size_t bsz) /* 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 */ - val += sizeof(_key) - 1U; res = xstrpisotime(val, &on); - if (on == NULL || !isspace(*on)) { - /* hm, can we trust that number? Best not. */ - return (time_t)-1; + if (on != eol) { + /* line must end here */ + return -1; } return res; } @@ -792,4 +815,12 @@ _warc_find_eoh(const char *buf, size_t bsz) 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 */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c index ec623c6..eeac1c8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c @@ -43,8 +43,6 @@ __FBSDID("$FreeBSD$"); #endif #if HAVE_LZMA_H #include <cm_lzma.h> -#elif HAVE_LZMADEC_H -#include <lzmadec.h> #endif #ifdef HAVE_ZLIB_H #include <cm_zlib.h> @@ -334,9 +332,6 @@ struct xar { #if HAVE_LZMA_H && HAVE_LIBLZMA lzma_stream lzstream; int lzstream_valid; -#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC - lzmadec_stream lzstream; - int lzstream_valid; #endif /* * For Checksum data. @@ -399,6 +394,7 @@ static void checksum_update(struct archive_read *, const void *, size_t, const void *, size_t); static int checksum_final(struct archive_read *, const void *, size_t, const void *, size_t); +static void checksum_cleanup(struct archive_read *); static int decompression_init(struct archive_read *, enum enctype); static int decompress(struct archive_read *, const void **, size_t *, const void *, size_t *); @@ -928,6 +924,7 @@ xar_cleanup(struct archive_read *a) int r; xar = (struct xar *)(a->format->data); + checksum_cleanup(a); r = decompression_cleanup(a); hdlink = xar->hdlink_list; while (hdlink != NULL) { @@ -938,6 +935,7 @@ xar_cleanup(struct archive_read *a) } for (i = 0; i < xar->file_queue.used; i++) file_free(xar->file_queue.files[i]); + free(xar->file_queue.files); while (xar->unknowntags != NULL) { struct unknown_tag *tag; @@ -1526,34 +1524,6 @@ decompression_init(struct archive_read *a, enum enctype encoding) xar->lzstream.total_in = 0; xar->lzstream.total_out = 0; break; -#elif defined(HAVE_LZMADEC_H) && defined(HAVE_LIBLZMADEC) - case LZMA: - if (xar->lzstream_valid) - lzmadec_end(&(xar->lzstream)); - r = lzmadec_init(&(xar->lzstream)); - if (r != LZMADEC_OK) { - switch (r) { - case LZMADEC_HEADER_ERROR: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Internal error initializing " - "compression library: " - "invalid header"); - break; - case LZMADEC_MEM_ERROR: - archive_set_error(&a->archive, - ENOMEM, - "Internal error initializing " - "compression library: " - "out of memory"); - break; - } - return (ARCHIVE_FATAL); - } - xar->lzstream_valid = 1; - xar->lzstream.total_in = 0; - xar->lzstream.total_out = 0; - break; #endif /* * Unsupported compression. @@ -1563,9 +1533,7 @@ decompression_init(struct archive_read *a, enum enctype encoding) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) -#if !defined(HAVE_LZMADEC_H) || !defined(HAVE_LIBLZMADEC) case LZMA: -#endif case XZ: #endif switch (xar->entry_encoding) { @@ -1685,46 +1653,12 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, *used = avail_in - xar->lzstream.avail_in; *outbytes = avail_out - xar->lzstream.avail_out; break; -#elif defined(HAVE_LZMADEC_H) && defined(HAVE_LIBLZMADEC) - case LZMA: - xar->lzstream.next_in = (unsigned char *)(uintptr_t)b; - xar->lzstream.avail_in = avail_in; - xar->lzstream.next_out = (unsigned char *)outbuff; - xar->lzstream.avail_out = avail_out; - r = lzmadec_decode(&(xar->lzstream), 0); - switch (r) { - case LZMADEC_STREAM_END: /* Found end of stream. */ - switch (lzmadec_end(&(xar->lzstream))) { - case LZMADEC_OK: - break; - default: - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "Failed to clean up lzmadec decompressor"); - return (ARCHIVE_FATAL); - } - xar->lzstream_valid = 0; - /* FALLTHROUGH */ - case LZMADEC_OK: /* Decompressor made some progress. */ - break; - default: - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "lzmadec decompression failed(%d)", - r); - return (ARCHIVE_FATAL); - } - *used = avail_in - xar->lzstream.avail_in; - *outbytes = avail_out - xar->lzstream.avail_out; - break; #endif #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) -#if !defined(HAVE_LZMADEC_H) || !defined(HAVE_LIBLZMADEC) case LZMA: -#endif case XZ: #endif case NONE: @@ -1788,6 +1722,16 @@ decompression_cleanup(struct archive_read *a) } static void +checksum_cleanup(struct archive_read *a) { + struct xar *xar; + + xar = (struct xar *)(a->format->data); + + _checksum_final(&(xar->a_sumwrk), NULL, 0); + _checksum_final(&(xar->e_sumwrk), NULL, 0); +} + +static void xmlattr_cleanup(struct xmlattr_list *list) { struct xmlattr *attr, *next; @@ -3116,7 +3060,7 @@ xml2_read_cb(void *context, char *buffer, int len) struct xar *xar; const void *d; size_t outbytes; - size_t used; + size_t used = 0; int r; a = (struct archive_read *)context; @@ -3240,6 +3184,9 @@ expat_xmlattr_setup(struct archive_read *a, value = strdup(atts[1]); if (attr == NULL || name == NULL || value == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); + free(attr); + free(name); + free(value); return (ARCHIVE_FATAL); } attr->name = name; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c index c50ba84..e56bd63 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c @@ -199,7 +199,7 @@ struct zip { struct trad_enc_ctx tctx; char tctx_valid; - /* WinZip AES decyption. */ + /* WinZip AES decryption. */ /* Contexts used for AES decryption. */ archive_crypto_ctx cctx; char cctx_valid; @@ -242,7 +242,7 @@ trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) } static uint8_t -trad_enc_decypt_byte(struct trad_enc_ctx *ctx) +trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; @@ -257,7 +257,7 @@ trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, max = (unsigned)((in_len < out_len)? in_len: out_len); for (i = 0; i < max; i++) { - uint8_t t = in[i] ^ trad_enc_decypt_byte(ctx); + uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx); out[i] = t; trad_enc_update_keys(ctx, t); } @@ -418,18 +418,30 @@ zip_time(const char *p) * id1+size1+data1 + id2+size2+data2 ... * triplets. id and size are 2 bytes each. */ -static void -process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) +static int +process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; - while (offset < extra_length - 4) { + if (extra_length == 0) { + return ARCHIVE_OK; + } + + if (extra_length < 4) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length); + return ARCHIVE_FAILED; + } + while (offset <= extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > extra_length) { - break; + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Extra data overflow: Need %d bytes but only found %d bytes", + (int)datasize, (int)(extra_length - offset)); + return ARCHIVE_FAILED; } #ifdef DEBUG fprintf(stderr, "Header id 0x%04x, length %d\n", @@ -440,26 +452,38 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) /* Zip64 extended information extra field. */ zip_entry->flags |= LA_USED_ZIP64; if (zip_entry->uncompressed_size == 0xffffffff) { - if (datasize < 8) - break; - zip_entry->uncompressed_size = - archive_le64dec(p + offset); + uint64_t t = 0; + if (datasize < 8 + || (t = archive_le64dec(p + offset)) > INT64_MAX) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit uncompressed size"); + return ARCHIVE_FAILED; + } + zip_entry->uncompressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->compressed_size == 0xffffffff) { - if (datasize < 8) - break; - zip_entry->compressed_size = - archive_le64dec(p + offset); + uint64_t t = 0; + if (datasize < 8 + || (t = archive_le64dec(p + offset)) > INT64_MAX) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit compressed size"); + return ARCHIVE_FAILED; + } + zip_entry->compressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->local_header_offset == 0xffffffff) { - if (datasize < 8) - break; - zip_entry->local_header_offset = - archive_le64dec(p + offset); + uint64_t t = 0; + if (datasize < 8 + || (t = archive_le64dec(p + offset)) > INT64_MAX) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit local header offset"); + return ARCHIVE_FAILED; + } + zip_entry->local_header_offset = t; offset += 8; datasize -= 8; } @@ -698,7 +722,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) break; } case 0x9901: - /* WinZIp AES extra data field. */ + /* WinZip AES extra data field. */ if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { /* Vendor version. */ zip_entry->aes_extra.vendor = @@ -715,13 +739,13 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) } offset += datasize; } -#ifdef DEBUG - if (offset != extra_length) - { - fprintf(stderr, - "Extra data field contents do not match reported size!\n"); + if (offset != extra_length) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed extra data: Consumed %d bytes of %d bytes", + (int)offset, (int)extra_length); + return ARCHIVE_FAILED; } -#endif + return ARCHIVE_OK; } /* @@ -840,7 +864,9 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } - process_extra(h, extra_length, zip_entry); + if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) { + return ARCHIVE_FATAL; + } __archive_read_consume(a, extra_length); /* Work around a bug in Info-Zip: When reading from a pipe, it @@ -850,29 +876,33 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip_entry->mode |= AE_IFREG; } - if ((zip_entry->mode & AE_IFMT) == 0) { - /* Especially in streaming mode, we can end up - here without having seen proper mode information. - Guess from the filename. */ + /* If the mode is totally empty, set some sane default. */ + if (zip_entry->mode == 0) { + zip_entry->mode |= 0664; + } + + /* Make sure that entries with a trailing '/' are marked as directories + * even if the External File Attributes contains bogus values. If this + * is not a directory and there is no type, assume regularfile. */ + if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) { + int has_slash; + wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); - if (len > 0 && wp[len - 1] == L'/') - zip_entry->mode |= AE_IFDIR; - else - zip_entry->mode |= AE_IFREG; + has_slash = len > 0 && wp[len - 1] == L'/'; } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; - if (len > 0 && cp[len - 1] == '/') - zip_entry->mode |= AE_IFDIR; - else - zip_entry->mode |= AE_IFREG; + has_slash = len > 0 && cp[len - 1] == '/'; } - if (zip_entry->mode == AE_IFDIR) { - zip_entry->mode |= 0775; - } else if (zip_entry->mode == AE_IFREG) { - zip_entry->mode |= 0664; + /* Correct file type as needed. */ + if (has_slash) { + zip_entry->mode &= ~AE_IFMT; + zip_entry->mode |= AE_IFDIR; + zip_entry->mode |= 0111; + } else if ((zip_entry->mode & AE_IFMT) == 0) { + zip_entry->mode |= AE_IFREG; } } @@ -887,6 +917,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_wstrcat(&s, wp); archive_wstrappend_wchar(&s, L'/'); archive_entry_copy_pathname_w(entry, s.s); + archive_wstring_free(&s); } } else { cp = archive_entry_pathname(entry); @@ -897,6 +928,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_strcat(&s, cp); archive_strappend_char(&s, '/'); archive_entry_set_pathname(entry, s.s); + archive_string_free(&s); } } } @@ -1136,11 +1168,18 @@ zip_read_data_none(struct archive_read *a, const void **_buff, || (zip->hctx_valid && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { if (zip->entry->flags & LA_USED_ZIP64) { + uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p + 4); - zip->entry->compressed_size = - archive_le64dec(p + 8); - zip->entry->uncompressed_size = - archive_le64dec(p + 16); + compressed = archive_le64dec(p + 8); + uncompressed = archive_le64dec(p + 16); + if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Overflow of 64-bit file sizes"); + return ARCHIVE_FAILED; + } + zip->entry->compressed_size = compressed; + zip->entry->uncompressed_size = uncompressed; zip->unconsumed = 24; } else { zip->entry->crc32 = archive_le32dec(p + 4); @@ -1293,7 +1332,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, && bytes_avail > zip->entry_bytes_remaining) { bytes_avail = (ssize_t)zip->entry_bytes_remaining; } - if (bytes_avail <= 0) { + if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); return (ARCHIVE_FATAL); @@ -1417,9 +1456,18 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, zip->unconsumed = 4; } if (zip->entry->flags & LA_USED_ZIP64) { + uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p); - zip->entry->compressed_size = archive_le64dec(p + 4); - zip->entry->uncompressed_size = archive_le64dec(p + 12); + compressed = archive_le64dec(p + 4); + uncompressed = archive_le64dec(p + 12); + if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Overflow of 64-bit file sizes"); + return ARCHIVE_FAILED; + } + zip->entry->compressed_size = compressed; + zip->entry->uncompressed_size = uncompressed; zip->unconsumed += 20; } else { zip->entry->crc32 = archive_le32dec(p); @@ -1500,7 +1548,7 @@ read_decryption_header(struct archive_read *a) case 0x6720:/* Blowfish */ case 0x6721:/* Twofish */ case 0x6801:/* RC4 */ - /* Suuported encryption algorithm. */ + /* Supported encryption algorithm. */ break; default: archive_set_error(&a->archive, @@ -1609,7 +1657,7 @@ read_decryption_header(struct archive_read *a) __archive_read_consume(a, 4); /*return (ARCHIVE_OK); - * This is not fully implemnted yet.*/ + * This is not fully implemented yet.*/ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encrypted file is unsupported"); return (ARCHIVE_FAILED); @@ -1691,7 +1739,7 @@ init_traditional_PKWARE_decryption(struct archive_read *a) } /* - * Initialize ctx for Traditional PKWARE Decyption. + * Initialize ctx for Traditional PKWARE Decryption. */ r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), p, ENC_HEADER_SIZE, &crcchk); @@ -2691,7 +2739,9 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) "Truncated ZIP file header"); return ARCHIVE_FATAL; } - process_extra(p + filename_length, extra_length, zip_entry); + if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) { + return ARCHIVE_FATAL; + } /* * Mac resource fork files are stored under the diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c index 282c58e..592ead2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.c +++ b/Utilities/cmlibarchive/libarchive/archive_string.c @@ -219,6 +219,12 @@ archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) return (as); } +struct archive_string * +archive_array_append(struct archive_string *as, const char *p, size_t s) +{ + return archive_string_append(as, p, s); +} + void archive_string_concat(struct archive_string *dest, struct archive_string *src) { @@ -559,7 +565,8 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, } if (count == 0 && length != 0) ret = -1; - } while (0); + break; + } while (1); } dest->length += count; dest->s[dest->length] = L'\0'; @@ -596,7 +603,7 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, wcs = dest->s + dest->length; /* * We cannot use mbsrtowcs/mbstowcs here because those may convert - * extra MBS when strlen(p) > len and one wide character consis of + * extra MBS when strlen(p) > len and one wide character consists of * multi bytes. */ while (*mbs && mbs_length > 0) { @@ -1247,7 +1254,7 @@ create_sconv_object(const char *fc, const char *tc, sc->cd = iconv_open(tc, fc); if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) { /* - * Unfortunaly, all of iconv implements do support + * Unfortunately, all of iconv implements do support * "CP932" character-set, so we should use "SJIS" * instead if iconv_open failed. */ @@ -1260,7 +1267,7 @@ create_sconv_object(const char *fc, const char *tc, /* * archive_mstring on Windows directly convert multi-bytes * into archive_wstring in order not to depend on locale - * so that you can do a I18N programing. This will be + * so that you can do a I18N programming. This will be * used only in archive_mstring_copy_mbs_len_l so far. */ if (flag & SCONV_FROM_CHARSET) { @@ -1725,7 +1732,7 @@ archive_string_conversion_from_charset(struct archive *a, const char *charset, * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP). * So we should make a string conversion between CP_ACP and CP_OEMCP - * for compatibillty. + * for compatibility. */ #if defined(_WIN32) && !defined(__CYGWIN__) struct archive_string_conv * @@ -1826,7 +1833,7 @@ archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt) * A filename in UTF-8 was made with libarchive 2.x in a wrong * assumption that wchar_t was Unicode. * This option enables simulating the assumption in order to read - * that filname correctly. + * that filename correctly. */ case SCONV_SET_OPT_UTF8_LIBARCHIVE2X: #if (defined(_WIN32) && !defined(__CYGWIN__)) \ @@ -1938,12 +1945,19 @@ archive_strncat_l(struct archive_string *as, const void *_p, size_t n, struct archive_string_conv *sc) { const void *s; - size_t length; + size_t length = 0; int i, r = 0, r2; + if (_p != NULL && n > 0) { + if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) + length = utf16nbytes(_p, n); + else + length = mbsnbytes(_p, n); + } + /* We must allocate memory even if there is no data for conversion * or copy. This simulates archive_string_append behavior. */ - if (_p == NULL || n == 0) { + if (length == 0) { int tn = 1; if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) tn = 2; @@ -1959,16 +1973,11 @@ archive_strncat_l(struct archive_string *as, const void *_p, size_t n, * If sc is NULL, we just make a copy. */ if (sc == NULL) { - length = mbsnbytes(_p, n); if (archive_string_append(as, _p, length) == NULL) return (-1);/* No memory */ return (0); } - if (sc->flag & SCONV_FROM_UTF16) - length = utf16nbytes(_p, n); - else - length = mbsnbytes(_p, n); s = _p; i = 0; if (sc->nconverter > 1) { @@ -1991,7 +2000,7 @@ archive_strncat_l(struct archive_string *as, const void *_p, size_t n, #if HAVE_ICONV /* - * Return -1 if conversion failes. + * Return -1 if conversion fails. */ static int iconv_strncat_in_locale(struct archive_string *as, const void *_p, @@ -2093,7 +2102,7 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, /* * Translate a string from a some CodePage to an another CodePage by - * Windows APIs, and copy the result. Return -1 if conversion failes. + * Windows APIs, and copy the result. Return -1 if conversion fails. */ static int strncat_in_codepage(struct archive_string *as, @@ -2217,7 +2226,7 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, /* * If a character is ASCII, this just copies it. If not, this - * assigns '?' charater instead but in UTF-8 locale this assigns + * assigns '?' character instead but in UTF-8 locale this assigns * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD, * a Replacement Character in Unicode. */ @@ -2297,7 +2306,7 @@ _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; - /* Invalide sequence or there are not plenty bytes. */ + /* Invalid sequence or there are not plenty bytes. */ if ((int)n < cnt) { cnt = (int)n; for (i = 1; i < cnt; i++) { @@ -2378,7 +2387,7 @@ _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) goto invalid_sequence; } - /* The code point larger than 0x10FFFF is not leagal + /* The code point larger than 0x10FFFF is not legal * Unicode values. */ if (wc > UNICODE_MAX) goto invalid_sequence; @@ -2396,7 +2405,7 @@ utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) int cnt; cnt = _utf8_to_unicode(pwc, s, n); - /* Any of Surrogate pair is not leagal Unicode values. */ + /* Any of Surrogate pair is not legal Unicode values. */ if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc)) return (-3); return (cnt); @@ -2457,7 +2466,7 @@ invalid_sequence: /* * Convert a Unicode code point to a single UTF-8 sequence. * - * NOTE:This function does not check if the Unicode is leagal or not. + * NOTE:This function does not check if the Unicode is legal or not. * Please you definitely check it before calling this. */ static size_t @@ -2551,9 +2560,9 @@ utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be) /* * Surrogate pair values(0xd800 through 0xdfff) are only - * used by UTF-16, so, after above culculation, the code + * used by UTF-16, so, after above calculation, the code * must not be surrogate values, and Unicode has no codes - * larger than 0x10ffff. Thus, those are not leagal Unicode + * larger than 0x10ffff. Thus, those are not legal Unicode * values. */ if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) { @@ -2900,7 +2909,7 @@ get_nfc(uint32_t uc, uint32_t uc2) /* * Normalize UTF-8/UTF-16BE characters to Form C and copy the result. * - * TODO: Convert composition exclusions,which are never converted + * TODO: Convert composition exclusions, which are never converted * from NFC,NFD,NFKC and NFKD, to Form C. */ static int @@ -3434,7 +3443,7 @@ strncat_from_utf8_libarchive2(struct archive_string *as, } /* - * As libarchie 2.x, translates the UTF-8 characters into + * As libarchive 2.x, translates the UTF-8 characters into * wide-characters in the assumption that WCS is Unicode. */ if (n < 0) { @@ -3473,7 +3482,7 @@ strncat_from_utf8_libarchive2(struct archive_string *as, /* * Convert a UTF-16BE/LE string to current locale and copy the result. - * Return -1 if conversion failes. + * Return -1 if conversion fails. */ static int win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, @@ -3552,18 +3561,19 @@ win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, ll = WideCharToMultiByte(sc->to_cp, 0, (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size, NULL, &defchar); - if (ll == 0 && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - /* Need more buffer for MBS. */ - ll = WideCharToMultiByte(sc->to_cp, 0, - (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL); - if (archive_string_ensure(as, ll +1) == NULL) - return (-1); - mbs = as->s + as->length; - mbs_size = as->buffer_length - as->length -1; - continue; + /* Exit loop if we succeeded */ + if (ll != 0 || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + break; } - } while (0); + /* Else expand buffer and loop to try again. */ + ll = WideCharToMultiByte(sc->to_cp, 0, + (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL); + if (archive_string_ensure(as, ll +1) == NULL) + return (-1); + mbs = as->s + as->length; + mbs_size = as->buffer_length - as->length -1; + } while (1); archive_string_free(&tmp); as->length += ll; as->s[as->length] = '\0'; @@ -3596,7 +3606,7 @@ is_big_endian(void) /* * Convert a current locale string to UTF-16BE/LE and copy the result. - * Return -1 if conversion failes. + * Return -1 if conversion fails. */ static int win_strncat_to_utf16(struct archive_string *as16, const void *_p, @@ -3634,19 +3644,20 @@ win_strncat_to_utf16(struct archive_string *as16, const void *_p, do { count = MultiByteToWideChar(sc->from_cp, MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1); - if (count == 0 && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - /* Need more buffer for UTF-16 string */ - count = MultiByteToWideChar(sc->from_cp, - MB_PRECOMPOSED, s, (int)length, NULL, 0); - if (archive_string_ensure(as16, (count +1) * 2) - == NULL) - return (-1); - u16 = as16->s + as16->length; - avail = as16->buffer_length - 2; - continue; + /* Exit loop if we succeeded */ + if (count != 0 || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + break; } - } while (0); + /* Expand buffer and try again */ + count = MultiByteToWideChar(sc->from_cp, + MB_PRECOMPOSED, s, (int)length, NULL, 0); + if (archive_string_ensure(as16, (count +1) * 2) + == NULL) + return (-1); + u16 = as16->s + as16->length; + avail = as16->buffer_length - 2; + } while (1); as16->length += count * 2; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; @@ -3700,7 +3711,7 @@ win_strncat_to_utf16le(struct archive_string *as16, const void *_p, /* * Convert a UTF-16BE string to current locale and copy the result. - * Return -1 if conversion failes. + * Return -1 if conversion fails. */ static int best_effort_strncat_from_utf16(struct archive_string *as, const void *_p, @@ -3758,7 +3769,7 @@ best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p, /* * Convert a current locale string to UTF-16BE/LE and copy the result. - * Return -1 if conversion failes. + * Return -1 if conversion fails. */ static int best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p, @@ -3942,7 +3953,7 @@ archive_mstring_get_mbs_l(struct archive_mstring *aes, #if defined(_WIN32) && !defined(__CYGWIN__) /* - * Internationalization programing on Windows must use Wide + * Internationalization programming on Windows must use Wide * characters because Windows platform cannot make locale UTF-8. */ if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) { @@ -4074,7 +4085,7 @@ archive_mstring_copy_mbs_len_l(struct archive_mstring *aes, archive_string_empty(&(aes->aes_utf8)); #if defined(_WIN32) && !defined(__CYGWIN__) /* - * Internationalization programing on Windows must use Wide + * Internationalization programming on Windows must use Wide * characters because Windows platform cannot make locale UTF-8. */ if (sc == NULL) { diff --git a/Utilities/cmlibarchive/libarchive/archive_string.h b/Utilities/cmlibarchive/libarchive/archive_string.h index 23f4916..56dfbb2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.h +++ b/Utilities/cmlibarchive/libarchive/archive_string.h @@ -81,6 +81,10 @@ archive_strappend_char(struct archive_string *, char); struct archive_wstring * archive_wstrappend_wchar(struct archive_wstring *, wchar_t); +/* Append a raw array to an archive_string, resizing as necessary */ +struct archive_string * +archive_array_append(struct archive_string *, const char *, size_t); + /* Convert a Unicode string to current locale and append the result. */ /* Returns -1 if conversion fails. */ int @@ -115,13 +119,13 @@ archive_string_conversion_set_opt(struct archive_string_conv *, int); /* Copy one archive_string to another in locale conversion. - * Return -1 if conversion failes. */ + * Return -1 if conversion fails. */ int archive_strncpy_l(struct archive_string *, const void *, size_t, struct archive_string_conv *); /* Copy one archive_string to another in locale conversion. - * Return -1 if conversion failes. */ + * Return -1 if conversion fails. */ int archive_strncat_l(struct archive_string *, const void *, size_t, struct archive_string_conv *); diff --git a/Utilities/cmlibarchive/libarchive/archive_string_composition.h b/Utilities/cmlibarchive/libarchive/archive_string_composition.h index be41e33..8902ac1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string_composition.h +++ b/Utilities/cmlibarchive/libarchive/archive_string_composition.h @@ -1009,7 +1009,7 @@ static const char u_decomposable_blocks[0x1D2+1] = { (((uc) > 0x1D244)?0:\ ccc_val[ccc_val_index[ccc_index[(uc)>>8]][((uc)>>4)&0x0F]][(uc)&0x0F]) -/* The table of the value of Canonical Cimbining Class */ +/* The table of the value of Canonical Combining Class */ static const unsigned char ccc_val[][16] = { /* idx=0: XXXX0 - XXXXF */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c index f89df1d..f56ca33 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.c +++ b/Utilities/cmlibarchive/libarchive/archive_util.c @@ -580,7 +580,7 @@ void __archive_ensure_cloexec_flag(int fd) { #if defined(_WIN32) && !defined(__CYGWIN__) - (void)fd; /* UNSED */ + (void)fd; /* UNUSED */ #else int flags; diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.c b/Utilities/cmlibarchive/libarchive/archive_windows.c index d4e93fe..6ff8749 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_windows.c @@ -766,7 +766,7 @@ __la_win_entry_in_posix_pathseparator(struct archive_entry *entry) has_backslash = 1; } /* - * If there is no backslach chars, return the original. + * If there is no backslash chars, return the original. */ if (!has_backslash) return (entry); @@ -891,7 +891,7 @@ __la_dosmaperr(unsigned long e) return; } - for (i = 0; i < (int)sizeof(doserrors); i++) + for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++) { if (doserrors[i].winerr == e) { diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.h b/Utilities/cmlibarchive/libarchive/archive_windows.h index 7c31a16..c3aed0c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.h +++ b/Utilities/cmlibarchive/libarchive/archive_windows.h @@ -224,16 +224,20 @@ #define S_IWUSR _S_IWUSR #define S_IRUSR _S_IRUSR #endif +#ifndef S_IRWXG #define S_IRWXG _S_IRWXG #define S_IXGRP _S_IXGRP #define S_IWGRP _S_IWGRP +#endif #ifndef S_IRGRP #define S_IRGRP _S_IRGRP #endif +#ifndef S_IRWXO #define S_IRWXO _S_IRWXO #define S_IXOTH _S_IXOTH #define S_IWOTH _S_IWOTH #define S_IROTH _S_IROTH +#endif #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c index e3fa335..0634a22 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.c +++ b/Utilities/cmlibarchive/libarchive/archive_write.c @@ -109,10 +109,9 @@ archive_write_new(void) struct archive_write *a; unsigned char *nulls; - a = (struct archive_write *)malloc(sizeof(*a)); + a = (struct archive_write *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); - memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_WRITE_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_write_vtable(); @@ -126,12 +125,11 @@ archive_write_new(void) /* Initialize a block of nulls for padding purposes. */ a->null_length = 1024; - nulls = (unsigned char *)malloc(a->null_length); + nulls = (unsigned char *)calloc(1, a->null_length); if (nulls == NULL) { free(a); return (NULL); } - memset(nulls, 0, a->null_length); a->nulls = nulls; return (&a->archive); } @@ -233,7 +231,7 @@ __archive_write_filter(struct archive_write_filter *f, if (length == 0) return(ARCHIVE_OK); if (f->write == NULL) - /* If unset, a fatal error has already ocuured, so this filter + /* If unset, a fatal error has already occurred, so this filter * didn't open. We cannot write anything. */ return(ARCHIVE_FATAL); r = (f->write)(f, buff, length); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c index 1d0ab8c..e655185 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c @@ -523,7 +523,7 @@ drive_compressor_independence(struct archive_write_filter *f, const char *p, archive_le32enc(data->out, outsize); data->out += 4; } else { - /* The buffer is not compressed. The commpressed size was + /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ archive_le32enc(data->out, length | 0x80000000); data->out += 4; @@ -608,7 +608,7 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p, archive_le32enc(data->out, outsize); data->out += 4; } else { - /* The buffer is not compressed. The commpressed size was + /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ archive_le32enc(data->out, length | 0x80000000); data->out += 4; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c index 31a1b6f..55b5e8e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c @@ -200,6 +200,7 @@ __archive_write_program_free(struct archive_write_program_data *data) if (data->child) CloseHandle(data->child); #endif + free(data->program_name); free(data->child_buf); free(data); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c index 4f849ce..e4a3535 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c @@ -233,7 +233,7 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f, if (f->code == ARCHIVE_FILTER_XZ) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT if (data->threads != 1) { - bzero(&mt_options, sizeof(mt_options)); + memset(&mt_options, 0, sizeof(mt_options)); mt_options.threads = data->threads; mt_options.timeout = 300; mt_options.filters = data->lzmafilters; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_acl.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_acl.c index 5cbba54..144ab7e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_acl.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_acl.c @@ -34,6 +34,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 0 #define _ACL_PRIVATE /* For debugging */ #include <sys/acl.h> #endif +#if HAVE_DARWIN_ACL +#include <membership.h> +#endif #ifdef HAVE_ERRNO_H #include <errno.h> #endif @@ -43,7 +46,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 0 #include "archive_acl_private.h" #include "archive_write_disk_private.h" -#ifndef HAVE_POSIX_ACL +#if !HAVE_POSIX_ACL && !HAVE_NFS4_ACL /* Default empty function body to satisfy mainline code. */ int archive_write_disk_set_acls(struct archive *a, int fd, const char *name, @@ -56,47 +59,111 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, return (ARCHIVE_OK); } -#else +#else /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */ + +#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 set_acl(struct archive *, int fd, const char *, struct archive_acl *, acl_type_t, int archive_entry_acl_type, const char *tn); -/* - * XXX TODO: What about ACL types other than ACCESS and DEFAULT? - */ int archive_write_disk_set_acls(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl) { - int ret; + int ret = ARCHIVE_OK; - if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) > 0) { - ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_ACCESS, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); - if (ret != ARCHIVE_OK) - return (ret); - ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_DEFAULT, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); +#if !HAVE_DARWIN_ACL + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { +#if HAVE_SUN_ACL + /* Solaris writes POSIX.1e access and default ACLs together */ + ret = set_acl(a, fd, name, abstract_acl, ACLENT_T, + ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e"); +#else /* HAVE_POSIX_ACL */ + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + ret = set_acl(a, fd, name, abstract_acl, + ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + "access"); + if (ret != ARCHIVE_OK) + return (ret); + } + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) + ret = set_acl(a, fd, name, abstract_acl, + ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, + "default"); +#endif /* !HAVE_SUN_ACL */ + /* Simultaneous POSIX.1e and NFSv4 is not supported */ return (ret); -#ifdef ACL_TYPE_NFS4 - } else if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) { - ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_NFS4, + } +#endif /* !HAVE_DARWIN_ACL */ +#if HAVE_NFS4_ACL + if ((archive_acl_types(abstract_acl) & + ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_PLATFORM_ACL_TYPE_NFS4, ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); - return (ret); -#endif - } else - return ARCHIVE_OK; + } +#endif /* HAVE_NFS4_ACL */ + return (ret); } -static struct { - int archive_perm; - int platform_perm; +/* + * 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}, -#ifdef ACL_TYPE_NFS4 +#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}, @@ -114,33 +181,69 @@ static struct { {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} #endif +#endif /* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */ }; -#ifdef ACL_TYPE_NFS4 -static struct { - int archive_inherit; - int platform_inherit; +#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 NFSv4 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_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 +#endif /* HAVE_NFS4_ACL */ static int set_acl(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl, acl_type_t acl_type, int ae_requested_type, const char *tname) { +#if HAVE_SUN_ACL + aclent_t *aclent; + ace_t *ace; + int e, r; + acl_t *acl; +#else acl_t acl; acl_entry_t acl_entry; acl_permset_t acl_permset; -#ifdef ACL_TYPE_NFS4 +#if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL acl_flagset_t acl_flagset; #endif +#endif /* HAVE_SUN_ACL */ +#if HAVE_ACL_TYPE_NFS4 + int r; +#endif int ret; int ae_type, ae_permset, ae_tag, ae_id; +#if HAVE_DARWIN_ACL + uuid_t ae_uuid; +#endif uid_t ae_uid; gid_t ae_gid; const char *ae_name; @@ -151,22 +254,165 @@ set_acl(struct archive *a, int fd, const char *name, entries = archive_acl_reset(abstract_acl, ae_requested_type); if (entries == 0) return (ARCHIVE_OK); + +#if HAVE_SUN_ACL + acl = NULL; + acl = malloc(sizeof(acl_t)); + if (acl == NULL) { + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Invalid ACL type"); + return (ARCHIVE_FAILED); + } + if (acl_type == ACE_T) + acl->acl_entry_size = sizeof(ace_t); + else if (acl_type == ACLENT_T) + acl->acl_entry_size = sizeof(aclent_t); + else { + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Invalid ACL type"); + acl_free(acl); + return (ARCHIVE_FAILED); + } + acl->acl_type = acl_type; + acl->acl_cnt = entries; + + acl->acl_aclp = malloc(entries * acl->acl_entry_size); + if (acl->acl_aclp == NULL) { + archive_set_error(a, errno, + "Can't allocate memory for acl buffer"); + acl_free(acl); + return (ARCHIVE_FAILED); + } +#else /* !HAVE_SUN_ACL */ acl = acl_init(entries); + if (acl == (acl_t)NULL) { + archive_set_error(a, errno, + "Failed to initialize ACL working storage"); + return (ARCHIVE_FAILED); + } +#endif /* !HAVE_SUN_ACL */ +#if HAVE_SUN_ACL + e = 0; +#endif while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { - acl_create_entry(&acl, &acl_entry); - +#if HAVE_SUN_ACL + ace = NULL; + aclent = NULL; + if (acl->acl_type == ACE_T) { + ace = &((ace_t *)acl->acl_aclp)[e]; + ace->a_who = -1; + ace->a_access_mask = 0; + ace->a_flags = 0; + } else { + aclent = &((aclent_t *)acl->acl_aclp)[e]; + aclent->a_id = -1; + aclent->a_type = 0; + aclent->a_perm = 0; + } +#else /* !HAVE_SUN_ACL */ +#if HAVE_DARWIN_ACL + /* + * Mac OS doesn't support NFSv4 ACLs for + * owner@, group@ and everyone@. + * We skip any of these ACLs found. + */ + if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ || + ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || + ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE) + continue; +#endif + if (acl_create_entry(&acl, &acl_entry) != 0) { + archive_set_error(a, errno, + "Failed to create a new ACL entry"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif /* !HAVE_SUN_ACL */ +#if HAVE_DARWIN_ACL + switch (ae_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW); + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY); + break; + default: + /* We don't support any other types on MacOS */ + continue; + } +#endif switch (ae_tag) { +#if HAVE_SUN_ACL + case ARCHIVE_ENTRY_ACL_USER: + ae_uid = archive_write_disk_uid(a, ae_name, ae_id); + if (acl->acl_type == ACE_T) + ace->a_who = ae_uid; + else { + aclent->a_id = ae_uid; + aclent->a_type |= USER; + } + break; + case ARCHIVE_ENTRY_ACL_GROUP: + ae_gid = archive_write_disk_gid(a, ae_name, ae_id); + if (acl->acl_type == ACE_T) { + ace->a_who = ae_gid; + ace->a_flags |= ACE_IDENTIFIER_GROUP; + } else { + aclent->a_id = ae_gid; + aclent->a_type |= GROUP; + } + break; + case ARCHIVE_ENTRY_ACL_USER_OBJ: + if (acl->acl_type == ACE_T) + ace->a_flags |= ACE_OWNER; + else + aclent->a_type |= USER_OBJ; + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + if (acl->acl_type == ACE_T) { + ace->a_flags |= ACE_GROUP; + ace->a_flags |= ACE_IDENTIFIER_GROUP; + } else + aclent->a_type |= GROUP_OBJ; + break; + case ARCHIVE_ENTRY_ACL_MASK: + aclent->a_type |= CLASS_OBJ; + break; + case ARCHIVE_ENTRY_ACL_OTHER: + aclent->a_type |= OTHER_OBJ; + break; + case ARCHIVE_ENTRY_ACL_EVERYONE: + ace->a_flags |= ACE_EVERYONE; + break; +#else /* !HAVE_SUN_ACL */ case ARCHIVE_ENTRY_ACL_USER: - acl_set_tag_type(acl_entry, ACL_USER); ae_uid = archive_write_disk_uid(a, ae_name, ae_id); +#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ + acl_set_tag_type(acl_entry, ACL_USER); acl_set_qualifier(acl_entry, &ae_uid); +#else /* MacOS */ + if (mbr_identifier_to_uuid(ID_TYPE_UID, &ae_uid, + sizeof(uid_t), ae_uuid) != 0) + continue; + if (acl_set_qualifier(acl_entry, &ae_uuid) != 0) + continue; +#endif /* HAVE_DARWIN_ACL */ break; case ARCHIVE_ENTRY_ACL_GROUP: - acl_set_tag_type(acl_entry, ACL_GROUP); ae_gid = archive_write_disk_gid(a, ae_name, ae_id); +#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ + acl_set_tag_type(acl_entry, ACL_GROUP); acl_set_qualifier(acl_entry, &ae_gid); +#else /* MacOS */ + if (mbr_identifier_to_uuid(ID_TYPE_GID, &ae_gid, + sizeof(gid_t), ae_uuid) != 0) + continue; + if (acl_set_qualifier(acl_entry, &ae_uuid) != 0) + continue; +#endif /* HAVE_DARWIN_ACL */ break; +#if !HAVE_DARWIN_ACL /* FreeBSD, Linux */ case ARCHIVE_ENTRY_ACL_USER_OBJ: acl_set_tag_type(acl_entry, ACL_USER_OBJ); break; @@ -179,85 +425,230 @@ set_acl(struct archive *a, int fd, const char *name, case ARCHIVE_ENTRY_ACL_OTHER: acl_set_tag_type(acl_entry, ACL_OTHER); break; -#ifdef ACL_TYPE_NFS4 +#if HAVE_ACL_TYPE_NFS4 /* FreeBSD only */ case ARCHIVE_ENTRY_ACL_EVERYONE: acl_set_tag_type(acl_entry, ACL_EVERYONE); break; #endif +#endif /* !HAVE_DARWIN_ACL */ +#endif /* !HAVE_SUN_ACL */ default: - /* XXX */ - break; + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Unknown ACL tag"); + ret = ARCHIVE_FAILED; + goto exit_free; } -#ifdef ACL_TYPE_NFS4 +#if HAVE_ACL_TYPE_NFS4 || HAVE_SUN_ACL + r = 0; switch (ae_type) { +#if HAVE_SUN_ACL + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + if (ace != NULL) + ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; + else + r = -1; + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + if (ace != NULL) + ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE; + else + r = -1; + break; + case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: + if (ace != NULL) + ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE; + else + r = -1; + break; + case ARCHIVE_ENTRY_ACL_TYPE_ALARM: + if (ace != NULL) + ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE; + else + r = -1; + break; + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + if (aclent == NULL) + r = -1; + break; + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + if (aclent != NULL) + aclent->a_type |= ACL_DEFAULT; + else + r = -1; + break; +#else /* !HAVE_SUN_ACL */ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); + r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); break; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: // These don't translate directly into the system ACL. break; +#endif /* !HAVE_SUN_ACL */ default: - // XXX error handling here. - break; + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Unknown ACL entry type"); + ret = ARCHIVE_FAILED; + goto exit_free; } -#endif - acl_get_permset(acl_entry, &acl_permset); - acl_clear_perms(acl_permset); + if (r != 0) { +#if HAVE_SUN_ACL + errno = EINVAL; +#endif + archive_set_error(a, errno, + "Failed to set ACL entry type"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif /* HAVE_ACL_TYPE_NFS4 || HAVE_SUN_ACL */ +#if HAVE_SUN_ACL + if (acl->acl_type == ACLENT_T) { + if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) + aclent->a_perm |= 1; + if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) + aclent->a_perm |= 2; + if (ae_permset & ARCHIVE_ENTRY_ACL_READ) + aclent->a_perm |= 4; + } else +#else + if (acl_get_permset(acl_entry, &acl_permset) != 0) { + archive_set_error(a, errno, + "Failed to get ACL permission set"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + if (acl_clear_perms(acl_permset) != 0) { + archive_set_error(a, errno, + "Failed to clear ACL permissions"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif /* !HAVE_SUN_ACL */ for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { - if (ae_permset & acl_perm_map[i].archive_perm) - acl_add_perm(acl_permset, - acl_perm_map[i].platform_perm); + if (ae_permset & acl_perm_map[i].archive_perm) { +#if HAVE_SUN_ACL + ace->a_access_mask |= + acl_perm_map[i].platform_perm; +#else + if (acl_add_perm(acl_permset, + acl_perm_map[i].platform_perm) != 0) { + archive_set_error(a, errno, + "Failed to add ACL permission"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif + } } -#ifdef ACL_TYPE_NFS4 - acl_get_flagset_np(acl_entry, &acl_flagset); - acl_clear_flags_np(acl_flagset); - for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { - if (ae_permset & acl_inherit_map[i].archive_inherit) - acl_add_flag_np(acl_flagset, - acl_inherit_map[i].platform_inherit); +#if HAVE_NFS4_ACL +#if HAVE_SUN_ACL + if (acl_type == ACE_T) +#elif HAVE_DARWIN_ACL + if (acl_type == ACL_TYPE_EXTENDED) +#else /* FreeBSD */ + if (acl_type == ACL_TYPE_NFS4) +#endif + { +#if HAVE_POSIX_ACL || HAVE_DARWIN_ACL + /* + * acl_get_flagset_np() fails with non-NFSv4 ACLs + */ + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { + archive_set_error(a, errno, + "Failed to get flagset from an NFSv4 ACL entry"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + if (acl_clear_flags_np(acl_flagset) != 0) { + archive_set_error(a, errno, + "Failed to clear flags from an NFSv4 ACL flagset"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif /* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */ + for (i = 0; i < (int)(sizeof(acl_inherit_map) /sizeof(acl_inherit_map[0])); ++i) { + if (ae_permset & acl_inherit_map[i].archive_inherit) { +#if HAVE_SUN_ACL + ace->a_flags |= + acl_inherit_map[i].platform_inherit; +#else /* !HAVE_SUN_ACL */ + if (acl_add_flag_np(acl_flagset, + acl_inherit_map[i].platform_inherit) != 0) { + archive_set_error(a, errno, + "Failed to add flag to NFSv4 ACL flagset"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif /* HAVE_SUN_ACL */ + } + } } +#endif /* HAVE_NFS4_ACL */ +#if HAVE_SUN_ACL + e++; #endif } +#if HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL /* Try restoring the ACL through 'fd' if we can. */ -#if HAVE_ACL_SET_FD - if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) - ret = ARCHIVE_OK; - else -#else -#if HAVE_ACL_SET_FD_NP - if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) - ret = ARCHIVE_OK; - else +#if HAVE_SUN_ACL || HAVE_ACL_SET_FD_NP + if (fd >= 0) +#else /* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */ + if (fd >= 0 && acl_type == ACL_TYPE_ACCESS) #endif + { +#if HAVE_SUN_ACL + if (facl_set(fd, acl) == 0) +#elif HAVE_ACL_SET_FD_NP + if (acl_set_fd_np(fd, acl, acl_type) == 0) +#else /* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */ + if (acl_set_fd(fd, acl) == 0) #endif -#if HAVE_ACL_SET_LINK_NP - if (acl_set_link_np(name, acl_type, acl) != 0) { - archive_set_error(a, errno, "Failed to set %s acl", tname); - ret = ARCHIVE_WARN; - } + ret = ARCHIVE_OK; + else { + if (errno == EOPNOTSUPP) { + /* Filesystem doesn't support ACLs */ + ret = ARCHIVE_OK; + } else { + archive_set_error(a, errno, + "Failed to set %s acl on fd", tname); + } + } + } else +#endif /* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL */ +#if HAVE_SUN_ACL + if (acl_set(name, acl) != 0) +#elif HAVE_ACL_SET_LINK_NP + if (acl_set_link_np(name, acl_type, acl) != 0) #else /* TODO: Skip this if 'name' is a symlink. */ - if (acl_set_file(name, acl_type, acl) != 0) { - archive_set_error(a, errno, "Failed to set %s acl", tname); - ret = ARCHIVE_WARN; - } + if (acl_set_file(name, acl_type, acl) != 0) #endif + { + if (errno == EOPNOTSUPP) { + /* Filesystem doesn't support ACLs */ + ret = ARCHIVE_OK; + } else { + archive_set_error(a, errno, "Failed to set %s acl", + tname); + ret = ARCHIVE_WARN; + } + } +exit_free: acl_free(acl); return (ret); } -#endif +#endif /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c index 5946683..53629ac 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c @@ -110,6 +110,18 @@ __FBSDID("$FreeBSD$"); #include <sys/fcntl1.h> #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 <TargetConditionals.h> #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H @@ -140,7 +152,17 @@ __FBSDID("$FreeBSD$"); #define O_BINARY 0 #endif #ifndef O_CLOEXEC -#define O_CLOEXEC 0 +#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 { @@ -298,7 +320,7 @@ struct archive_write_disk { #define MAXIMUM_DIR_MODE 0775 /* - * Maxinum uncompressed size of a decmpfs block. + * Maximum uncompressed size of a decmpfs block. */ #define MAX_DECMPFS_BLOCK_SIZE (64 * 1024) /* @@ -313,7 +335,7 @@ struct archive_write_disk { #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 difinitions. */ +/* decmpfs definitions. */ #define MAX_DECMPFS_XATTR_SIZE 3802 #ifndef DECMPFS_XATTR_NAME #define DECMPFS_XATTR_NAME "com.apple.decmpfs" @@ -326,12 +348,19 @@ struct archive_write_disk { #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); +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 *); @@ -362,11 +391,14 @@ 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 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 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) @@ -612,9 +644,9 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) /* * NOTE: UF_COMPRESSED is ignored even if the filesystem * supports HFS+ Compression because the file should - * have at least an extended attriute "com.apple.decmpfs" + * have at least an extended attribute "com.apple.decmpfs" * before the flag is set to indicate that the file have - * been compressed. If hte filesystem does not support + * 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) @@ -637,7 +669,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { - archive_set_error(&a->archive, errno, "chdir() failure"); + archive_set_error(&a->archive, errno, + "chdir() failure"); ret = ARCHIVE_FATAL; } close(a->restore_pwd); @@ -685,7 +718,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); - fe->birthtime_nanos = archive_entry_birthtime_nsec(entry); + fe->birthtime_nanos = archive_entry_birthtime_nsec( + entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; @@ -711,7 +745,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { - memcpy(fe->mac_metadata, metadata, metadata_size); + memcpy(fe->mac_metadata, metadata, + metadata_size); fe->mac_metadata_size = metadata_size; fe->fixup |= TODO_MAC_METADATA; } @@ -1224,7 +1259,7 @@ hfs_drive_compressor(struct archive_write_disk *a, const char *buff, ret = hfs_write_compressed_data(a, bytes_used + rsrc_size); a->compressed_buffer_remaining = a->compressed_buffer_size; - /* If the compressed size is not enouph smaller than + /* 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. */ @@ -1329,7 +1364,7 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, (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 goint to set compressed + /* Get the position where we are going to set compressed * data. */ a->compressed_rsrc_position = RSRC_H_SIZE + 4 + (block_count * 8); @@ -1402,7 +1437,7 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff, bytes_to_write = size; /* Seek if necessary to the specified offset. */ if (a->offset < a->fd_offset) { - /* Can't support backword move. */ + /* Can't support backward move. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek failed"); return (ARCHIVE_FATAL); @@ -1468,7 +1503,8 @@ _archive_write_disk_data_block(struct archive *_a, 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); + "Too much data: Truncating file at %ju bytes", + (uintmax_t)a->filesize); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 @@ -1666,10 +1702,25 @@ _archive_write_disk_finish_entry(struct archive *_a) * ACLs that prevent attribute changes (including time). */ if (a->todo & TODO_ACLS) { - int r2 = archive_write_disk_set_acls(&a->archive, a->fd, - archive_entry_pathname(a->entry), - archive_entry_acl(a->entry)); + 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) { +#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: @@ -1755,10 +1806,9 @@ archive_write_disk_new(void) { struct archive_write_disk *a; - a = (struct archive_write_disk *)malloc(sizeof(*a)); + a = (struct archive_write_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); - memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; @@ -1796,7 +1846,7 @@ edit_deep_directories(struct archive_write_disk *a) char *tail = a->name; /* If path is short, avoid the open() below. */ - if (strlen(tail) <= PATH_MAX) + if (strlen(tail) < PATH_MAX) return; /* Try to record our starting dir. */ @@ -1806,7 +1856,7 @@ edit_deep_directories(struct archive_write_disk *a) return; /* As long as the path is too long... */ - while (strlen(tail) > PATH_MAX) { + while (strlen(tail) >= PATH_MAX) { /* Locate a dir prefix shorter than PATH_MAX. */ tail += PATH_MAX - 8; while (tail > a->name && *tail != '/') @@ -1993,8 +2043,9 @@ restore_entry(struct archive_write_disk *a) if (en) { /* Everything failed; give up here. */ - archive_set_error(&a->archive, en, "Can't create '%s'", - a->name); + if ((&a->archive)->error == NULL) + archive_set_error(&a->archive, en, "Can't create '%s'", + a->name); return (ARCHIVE_FAILED); } @@ -2014,6 +2065,11 @@ create_filesystem_object(struct archive_write_disk *a) 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. */ @@ -2022,6 +2078,43 @@ create_filesystem_object(struct archive_write_disk *a) #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 @@ -2039,11 +2132,20 @@ create_filesystem_object(struct archive_write_disk *a) 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); - __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 @@ -2190,8 +2292,12 @@ _archive_write_disk_close(struct archive *_a) if (p->fixup & TODO_MODE_BASE) chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) - archive_write_disk_set_acls(&a->archive, - -1, p->name, &p->acl); +#ifdef HAVE_DARWIN_ACL + if (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); @@ -2351,116 +2457,283 @@ current_fixup(struct archive_write_disk *a, const char *pathname) return (a->current_fixup); } -/* TODO: Make this work. */ -/* - * TODO: The deep-directory support bypasses this; disable deep directory - * support if we're doing symlink checks. - */ +/* 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(struct archive_write_disk *a) +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)a; /* UNUSED */ + (void)path; /* UNUSED */ + (void)error_number; /* UNUSED */ + (void)error_string; /* UNUSED */ + (void)flags; /* UNUSED */ return (ARCHIVE_OK); #else - char *pn; + 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 */ - /* Whatever we checked last time doesn't need to be re-checked. */ - pn = a->name; - if (archive_strlen(&(a->path_safe)) > 0) { - char *p = a->path_safe.s; - while ((*pn != '\0') && (*p == *pn)) - ++p, ++pn; - } + 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(pn == a->name && pn[0] == '/') - ++pn; - c = pn[0]; - /* Keep going until we've checked the entire name. */ - while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) { + 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 (*pn != '\0' && *pn != '/') - ++pn; - c = pn[0]; - pn[0] = '\0'; + 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(a->name, &st); + r = lstat(head, &st); if (r != 0) { + tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ - if (errno == ENOENT) + 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 (c == '\0') { + if (last) { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ - if (unlink(a->name)) { - archive_set_error(&a->archive, errno, + if (unlink(head)) { + tail[0] = c; + fsobj_error(a_eno, a_estr, errno, "Could not remove symlink %s", - a->name); - pn[0] = c; - return (ARCHIVE_FAILED); + path); + res = ARCHIVE_FAILED; + break; } - a->pst = NULL; /* * Even if we did remove it, a warning * is in order. The warning is silly, * though, if we're just replacing one * symlink with another symlink. */ - if (!S_ISLNK(a->mode)) { - archive_set_error(&a->archive, 0, - "Removing symlink %s", - a->name); + 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! */ - pn[0] = c; - return (0); - } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { + res = ARCHIVE_OK; + break; + } else if (flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ - if (unlink(a->name) != 0) { - archive_set_error(&a->archive, 0, - "Cannot remove intervening symlink %s", - a->name); - pn[0] = c; - return (ARCHIVE_FAILED); + 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; } - a->pst = NULL; } else { - archive_set_error(&a->archive, 0, - "Cannot extract through symlink %s", - a->name); - pn[0] = c; - return (ARCHIVE_FAILED); + tail[0] = c; + fsobj_error(a_eno, a_estr, 0, + "Cannot extract through symlink %s", path); + res = ARCHIVE_FAILED; + break; } } - pn[0] = c; - if (pn[0] != '\0') - pn++; /* Advance to the next segment. */ + /* 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); + } } - pn[0] = c; - /* We've checked and/or cleaned the whole path, so remember it. */ - archive_strcpy(&a->path_safe, a->name); - return (ARCHIVE_OK); +#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 '/' . @@ -2471,7 +2744,7 @@ check_symlinks(struct archive_write_disk *a) * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static void -cleanup_pathname_win(struct archive_write_disk *a) +cleanup_pathname_win(char *path) { wchar_t wc; char *p; @@ -2482,7 +2755,7 @@ cleanup_pathname_win(struct archive_write_disk *a) mb = 0; complete = 1; utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0; - for (p = a->name; *p != '\0'; p++) { + for (p = path; *p != '\0'; p++) { ++alen; if (*p == '\\') { /* If previous byte is smaller than 128, @@ -2507,7 +2780,7 @@ cleanup_pathname_win(struct archive_write_disk *a) /* * Convert path separator in wide-character. */ - p = a->name; + p = path; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == (size_t)-1) { @@ -2534,26 +2807,27 @@ cleanup_pathname_win(struct archive_write_disk *a) * is set) if the path is absolute. */ static int -cleanup_pathname(struct archive_write_disk *a) +cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr, + int flags) { char *dest, *src; char separator = '\0'; - dest = src = a->name; + dest = src = path; if (*src == '\0') { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid empty pathname"); + fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, + "Invalid empty ", "pathname"); return (ARCHIVE_FAILED); } #if defined(__CYGWIN__) - cleanup_pathname_win(a); + cleanup_pathname_win(path); #endif /* Skip leading '/'. */ if (*src == '/') { - if (a->flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Path is absolute"); + if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { + fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, + "Path is ", "absolute"); return (ARCHIVE_FAILED); } @@ -2580,10 +2854,11 @@ cleanup_pathname(struct archive_write_disk *a) } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ - if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { - archive_set_error(&a->archive, + if (flags + & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { + fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, - "Path contains '..'"); + "Path contains ", "'..'"); return (ARCHIVE_FAILED); } } @@ -2614,7 +2889,7 @@ cleanup_pathname(struct archive_write_disk *a) * We've just copied zero or more path elements, not including the * final '/'. */ - if (dest == a->name) { + if (dest == path) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. @@ -2629,6 +2904,23 @@ cleanup_pathname(struct archive_write_disk *a) 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. @@ -2707,7 +2999,8 @@ create_dir(struct archive_write_disk *a, char *path) } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ - archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); + archive_set_error(&a->archive, errno, + "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; @@ -3232,7 +3525,8 @@ clear_nochange_fflags(struct archive_write_disk *a) nochange_flags |= EXT2_IMMUTABLE_FL; #endif - return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); + return (set_fflags_platform(a, a->fd, a->name, mode, 0, + nochange_flags)); } @@ -3487,6 +3781,9 @@ exit_xattr: 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; @@ -3514,6 +3811,7 @@ exit_acl: if (dfacl) acl_free(dfacl); return (ret); +#endif } static int @@ -3753,7 +4051,8 @@ set_xattrs(struct archive_write_disk *a) if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; - archive_set_error(&a->archive, errno, + archive_set_error(&a->archive, + errno, "Cannot restore extended " "attributes on this file " "system"); @@ -3764,7 +4063,8 @@ set_xattrs(struct archive_write_disk *a) ret = ARCHIVE_WARN; } } else { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended attribute encountered"); ret = ARCHIVE_WARN; } @@ -3808,19 +4108,22 @@ set_xattrs(struct archive_write_disk *a) errno = 0; #if HAVE_EXTATTR_SET_FD if (a->fd >= 0) - e = extattr_set_fd(a->fd, namespace, name, value, size); + 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); + 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, + archive_set_error(&a->archive, + errno, "Cannot restore extended " "attributes on this file " "system"); @@ -3866,10 +4169,10 @@ older(struct stat *st, struct archive_entry *entry) { /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ - if (st->st_mtime < archive_entry_mtime(entry)) + if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry))) return (1); /* Definitely younger. */ - if (st->st_mtime > archive_entry_mtime(entry)) + 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 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c index 3b868fb..5c766d7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c @@ -84,15 +84,13 @@ static void cleanup(void *); int archive_write_disk_set_standard_lookup(struct archive *a) { - struct bucket *ucache = malloc(cache_size * sizeof(struct bucket)); - struct bucket *gcache = malloc(cache_size * sizeof(struct bucket)); + struct bucket *ucache = calloc(cache_size, sizeof(struct bucket)); + struct bucket *gcache = calloc(cache_size, sizeof(struct bucket)); if (ucache == NULL || gcache == NULL) { free(ucache); free(gcache); return (ARCHIVE_FATAL); } - memset(ucache, 0, cache_size * sizeof(struct bucket)); - memset(gcache, 0, cache_size * sizeof(struct bucket)); archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup); archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup); return (ARCHIVE_OK); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c index da76c54..94b016e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c @@ -192,7 +192,7 @@ struct archive_write_disk { /* * Default mode for dirs created automatically (will be modified by umask). - * Note that POSIX specifies 0777 for implicity-created dirs, "modified + * Note that POSIX specifies 0777 for implicitly-created dirs, "modified * by the process' file creation mask." */ #define DEFAULT_DIR_MODE 0777 @@ -396,7 +396,7 @@ permissive_name_w(struct archive_write_disk *a) } /* - * A full-pathname pointig a network drive + * A full-pathname pointing to a network drive * like "\\<server-name>\<share-name>\file". */ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') { @@ -1221,10 +1221,9 @@ archive_write_disk_new(void) { struct archive_write_disk *a; - a = (struct archive_write_disk *)malloc(sizeof(*a)); + a = (struct archive_write_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); - memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open.3 b/Utilities/cmlibarchive/libarchive/archive_write_open.3 index a52959b..457873e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_open.3 @@ -66,6 +66,7 @@ Freeze the settings, open the archive, and prepare for writing entries. This is the most generic form of this function, which accepts pointers to three callback functions which will be invoked by the compression layer to write the constructed archive. +This does not alter the default archive padding. .It Fn archive_write_open_fd A convenience form of .Fn archive_write_open @@ -123,12 +124,21 @@ is currently in use. You should be careful to ensure that this variable remains allocated until after the archive is closed. +This function will disable padding unless you +have specifically set the block size. .El More information about the .Va struct archive object and the overall design of the library can be found in the .Xr libarchive 3 overview. +.Pp +Note that the convenience forms above vary in how +they block the output. +See +.Xr archive_write_blocksize 3 +if you need to control the block size used for writes +or the end-of-file padding behavior. .\" .Sh CLIENT CALLBACKS To use this library, you will need to define and register @@ -226,6 +236,7 @@ functions. .Xr tar 1 , .Xr libarchive 3 , .Xr archive_write 3 , +.Xr archive_write_blocksize 3 , .Xr archive_write_filter 3 , .Xr archive_write_format 3 , .Xr archive_write_new 3 , diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c b/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c index 4f8d679..ea6ae0a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c @@ -53,12 +53,11 @@ archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t { struct write_memory_data *mine; - mine = (struct write_memory_data *)malloc(sizeof(*mine)); + mine = (struct write_memory_data *)calloc(1, sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } - memset(mine, 0, sizeof(*mine)); mine->buff = buff; mine->size = buffSize; mine->client_size = used; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c index 8718d25..3fc5a07 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c @@ -205,7 +205,7 @@ struct _7zip { /* * The list of the file entries which has its contents is used to * manage struct file objects. - * We use 'next' a menber of struct file to chain. + * We use 'next' (a member of struct file) to chain. */ struct { struct file *first; @@ -1358,7 +1358,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, if (r < 0) return (r); - /* Write Nume size. */ + /* Write Name size. */ r = enc_uint64(a, zip->total_bytes_entry_name+1); if (r < 0) return (r); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c index 9f17564..c9771d8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c @@ -126,12 +126,11 @@ archive_write_set_format_ar(struct archive_write *a) if (a->format_free != NULL) (a->format_free)(a); - ar = (struct ar_w *)malloc(sizeof(*ar)); + ar = (struct ar_w *)calloc(1, sizeof(*ar)); if (ar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); return (ARCHIVE_FATAL); } - memset(ar, 0, sizeof(*ar)); a->format_data = ar; a->format_name = "ar"; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c index 3526493..a4c9d1e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c @@ -289,7 +289,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c index a9bfa80..957f1a3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c @@ -116,12 +116,11 @@ archive_write_set_format_cpio_newc(struct archive *_a) if (a->format_free != NULL) (a->format_free)(a); - cpio = (struct cpio *)malloc(sizeof(*cpio)); + cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } - memset(cpio, 0, sizeof(*cpio)); a->format_data = cpio; a->format_name = "cpio"; a->format_options = archive_write_newc_options; @@ -232,7 +231,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c index 1d635d2..2d858c9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c @@ -119,9 +119,9 @@ static const char template_header[] = { '0','0','0','0','0','0', '0','\0', /* gid, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', - /* size, space termation: 12 bytes */ + /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', '\0', - /* mtime, space termation: 12 bytes */ + /* mtime, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', '\0', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', @@ -368,7 +368,7 @@ archive_write_gnutar_header(struct archive_write *a, } #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { @@ -478,15 +478,15 @@ archive_write_gnutar_header(struct archive_write *a, archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'K'); + archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); - if(ret < ARCHIVE_WARN) + if (ret < ARCHIVE_WARN) goto exit_write_header; - archive_entry_free(temp); /* Write name and trailing null byte. */ ret = __archive_write_output(a, gnutar->linkname, length); - if(ret < ARCHIVE_WARN) + if (ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to 512 bytes */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); @@ -508,12 +508,12 @@ archive_write_gnutar_header(struct archive_write *a, archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'L'); + archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) goto exit_write_header; - archive_entry_free(temp); /* Write pathname + trailing null byte. */ ret = __archive_write_output(a, pathname, length); if(ret < ARCHIVE_WARN) diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c index 879a776..4adf68e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c @@ -161,7 +161,7 @@ struct isofile { /* Used for managing struct isofile list. */ struct isofile *allnext; struct isofile *datanext; - /* Used for managing a hardlined struct isofile list. */ + /* Used for managing a hardlinked struct isofile list. */ struct isofile *hlnext; struct isofile *hardlink_target; @@ -436,7 +436,7 @@ struct iso_option { * Type : string * Default: Auto detect * : We check a size of boot image; - * : If ths size is just 1.22M/1.44M/2.88M, + * : If the size is just 1.22M/1.44M/2.88M, * : we assume boot_type is 'fd'; * : otherwise boot_type is 'no-emulation'. * COMPAT : @@ -528,7 +528,7 @@ struct iso_option { * - 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 maxinum length of files and directories is raised to 193. + * - the maximum length of files and directories is raised to 193. * if rockridge option is disabled, raised to 207. */ unsigned int iso_level:3; @@ -626,7 +626,7 @@ struct iso_option { * : 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 - * : badtar options instead. + * : bsdtar options instead. * Type : boolean/string * Default: Enabled as rockridge=useful * COMPAT : mkisofs -r / -R @@ -660,7 +660,7 @@ struct iso_option { * : for making zisofs. * : When the file size is less than one Logical Block * : size, that file will not zisofs'ed since it does - * : reduece an ISO-image size. + * : reduce an ISO-image size. * : * : When you specify option 'boot=<boot-image>', that * : 'boot-image' file won't be converted to zisofs file. @@ -680,7 +680,7 @@ 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 iamge can be created. */ + * save to until ISO image can be created. */ int temp_fd; struct isofile *cur_file; @@ -703,7 +703,7 @@ struct iso9660 { } all_file_list; /* A list of struct isofile entries which have its - * contents and are not a directory, a hardlined file + * contents and are not a directory, a hardlinked file * and a symlink file. */ struct { struct isofile *first; @@ -1907,9 +1907,9 @@ iso9660_close(struct archive_write *a) iso9660->primary.rootent); if (ret < 0) return (ret); - /* Make sure we have UTF-16BE convertors. - * if there is no file entry, convertors are still - * uninitilized. */ + /* 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( @@ -1995,7 +1995,7 @@ iso9660_close(struct archive_write *a) * Write an ISO 9660 image. */ - /* Switc to start using wbuff as file buffer. */ + /* Switch to start using wbuff as file buffer. */ iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_STREAM; iso9660->wbuff_offset = 0; @@ -2524,7 +2524,8 @@ get_tmfromtime(struct tm *tm, time_t *t) tzset(); localtime_r(t, tm); #elif HAVE__LOCALTIME64_S - _localtime64_s(tm, t); + __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 @@ -2553,7 +2554,7 @@ set_date_time(unsigned char *p, time_t t) static void set_date_time_null(unsigned char *p) { - memset(p, '0', 16); + memset(p, (int)'0', 16); p[16] = 0; } @@ -2959,7 +2960,7 @@ set_directory_record_rr(unsigned char *bp, int dr_len, gid = archive_entry_gid(file->entry); if (iso9660->opt.rr == OPT_RR_USEFUL) { /* - * This action is simular mkisofs -r option + * This action is similar to mkisofs -r option * but our rockridge=useful option does not * set a zero to uid and gid. */ @@ -3024,8 +3025,8 @@ set_directory_record_rr(unsigned char *bp, int dr_len, * +----+----+----+----+----+ * 10 11 12 13 14 15 * - * - cflg : flag of componet - * - clen : length of componet + * - cflg : flag of component + * - clen : length of component */ const char *sl; char sl_last; @@ -3108,7 +3109,7 @@ set_directory_record_rr(unsigned char *bp, int dr_len, /* * flg len * +----+----+ - * | 02 | 00 | CURREENT component. + * | 02 | 00 | CURRENT component. * +----+----+ (".") */ if (nc != NULL) { @@ -3947,7 +3948,7 @@ write_VD(struct archive_write *a, struct vdd *vdd) "Abstract File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); - /* Bibliongraphic File Identifier */ + /* Bibliographic File Identifier */ r = set_file_identifier(bp, 777, 813, vdc, a, vdd, &(iso9660->bibliographic_file_identifier), "Bibliongraphic File", 0, D_CHAR); @@ -4073,7 +4074,10 @@ write_information_block(struct archive_write *a) memset(info.s, 0, info_size); opt = 0; #if defined(HAVE__CTIME64_S) - _ctime64_s(buf, sizeof(buf), &(iso9660->birth_time)); + { + __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 @@ -4558,7 +4562,7 @@ write_file_descriptors(struct archive_write *a) file->cur_content = &(file->content); do { blocks += file->cur_content->blocks; - /* Next fragument */ + /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } @@ -4748,7 +4752,7 @@ isofile_gen_utility_names(struct archive_write *a, struct isofile *file) } /* - * Converte a filename to UTF-16BE. + * Convert a filename to UTF-16BE. */ if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len, iso9660->sconv_to_utf16be)) { @@ -5512,7 +5516,7 @@ isoent_setup_file_location(struct iso9660 *iso9660, int location) file->cur_content->location = location; location += file->cur_content->blocks; total_block += file->cur_content->blocks; - /* Next fragument */ + /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } @@ -6135,7 +6139,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, off = ffmax - extlen; if (off == 0) { /* A dot('.') character - * does't place to the first + * doesn't place to the first * byte of identifier. */ off ++; extlen --; @@ -6164,7 +6168,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, 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 identififier. */ + * hexadecimal number to avoid duplicate identifier. */ if (iso9660->opt.iso_level == 1) { if (ext_off >= 5) noff = 5; @@ -6742,7 +6746,7 @@ isoent_rr_move(struct archive_write *a) int r; pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]); - /* Theare aren't level 8 directories reaching a deepr level. */ + /* There aren't level 8 directories reaching a deeper level. */ if (pt->cnt == 0) return (ARCHIVE_OK); @@ -6813,7 +6817,7 @@ _compare_path_table(const void *v1, const void *v2) if (cmp != 0) return (cmp); - /* Compare indetifier */ + /* Compare identifier */ s1 = p1->identifier; s2 = p2->identifier; l = p1->ext_off; @@ -6855,7 +6859,7 @@ _compare_path_table_joliet(const void *v1, const void *v2) if (cmp != 0) return (cmp); - /* Compare indetifier */ + /* Compare identifier */ s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; l = p1->ext_off; @@ -7149,7 +7153,7 @@ isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent) iso9660->el_torito.catalog = isoent; /* - * Get a boot medai type. + * Get a boot media type. */ switch (iso9660->opt.boot_type) { default: diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c index b686303..493d473 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c @@ -1840,9 +1840,9 @@ mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, len = strlen(p); /* - * Add "./" prefiex. + * Add "./" prefix. * NOTE: If the pathname does not have a path separator, we have - * to add "./" to the head of the pathename because mtree reader + * to add "./" to the head of the pathname because mtree reader * will suppose that it is v1(a.k.a classic) mtree format and * change the directory unexpectedly and so it will make a wrong * path. diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c index 6f7fe78..6a301ac 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 2003-2007 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 @@ -61,15 +62,24 @@ struct pax { struct sparse_block *sparse_tail; struct archive_string_conv *sconv_utf8; int opt_binary; + + unsigned flags; +#define WRITE_SCHILY_XATTR (1 << 0) +#define WRITE_LIBARCHIVE_XATTR (1 << 1) }; static void add_pax_attr(struct archive_string *, const char *key, const char *value); +static void add_pax_attr_binary(struct archive_string *, + const char *key, + const char *value, size_t value_len); static void add_pax_attr_int(struct archive_string *, const char *key, int64_t value); static void add_pax_attr_time(struct archive_string *, const char *key, int64_t sec, unsigned long nanos); +static int add_pax_acl(struct archive_write *, + struct archive_entry *, struct pax *, int); static ssize_t archive_write_pax_data(struct archive_write *, const void *, size_t); static int archive_write_pax_close(struct archive_write *); @@ -127,13 +137,14 @@ archive_write_set_format_pax(struct archive *_a) if (a->format_free != NULL) (a->format_free)(a); - pax = (struct pax *)malloc(sizeof(*pax)); + pax = (struct pax *)calloc(1, sizeof(*pax)); if (pax == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); return (ARCHIVE_FATAL); } - memset(pax, 0, sizeof(*pax)); + pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR; + a->format_data = pax; a->format_name = "pax"; a->format_options = archive_write_pax_options; @@ -273,6 +284,17 @@ add_pax_attr_int(struct archive_string *as, const char *key, int64_t value) static void add_pax_attr(struct archive_string *as, const char *key, const char *value) { + add_pax_attr_binary(as, key, value, strlen(value)); +} + +/* + * Add a key/value attribute to the pax header. This function handles + * binary values. + */ +static void +add_pax_attr_binary(struct archive_string *as, const char *key, + const char *value, size_t value_len) +{ int digits, i, len, next_ten; char tmp[1 + 3 * sizeof(int)]; /* < 3 base-10 digits per byte */ @@ -280,7 +302,7 @@ add_pax_attr(struct archive_string *as, const char *key, const char *value) * PAX attributes have the following layout: * <len> <space> <key> <=> <value> <nl> */ - len = 1 + (int)strlen(key) + 1 + (int)strlen(value) + 1; + len = 1 + (int)strlen(key) + 1 + (int)value_len + 1; /* * The <len> field includes the length of the <len> field, so @@ -311,21 +333,47 @@ add_pax_attr(struct archive_string *as, const char *key, const char *value) archive_strappend_char(as, ' '); archive_strcat(as, key); archive_strappend_char(as, '='); - archive_strcat(as, value); + archive_array_append(as, value, value_len); archive_strappend_char(as, '\n'); } +static void +archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name, + const void *value, size_t value_len) +{ + struct archive_string s; + char *encoded_value; + + if (pax->flags & WRITE_LIBARCHIVE_XATTR) { + encoded_value = base64_encode((const char *)value, value_len); + + if (encoded_name != NULL && encoded_value != NULL) { + archive_string_init(&s); + archive_strcpy(&s, "LIBARCHIVE.xattr."); + archive_strcat(&s, encoded_name); + add_pax_attr(&(pax->pax_header), s.s, encoded_value); + archive_string_free(&s); + } + free(encoded_value); + } + if (pax->flags & WRITE_SCHILY_XATTR) { + archive_string_init(&s); + archive_strcpy(&s, "SCHILY.xattr."); + archive_strcat(&s, encoded_name); + add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len); + archive_string_free(&s); + } +} + static int archive_write_pax_header_xattrs(struct archive_write *a, struct pax *pax, struct archive_entry *entry) { - struct archive_string s; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; - char *encoded_value; char *url_encoded_name = NULL, *encoded_name = NULL; size_t size; int r; @@ -346,16 +394,9 @@ archive_write_pax_header_xattrs(struct archive_write *a, } } - encoded_value = base64_encode((const char *)value, size); + archive_write_pax_header_xattr(pax, encoded_name, + value, size); - if (encoded_name != NULL && encoded_value != NULL) { - archive_string_init(&s); - archive_strcpy(&s, "LIBARCHIVE.xattr."); - archive_strcat(&s, encoded_name); - add_pax_attr(&(pax->pax_header), s.s, encoded_value); - archive_string_free(&s); - } - free(encoded_value); } return (ARCHIVE_OK); } @@ -450,6 +491,45 @@ get_entry_symlink(struct archive_write *a, struct archive_entry *entry, return (ARCHIVE_OK); } +/* Add ACL to pax header */ +static int +add_pax_acl(struct archive_write *a, + struct archive_entry *entry, struct pax *pax, int flags) +{ + char *p; + const char *attr; + int acl_types; + + acl_types = archive_entry_acl_types(entry); + + if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) + attr = "SCHILY.acl.ace"; + else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) + attr = "SCHILY.acl.access"; + else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) + attr = "SCHILY.acl.default"; + else + return (ARCHIVE_FATAL); + + p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8); + if (p == NULL) { + if (errno == ENOMEM) { + archive_set_error(&a->archive, ENOMEM, "%s %s", + "Can't allocate memory for ", attr); + return (ARCHIVE_FATAL); + } + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s", + "Can't translate ", attr, " to UTF-8"); + return(ARCHIVE_WARN); + } else if (*p != '\0') { + add_pax_attr(&(pax->pax_header), + attr, p); + free(p); + } + return(ARCHIVE_OK); +} + /* * TODO: Consider adding 'comment' and 'charset' fields to * archive_entry so that clients can specify them. Also, consider @@ -466,6 +546,7 @@ archive_write_pax_header(struct archive_write *a, const char *p; const char *suffix; int need_extension, r, ret; + int acl_types; int sparse_count; uint64_t sparse_total, real_size; struct pax *pax; @@ -709,7 +790,7 @@ archive_write_pax_header(struct archive_write *a, /* Copy entry so we can modify it as needed. */ #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry_original); if (entry_main == entry_original) @@ -1017,16 +1098,6 @@ archive_write_pax_header(struct archive_write *a, if (!need_extension && p != NULL && *p != '\0') need_extension = 1; - /* If there are non-trivial ACL entries, we need an extension. */ - if (!need_extension && archive_entry_acl_count(entry_original, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0) - need_extension = 1; - - /* If there are non-trivial ACL entries, we need an extension. */ - if (!need_extension && archive_entry_acl_count(entry_original, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0) - need_extension = 1; - /* If there are extended attributes, we need an extension */ if (!need_extension && archive_entry_xattr_count(entry_original) > 0) need_extension = 1; @@ -1035,6 +1106,12 @@ archive_write_pax_header(struct archive_write *a, if (!need_extension && sparse_count > 0) need_extension = 1; + acl_types = archive_entry_acl_types(entry_original); + + /* If there are any ACL entries, we need an extension */ + if (!need_extension && acl_types != 0) + need_extension = 1; + /* * Libarchive used to include these in extended headers for * restricted pax format, but that confused people who @@ -1086,43 +1163,29 @@ archive_write_pax_header(struct archive_write *a, add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p); /* I use star-compatible ACL attributes. */ - r = archive_entry_acl_text_l(entry_original, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS | - ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, - &p, NULL, pax->sconv_utf8); - if (r != 0) { - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for " - "ACL.access"); + if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + ret = add_pax_acl(a, entry_original, pax, + ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | + ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | + ARCHIVE_ENTRY_ACL_STYLE_COMPACT); + if (ret == ARCHIVE_FATAL) return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Can't translate ACL.access to UTF-8"); - ret = ARCHIVE_WARN; - } else if (p != NULL && *p != '\0') { - add_pax_attr(&(pax->pax_header), - "SCHILY.acl.access", p); } - r = archive_entry_acl_text_l(entry_original, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | - ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, - &p, NULL, pax->sconv_utf8); - if (r != 0) { - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for " - "ACL.default"); + if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + ret = add_pax_acl(a, entry_original, pax, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS | + ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | + ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA); + if (ret == ARCHIVE_FATAL) + return (ARCHIVE_FATAL); + } + if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) { + ret = add_pax_acl(a, entry_original, pax, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | + ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | + ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA); + if (ret == ARCHIVE_FATAL) return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Can't translate ACL.default to UTF-8"); - ret = ARCHIVE_WARN; - } else if (p != NULL && *p != '\0') { - add_pax_attr(&(pax->pax_header), - "SCHILY.acl.default", p); } /* We use GNU-tar-compatible sparse attributes. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c index c033fb3..5be310a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c @@ -113,12 +113,11 @@ archive_write_set_format_shar(struct archive *_a) if (a->format_free != NULL) (a->format_free)(a); - shar = (struct shar *)malloc(sizeof(*shar)); + shar = (struct shar *)calloc(1, sizeof(*shar)); if (shar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data"); return (ARCHIVE_FATAL); } - memset(shar, 0, sizeof(*shar)); archive_string_init(&shar->work); archive_string_init(&shar->quoted_name); a->format_data = shar; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c index 484ab34..c54aeab 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c @@ -114,9 +114,9 @@ static const char template_header[] = { '0','0','0','0','0','0', ' ','\0', /* gid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', - /* size, space termation: 12 bytes */ + /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', - /* mtime, space termation: 12 bytes */ + /* mtime, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', @@ -184,13 +184,12 @@ archive_write_set_format_ustar(struct archive *_a) return (ARCHIVE_FATAL); } - ustar = (struct ustar *)malloc(sizeof(*ustar)); + ustar = (struct ustar *)calloc(1, sizeof(*ustar)); if (ustar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return (ARCHIVE_FATAL); } - memset(ustar, 0, sizeof(*ustar)); a->format_data = ustar; a->format_name = "ustar"; a->format_options = archive_write_ustar_options; @@ -307,7 +306,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) * case getting WCS failed. On POSIX, this is a * normal operation. */ - if (p != NULL && p[strlen(p) - 1] != '/') { + if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); @@ -336,7 +335,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) } #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c index 17efbaf..62b1522 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c @@ -98,9 +98,9 @@ static const char template_header[] = { '0','0','0','0','0','0', ' ','\0', /* gid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', - /* size, space termation: 12 bytes */ + /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', - /* mtime, space termation: 12 bytes */ + /* mtime, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', @@ -161,13 +161,12 @@ archive_write_set_format_v7tar(struct archive *_a) return (ARCHIVE_FATAL); } - v7tar = (struct v7tar *)malloc(sizeof(*v7tar)); + v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar)); if (v7tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate v7tar data"); return (ARCHIVE_FATAL); } - memset(v7tar, 0, sizeof(*v7tar)); a->format_data = v7tar; a->format_name = "tar (non-POSIX)"; a->format_options = archive_write_v7tar_options; @@ -314,7 +313,7 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) } #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c index ea66929..8b6daf9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c @@ -79,7 +79,7 @@ typedef enum { WT_RVIS, /* conversion, unsupported */ WT_CONV, - /* continutation, unsupported at the moment */ + /* continuation, unsupported at the moment */ WT_CONT, /* invalid type */ LAST_WT diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c index 58ad9e0..3c617ec 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c @@ -63,7 +63,7 @@ __FBSDID("$FreeBSD$"); * - When writing an XML element <link type="<file-type>">, <file-type> * which is a file type a symbolic link is referencing is always marked * as "broken". Xar utility uses stat(2) to get the file type, but, in - * libarcive format writer, we should not use it; if it is needed, we + * libarchive format writer, we should not use it; if it is needed, we * should get about it at archive_read_disk.c. * - It is possible to appear both <flags> and <ext2> elements. * Xar utility generates <flags> on BSD platform and <ext2> on Linux @@ -192,7 +192,7 @@ struct file { struct file *parent; /* parent directory entry */ /* * To manage sub directory files. - * We use 'chnext' a menber of struct file to chain. + * We use 'chnext' (a member of struct file) to chain. */ struct { struct file *first; @@ -258,7 +258,7 @@ struct xar { /* * The list of all file entries is used to manage struct file * objects. - * We use 'next' a menber of struct file to chain. + * We use 'next' (a member of struct file) to chain. */ struct { struct file *first; @@ -266,7 +266,7 @@ struct xar { } file_list; /* * The list of hard-linked file entries. - * We use 'hlnext' a menber of struct file to chain. + * We use 'hlnext' (a member of struct file) to chain. */ struct archive_rb_tree hardlink_rbtree; }; @@ -1227,7 +1227,7 @@ make_file_entry(struct archive_write *a, xmlTextWriterPtr writer, case AE_IFLNK: /* * xar utility has checked a file type, which - * a symblic-link file has referenced. + * a symbolic-link file has referenced. * For example: * <link type="directory">../ref/</link> * The symlink target file is "../ref/" and its @@ -1237,8 +1237,8 @@ make_file_entry(struct archive_write *a, xmlTextWriterPtr writer, * The symlink target file is "../f" and its * file type is a regular file. * - * But our implemention cannot do it, and then we - * always record that a attribute "type" is "borken", + * But our implementation cannot do it, and then we + * always record that a attribute "type" is "broken", * for example: * <link type="broken">foo/bar</link> * It means "foo/bar" is not reachable. @@ -1544,7 +1544,7 @@ make_toc(struct archive_write *a) } /* - * Start recoding TOC + * Start recording TOC */ r = xmlTextWriterStartElement(writer, BAD_CAST("xar")); if (r < 0) { @@ -1961,6 +1961,7 @@ file_free(struct file *file) archive_string_free(&(file->basename)); archive_string_free(&(file->symlink)); archive_string_free(&(file->script)); + archive_entry_free(file->entry); free(file); } @@ -2484,7 +2485,7 @@ file_connect_hardlink_files(struct xar *xar) archive_entry_set_nlink(target->entry, hl->nlink); if (hl->nlink > 1) /* It means this file is a hardlink - * targe itself. */ + * target itself. */ target->hardlink_target = target; for (nf = target->hlnext; nf != NULL; nf = nf->hlnext) { @@ -2913,7 +2914,7 @@ compression_init_encoder_xz(struct archive *a, *strm = lzma_init_data; #ifdef HAVE_LZMA_STREAM_ENCODER_MT if (threads > 1) { - bzero(&mt_options, sizeof(mt_options)); + memset(&mt_options, 0, sizeof(mt_options)); mt_options.threads = threads; mt_options.timeout = 300; mt_options.filters = lzmafilters; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c index 0b2c69b..a4ae229 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c @@ -592,7 +592,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) #if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure the path separators in pahtname, hardlink and symlink + /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ zip->entry = __la_win_entry_in_posix_pathseparator(entry); if (zip->entry == entry) @@ -878,7 +878,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) { memcpy(e, "\001\231\007\000\001\000AE", 8); - /* AES vendoer version AE-2 does not store a CRC. + /* AES vendor version AE-2 does not store a CRC. * WinZip 11 uses AE-1, which does store the CRC, * but it does not store the CRC when the file size * is less than 20 bytes. So we simulate what @@ -1013,7 +1013,7 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s) if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: - /* Initialize traditoinal PKWARE encryption context. */ + /* Initialize traditional PKWARE encryption context. */ if (!zip->tctx_valid) { ret = init_traditional_pkware_encryption(a); if (ret != ARCHIVE_OK) @@ -1499,7 +1499,7 @@ trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) } static uint8_t -trad_enc_decypt_byte(struct trad_enc_ctx *ctx) +trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; @@ -1515,7 +1515,7 @@ trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, for (i = 0; i < max; i++) { uint8_t t = in[i]; - out[i] = t ^ trad_enc_decypt_byte(ctx); + out[i] = t ^ trad_enc_decrypt_byte(ctx); trad_enc_update_keys(ctx, t); } return i; @@ -1626,7 +1626,7 @@ init_winzip_aes_encryption(struct archive_write *a) return (ARCHIVE_FAILED); } - /* Set a passowrd verification value after the 'salt'. */ + /* Set a password verification value after the 'salt'. */ salt[salt_len] = derived_key[key_len * 2]; salt[salt_len + 1] = derived_key[key_len * 2 + 1]; diff --git a/Utilities/cmlibarchive/libarchive/config_freebsd.h b/Utilities/cmlibarchive/libarchive/config_freebsd.h index 2073431..215e886 100644 --- a/Utilities/cmlibarchive/libarchive/config_freebsd.h +++ b/Utilities/cmlibarchive/libarchive/config_freebsd.h @@ -28,6 +28,7 @@ /* FreeBSD 5.0 and later have ACL and extattr support. */ #if __FreeBSD__ > 4 #define HAVE_ACL_CREATE_ENTRY 1 +#define HAVE_ACL_GET_FD_NP 1 #define HAVE_ACL_GET_LINK_NP 1 #define HAVE_ACL_GET_PERM_NP 1 #define HAVE_ACL_INIT 1 @@ -39,6 +40,7 @@ #define HAVE_EXTATTR_LIST_FILE 1 #define HAVE_EXTATTR_SET_FD 1 #define HAVE_EXTATTR_SET_FILE 1 +#define HAVE_STRUCT_XVFSCONF 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 #endif diff --git a/Utilities/cmlibarchive/libarchive/libarchive-formats.5 b/Utilities/cmlibarchive/libarchive/libarchive-formats.5 index 9cec760..62359dd 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive-formats.5 +++ b/Utilities/cmlibarchive/libarchive/libarchive-formats.5 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 18, 2012 +.Dd December 27, 2016 .Dt LIBARCHIVE-FORMATS 5 .Os .Sh NAME @@ -191,8 +191,6 @@ and device numbers. .It Solaris extensions Libarchive recognizes ACL and extended attribute records written by Solaris tar. -Currently, libarchive only has support for old-style ACLs; the -newer NFSv4 ACLs are recognized but discarded. .El .Pp The first tar program appeared in Seventh Edition Unix in 1979. diff --git a/Utilities/cmlibarchive/libarchive/tar.5 b/Utilities/cmlibarchive/libarchive/tar.5 index 6e6f0c0..30b837d 100644 --- a/Utilities/cmlibarchive/libarchive/tar.5 +++ b/Utilities/cmlibarchive/libarchive/tar.5 @@ -1,4 +1,5 @@ .\" 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 @@ -24,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 23, 2011 +.Dd December 27, 2016 .Dt TAR 5 .Os .Sh NAME @@ -440,11 +441,11 @@ archives to store files much larger than the historic 8GB limit. Vendor-specific attributes used by Joerg Schilling's .Nm star implementation. -.It Cm SCHILY.acl.access , Cm SCHILY.acl.default -Stores the access and default ACLs as textual strings in a format +.It Cm SCHILY.acl.access , Cm SCHILY.acl.default, Cm SCHILY.acl.ace +Stores the access, default and NFSv4 ACLs as textual strings in a format that is an extension of the format specified by POSIX.1e draft 17. -In particular, each user or group access specification can include a fourth -colon-separated field with the numeric UID or GID. +In particular, each user or group access specification can include +an additional colon-separated field with the numeric UID or GID. This allows ACLs to be restored on systems that may not have complete user or group information available (such as when NIS/YP or LDAP services are temporarily unavailable). diff --git a/Utilities/cmlibarchive/libarchive/xxhash.c b/Utilities/cmlibarchive/libarchive/xxhash.c index 262fecb..6f5ba52 100644 --- a/Utilities/cmlibarchive/libarchive/xxhash.c +++ b/Utilities/cmlibarchive/libarchive/xxhash.c @@ -29,13 +29,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ - #include "archive_platform.h" -#include "archive_xxhash.h" #include <stdlib.h> #include <string.h> +#include "archive_xxhash.h" + #ifdef HAVE_LIBLZ4 /*************************************** @@ -61,7 +61,7 @@ You can contact the author at : ** By default, xxHash library provides endian-independent Hash values, based on little-endian convention. ** Results are therefore identical for little-endian and big-endian CPU. ** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. -** Should endian-independance be of no importance for your application, you may set the #define below to 1. +** Should endian-independence be of no importance for your application, you may set the #define below to 1. ** It will improve speed for Big-endian CPU. ** This option has no impact on Little_Endian CPU. */ diff --git a/Utilities/cmlibuv/include/pthread-barrier.h b/Utilities/cmlibuv/include/pthread-barrier.h index 3e01705..900ebed 100644 --- a/Utilities/cmlibuv/include/pthread-barrier.h +++ b/Utilities/cmlibuv/include/pthread-barrier.h @@ -18,7 +18,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define _UV_PTHREAD_BARRIER_ #include <errno.h> #include <pthread.h> +#if !defined(__MVS__) #include <semaphore.h> /* sem_t */ +#endif #define PTHREAD_BARRIER_SERIAL_THREAD 0x12345 diff --git a/Utilities/cmlibuv/include/uv-os390.h b/Utilities/cmlibuv/include/uv-os390.h index b0b068f..58f9261 100644 --- a/Utilities/cmlibuv/include/uv-os390.h +++ b/Utilities/cmlibuv/include/uv-os390.h @@ -24,4 +24,7 @@ #define UV_PLATFORM_SEM_T int +#define UV_PLATFORM_LOOP_FIELDS \ + void* ep; \ + #endif /* UV_MVS_H */ diff --git a/Utilities/cmlibuv/include/uv-unix.h b/Utilities/cmlibuv/include/uv-unix.h index bca2714..3030f71 100644 --- a/Utilities/cmlibuv/include/uv-unix.h +++ b/Utilities/cmlibuv/include/uv-unix.h @@ -36,7 +36,9 @@ #include <termios.h> #include <pwd.h> +#if !defined(__MVS__) #include <semaphore.h> +#endif #include <pthread.h> #include <signal.h> @@ -44,6 +46,8 @@ #if defined(__linux__) # include "uv-linux.h" +#elif defined (__MVS__) +# include "uv-os390.h" #elif defined(_AIX) # include "uv-aix.h" #elif defined(__sun) diff --git a/Utilities/cmlibuv/include/uv-version.h b/Utilities/cmlibuv/include/uv-version.h index 3cb9b5f..e165809 100644 --- a/Utilities/cmlibuv/include/uv-version.h +++ b/Utilities/cmlibuv/include/uv-version.h @@ -31,8 +31,8 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 9 -#define UV_VERSION_PATCH 2 +#define UV_VERSION_MINOR 11 +#define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 0 #define UV_VERSION_SUFFIX "dev" diff --git a/Utilities/cmlibuv/include/uv-win.h b/Utilities/cmlibuv/include/uv-win.h index 89ee09a..d3e32a5 100644 --- a/Utilities/cmlibuv/include/uv-win.h +++ b/Utilities/cmlibuv/include/uv-win.h @@ -49,6 +49,7 @@ typedef struct pollfd { #include <process.h> #include <signal.h> +#include <fcntl.h> #include <sys/stat.h> #if defined(_MSC_VER) && _MSC_VER < 1600 diff --git a/Utilities/cmlibuv/include/uv.h b/Utilities/cmlibuv/include/uv.h index e3e20dc..5879764 100644 --- a/Utilities/cmlibuv/include/uv.h +++ b/Utilities/cmlibuv/include/uv.h @@ -367,6 +367,8 @@ typedef enum { } uv_membership; +UV_EXTERN int uv_translate_sys_error(int sys_errno); + UV_EXTERN const char* uv_strerror(int err); UV_EXTERN const char* uv_err_name(int err); diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c index 652cd98..1d2cd4a 100644 --- a/Utilities/cmlibuv/src/unix/aix.c +++ b/Utilities/cmlibuv/src/unix/aix.c @@ -64,6 +64,11 @@ #define RDWR_BUF_SIZE 4096 #define EQ(a,b) (strcmp(a,b) == 0) +static void* args_mem = NULL; +static char** process_argv = NULL; +static int process_argc = 0; +static char* process_title_ptr = NULL; + int uv__platform_loop_init(uv_loop_t* loop) { loop->fs_fd = -1; @@ -753,6 +758,13 @@ static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int assert((bytes >= 0) && "uv__ahafs_event - Error reading monitor file"); + /* In file / directory move cases, AIX Event infrastructure + * produces a second event with no data. + * Ignore it and return gracefully. + */ + if(bytes == 0) + return; + /* Parse the data */ if(bytes > 0) rc = uv__parse_data(result_data, &events, handle); @@ -881,24 +893,94 @@ void uv__fs_event_close(uv_fs_event_t* handle) { char** uv_setup_args(int argc, char** argv) { - return argv; + char** new_argv; + size_t size; + char* s; + int i; + + if (argc <= 0) + return argv; + + /* Save the original pointer to argv. + * AIX uses argv to read the process name. + * (Not the memory pointed to by argv[0..n] as on Linux.) + */ + process_argv = argv; + process_argc = argc; + + /* Calculate how much memory we need for the argv strings. */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + + /* Add space for the argv pointers. */ + size += (argc + 1) * sizeof(char*); + + new_argv = uv__malloc(size); + if (new_argv == NULL) + return argv; + args_mem = new_argv; + + /* Copy over the strings and set up the pointer table. */ + s = (char*) &new_argv[argc + 1]; + for (i = 0; i < argc; i++) { + size = strlen(argv[i]) + 1; + memcpy(s, argv[i], size); + new_argv[i] = s; + s += size; + } + new_argv[i] = NULL; + + return new_argv; } int uv_set_process_title(const char* title) { + char* new_title; + + /* We cannot free this pointer when libuv shuts down, + * the process may still be using it. + */ + new_title = uv__strdup(title); + if (new_title == NULL) + return -ENOMEM; + + /* If this is the first time this is set, + * don't free and set argv[1] to NULL. + */ + if (process_title_ptr != NULL) + uv__free(process_title_ptr); + + process_title_ptr = new_title; + + process_argv[0] = process_title_ptr; + if (process_argc > 1) + process_argv[1] = NULL; + return 0; } int uv_get_process_title(char* buffer, size_t size) { + size_t len; + len = strlen(process_argv[0]); if (buffer == NULL || size == 0) return -EINVAL; + else if (size <= len) + return -ENOBUFS; + + memcpy(buffer, process_argv[0], len + 1); - buffer[0] = '\0'; return 0; } +UV_DESTRUCTOR(static void free_args_mem(void)) { + uv__free(args_mem); /* Keep valgrind happy. */ + args_mem = NULL; +} + + int uv_resident_set_memory(size_t* rss) { char pp[64]; psinfo_t psinfo; diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h index 815e355..9dac255 100644 --- a/Utilities/cmlibuv/src/unix/atomic-ops.h +++ b/Utilities/cmlibuv/src/unix/atomic-ops.h @@ -43,8 +43,12 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { __compare_and_swap(ptr, &oldval, newval); return out; #elif defined(__MVS__) - return __plo_CS(ptr, (unsigned int*) ptr, - oldval, (unsigned int*) &newval); + unsigned int op4; + if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, + (unsigned int*) ptr, *ptr, &op4)) + return oldval; + else + return op4; #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif @@ -67,13 +71,18 @@ UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { # endif /* if defined(__64BIT__) */ return out; #elif defined (__MVS__) -# ifdef _LP64 - return __plo_CSGR(ptr, (unsigned long long*) ptr, - oldval, (unsigned long long*) &newval); -# else - return __plo_CS(ptr, (unsigned int*) ptr, - oldval, (unsigned int*) &newval); -# endif +#ifdef _LP64 + unsigned long long op4; + if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval, + (unsigned long long*) ptr, *ptr, &op4)) +#else + unsigned long op4; + if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, + (unsigned int*) ptr, *ptr, &op4)) +#endif + return oldval; + else + return op4; #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index d88fc1d..9ef7134 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c @@ -98,7 +98,7 @@ uint64_t uv_hrtime(void) { void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { - assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); + assert(!uv__is_closing(handle)); handle->flags |= UV_CLOSING; handle->close_cb = close_cb; @@ -517,6 +517,9 @@ int uv__close_nocheckstdio(int fd) { int uv__close(int fd) { assert(fd > STDERR_FILENO); /* Catch stdio close bugs. */ +#if defined(__MVS__) + epoll_file_close(fd); +#endif return uv__close_nocheckstdio(fd); } @@ -836,13 +839,8 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { * every tick of the event loop but the other backends allow us to * short-circuit here if the event mask is unchanged. */ - if (w->events == w->pevents) { - if (w->events == 0 && !QUEUE_EMPTY(&w->watcher_queue)) { - QUEUE_REMOVE(&w->watcher_queue); - QUEUE_INIT(&w->watcher_queue); - } + if (w->events == w->pevents) return; - } #endif if (QUEUE_EMPTY(&w->watcher_queue)) @@ -1236,3 +1234,9 @@ void uv_os_free_passwd(uv_passwd_t* pwd) { int uv_os_get_passwd(uv_passwd_t* pwd) { return uv__getpwuid_r(pwd); } + + +int uv_translate_sys_error(int sys_errno) { + /* If < 0 then it's already a libuv error. */ + return sys_errno <= 0 ? sys_errno : -sys_errno; +} diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index 3d478b7..f9513ea 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c @@ -129,8 +129,23 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { #if defined(__linux__) || defined(__sun) || defined(__NetBSD__) return fdatasync(req->file); -#elif defined(__APPLE__) && defined(SYS_fdatasync) - return syscall(SYS_fdatasync, req->file); +#elif defined(__APPLE__) + /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache + * to the drive platters. This is in contrast to Linux's fdatasync and fsync + * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent + * for flushing buffered data to permanent storage. + */ + return fcntl(req->file, F_FULLFSYNC); +#else + return fsync(req->file); +#endif +} + + +static ssize_t uv__fs_fsync(uv_fs_t* req) { +#if defined(__APPLE__) + /* See the comment in uv__fs_fdatasync. */ + return fcntl(req->file, F_FULLFSYNC); #else return fsync(req->file); #endif @@ -365,7 +380,6 @@ static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) { static ssize_t uv__fs_scandir(uv_fs_t* req) { uv__dirent_t **dents; - int saved_errno; int n; dents = NULL; @@ -374,28 +388,17 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) { /* NOTE: We will use nbufs as an index field */ req->nbufs = 0; - if (n == 0) - goto out; /* osx still needs to deallocate some memory */ - else if (n == -1) - return n; - - req->ptr = dents; - - return n; - -out: - saved_errno = errno; - if (dents != NULL) { - int i; - - /* Memory was allocated using the system allocator, so use free() here. */ - for (i = 0; i < n; i++) - free(dents[i]); + if (n == 0) { + /* OS X still needs to deallocate some memory. + * Memory was allocated using the system allocator, so use free() here. + */ free(dents); + dents = NULL; + } else if (n == -1) { + return n; } - errno = saved_errno; - req->ptr = NULL; + req->ptr = dents; return n; } @@ -798,6 +801,10 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_flags = 0; dst->st_gen = 0; #elif !defined(_AIX) && ( \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) || \ defined(_GNU_SOURCE) || \ defined(_BSD_SOURCE) || \ defined(_SVID_SOURCE) || \ @@ -809,9 +816,7 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec; dst->st_ctim.tv_sec = src->st_ctim.tv_sec; dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec; -# if defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__OpenBSD__) || \ +# if defined(__FreeBSD__) || \ defined(__NetBSD__) dst->st_birthtim.tv_sec = src->st_birthtim.tv_sec; dst->st_birthtim.tv_nsec = src->st_birthtim.tv_nsec; @@ -945,7 +950,7 @@ static void uv__fs_work(struct uv__work* w) { X(FCHOWN, fchown(req->file, req->uid, req->gid)); X(FDATASYNC, uv__fs_fdatasync(req)); X(FSTAT, uv__fs_fstat(req->file, &req->statbuf)); - X(FSYNC, fsync(req->file)); + X(FSYNC, uv__fs_fsync(req)); X(FTRUNCATE, ftruncate(req->file, req->off)); X(FUTIME, uv__fs_futime(req)); X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h index 2570026..b48f8fa 100644 --- a/Utilities/cmlibuv/src/unix/internal.h +++ b/Utilities/cmlibuv/src/unix/internal.h @@ -38,6 +38,10 @@ # include "linux-syscalls.h" #endif /* __linux__ */ +#if defined(__MVS__) +# include "os390-syscalls.h" +#endif /* __MVS__ */ + #if defined(__sun) # include <sys/port.h> # include <port.h> @@ -51,6 +55,10 @@ # include <poll.h> #endif /* _AIX */ +#if defined(__APPLE__) && !TARGET_OS_IPHONE +# include <AvailabilityMacros.h> +#endif + #if defined(__ANDROID__) int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); # ifdef pthread_sigmask @@ -154,7 +162,8 @@ struct uv__stream_queued_fds_s { defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__FreeBSD_kernel__) || \ - defined(__linux__) + defined(__linux__) || \ + defined(__OpenBSD__) #define uv__cloexec uv__cloexec_ioctl #define uv__nonblock uv__nonblock_ioctl #else @@ -268,7 +277,6 @@ int uv__make_socketpair(int fds[2], int flags); int uv__make_pipe(int fds[2], int flags); #if defined(__APPLE__) -#include <AvailabilityMacros.h> int uv__fsevents_init(uv_fs_event_t* handle); int uv__fsevents_close(uv_fs_event_t* handle); diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c index 909288c..ac28b69 100644 --- a/Utilities/cmlibuv/src/unix/openbsd.c +++ b/Utilities/cmlibuv/src/unix/openbsd.c @@ -163,7 +163,7 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { uv__free(process_title); process_title = uv__strdup(title); - setproctitle(title); + setproctitle("%s", title); return 0; } diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.c b/Utilities/cmlibuv/src/unix/os390-syscalls.c new file mode 100644 index 0000000..2bf3b73 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.c @@ -0,0 +1,334 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#include "os390-syscalls.h" +#include <errno.h> +#include <stdlib.h> +#include <assert.h> +#include <search.h> + +#define CW_CONDVAR 32 + +#pragma linkage(BPX4CTW, OS) +#pragma linkage(BPX1CTW, OS) + +static int number_of_epolls; +static QUEUE global_epoll_queue; +static uv_mutex_t global_epoll_lock; +static uv_once_t once = UV_ONCE_INIT; + +int scandir(const char* maindir, struct dirent*** namelist, + int (*filter)(const struct dirent*), + int (*compar)(const struct dirent**, + const struct dirent **)) { + struct dirent** nl; + struct dirent* dirent; + unsigned count; + size_t allocated; + DIR* mdir; + + nl = NULL; + count = 0; + allocated = 0; + mdir = opendir(maindir); + if (!mdir) + return -1; + + while (1) { + dirent = readdir(mdir); + if (!dirent) + break; + if (!filter || filter(dirent)) { + struct dirent* copy; + copy = uv__malloc(sizeof(*copy)); + if (!copy) { + while (count) { + dirent = nl[--count]; + uv__free(dirent); + } + uv__free(nl); + closedir(mdir); + errno = ENOMEM; + return -1; + } + memcpy(copy, dirent, sizeof(*copy)); + + nl = uv__realloc(nl, sizeof(*copy) * (count + 1)); + nl[count++] = copy; + } + } + + qsort(nl, count, sizeof(struct dirent *), + (int (*)(const void *, const void *)) compar); + + closedir(mdir); + + *namelist = nl; + return count; +} + + +static unsigned int next_power_of_two(unsigned int val) { + val -= 1; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val += 1; + return val; +} + + +static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { + unsigned int newsize; + unsigned int i; + struct pollfd* newlst; + + if (len <= lst->size) + return; + + newsize = next_power_of_two(len); + newlst = uv__realloc(lst->items, newsize * sizeof(lst->items[0])); + + if (newlst == NULL) + abort(); + for (i = lst->size; i < newsize; ++i) + newlst[i].fd = -1; + + lst->items = newlst; + lst->size = newsize; +} + + +static void epoll_init() { + QUEUE_INIT(&global_epoll_queue); + if (uv_mutex_init(&global_epoll_lock)) + abort(); +} + + +uv__os390_epoll* epoll_create1(int flags) { + uv__os390_epoll* lst; + + uv_once(&once, epoll_init); + uv_mutex_lock(&global_epoll_lock); + lst = uv__malloc(sizeof(*lst)); + if (lst == -1) + return NULL; + QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member); + uv_mutex_unlock(&global_epoll_lock); + + /* initialize list */ + lst->size = 0; + lst->items = NULL; + return lst; +} + + +int epoll_ctl(uv__os390_epoll* lst, + int op, + int fd, + struct epoll_event *event) { + if(op == EPOLL_CTL_DEL) { + if (fd >= lst->size || lst->items[fd].fd == -1) { + errno = ENOENT; + return -1; + } + lst->items[fd].fd = -1; + } else if(op == EPOLL_CTL_ADD) { + maybe_resize(lst, fd + 1); + if (lst->items[fd].fd != -1) { + errno = EEXIST; + return -1; + } + lst->items[fd].fd = fd; + lst->items[fd].events = event->events; + } else if(op == EPOLL_CTL_MOD) { + if (fd >= lst->size || lst->items[fd].fd == -1) { + errno = ENOENT; + return -1; + } + lst->items[fd].events = event->events; + } else + abort(); + + return 0; +} + + +int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, + int maxevents, int timeout) { + size_t size; + struct pollfd* pfds; + int pollret; + int reventcount; + + uv_mutex_lock(&global_epoll_lock); + uv_mutex_unlock(&global_epoll_lock); + size = lst->size; + pfds = lst->items; + pollret = poll(pfds, size, timeout); + if(pollret == -1) + return pollret; + + reventcount = 0; + for (int i = 0; i < lst->size && i < maxevents; ++i) { + struct epoll_event ev; + + ev.events = 0; + ev.fd = pfds[i].fd; + if(!pfds[i].revents) + continue; + + if(pfds[i].revents & POLLRDNORM) + ev.events = ev.events | POLLIN; + + if(pfds[i].revents & POLLWRNORM) + ev.events = ev.events | POLLOUT; + + if(pfds[i].revents & POLLHUP) + ev.events = ev.events | POLLHUP; + + pfds[i].revents = 0; + events[reventcount++] = ev; + } + + return reventcount; +} + + +int epoll_file_close(int fd) { + QUEUE* q; + + uv_once(&once, epoll_init); + uv_mutex_lock(&global_epoll_lock); + QUEUE_FOREACH(q, &global_epoll_queue) { + uv__os390_epoll* lst; + + lst = QUEUE_DATA(q, uv__os390_epoll, member); + if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1) + lst->items[fd].fd = -1; + } + + uv_mutex_unlock(&global_epoll_lock); + return 0; +} + +void epoll_queue_close(uv__os390_epoll* lst) { + uv_mutex_lock(&global_epoll_lock); + QUEUE_REMOVE(&lst->member); + uv_mutex_unlock(&global_epoll_lock); + uv__free(lst->items); + lst->items = NULL; +} + + +int nanosleep(const struct timespec* req, struct timespec* rem) { + unsigned nano; + unsigned seconds; + unsigned events; + unsigned secrem; + unsigned nanorem; + int rv; + int rc; + int rsn; + + nano = (int)req->tv_nsec; + seconds = req->tv_sec; + events = CW_CONDVAR; + +#if defined(_LP64) + BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn); +#else + BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn); +#endif + + assert(rv == -1 && errno == EAGAIN); + + if(rem != NULL) { + rem->tv_nsec = nanorem; + rem->tv_sec = secrem; + } + + return 0; +} + + +char* mkdtemp(char* path) { + static const char* tempchars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const size_t num_chars = 62; + static const size_t num_x = 6; + char *ep, *cp; + unsigned int tries, i; + size_t len; + uint64_t v; + int fd; + int retval; + int saved_errno; + + len = strlen(path); + ep = path + len; + if (len < num_x || strncmp(ep - num_x, "XXXXXX", num_x)) { + errno = EINVAL; + return NULL; + } + + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + return NULL; + + tries = TMP_MAX; + retval = -1; + do { + if (read(fd, &v, sizeof(v)) != sizeof(v)) + break; + + cp = ep - num_x; + for (i = 0; i < num_x; i++) { + *cp++ = tempchars[v % num_chars]; + v /= num_chars; + } + + if (mkdir(path, S_IRWXU) == 0) { + retval = 0; + break; + } + else if (errno != EEXIST) + break; + } while (--tries); + + saved_errno = errno; + uv__close(fd); + if (tries == 0) { + errno = EEXIST; + return NULL; + } + + if (retval == -1) { + errno = saved_errno; + return NULL; + } + + return path; +} diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.h b/Utilities/cmlibuv/src/unix/os390-syscalls.h new file mode 100644 index 0000000..61a7cee --- /dev/null +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.h @@ -0,0 +1,69 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#ifndef UV_OS390_SYSCALL_H_ +#define UV_OS390_SYSCALL_H_ + +#include "uv.h" +#include "internal.h" +#include <dirent.h> +#include <poll.h> +#include <pthread.h> + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 +#define MAX_EPOLL_INSTANCES 256 +#define MAX_ITEMS_PER_EPOLL 1024 + +#define UV__O_CLOEXEC 0x80000 +#define UV__EPOLL_CLOEXEC UV__O_CLOEXEC +#define UV__EPOLL_CTL_ADD EPOLL_CTL_ADD +#define UV__EPOLL_CTL_DEL EPOLL_CTL_DEL +#define UV__EPOLL_CTL_MOD EPOLL_CTL_MOD + +struct epoll_event { + int events; + int fd; +}; + +typedef struct { + QUEUE member; + struct pollfd* items; + unsigned long size; +} uv__os390_epoll; + +/* epoll api */ +uv__os390_epoll* epoll_create1(int flags); +int epoll_ctl(uv__os390_epoll* ep, int op, int fd, struct epoll_event *event); +int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, int timeout); +int epoll_file_close(int fd); + +/* utility functions */ +int nanosleep(const struct timespec* req, struct timespec* rem); +int scandir(const char* maindir, struct dirent*** namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, + const struct dirent **)); +char *mkdtemp(char* path); + +#endif /* UV_OS390_SYSCALL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c index bcdbc4b..be325a9 100644 --- a/Utilities/cmlibuv/src/unix/os390.c +++ b/Utilities/cmlibuv/src/unix/os390.c @@ -20,6 +20,628 @@ */ #include "internal.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <utmpx.h> +#include <unistd.h> +#include <sys/ps.h> +#if defined(__clang__) +#include "csrsic.h" +#else +#include "//'SYS1.SAMPLIB(CSRSIC)'" +#endif + +#define CVT_PTR 0x10 +#define CSD_OFFSET 0x294 + +/* + Long-term average CPU service used by this logical partition, + in millions of service units per hour. If this value is above + the partition's defined capacity, the partition will be capped. + It is calculated using the physical CPU adjustment factor + (RCTPCPUA) so it may not match other measures of service which + are based on the logical CPU adjustment factor. It is available + if the hardware supports LPAR cluster. +*/ +#define RCTLACS_OFFSET 0xC4 + +/* 32-bit count of alive CPUs. This includes both CPs and IFAs */ +#define CSD_NUMBER_ONLINE_CPUS 0xD4 + +/* Address of system resources manager (SRM) control table */ +#define CVTOPCTP_OFFSET 0x25C + +/* Address of the RCT table */ +#define RMCTRCT_OFFSET 0xE4 + +/* Address of the rsm control and enumeration area. */ +#define CVTRCEP_OFFSET 0x490 + +/* + Number of frames currently available to system. + Excluded are frames backing perm storage, frames offline, and bad frames. +*/ +#define RCEPOOL_OFFSET 0x004 + +/* Total number of frames currently on all available frame queues. */ +#define RCEAFC_OFFSET 0x088 + +/* CPC model length from the CSRSI Service. */ +#define CPCMODEL_LENGTH 16 + +/* Thread Entry constants */ +#define PGTH_CURRENT 1 +#define PGTH_LEN 26 +#define PGTHAPATH 0x20 +#pragma linkage(BPX4GTH, OS) +#pragma linkage(BPX1GTH, OS) + +typedef unsigned data_area_ptr_assign_type; + +typedef union { + struct { +#if defined(_LP64) + data_area_ptr_assign_type lower; +#endif + data_area_ptr_assign_type assign; + }; + char* deref; +} data_area_ptr; + + +void uv_loadavg(double avg[3]) { + /* TODO: implement the following */ + avg[0] = 0; + avg[1] = 0; + avg[2] = 0; +} + + +int uv__platform_loop_init(uv_loop_t* loop) { + uv__os390_epoll* ep; + + ep = epoll_create1(UV__EPOLL_CLOEXEC); + loop->ep = ep; + if (ep == NULL) + return -errno; + + return 0; +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + if (loop->ep != NULL) { + epoll_queue_close(loop->ep); + loop->ep = NULL; + } +} + + +uint64_t uv__hrtime(uv_clocktype_t type) { + struct timeval time; + gettimeofday(&time, NULL); + return (uint64_t) time.tv_sec * 1e9 + time.tv_usec * 1e3; +} + + +/* + Get the exe path using the thread entry information + in the address space. +*/ +static int getexe(const int pid, char* buf, size_t len) { + struct { + int pid; + int thid[2]; + char accesspid; + char accessthid; + char asid[2]; + char loginname[8]; + char flag; + char len; + } Input_data; + + union { + struct { + char gthb[4]; + int pid; + int thid[2]; + char accesspid; + char accessthid[3]; + int lenused; + int offsetProcess; + int offsetConTTY; + int offsetPath; + int offsetCommand; + int offsetFileData; + int offsetThread; + } Output_data; + char buf[2048]; + } Output_buf; + + struct Output_path_type { + char gthe[4]; + short int len; + char path[1024]; + }; + + int Input_length; + int Output_length; + void* Input_address; + void* Output_address; + struct Output_path_type* Output_path; + int rv; + int rc; + int rsn; + + Input_length = PGTH_LEN; + Output_length = sizeof(Output_buf); + Output_address = &Output_buf; + Input_address = &Input_data; + memset(&Input_data, 0, sizeof Input_data); + Input_data.flag |= PGTHAPATH; + Input_data.pid = pid; + Input_data.accesspid = PGTH_CURRENT; + +#ifdef _LP64 + BPX4GTH(&Input_length, + &Input_address, + &Output_length, + &Output_address, + &rv, + &rc, + &rsn); +#else + BPX1GTH(&Input_length, + &Input_address, + &Output_length, + &Output_address, + &rv, + &rc, + &rsn); +#endif + + if (rv == -1) { + errno = rc; + return -1; + } + + /* Check highest byte to ensure data availability */ + assert(((Output_buf.Output_data.offsetPath >>24) & 0xFF) == 'A'); + + /* Get the offset from the lowest 3 bytes */ + Output_path = (char*)(&Output_buf) + + (Output_buf.Output_data.offsetPath & 0x00FFFFFF); + + if (Output_path->len >= len) { + errno = ENOBUFS; + return -1; + } + + strncpy(buf, Output_path->path, len); + + return 0; +} + + +/* + * We could use a static buffer for the path manipulations that we need outside + * of the function, but this function could be called by multiple consumers and + * we don't want to potentially create a race condition in the use of snprintf. + * There is no direct way of getting the exe path in zOS - either through /procfs + * or through some libc APIs. The below approach is to parse the argv[0]'s pattern + * and use it in conjunction with PATH environment variable to craft one. + */ +int uv_exepath(char* buffer, size_t* size) { + int res; + char args[PATH_MAX]; + char abspath[PATH_MAX]; + size_t abspath_size; + int pid; + + if (buffer == NULL || size == NULL || *size == 0) + return -EINVAL; + + pid = getpid(); + res = getexe(pid, args, sizeof(args)); + if (res < 0) + return -EINVAL; + + /* + * Possibilities for args: + * i) an absolute path such as: /home/user/myprojects/nodejs/node + * ii) a relative path such as: ./node or ../myprojects/nodejs/node + * iii) a bare filename such as "node", after exporting PATH variable + * to its location. + */ + + /* Case i) and ii) absolute or relative paths */ + if (strchr(args, '/') != NULL) { + if (realpath(args, abspath) != abspath) + return -errno; + + abspath_size = strlen(abspath); + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; + } else { + /* Case iii). Search PATH environment variable */ + char trypath[PATH_MAX]; + char* clonedpath = NULL; + char* token = NULL; + char* path = getenv("PATH"); + + if (path == NULL) + return -EINVAL; + + clonedpath = uv__strdup(path); + if (clonedpath == NULL) + return -ENOMEM; + + token = strtok(clonedpath, ":"); + while (token != NULL) { + snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); + if (realpath(trypath, abspath) == abspath) { + /* Check the match is executable */ + if (access(abspath, X_OK) == 0) { + abspath_size = strlen(abspath); + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + uv__free(clonedpath); + return 0; + } + } + token = strtok(NULL, ":"); + } + uv__free(clonedpath); + + /* Out of tokens (path entries), and no match found */ + return -EINVAL; + } +} + + +uint64_t uv_get_free_memory(void) { + uint64_t freeram; + + data_area_ptr cvt = {0}; + data_area_ptr rcep = {0}; + cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); + rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); + freeram = *((uint64_t*)(rcep.deref + RCEAFC_OFFSET)) * 4; + return freeram; +} + + +uint64_t uv_get_total_memory(void) { + uint64_t totalram; + + data_area_ptr cvt = {0}; + data_area_ptr rcep = {0}; + cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); + rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); + totalram = *((uint64_t*)(rcep.deref + RCEPOOL_OFFSET)) * 4; + return totalram; +} + + +int uv_resident_set_memory(size_t* rss) { + W_PSPROC buf; + + memset(&buf, 0, sizeof(buf)); + if (w_getpsent(0, &buf, sizeof(W_PSPROC)) == -1) + return -EINVAL; + + *rss = buf.ps_size; + return 0; +} + + +int uv_uptime(double* uptime) { + struct utmpx u ; + struct utmpx *v; + time64_t t; + + u.ut_type = BOOT_TIME; + v = getutxid(&u); + if (v == NULL) + return -1; + *uptime = difftime64(time64(&t), v->ut_tv.tv_sec); + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + uv_cpu_info_t* cpu_info; + int result; + int idx; + siv1v2 info; + data_area_ptr cvt = {0}; + data_area_ptr csd = {0}; + data_area_ptr rmctrct = {0}; + data_area_ptr cvtopctp = {0}; + int cpu_usage_avg; + + cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); + + csd.assign = *((data_area_ptr_assign_type *) (cvt.deref + CSD_OFFSET)); + cvtopctp.assign = *((data_area_ptr_assign_type *) (cvt.deref + CVTOPCTP_OFFSET)); + rmctrct.assign = *((data_area_ptr_assign_type *) (cvtopctp.deref + RMCTRCT_OFFSET)); + + *count = *((int*) (csd.deref + CSD_NUMBER_ONLINE_CPUS)); + cpu_usage_avg = *((unsigned short int*) (rmctrct.deref + RCTLACS_OFFSET)); + + *cpu_infos = uv__malloc(*count * sizeof(uv_cpu_info_t)); + if (!*cpu_infos) + return -ENOMEM; + + cpu_info = *cpu_infos; + idx = 0; + while (idx < *count) { + cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability); + cpu_info->model = uv__malloc(CPCMODEL_LENGTH + 1); + memset(cpu_info->model, '\0', CPCMODEL_LENGTH + 1); + memcpy(cpu_info->model, info.siv1v2si11v1.si11v1cpcmodel, CPCMODEL_LENGTH); + cpu_info->cpu_times.user = cpu_usage_avg; + /* TODO: implement the following */ + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + cpu_info->cpu_times.nice = 0; + ++cpu_info; + ++idx; + } + + return 0; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + for (int i = 0; i < count; ++i) + uv__free(cpu_infos[i].model); + uv__free(cpu_infos); +} + + +static int uv__interface_addresses_v6(uv_interface_address_t** addresses, + int* count) { + uv_interface_address_t* address; + int sockfd; + int maxsize; + __net_ifconf6header_t ifc; + __net_ifconf6entry_t* ifr; + __net_ifconf6entry_t* p; + __net_ifconf6entry_t flg; + + *count = 0; + /* Assume maximum buffer size allowable */ + maxsize = 16384; + + if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) + return -errno; + + ifc.__nif6h_version = 1; + ifc.__nif6h_buflen = maxsize; + ifc.__nif6h_buffer = uv__calloc(1, maxsize);; + + if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { + uv__close(sockfd); + return -errno; + } + + + *count = 0; + ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); + while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { + p = ifr; + ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); + + if (!(p->__nif6e_addr.sin6_family == AF_INET6 || + p->__nif6e_addr.sin6_family == AF_INET)) + continue; + + if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) + continue; + + ++(*count); + } + + /* Alloc the return interface structs */ + *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + uv__close(sockfd); + return -ENOMEM; + } + address = *addresses; + + ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); + while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { + p = ifr; + ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); + + if (!(p->__nif6e_addr.sin6_family == AF_INET6 || + p->__nif6e_addr.sin6_family == AF_INET)) + continue; + + if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->__nif6e_name); + + if (p->__nif6e_addr.sin6_family == AF_INET6) + address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr); + else + address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr); + + /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ + + address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; + + address++; + } + + uv__close(sockfd); + return 0; +} + + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + int sockfd; + int maxsize; + struct ifconf ifc; + struct ifreq flg; + struct ifreq* ifr; + struct ifreq* p; + int count_v6; + + /* get the ipv6 addresses first */ + uv_interface_address_t* addresses_v6; + uv__interface_addresses_v6(&addresses_v6, &count_v6); + + /* now get the ipv4 addresses */ + *count = 0; + + /* Assume maximum buffer size allowable */ + maxsize = 16384; + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (0 > sockfd) + return -errno; + + ifc.ifc_req = uv__calloc(1, maxsize); + ifc.ifc_len = maxsize; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + uv__close(sockfd); + return -errno; + } + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) + + /* Count all up and running ipv4/ipv6 addresses */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv__close(sockfd); + return -errno; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + /* Alloc the return interface structs */ + *addresses = uv__malloc((*count + count_v6) * + sizeof(uv_interface_address_t)); + + if (!(*addresses)) { + uv__close(sockfd); + return -ENOMEM; + } + address = *addresses; + + /* copy over the ipv6 addresses */ + memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); + address += count_v6; + *count += count_v6; + uv__free(addresses_v6); + + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv__close(sockfd); + return -ENOSYS; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->ifr_name); + + if (p->ifr_addr.sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + } + + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; + address++; + } + +#undef ADDR_SIZE +#undef MAX + + uv__close(sockfd); + return 0; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + for (i = 0; i < count; ++i) + uv__free(addresses[i].name); + uv__free(addresses); +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct epoll_event* events; + struct epoll_event dummy; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].fd == fd) + events[i].fd = -1; + + /* Remove the file descriptor from the epoll. */ + if (loop->ep != NULL) + epoll_ctl(loop->ep, UV__EPOLL_CTL_DEL, fd, &dummy); +} + int uv__io_check_fd(uv_loop_t* loop, int fd) { struct pollfd p[1]; @@ -40,3 +662,204 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { return 0; } + + +void uv__fs_event_close(uv_fs_event_t* handle) { + UNREACHABLE(); +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + return -ENOSYS; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, + const char* filename, unsigned int flags) { + return -ENOSYS; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + return -ENOSYS; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + static const int max_safe_timeout = 1789569; + struct epoll_event events[1024]; + struct epoll_event* pe; + struct epoll_event e; + int real_timeout; + QUEUE* q; + uv__io_t* w; + uint64_t base; + int count; + int nfds; + int fd; + int op; + int i; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + uv_stream_t* stream; + + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + + assert(w->pevents != 0); + assert(w->fd >= 0); + + stream= container_of(w, uv_stream_t, io_watcher); + + assert(w->fd < (int) loop->nwatchers); + + e.events = w->pevents; + e.fd = w->fd; + + if (w->events == 0) + op = UV__EPOLL_CTL_ADD; + else + op = UV__EPOLL_CTL_MOD; + + /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching + * events, skip the syscall and squelch the events after epoll_wait(). + */ + if (epoll_ctl(loop->ep, op, w->fd, &e)) { + if (errno != EEXIST) + abort(); + + assert(op == UV__EPOLL_CTL_ADD); + + /* We've reactivated a file descriptor that's been watched before. */ + if (epoll_ctl(loop->ep, UV__EPOLL_CTL_MOD, w->fd, &e)) + abort(); + } + + w->events = w->pevents; + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + int nevents = 0; + + nfds = 0; + for (;;) { + if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) + timeout = max_safe_timeout; + + nfds = epoll_wait(loop->ep, events, + ARRAY_SIZE(events), timeout); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + base = loop->time; + SAVE_ERRNO(uv__update_time(loop)); + if (nfds == 0) { + assert(timeout != -1); + timeout = real_timeout - timeout; + if (timeout > 0) + continue; + + return; + } + + if (nfds == -1) { + + if (errno != EINTR) + abort(); + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + epoll_ctl(loop->ep, UV__EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= w->pevents & (POLLIN | POLLOUT); + + if (pe->events != 0) { + w->cb(loop, w, pe->events); + nevents++; + } + } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + return; + + timeout = real_timeout; + } +} + +void uv__set_process_title(const char* title) { + /* do nothing */ +} diff --git a/Utilities/cmlibuv/src/unix/poll.c b/Utilities/cmlibuv/src/unix/poll.c index 4c0d478..370994b 100644 --- a/Utilities/cmlibuv/src/unix/poll.c +++ b/Utilities/cmlibuv/src/unix/poll.c @@ -92,7 +92,7 @@ static void uv__poll_stop(uv_poll_t* handle) { int uv_poll_stop(uv_poll_t* handle) { - assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); + assert(!uv__is_closing(handle)); uv__poll_stop(handle); return 0; } @@ -102,7 +102,7 @@ int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { int events; assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); - assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); + assert(!uv__is_closing(handle)); uv__poll_stop(handle); diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c index 45f5b45..f2fe305 100644 --- a/Utilities/cmlibuv/src/unix/process.c +++ b/Utilities/cmlibuv/src/unix/process.c @@ -323,7 +323,7 @@ static void uv__process_child_init(const uv_process_options_t* options, } if (fd == use_fd) - uv__cloexec(use_fd, 0); + uv__cloexec_fcntl(use_fd, 0); else fd = dup2(use_fd, fd); @@ -333,7 +333,7 @@ static void uv__process_child_init(const uv_process_options_t* options, } if (fd <= 2) - uv__nonblock(fd, 0); + uv__nonblock_fcntl(fd, 0); if (close_fd >= stdio_count) uv__close(close_fd); diff --git a/Utilities/cmlibuv/src/unix/proctitle.c b/Utilities/cmlibuv/src/unix/proctitle.c index 08d875f..9160f7e 100644 --- a/Utilities/cmlibuv/src/unix/proctitle.c +++ b/Utilities/cmlibuv/src/unix/proctitle.c @@ -48,9 +48,15 @@ char** uv_setup_args(int argc, char** argv) { for (i = 0; i < argc; i++) size += strlen(argv[i]) + 1; +#if defined(__MVS__) + /* argv is not adjacent. So just use argv[0] */ + process_title.str = argv[0]; + process_title.len = strlen(argv[0]); +#else process_title.str = argv[0]; process_title.len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0]; assert(process_title.len + 1 == size); /* argv memory should be adjacent. */ +#endif /* Add space for the argv pointers. */ size += (argc + 1) * sizeof(char*); diff --git a/Utilities/cmlibuv/src/unix/pthread-barrier.c b/Utilities/cmlibuv/src/unix/pthread-barrier.c index f57bf25..b6e604d 100644 --- a/Utilities/cmlibuv/src/unix/pthread-barrier.c +++ b/Utilities/cmlibuv/src/unix/pthread-barrier.c @@ -73,7 +73,8 @@ int pthread_barrier_wait(pthread_barrier_t* barrier) { if (++b->in == b->threshold) { b->in = 0; b->out = b->threshold - 1; - assert(pthread_cond_signal(&b->cond) == 0); + rc = pthread_cond_signal(&b->cond); + assert(rc == 0); pthread_mutex_unlock(&b->mutex); return PTHREAD_BARRIER_SERIAL_THREAD; diff --git a/Utilities/cmlibuv/src/unix/signal.c b/Utilities/cmlibuv/src/unix/signal.c index d82b9b7..dbd8f86 100644 --- a/Utilities/cmlibuv/src/unix/signal.c +++ b/Utilities/cmlibuv/src/unix/signal.c @@ -43,7 +43,7 @@ static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2); static void uv__signal_stop(uv_signal_t* handle); -static pthread_once_t uv__signal_global_init_guard = PTHREAD_ONCE_INIT; +static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT; static struct uv__signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree); static int uv__signal_lock_pipefd[2]; @@ -64,7 +64,7 @@ static void uv__signal_global_init(void) { void uv__signal_global_once_init(void) { - pthread_once(&uv__signal_global_init_guard, uv__signal_global_init); + uv_once(&uv__signal_global_init_guard, uv__signal_global_init); } @@ -290,7 +290,7 @@ int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { sigset_t saved_sigmask; int err; - assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); + assert(!uv__is_closing(handle)); /* If the user supplies signum == 0, then return an error already. If the * signum is otherwise invalid then uv__signal_register will find out @@ -434,7 +434,7 @@ static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { int uv_signal_stop(uv_signal_t* handle) { - assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); + assert(!uv__is_closing(handle)); uv__signal_stop(handle); return 0; } diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c index d20d0bc..7059df1 100644 --- a/Utilities/cmlibuv/src/unix/stream.c +++ b/Utilities/cmlibuv/src/unix/stream.c @@ -390,7 +390,7 @@ failed_malloc: int uv__stream_open(uv_stream_t* stream, int fd, int flags) { -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__MVS__) int enable; #endif @@ -409,7 +409,7 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { return -errno; } -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__MVS__) enable = 1; if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) && errno != ENOTSOCK && @@ -1194,6 +1194,30 @@ static void uv__read(uv_stream_t* stream) { return; } } + +#if defined(__MVS__) + if (is_ipc && msg.msg_controllen > 0) { + uv_buf_t blankbuf; + int nread; + struct iovec *old; + + blankbuf.base = 0; + blankbuf.len = 0; + old = msg.msg_iov; + msg.msg_iov = (struct iovec*) &blankbuf; + nread = 0; + do { + nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); + err = uv__stream_recv_cmsg(stream, &msg); + if (err != 0) { + stream->read_cb(stream, err, &buf); + msg.msg_iov = old; + return; + } + } while (nread == 0 && msg.msg_controllen > 0); + msg.msg_iov = old; + } +#endif stream->read_cb(stream, nread, &buf); /* Return if we didn't fill the buffer, there is no more data to read. */ @@ -1221,8 +1245,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { if (!(stream->flags & UV_STREAM_WRITABLE) || stream->flags & UV_STREAM_SHUT || stream->flags & UV_STREAM_SHUTTING || - stream->flags & UV_CLOSED || - stream->flags & UV_CLOSING) { + uv__is_closing(stream)) { return -ENOTCONN; } diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c index 3e7a759..a43f7f1 100644 --- a/Utilities/cmlibuv/src/unix/sunos.c +++ b/Utilities/cmlibuv/src/unix/sunos.c @@ -695,6 +695,11 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { uv__free(cpu_infos); } +#ifdef SUNOS_NO_IFADDRS +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + return -ENOSYS; +} +#else /* SUNOS_NO_IFADDRS */ /* * Inspired By: * https://blogs.oracle.com/paulie/entry/retrieving_mac_address_in_solaris @@ -742,9 +747,6 @@ static int uv__set_phys_addr(uv_interface_address_t* address, } int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { -#ifdef SUNOS_NO_IFADDRS - return -ENOSYS; -#else uv_interface_address_t* address; struct ifaddrs* addrs; struct ifaddrs* ent; @@ -805,9 +807,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { freeifaddrs(addrs); return 0; -#endif /* SUNOS_NO_IFADDRS */ } - +#endif /* SUNOS_NO_IFADDRS */ void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index 52989f7..a9b5e4c 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c @@ -40,28 +40,8 @@ #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) -struct thread_ctx { - void (*entry)(void* arg); - void* arg; -}; - - -static void* uv__thread_start(void *arg) -{ - struct thread_ctx *ctx_p; - struct thread_ctx ctx; - - ctx_p = arg; - ctx = *ctx_p; - uv__free(ctx_p); - ctx.entry(ctx.arg); - - return 0; -} - int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { - struct thread_ctx* ctx; int err; pthread_attr_t* attr; #if defined(__APPLE__) @@ -69,13 +49,6 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { struct rlimit lim; #endif - ctx = uv__malloc(sizeof(*ctx)); - if (ctx == NULL) - return UV_ENOMEM; - - ctx->entry = entry; - ctx->arg = arg; - /* On OSX threads other than the main thread are created with a reduced stack * size by default, adjust it to RLIMIT_STACK. */ @@ -99,14 +72,11 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { attr = NULL; #endif - err = pthread_create(tid, attr, uv__thread_start, ctx); + err = pthread_create(tid, attr, (void*(*)(void*)) entry, arg); if (attr != NULL) pthread_attr_destroy(attr); - if (err) - uv__free(ctx); - return -err; } diff --git a/Utilities/cmlibuv/src/uv-common.c b/Utilities/cmlibuv/src/uv-common.c index 46d9546..bc7d137 100644 --- a/Utilities/cmlibuv/src/uv-common.c +++ b/Utilities/cmlibuv/src/uv-common.c @@ -512,8 +512,18 @@ void uv__fs_scandir_cleanup(uv_fs_t* req) { int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { uv__dirent_t** dents; uv__dirent_t* dent; + unsigned int* nbufs; - unsigned int* nbufs = uv__get_nbufs(req); + /* Check to see if req passed */ + if (req->result < 0) + return req->result; + + /* Ptr will be null if req was canceled or no files found */ + if (!req->ptr) + return UV_EOF; + + nbufs = uv__get_nbufs(req); + assert(nbufs); dents = req->ptr; diff --git a/Utilities/cmlibuv/src/win/core.c b/Utilities/cmlibuv/src/win/core.c index 9d00afc..e84186d 100644 --- a/Utilities/cmlibuv/src/win/core.c +++ b/Utilities/cmlibuv/src/win/core.c @@ -35,10 +35,6 @@ #include "handle-inl.h" #include "req-inl.h" - -static uv_loop_t default_loop_struct; -static uv_loop_t* default_loop_ptr; - /* uv_once initialization guards */ static uv_once_t uv_init_guard_ = UV_ONCE_INIT; diff --git a/Utilities/cmlibuv/src/win/error.c b/Utilities/cmlibuv/src/win/error.c index c512f35..642d111 100644 --- a/Utilities/cmlibuv/src/win/error.c +++ b/Utilities/cmlibuv/src/win/error.c @@ -71,6 +71,7 @@ int uv_translate_sys_error(int sys_errno) { switch (sys_errno) { case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; + case ERROR_ELEVATION_REQUIRED: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; diff --git a/Utilities/cmlibuv/src/win/fs-event.c b/Utilities/cmlibuv/src/win/fs-event.c index 03e4adc..05fc1d0 100644 --- a/Utilities/cmlibuv/src/win/fs-event.c +++ b/Utilities/cmlibuv/src/win/fs-event.c @@ -188,7 +188,6 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (is_path_dir) { /* path is a directory, so that's the directory that we will watch. */ - handle->dirw = pathw; dir_to_watch = pathw; } else { /* @@ -274,6 +273,8 @@ int uv_fs_event_start(uv_fs_event_t* handle, goto error; } + assert(is_path_dir ? pathw != NULL : pathw == NULL); + handle->dirw = pathw; handle->req_pending = 1; return 0; diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c index f1711ac..6902d4f 100644 --- a/Utilities/cmlibuv/src/win/fs.c +++ b/Utilities/cmlibuv/src/win/fs.c @@ -123,7 +123,7 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path, const char* new_path, const int copy_path) { char* buf; char* pos; - ssize_t buf_sz = 0, path_len, pathw_len = 0, new_pathw_len = 0; + ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0; /* new_path can only be set if path is also set. */ assert(new_path == NULL || path != NULL); @@ -403,7 +403,6 @@ void fs__open(uv_fs_t* req) { switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { case _O_RDONLY: access = FILE_GENERIC_READ; - attributes |= FILE_FLAG_BACKUP_SEMANTICS; break; case _O_WRONLY: access = FILE_GENERIC_WRITE; @@ -418,7 +417,6 @@ void fs__open(uv_fs_t* req) { if (flags & _O_APPEND) { access &= ~FILE_WRITE_DATA; access |= FILE_APPEND_DATA; - attributes &= ~FILE_FLAG_BACKUP_SEMANTICS; } /* diff --git a/Utilities/cmlibuv/src/win/getaddrinfo.c b/Utilities/cmlibuv/src/win/getaddrinfo.c index 744f8e0..c13bfec 100644 --- a/Utilities/cmlibuv/src/win/getaddrinfo.c +++ b/Utilities/cmlibuv/src/win/getaddrinfo.c @@ -262,8 +262,7 @@ int uv_getaddrinfo(uv_loop_t* loop, int err; if (req == NULL || (node == NULL && service == NULL)) { - err = WSAEINVAL; - goto error; + return UV_EINVAL; } uv_req_init(loop, (uv_req_t*)req); diff --git a/Utilities/cmlibuv/src/win/process-stdio.c b/Utilities/cmlibuv/src/win/process-stdio.c index e3c06f5..032e309 100644 --- a/Utilities/cmlibuv/src/win/process-stdio.c +++ b/Utilities/cmlibuv/src/win/process-stdio.c @@ -372,6 +372,7 @@ int uv__stdio_create(uv_loop_t* loop, case FILE_TYPE_PIPE: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; + break; case FILE_TYPE_CHAR: case FILE_TYPE_REMOTE: diff --git a/Utilities/cmlibuv/src/win/process.c b/Utilities/cmlibuv/src/win/process.c index 855c374..bdf88d2 100644 --- a/Utilities/cmlibuv/src/win/process.c +++ b/Utilities/cmlibuv/src/win/process.c @@ -492,7 +492,7 @@ WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) { * input : hello\\"world * output: "hello\\\\\"world" * input : hello world\ - * output: "hello world\" + * output: "hello world\\" */ *(target++) = L'"'; diff --git a/Utilities/cmlibuv/src/win/signal.c b/Utilities/cmlibuv/src/win/signal.c index 2c64a55..af7974c 100644 --- a/Utilities/cmlibuv/src/win/signal.c +++ b/Utilities/cmlibuv/src/win/signal.c @@ -30,12 +30,14 @@ RB_HEAD(uv_signal_tree_s, uv_signal_s); static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree); -static ssize_t volatile uv__signal_control_handler_refs = 0; static CRITICAL_SECTION uv__signal_lock; +static BOOL WINAPI uv__signal_control_handler(DWORD type); void uv_signals_init() { InitializeCriticalSection(&uv__signal_lock); + if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) + abort(); } @@ -125,102 +127,6 @@ static BOOL WINAPI uv__signal_control_handler(DWORD type) { } -static int uv__signal_register_control_handler() { - /* When this function is called, the uv__signal_lock must be held. */ - - /* If the console control handler has already been hooked, just add a */ - /* reference. */ - if (uv__signal_control_handler_refs > 0) { - uv__signal_control_handler_refs++; - return 0; - } - - if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) - return GetLastError(); - - uv__signal_control_handler_refs++; - - return 0; -} - - -static void uv__signal_unregister_control_handler() { - /* When this function is called, the uv__signal_lock must be held. */ - BOOL r; - - /* Don't unregister if the number of console control handlers exceeds one. */ - /* Just remove a reference in that case. */ - if (uv__signal_control_handler_refs > 1) { - uv__signal_control_handler_refs--; - return; - } - - assert(uv__signal_control_handler_refs == 1); - - r = SetConsoleCtrlHandler(uv__signal_control_handler, FALSE); - /* This should never fail; if it does it is probably a bug in libuv. */ - assert(r); - - uv__signal_control_handler_refs--; -} - - -static int uv__signal_register(int signum) { - switch (signum) { - case SIGINT: - case SIGBREAK: - case SIGHUP: - return uv__signal_register_control_handler(); - - case SIGWINCH: - /* SIGWINCH is generated in tty.c. No need to register anything. */ - return 0; - - case SIGILL: - case SIGABRT_COMPAT: - case SIGFPE: - case SIGSEGV: - case SIGTERM: - case SIGABRT: - /* Signal is never raised. */ - return 0; - - default: - /* Invalid signal. */ - return ERROR_INVALID_PARAMETER; - } -} - - -static void uv__signal_unregister(int signum) { - switch (signum) { - case SIGINT: - case SIGBREAK: - case SIGHUP: - uv__signal_unregister_control_handler(); - return; - - case SIGWINCH: - /* SIGWINCH is generated in tty.c. No need to unregister anything. */ - return; - - case SIGILL: - case SIGABRT_COMPAT: - case SIGFPE: - case SIGSEGV: - case SIGTERM: - case SIGABRT: - /* Nothing is registered for this signal. */ - return; - - default: - /* Libuv bug. */ - assert(0 && "Invalid signum"); - return; - } -} - - int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { uv_req_t* req; @@ -247,8 +153,6 @@ int uv_signal_stop(uv_signal_t* handle) { EnterCriticalSection(&uv__signal_lock); - uv__signal_unregister(handle->signum); - removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle); assert(removed_handle == handle); @@ -262,14 +166,9 @@ int uv_signal_stop(uv_signal_t* handle) { int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { - int err; - - /* If the user supplies signum == 0, then return an error already. If the */ - /* signum is otherwise invalid then uv__signal_register will find out */ - /* eventually. */ - if (signum == 0) { + /* Test for invalid signal values. */ + if (signum != SIGWINCH && (signum <= 0 || signum >= NSIG)) return UV_EINVAL; - } /* Short circuit: if the signal watcher is already watching {signum} don't */ /* go through the process of deregistering and registering the handler. */ @@ -289,13 +188,6 @@ int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { EnterCriticalSection(&uv__signal_lock); - err = uv__signal_register(signum); - if (err) { - /* Uh-oh, didn't work. */ - LeaveCriticalSection(&uv__signal_lock); - return uv_translate_sys_error(err); - } - handle->signum = signum; RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle); diff --git a/Utilities/cmlibuv/src/win/tty.c b/Utilities/cmlibuv/src/win/tty.c index 18d68d0..1b7adf6 100644 --- a/Utilities/cmlibuv/src/win/tty.c +++ b/Utilities/cmlibuv/src/win/tty.c @@ -56,6 +56,7 @@ #define ANSI_BACKSLASH_SEEN 0x80 #define MAX_INPUT_BUFFER_LENGTH 8192 +#define MAX_CONSOLE_CHAR 8192 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 @@ -1003,6 +1004,9 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, if (handle->tty.rd.last_key_len > 0) { SET_REQ_SUCCESS(&handle->read_req); uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req); + /* Make sure no attempt is made to insert it again until it's handled. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; return 0; } @@ -1616,17 +1620,29 @@ static int uv_tty_write_bufs(uv_tty_t* handle, DWORD* error) { /* We can only write 8k characters at a time. Windows can't handle */ /* much more characters in a single console write anyway. */ - WCHAR utf16_buf[8192]; + WCHAR utf16_buf[MAX_CONSOLE_CHAR]; + WCHAR* utf16_buffer; DWORD utf16_buf_used = 0; - unsigned int i; - -#define FLUSH_TEXT() \ - do { \ - if (utf16_buf_used > 0) { \ - uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \ - utf16_buf_used = 0; \ - } \ - } while (0) + unsigned int i, len, max_len, pos; + int allocate = 0; + +#define FLUSH_TEXT() \ + do { \ + pos = 0; \ + do { \ + len = utf16_buf_used - pos; \ + if (len > MAX_CONSOLE_CHAR) \ + len = MAX_CONSOLE_CHAR; \ + uv_tty_emit_text(handle, &utf16_buffer[pos], len, error); \ + pos += len; \ + } while (pos < utf16_buf_used); \ + if (allocate) { \ + uv__free(utf16_buffer); \ + allocate = 0; \ + utf16_buffer = utf16_buf; \ + } \ + utf16_buf_used = 0; \ + } while (0) #define ENSURE_BUFFER_SPACE(wchars_needed) \ if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { \ @@ -1644,38 +1660,47 @@ static int uv_tty_write_bufs(uv_tty_t* handle, /* state. */ *error = ERROR_SUCCESS; + utf16_buffer = utf16_buf; + uv_sem_wait(&uv_tty_output_lock); for (i = 0; i < nbufs; i++) { uv_buf_t buf = bufs[i]; unsigned int j; - if (uv__vterm_state == UV_SUPPORTED) { - utf16_buf_used = MultiByteToWideChar(CP_UTF8, - 0, - buf.base, - buf.len, - NULL, - 0); + if (uv__vterm_state == UV_SUPPORTED && buf.len > 0) { + utf16_buf_used = MultiByteToWideChar(CP_UTF8, + 0, + buf.base, + buf.len, + NULL, + 0); + + if (utf16_buf_used == 0) { + *error = GetLastError(); + break; + } - if (utf16_buf_used == 0) { - *error = GetLastError(); - break; - } + max_len = (utf16_buf_used + 1) * sizeof(WCHAR); + allocate = max_len > MAX_CONSOLE_CHAR; + if (allocate) + utf16_buffer = uv__malloc(max_len); + if (!MultiByteToWideChar(CP_UTF8, + 0, + buf.base, + buf.len, + utf16_buffer, + utf16_buf_used)) { + if (allocate) + uv__free(utf16_buffer); + *error = GetLastError(); + break; + } - if (!MultiByteToWideChar(CP_UTF8, - 0, - buf.base, - buf.len, - utf16_buf, - utf16_buf_used)) { - *error = GetLastError(); - break; - } + FLUSH_TEXT(); - FLUSH_TEXT(); - continue; - } + continue; + } for (j = 0; j < buf.len; j++) { unsigned char c = buf.base[j]; diff --git a/Utilities/cmlibuv/src/win/winapi.h b/Utilities/cmlibuv/src/win/winapi.h index 597a93d..4b0eeca 100644 --- a/Utilities/cmlibuv/src/win/winapi.h +++ b/Utilities/cmlibuv/src/win/winapi.h @@ -4615,6 +4615,10 @@ typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile) #endif /* from winerror.h */ +#ifndef ERROR_ELEVATION_REQUIRED +# define ERROR_ELEVATION_REQUIRED 740 +#endif + #ifndef ERROR_SYMLINK_NOT_SUPPORTED # define ERROR_SYMLINK_NOT_SUPPORTED 1464 #endif |