]> wimlib.net Git - wimlib/commitdiff
mount_image.c: add fallback definitions of RENAME_* constants master
authorEric Biggers <ebiggers3@gmail.com>
Sat, 20 Apr 2024 05:21:52 +0000 (22:21 -0700)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 20 Apr 2024 05:24:03 +0000 (22:24 -0700)
This is needed on older distros.

Reported at https://wimlib.net/forums/viewtopic.php?t=743

257 files changed:
.clang-format [new file with mode: 0644]
.gitattributes [new file with mode: 0644]
.github/workflows/ci.yml [new file with mode: 0644]
.gitignore
COPYING
COPYING.CC0 [deleted file]
COPYING.GPLv3
COPYING.LGPLv3
INSTALL [deleted file]
Makefile.am
NEWS [deleted file]
NEWS.md [new file with mode: 0644]
README [deleted file]
README.WINDOWS [deleted file]
README.WINDOWS.md [new file with mode: 0644]
README.md [new file with mode: 0644]
bootstrap
build-aux/nasm_lt.sh [deleted file]
configure.ac
debian/changelog [deleted file]
debian/compat [deleted file]
debian/control [deleted file]
debian/copyright [deleted file]
debian/rules [deleted file]
debian/source/format [deleted file]
debian/watch [deleted file]
debian/wimlib-dev.install [deleted file]
debian/wimlib-doc.docs [deleted file]
debian/wimlib-doc.examples [deleted file]
debian/wimlib15.install [deleted file]
debian/wimtools.docs [deleted file]
debian/wimtools.install [deleted file]
doc/man1/mkwinpeimg.1
doc/man1/wimapply.1
doc/man1/wimcapture.1
doc/man1/wimdelete.1
doc/man1/wimdir.1
doc/man1/wimexport.1
doc/man1/wimextract.1
doc/man1/wiminfo.1
doc/man1/wimjoin.1
doc/man1/wimlib-imagex.1
doc/man1/wimmount.1
doc/man1/wimoptimize.1
doc/man1/wimsplit.1
doc/man1/wimupdate.1
doc/man1/wimverify.1
examples/applywim.c
examples/capturewim.c
examples/compressfile.c
examples/decompressfile.c
examples/updatewim.c
include/wimlib.h
include/wimlib/apply.h
include/wimlib/assert.h
include/wimlib/avl_tree.h
include/wimlib/bitops.h
include/wimlib/blob_table.h
include/wimlib/bt_matchfinder.h
include/wimlib/chunk_compressor.h
include/wimlib/compiler.h
include/wimlib/compress_common.h
include/wimlib/cpu_features.h [new file with mode: 0644]
include/wimlib/decompress_common.h
include/wimlib/dentry.h
include/wimlib/divsufsort.h
include/wimlib/encoding.h
include/wimlib/endianness.h
include/wimlib/error.h
include/wimlib/file_io.h
include/wimlib/glob.h
include/wimlib/hc_matchfinder.h
include/wimlib/header.h
include/wimlib/inode.h
include/wimlib/inode_table.h
include/wimlib/integrity.h
include/wimlib/lcpit_matchfinder.h
include/wimlib/lz_extend.h [deleted file]
include/wimlib/lz_hash.h [deleted file]
include/wimlib/lzms_common.h
include/wimlib/lzx_common.h
include/wimlib/matchfinder_common.h [new file with mode: 0644]
include/wimlib/metadata.h
include/wimlib/ntfs_3g.h
include/wimlib/pathlist.h
include/wimlib/paths.h
include/wimlib/pattern.h
include/wimlib/progress.h
include/wimlib/registry.h
include/wimlib/reparse.h
include/wimlib/resource.h
include/wimlib/scan.h
include/wimlib/security.h
include/wimlib/security_descriptor.h
include/wimlib/sha1.h
include/wimlib/solid.h
include/wimlib/tagged_items.h
include/wimlib/test_support.h
include/wimlib/textfile.h
include/wimlib/threads.h [new file with mode: 0644]
include/wimlib/timestamp.h
include/wimlib/unaligned.h
include/wimlib/unix_data.h
include/wimlib/util.h
include/wimlib/wim.h
include/wimlib/wimboot.h
include/wimlib/win32.h
include/wimlib/win32_common.h
include/wimlib/win32_vss.h
include/wimlib/wof.h
include/wimlib/write.h
include/wimlib/x86_cpu_features.h [deleted file]
include/wimlib/xattr.h
include/wimlib/xml.h
include/wimlib/xml_windows.h
include/wimlib/xmlproc.h [new file with mode: 0644]
include/wimlib_tchar.h
m4/ax_pthread.m4
m4/nasm.m4 [deleted file]
programs/imagex-win32.c
programs/imagex-win32.h
programs/imagex.c
programs/mkwinpeimg.in
programs/wgetopt.c
programs/wgetopt.h
rpm/wimtools.spec [deleted file]
src/add_image.c
src/avl_tree.c
src/blob_table.c
src/compress.c
src/compress_common.c
src/compress_parallel.c
src/compress_serial.c
src/cpu_features.c [new file with mode: 0644]
src/decompress.c
src/decompress_common.c
src/delete_image.c
src/dentry.c
src/divsufsort.c
src/encoding.c
src/error.c
src/export_image.c
src/extract.c
src/file_io.c
src/header.c
src/inode.c
src/inode_fixup.c
src/inode_table.c
src/integrity.c
src/iterate_dir.c
src/join.c
src/lcpit_matchfinder.c
src/lzms_common.c
src/lzms_compress.c
src/lzms_decompress.c
src/lzx_common.c
src/lzx_compress.c
src/lzx_decompress.c
src/metadata_resource.c
src/mount_image.c
src/ntfs-3g_apply.c
src/ntfs-3g_capture.c
src/pathlist.c
src/paths.c
src/pattern.c
src/progress.c
src/reference.c
src/registry.c
src/reparse.c
src/resource.c
src/scan.c
src/security.c
src/sha1-ssse3.asm [deleted file]
src/sha1.c
src/solid.c
src/split.c
src/tagged_items.c
src/template.c
src/test_support.c
src/textfile.c
src/threads.c [new file with mode: 0644]
src/timestamp.c
src/unix_apply.c
src/unix_capture.c
src/update_image.c
src/util.c
src/verify.c
src/wim.c
src/wimboot.c
src/win32_apply.c
src/win32_capture.c
src/win32_common.c
src/win32_replacements.c
src/win32_vss.c
src/write.c
src/x86_cpu_features.c [deleted file]
src/xml.c
src/xml_windows.c
src/xmlproc.c [new file with mode: 0644]
src/xpress_compress.c
src/xpress_decompress.c
tests/test-imagex
tests/test-imagex-capture_and_apply
tests/test-imagex-mount
tests/test-imagex-ntfs
tests/test-imagex-update_and_extract
tests/tree-cmp.c
tests/wims/README
tests/wims/corrupted_file_1.wim [new file with mode: 0644]
tests/wims/corrupted_file_2.wim [new file with mode: 0644]
tests/win32-test-imagex-capture_and_apply.bat
tests/win32-test-imagex-capture_and_apply.sh [new file with mode: 0755]
tests/wlfuzz.c
tools/clang-build-with-cfi [deleted file]
tools/clang-scan-build [deleted file]
tools/compress_upcase_table.c
tools/generate_language_id_map.c
tools/get-version-number.sh [new file with mode: 0755]
tools/libFuzzer/compress/corpus/lzms20 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/lzms50 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/lzms80 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/lzx20 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/lzx50 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/lzx80 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/xpress20 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/xpress50 [new file with mode: 0644]
tools/libFuzzer/compress/corpus/xpress80 [new file with mode: 0644]
tools/libFuzzer/compress/fuzz.c [new file with mode: 0644]
tools/libFuzzer/decompress/corpus/lzms [new file with mode: 0644]
tools/libFuzzer/decompress/corpus/lzx [new file with mode: 0644]
tools/libFuzzer/decompress/corpus/xpress [new file with mode: 0644]
tools/libFuzzer/decompress/fuzz.c [new file with mode: 0644]
tools/libFuzzer/encoding/corpus/0 [new file with mode: 0644]
tools/libFuzzer/encoding/corpus/1 [new file with mode: 0644]
tools/libFuzzer/encoding/fuzz.c [new file with mode: 0644]
tools/libFuzzer/fault-injection.c [new file with mode: 0644]
tools/libFuzzer/fuzz.sh [new file with mode: 0755]
tools/libFuzzer/fuzzer.h [new file with mode: 0644]
tools/libFuzzer/test-one-input.c [new file with mode: 0644]
tools/libFuzzer/wim/corpus/0 [new file with mode: 0644]
tools/libFuzzer/wim/fuzz.c [new file with mode: 0644]
tools/libFuzzer/xml_windows/corpus/dll [new file with mode: 0644]
tools/libFuzzer/xml_windows/corpus/registry [new file with mode: 0644]
tools/libFuzzer/xml_windows/fuzz.c [new file with mode: 0644]
tools/libFuzzer/xmlproc/corpus/0 [new file with mode: 0644]
tools/libFuzzer/xmlproc/fuzz.c [new file with mode: 0644]
tools/make-releases [deleted file]
tools/make-releases.sh [new file with mode: 0755]
tools/make-windows-release [deleted file]
tools/repack-windows-release.sh [new file with mode: 0755]
tools/run-sparse [deleted file]
tools/run-sparse.sh [new file with mode: 0755]
tools/test-examples.sh [moved from tools/test-examples with 96% similarity]
tools/update-version.sh [moved from tools/update-version with 67% similarity]
tools/windeps/Makefile [deleted file]
tools/windeps/sha256sums [deleted file]
tools/windows-build.sh [new file with mode: 0755]

diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..069b7eb
--- /dev/null
@@ -0,0 +1,34 @@
+BasedOnStyle: LLVM
+AllowShortFunctionsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+AlignConsecutiveMacros: Consecutive
+AlwaysBreakAfterReturnType: All
+BreakBeforeBraces: Linux
+Cpp11BracedListStyle: false
+ForEachMacros:
+  - 'avl_tree_for_each_in_order'
+  - 'avl_tree_for_each_in_postorder'
+  - 'avl_tree_for_each_in_reverse_order'
+  - 'dentry_for_each_ci_match'
+  - 'for_dentry_child'
+  - 'for_dentry_child_postorder'
+  - 'for_inode_child'
+  - 'for_inode_child_postorder'
+  - 'hlist_for_each_entry'
+  - 'hlist_for_each_entry_safe'
+  - 'image_for_each_inode'
+  - 'image_for_each_inode_safe'
+  - 'image_for_each_unhashed_blob'
+  - 'image_for_each_unhashed_blob_safe'
+  - 'inode_for_each_dentry'
+  - 'inode_for_each_extraction_alias'
+  - 'list_for_each'
+  - 'list_for_each_entry'
+  - 'list_for_each_entry_reverse'
+  - 'list_for_each_entry_safe'
+  - 'xml_node_for_each_child'
+IncludeBlocks: Preserve
+IndentCaseLabels: false
+IndentWidth: 8
+SpaceBeforeParens: ControlStatementsExceptControlMacros
+UseTab: Always
diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..20c18d9
--- /dev/null
@@ -0,0 +1,2 @@
+# Do not mess with line endings
+* -text
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644 (file)
index 0000000..ee22013
--- /dev/null
@@ -0,0 +1,281 @@
+name: CI
+on: [pull_request, push]
+env:
+  DEF_CFLAGS: -O2 -g -Wall -Werror
+  DEPENDENCIES: autoconf automake libtool pkgconf libfuse3-dev fuse3 ntfs-3g-dev ntfs-3g
+
+jobs:
+  gcc-build-and-test:
+    name: Build and test with gcc
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y $DEPENDENCIES
+    - run: ./bootstrap
+    - run: ./configure CC=gcc CFLAGS="$DEF_CFLAGS"
+    - run: make -j8 check V=1
+    - run: make -j8 install V=1 DESTDIR=$PWD/installdir
+    - run: make -j8 uninstall V=1 DESTDIR=$PWD/installdir
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: gcc-test-logs
+        path: tests/*.log
+
+  clang-build-and-test:
+    name: Build and test with clang
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y clang $DEPENDENCIES
+    - run: ./bootstrap
+    - run: ./configure CC=clang CFLAGS="$DEF_CFLAGS"
+    - run: make -j8 check V=1
+    - run: make -j8 install V=1 DESTDIR=$PWD/installdir
+    - run: make -j8 uninstall V=1 DESTDIR=$PWD/installdir
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: clang-test-logs
+        path: tests/*.log
+
+  i386-build-and-test:
+    name: Build and test with gcc -m32
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo dpkg --add-architecture i386
+        sudo apt-get update
+        sudo apt-get install -y gcc-multilib $DEPENDENCIES
+    - run: ./bootstrap
+    - run: ./configure CC=gcc CFLAGS="-m32 $DEF_CFLAGS" --without-fuse --without-ntfs-3g
+    - run: make -j8 check V=1
+    - run: make -j8 install V=1 DESTDIR=$PWD/installdir
+    - run: make -j8 uninstall V=1 DESTDIR=$PWD/installdir
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: i386-test-logs
+        path: tests/*.log
+
+  asan-build-and-test:
+    name: Build and test with ASAN enabled
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y clang $DEPENDENCIES
+    - run: ./bootstrap
+    - run: ./configure CC=clang CFLAGS="$DEF_CFLAGS -fsanitize=address -fno-sanitize-recover=address"
+    - run: make -j8 check V=1
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: asan-test-logs
+        path: tests/*.log
+
+  ubsan-build-and-test:
+    name: Build and test with UBSAN enabled
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y clang $DEPENDENCIES
+    - run: ./bootstrap
+    - run: ./configure CC=clang CFLAGS="$DEF_CFLAGS -fsanitize=undefined -fno-sanitize-recover=undefined"
+    - run: make -j8 check V=1
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: ubsan-test-logs
+        path: tests/*.log
+
+  run-shellcheck:
+    name: Run shellcheck
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y shellcheck
+    - name: Run shellcheck
+      run: shellcheck tools/*.sh tools/*/*.sh
+
+  macos-build-and-test:
+    name: Build and test on macOS
+    runs-on: macos-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        brew install autoconf automake libtool pkg-config
+    - run: ./bootstrap
+    - run: ./configure CFLAGS="$DEF_CFLAGS" --without-fuse --without-ntfs-3g
+    - run: make -j8 check V=1
+    - run: make -j8 install DESTDIR=$PWD/installdir
+    - run: make -j8 uninstall DESTDIR=$PWD/installdir
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: macos-test-logs
+        path: tests/*.log
+
+  windows-build:
+    name: Build on Windows
+    runs-on: windows-latest
+    strategy:
+      matrix:
+        include:
+        - { msystem: MINGW32, cc_pkg: mingw-w64-i686-gcc }
+        - { msystem: MINGW64, cc_pkg: mingw-w64-x86_64-gcc }
+        - { msystem: CLANG32, cc_pkg: mingw-w64-clang-i686-clang }
+        - { msystem: CLANG64, cc_pkg: mingw-w64-clang-x86_64-clang }
+        - { msystem: CLANGARM64, options: --install-prerequisites }
+    defaults:
+      run:
+        shell: msys2 {0}
+    steps:
+    - uses: actions/checkout@v4
+      with:
+        fetch-depth: 0  # Need tags for tools/get-version-number.sh
+    - uses: msys2/setup-msys2@v2
+      with:
+        msystem: ${{matrix.msystem}}
+        update: true
+        install: >
+          autoconf
+          automake
+          git
+          libtool
+          make
+          ${{matrix.cc_pkg}}
+          pkgconf
+    - run: CFLAGS="$DEF_CFLAGS" ./tools/windows-build.sh ${{matrix.options}}
+    - uses: actions/upload-artifact@v4
+      with:
+        name: windows-${{matrix.msystem}}-bin
+        path: wimlib-*-bin
+
+  win32-test-imagex-capture-and-apply:
+    name: Run win32-test-imagex-capture_and_apply.bat
+    runs-on: windows-latest
+    defaults:
+      run:
+        shell: msys2 {0}
+    steps:
+    - uses: actions/checkout@v4
+      with:
+        fetch-depth: 0  # Need tags for tools/get-version-number.sh
+    - uses: msys2/setup-msys2@v2
+      with:
+        msystem: MINGW64
+        update: true
+        install: >
+          autoconf
+          automake
+          git
+          libtool
+          make
+          mingw-w64-x86_64-gcc
+          pkgconf
+    - run: tests/win32-test-imagex-capture_and_apply.sh
+
+  fuzz-with-libFuzzer:
+    name: Fuzz with libFuzzer (${{matrix.target}} ${{matrix.sanitizer}})
+    strategy:
+      matrix:
+        include:
+        - target: wim
+          sanitizer:
+        - target: wim
+          sanitizer: --asan --ubsan
+        - target: encoding
+          sanitizer: --asan --ubsan
+        - target: xmlproc
+          sanitizer:
+        - target: xmlproc
+          sanitizer: --asan --ubsan
+        - target: xml_windows
+          sanitizer: --asan --ubsan
+        - target: compress
+          sanitizer:
+        - target: compress
+          sanitizer: --asan --ubsan
+        - target: decompress
+          sanitizer:
+        - target: decompress
+          sanitizer: --asan --ubsan
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y clang $DEPENDENCIES
+    - run: ./bootstrap
+    - name: Fuzz
+      run: |
+        tools/libFuzzer/fuzz.sh --time=120 ${{matrix.sanitizer}} \
+            ${{matrix.target}}
+
+  fuzz-with-wlfuzz-linux:
+    name: Fuzz with wlfuzz (Linux, ${{matrix.sanitizer}})
+    strategy:
+      matrix:
+        include:
+        - sanitizer: none
+          cflags:
+        - sanitizer: ASAN
+          cflags: -fsanitize=address -fno-sanitize-recover=address
+        - sanitizer: UBSAN
+          cflags: -fsanitize=undefined -fno-sanitize-recover=undefined
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Install dependencies
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y clang $DEPENDENCIES
+    - run: ./bootstrap
+    - run: ./configure --enable-test-support CC=clang CFLAGS="$DEF_CFLAGS ${{matrix.cflags}}"
+    - run: make -j8 tests/wlfuzz
+    - run: TMPDIR=$PWD/tmp.wlfuzz tests/wlfuzz 120
+
+  fuzz-with-wlfuzz-windows:
+    name: Fuzz with wlfuzz (Windows)
+    runs-on: windows-latest
+    defaults:
+      run:
+        shell: msys2 {0}
+    steps:
+    - uses: actions/checkout@v4
+      with:
+        fetch-depth: 0  # Need tags for tools/get-version-number.sh
+    - uses: msys2/setup-msys2@v2
+      with:
+        msystem: MINGW64
+        update: true
+        install: >
+          autoconf
+          automake
+          git
+          libtool
+          make
+          mingw-w64-x86_64-gcc
+          pkgconf
+    - run: CFLAGS="$DEF_CFLAGS" ./tools/windows-build.sh -- --enable-test-support
+    - run: make tests/wlfuzz.exe
+    - run: TMPDIR=$PWD/tmp.wlfuzz tests/wlfuzz 120
index c472377c038d67f5a99352e7c3ced1e7c4484788..908421a5c4af52708d12365194a0d4f277730474 100644 (file)
@@ -24,6 +24,7 @@
 /config.log
 /config.status
 /configure
+/configure~
 /cscope.*
 /doc/Doxyfile
 /doc/html/
 /tests/tree-cmp
 /tests/wlfuzz
 /tests/wlfuzz.exe
-/tools/windeps/*.tar.*
-/tools/windeps/COPYING.*
-/tools/windeps/build_*
-/tools/windeps/libxml2*
-/tools/windeps/mingw*
-/tools/windeps/sysroot_*
-/tools/windeps/winpthreads*
+/tools/libFuzzer/*/fuzz
+/tools/libFuzzer/test-one-input
 /wimlib-*-bin/
 /wimlib-*.tar
 /wimlib-*.tar.*
diff --git a/COPYING b/COPYING
index c26af332a990696e75a452abbd43f278ccdcc503..4c7ff4cf678164e1578a7cdd64e44eb4161d1267 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -1,28 +1,27 @@
-Unless otherwise specified, wimlib and its associated programs, scripts,
-documentation, and other files may be redistributed and/or modified under the
-terms of the GNU General Public License; either version 3 of the License, or (at
-your option) any later version.  There is NO WARRANTY, to the extent permitted
-by law.  See the file COPYING.GPLv3 for more details.
+wimlib (meaning all programs, scripts, libraries, documentation, and other files
+that are part of the wimlib project -- not just the "libwim" library) may be
+redistributed and/or modified under the terms of the GNU General Public License;
+either version 3 of the License, or (at your option) any later version.  A copy
+of this license can be found in the file COPYING.GPLv3.
 
-Alternatively, when not prohibited by conflict with a third-party software
-license, the library portion of wimlib may be redistributed and/or modified
-under the terms of the GNU Lesser General Public License; either version 3 of
-the License, or (at your option) any later version.  There is NO WARRANTY, to
-the extent permitted by law.  See the file COPYING.LGPLv3 for more details.
+Also, when not prohibited by a third-party software license, libwim (the library
+portion of wimlib) may be redistributed and/or modified under the terms of the
+GNU Lesser General Public License; either version 3 of the License, or (at your
+option) any later version.  A copy of this license can be found in the file
+COPYING.LGPLv3.  This is offered as a "dual license", meaning that you can
+choose either this LGPLv3+ option or the above-mentioned GPLv3+ option.
 
-----------------------------------------
+In either case there is NO WARRANTY, to the extent permitted by law.
 
-NOTE! The primary reason for the GPL/LGPL "dual licensing" for the library is
-that on UNIX-like systems, wimlib can optionally be linked to the third-party
-library "libntfs-3g", which is licensed GPLv2+.  Under some interpretations of
-the GPL, this would require that wimlib be licensed under the GPL as well.
-However, a binary copy of wimlib that was compiled without libntfs-3g support
-(for example; the exception may be applicable in other situations as well)
-logically cannot be affected by libntfs-3g's license and should therefore be
-free to be redistributed under the LGPL instead of the GPL.
+--------------------------------------------------------------------------------
 
-NOTE! The file COPYING.CC0 contains a public domain dedication.  This public
-domain dedication does not apply to wimlib as a whole, but rather to individual
-source code files which the author(s) have elected to place into the public
-domain, as noted in the corresponding file headers.  As usual, such code carries
-NO WARRANTY, to the extent permitted by law.  See COPYING.CC0 for more details.
+NOTE! The reason for the "when not prohibited by a third-party software license"
+condition on the LGPL option for libwim is that libwim can optionally be linked
+to the third-party library "libntfs-3g", which is licensed under the GPL.
+Usually the GPL is interpreted in a way that means that any binary that uses a
+GPL library must be licensed under the GPL as well, not (for example) the LGPL.
+
+Therefore, if your build of libwim links to libntfs-3g, then you can't choose
+the LGPL option.  You may choose the LGPL option for Windows builds of libwim,
+since they don't link to libntfs-3g.  Likewise, you may choose the LGPL option
+for UNIX builds of libwim that were built with './configure --without-ntfs-3g'.
diff --git a/COPYING.CC0 b/COPYING.CC0
deleted file mode 100644 (file)
index 0e259d4..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-Creative Commons Legal Code
-
-CC0 1.0 Universal
-
-    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
-    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
-    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
-    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
-    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
-    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
-    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
-    HEREUNDER.
-
-Statement of Purpose
-
-The laws of most jurisdictions throughout the world automatically confer
-exclusive Copyright and Related Rights (defined below) upon the creator
-and subsequent owner(s) (each and all, an "owner") of an original work of
-authorship and/or a database (each, a "Work").
-
-Certain owners wish to permanently relinquish those rights to a Work for
-the purpose of contributing to a commons of creative, cultural and
-scientific works ("Commons") that the public can reliably and without fear
-of later claims of infringement build upon, modify, incorporate in other
-works, reuse and redistribute as freely as possible in any form whatsoever
-and for any purposes, including without limitation commercial purposes.
-These owners may contribute to the Commons to promote the ideal of a free
-culture and the further production of creative, cultural and scientific
-works, or to gain reputation or greater distribution for their Work in
-part through the use and efforts of others.
-
-For these and/or other purposes and motivations, and without any
-expectation of additional consideration or compensation, the person
-associating CC0 with a Work (the "Affirmer"), to the extent that he or she
-is an owner of Copyright and Related Rights in the Work, voluntarily
-elects to apply CC0 to the Work and publicly distribute the Work under its
-terms, with knowledge of his or her Copyright and Related Rights in the
-Work and the meaning and intended legal effect of CC0 on those rights.
-
-1. Copyright and Related Rights. A Work made available under CC0 may be
-protected by copyright and related or neighboring rights ("Copyright and
-Related Rights"). Copyright and Related Rights include, but are not
-limited to, the following:
-
-  i. the right to reproduce, adapt, distribute, perform, display,
-     communicate, and translate a Work;
- ii. moral rights retained by the original author(s) and/or performer(s);
-iii. publicity and privacy rights pertaining to a person's image or
-     likeness depicted in a Work;
- iv. rights protecting against unfair competition in regards to a Work,
-     subject to the limitations in paragraph 4(a), below;
-  v. rights protecting the extraction, dissemination, use and reuse of data
-     in a Work;
- vi. database rights (such as those arising under Directive 96/9/EC of the
-     European Parliament and of the Council of 11 March 1996 on the legal
-     protection of databases, and under any national implementation
-     thereof, including any amended or successor version of such
-     directive); and
-vii. other similar, equivalent or corresponding rights throughout the
-     world based on applicable law or treaty, and any national
-     implementations thereof.
-
-2. Waiver. To the greatest extent permitted by, but not in contravention
-of, applicable law, Affirmer hereby overtly, fully, permanently,
-irrevocably and unconditionally waives, abandons, and surrenders all of
-Affirmer's Copyright and Related Rights and associated claims and causes
-of action, whether now known or unknown (including existing as well as
-future claims and causes of action), in the Work (i) in all territories
-worldwide, (ii) for the maximum duration provided by applicable law or
-treaty (including future time extensions), (iii) in any current or future
-medium and for any number of copies, and (iv) for any purpose whatsoever,
-including without limitation commercial, advertising or promotional
-purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
-member of the public at large and to the detriment of Affirmer's heirs and
-successors, fully intending that such Waiver shall not be subject to
-revocation, rescission, cancellation, termination, or any other legal or
-equitable action to disrupt the quiet enjoyment of the Work by the public
-as contemplated by Affirmer's express Statement of Purpose.
-
-3. Public License Fallback. Should any part of the Waiver for any reason
-be judged legally invalid or ineffective under applicable law, then the
-Waiver shall be preserved to the maximum extent permitted taking into
-account Affirmer's express Statement of Purpose. In addition, to the
-extent the Waiver is so judged Affirmer hereby grants to each affected
-person a royalty-free, non transferable, non sublicensable, non exclusive,
-irrevocable and unconditional license to exercise Affirmer's Copyright and
-Related Rights in the Work (i) in all territories worldwide, (ii) for the
-maximum duration provided by applicable law or treaty (including future
-time extensions), (iii) in any current or future medium and for any number
-of copies, and (iv) for any purpose whatsoever, including without
-limitation commercial, advertising or promotional purposes (the
-"License"). The License shall be deemed effective as of the date CC0 was
-applied by Affirmer to the Work. Should any part of the License for any
-reason be judged legally invalid or ineffective under applicable law, such
-partial invalidity or ineffectiveness shall not invalidate the remainder
-of the License, and in such case Affirmer hereby affirms that he or she
-will not (i) exercise any of his or her remaining Copyright and Related
-Rights in the Work or (ii) assert any associated claims and causes of
-action with respect to the Work, in either case contrary to Affirmer's
-express Statement of Purpose.
-
-4. Limitations and Disclaimers.
-
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
-    surrendered, licensed or otherwise affected by this document.
- b. Affirmer offers the Work as-is and makes no representations or
-    warranties of any kind concerning the Work, express, implied,
-    statutory or otherwise, including without limitation warranties of
-    title, merchantability, fitness for a particular purpose, non
-    infringement, or the absence of latent or other defects, accuracy, or
-    the present or absence of errors, whether or not discoverable, all to
-    the greatest extent permissible under applicable law.
- c. Affirmer disclaims responsibility for clearing rights of other persons
-    that may apply to the Work or any use thereof, including without
-    limitation any person's Copyright and Related Rights in the Work.
-    Further, Affirmer disclaims responsibility for obtaining any necessary
-    consents, permissions or other rights required for any use of the
-    Work.
- d. Affirmer understands and acknowledges that Creative Commons is not a
-    party to this document and has no duty or obligation with respect to
-    this CC0 or use of the Work.
index 94a9ed024d3859793618152ea559a168bbcbb5e2..f288702d2fa16d3cdf0035b15a9fcbc552cd88e7 100644 (file)
@@ -1,7 +1,7 @@
                     GNU GENERAL PUBLIC LICENSE
                        Version 3, 29 June 2007
 
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 Also add information on how to contact you by electronic and paper mail.
 
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
   You should also get your employer (if you work as a programmer) or school,
 if any, to sign a "copyright disclaimer" for the program, if necessary.
 For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
+<https://www.gnu.org/licenses/>.
 
   The GNU General Public License does not permit incorporating your program
 into proprietary programs.  If your program is a subroutine library, you
 may consider it more useful to permit linking proprietary applications with
 the library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.  But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
index cca7fc278f5c81ce23a2687208f0d63a6ea44009..0a041280bd00a9d068f503b8ee7ce35214bd24a1 100644 (file)
@@ -1,7 +1,7 @@
-                  GNU LESSER GENERAL PUBLIC LICENSE
+                   GNU LESSER GENERAL PUBLIC LICENSE
                        Version 3, 29 June 2007
 
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
diff --git a/INSTALL b/INSTALL
deleted file mode 100644 (file)
index 6e90e07..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,370 +0,0 @@
-Installation Instructions
-*************************
-
-Copyright (C) 1994-1996, 1999-2002, 2004-2012 Free Software Foundation,
-Inc.
-
-   Copying and distribution of this file, with or without modification,
-are permitted in any medium without royalty provided the copyright
-notice and this notice are preserved.  This file is offered as-is,
-without warranty of any kind.
-
-Basic Installation
-==================
-
-   Briefly, the shell commands `./configure; make; make install' should
-configure, build, and install this package.  The following
-more-detailed instructions are generic; see the `README' file for
-instructions specific to this package.  Some packages provide this
-`INSTALL' file but do not implement all of the features documented
-below.  The lack of an optional feature in a given package is not
-necessarily a bug.  More recommendations for GNU packages can be found
-in *note Makefile Conventions: (standards)Makefile Conventions.
-
-   The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation.  It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions.  Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, and a
-file `config.log' containing compiler output (useful mainly for
-debugging `configure').
-
-   It can also use an optional file (typically called `config.cache'
-and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring.  Caching is
-disabled by default to prevent problems with accidental use of stale
-cache files.
-
-   If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release.  If you are using the cache, and at
-some point `config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
-   The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'.  You need `configure.ac' if
-you want to change it or regenerate `configure' using a newer version
-of `autoconf'.
-
-   The simplest way to compile this package is:
-
-  1. `cd' to the directory containing the package's source code and type
-     `./configure' to configure the package for your system.
-
-     Running `configure' might take a while.  While running, it prints
-     some messages telling which features it is checking for.
-
-  2. Type `make' to compile the package.
-
-  3. Optionally, type `make check' to run any self-tests that come with
-     the package, generally using the just-built uninstalled binaries.
-
-  4. Type `make install' to install the programs and any data files and
-     documentation.  When installing into a prefix owned by root, it is
-     recommended that the package be configured and built as a regular
-     user, and only the `make install' phase executed with root
-     privileges.
-
-  5. Optionally, type `make installcheck' to repeat any self-tests, but
-     this time using the binaries in their final installed location.
-     This target does not install anything.  Running this target as a
-     regular user, particularly if the prior `make install' required
-     root privileges, verifies that the installation completed
-     correctly.
-
-  6. You can remove the program binaries and object files from the
-     source code directory by typing `make clean'.  To also remove the
-     files that `configure' created (so you can compile the package for
-     a different kind of computer), type `make distclean'.  There is
-     also a `make maintainer-clean' target, but that is intended mainly
-     for the package's developers.  If you use it, you may have to get
-     all sorts of other programs in order to regenerate files that came
-     with the distribution.
-
-  7. Often, you can also type `make uninstall' to remove the installed
-     files again.  In practice, not all packages have tested that
-     uninstallation works correctly, even though it is required by the
-     GNU Coding Standards.
-
-  8. Some packages, particularly those that use Automake, provide `make
-     distcheck', which can by used by developers to test that all other
-     targets like `make install' and `make uninstall' work correctly.
-     This target is generally not run by end users.
-
-Compilers and Options
-=====================
-
-   Some systems require unusual options for compilation or linking that
-the `configure' script does not know about.  Run `./configure --help'
-for details on some of the pertinent environment variables.
-
-   You can give `configure' initial values for configuration parameters
-by setting variables in the command line or in the environment.  Here
-is an example:
-
-     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
-   *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
-   You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory.  To do this, you can use GNU `make'.  `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script.  `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.  This
-is known as a "VPATH" build.
-
-   With a non-GNU `make', it is safer to compile the package for one
-architecture at a time in the source code directory.  After you have
-installed the package for one architecture, use `make distclean' before
-reconfiguring for another architecture.
-
-   On MacOS X 10.5 and later systems, you can create libraries and
-executables that work on multiple system types--known as "fat" or
-"universal" binaries--by specifying multiple `-arch' options to the
-compiler but only a single `-arch' option to the preprocessor.  Like
-this:
-
-     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
-                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
-                 CPP="gcc -E" CXXCPP="g++ -E"
-
-   This is not guaranteed to produce working output in all cases, you
-may have to build one architecture at a time and combine the results
-using the `lipo' tool if you have problems.
-
-Installation Names
-==================
-
-   By default, `make install' installs the package's commands under
-`/usr/local/bin', include files under `/usr/local/include', etc.  You
-can specify an installation prefix other than `/usr/local' by giving
-`configure' the option `--prefix=PREFIX', where PREFIX must be an
-absolute file name.
-
-   You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files.  If you
-pass the option `--exec-prefix=PREFIX' to `configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
-   In addition, if you use an unusual directory layout you can give
-options like `--bindir=DIR' to specify different values for particular
-kinds of files.  Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.  In general, the
-default for these options is expressed in terms of `${prefix}', so that
-specifying just `--prefix' will affect all of the other directory
-specifications that were not explicitly provided.
-
-   The most portable way to affect installation locations is to pass the
-correct locations to `configure'; however, many packages provide one or
-both of the following shortcuts of passing variable assignments to the
-`make install' command line to change installation locations without
-having to reconfigure or recompile.
-
-   The first method involves providing an override variable for each
-affected directory.  For example, `make install
-prefix=/alternate/directory' will choose an alternate location for all
-directory configuration variables that were expressed in terms of
-`${prefix}'.  Any directories that were specified during `configure',
-but not in terms of `${prefix}', must each be overridden at install
-time for the entire installation to be relocated.  The approach of
-makefile variable overrides for each directory variable is required by
-the GNU Coding Standards, and ideally causes no recompilation.
-However, some platforms have known limitations with the semantics of
-shared libraries that end up requiring recompilation when using this
-method, particularly noticeable in packages that use GNU Libtool.
-
-   The second method involves providing the `DESTDIR' variable.  For
-example, `make install DESTDIR=/alternate/directory' will prepend
-`/alternate/directory' before all installation names.  The approach of
-`DESTDIR' overrides is not required by the GNU Coding Standards, and
-does not work on platforms that have drive letters.  On the other hand,
-it does better at avoiding recompilation issues, and works well even
-when some directory options were not specified in terms of `${prefix}'
-at `configure' time.
-
-Optional Features
-=================
-
-   If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-   Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System).  The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
-   For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-   Some packages offer the ability to configure how verbose the
-execution of `make' will be.  For these packages, running `./configure
---enable-silent-rules' sets the default to minimal output, which can be
-overridden with `make V=1'; while running `./configure
---disable-silent-rules' sets the default to verbose, which can be
-overridden with `make V=0'.
-
-Particular systems
-==================
-
-   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
-CC is not installed, it is recommended to use the following options in
-order to use an ANSI C compiler:
-
-     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
-
-and if that doesn't work, install pre-built binaries of GCC for HP-UX.
-
-   HP-UX `make' updates targets which have the same time stamps as
-their prerequisites, which makes it generally unusable when shipped
-generated files such as `configure' are involved.  Use GNU `make'
-instead.
-
-   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
-parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
-a workaround.  If GNU CC is not installed, it is therefore recommended
-to try
-
-     ./configure CC="cc"
-
-and if that doesn't work, try
-
-     ./configure CC="cc -nodtk"
-
-   On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
-directory contains several dysfunctional programs; working variants of
-these programs are available in `/usr/bin'.  So, if you need `/usr/ucb'
-in your `PATH', put it _after_ `/usr/bin'.
-
-   On Haiku, software installed for all users goes in `/boot/common',
-not `/usr/local'.  It is recommended to use the following options:
-
-     ./configure --prefix=/boot/common
-
-Specifying the System Type
-==========================
-
-   There may be some features `configure' cannot figure out
-automatically, but needs to determine by the type of machine the package
-will run on.  Usually, assuming the package is built to be run on the
-_same_ architectures, `configure' can figure that out, but if it prints
-a message saying it cannot guess the machine type, give it the
-`--build=TYPE' option.  TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name which has the form:
-
-     CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
-     OS
-     KERNEL-OS
-
-   See the file `config.sub' for the possible values of each field.  If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
-   If you are _building_ compiler tools for cross-compiling, you should
-use the option `--target=TYPE' to select the type of system they will
-produce code for.
-
-   If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with `--host=TYPE'.
-
-Sharing Defaults
-================
-
-   If you want to set default values for `configure' scripts to share,
-you can create a site shell script called `config.site' that gives
-default values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists.  Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Defining Variables
-==================
-
-   Variables not defined in a site shell script can be set in the
-environment passed to `configure'.  However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost.  In order to avoid this problem, you should set
-them in the `configure' command line, using `VAR=value'.  For example:
-
-     ./configure CC=/usr/local2/bin/gcc
-
-causes the specified `gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf limitation.  Until the limitation is lifted, you can use
-this workaround:
-
-     CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-`configure' Invocation
-======================
-
-   `configure' recognizes the following options to control how it
-operates.
-
-`--help'
-`-h'
-     Print a summary of all of the options to `configure', and exit.
-
-`--help=short'
-`--help=recursive'
-     Print a summary of the options unique to this package's
-     `configure', and exit.  The `short' variant lists options used
-     only in the top level, while the `recursive' variant lists options
-     also present in any nested packages.
-
-`--version'
-`-V'
-     Print the version of Autoconf used to generate the `configure'
-     script, and exit.
-
-`--cache-file=FILE'
-     Enable the cache: use and save the results of the tests in FILE,
-     traditionally `config.cache'.  FILE defaults to `/dev/null' to
-     disable caching.
-
-`--config-cache'
-`-C'
-     Alias for `--cache-file=config.cache'.
-
-`--quiet'
-`--silent'
-`-q'
-     Do not print messages saying which checks are being made.  To
-     suppress all normal output, redirect it to `/dev/null' (any error
-     messages will still be shown).
-
-`--srcdir=DIR'
-     Look for the package's source code in directory DIR.  Usually
-     `configure' can determine that directory automatically.
-
-`--prefix=DIR'
-     Use DIR as the installation prefix.  *note Installation Names::
-     for more details, including other options available for fine-tuning
-     the installation locations.
-
-`--no-create'
-`-n'
-     Run the configure checks, but stop before creating any output
-     files.
-
-`configure' also accepts some other, not widely useful, options.  Run
-`configure --help' for more details.
index 9283891461d24f0f6969827270951cba85d1ec77..4d6576a1a7675b6a6035f2753201276d98b5cb42 100644 (file)
@@ -17,10 +17,10 @@ AM_CFLAGS   = -std=gnu99 $(PLATFORM_CFLAGS) -fno-common     \
 
 AM_LDFLAGS     = $(PLATFORM_LDFLAGS)
 
-EXTRA_DIST     = README README.WINDOWS                                 \
-                 COPYING COPYING.GPLv3 COPYING.LGPLv3 COPYING.CC0      \
-                 examples                                              \
-                 debian rpm
+EXTRA_DIST     = README.md README.WINDOWS.md NEWS.md           \
+                 COPYING COPYING.GPLv3 COPYING.LGPLv3          \
+                 examples                                      \
+                 tools/get-version-number.sh tools/windows-build.sh
 
 ##############################################################################
 #                              Library                                      #
@@ -41,6 +41,7 @@ libwim_la_SOURCES =           \
        src/compress_common.c   \
        src/compress_parallel.c \
        src/compress_serial.c   \
+       src/cpu_features.c      \
        src/decompress.c        \
        src/decompress_common.c \
        src/delete_image.c      \
@@ -83,15 +84,16 @@ libwim_la_SOURCES =         \
        src/tagged_items.c      \
        src/template.c          \
        src/textfile.c          \
+       src/threads.c           \
        src/timestamp.c         \
        src/update_image.c      \
        src/util.c              \
        src/verify.c            \
        src/wim.c               \
        src/write.c             \
-       src/x86_cpu_features.c  \
        src/xml.c               \
        src/xml_windows.c       \
+       src/xmlproc.c           \
        src/xpress_compress.c   \
        src/xpress_decompress.c \
        include/wimlib/alloca.h         \
@@ -106,6 +108,7 @@ libwim_la_SOURCES =         \
        include/wimlib/compressor_ops.h \
        include/wimlib/compress_common.h        \
        include/wimlib/chunk_compressor.h       \
+       include/wimlib/cpu_features.h   \
        include/wimlib/decompressor_ops.h       \
        include/wimlib/decompress_common.h      \
        include/wimlib/dentry.h         \
@@ -123,12 +126,11 @@ libwim_la_SOURCES =               \
        include/wimlib/integrity.h      \
        include/wimlib/lcpit_matchfinder.h      \
        include/wimlib/list.h           \
-       include/wimlib/lz_extend.h      \
-       include/wimlib/lz_hash.h        \
        include/wimlib/lzms_common.h    \
        include/wimlib/lzms_constants.h \
        include/wimlib/lzx_common.h     \
        include/wimlib/lzx_constants.h  \
+       include/wimlib/matchfinder_common.h     \
        include/wimlib/metadata.h       \
        include/wimlib/object_id.h      \
        include/wimlib/pathlist.h       \
@@ -145,6 +147,7 @@ libwim_la_SOURCES =         \
        include/wimlib/solid.h          \
        include/wimlib/tagged_items.h   \
        include/wimlib/textfile.h       \
+       include/wimlib/threads.h        \
        include/wimlib/timestamp.h      \
        include/wimlib/types.h          \
        include/wimlib/unaligned.h      \
@@ -152,10 +155,10 @@ libwim_la_SOURCES =               \
        include/wimlib/util.h           \
        include/wimlib/wim.h            \
        include/wimlib/write.h          \
-       include/wimlib/x86_cpu_features.h       \
        include/wimlib/xattr.h          \
        include/wimlib/xml.h            \
        include/wimlib/xml_windows.h    \
+       include/wimlib/xmlproc.h        \
        include/wimlib/xpress_constants.h
 
 if WITH_NTFS_3G
@@ -176,7 +179,7 @@ libwim_la_SOURCES += src/wimboot.c                  \
                     include/wimlib/win32_common.h      \
                     include/wimlib/win32_vss.h         \
                     include/wimlib/wof.h
-PLATFORM_LIBS = -lmsvcrt -lntdll
+PLATFORM_LIBS = -lntdll
 else
 libwim_la_SOURCES += src/unix_apply.c          \
                     src/unix_capture.c
@@ -186,45 +189,27 @@ endif
 if ENABLE_TEST_SUPPORT
 libwim_la_SOURCES += src/test_support.c                \
                     include/wimlib/test_support.h
-if WINDOWS_NATIVE_BUILD
-# Workaround for "multiple definition" error when math symbols are present in
-# both libmsvcrt.a and ntdll.a
-AM_LDFLAGS += -Wl,--allow-multiple-definition
+if !WINDOWS_NATIVE_BUILD
+PLATFORM_LIBS += -lm
 endif
 endif
 
 libwim_la_CFLAGS =             \
+       -DBUILDING_WIMLIB       \
        $(AM_CFLAGS)            \
        $(PTHREAD_CFLAGS)       \
-       $(LIBXML2_CFLAGS)       \
        $(LIBNTFS_3G_CFLAGS)    \
-       $(LIBFUSE_CFLAGS)       \
-       $(LIBCRYPTO_CFLAGS)
+       $(LIBFUSE_CFLAGS)
 
-libwim_la_LDFLAGS = $(AM_LDFLAGS) -version-info 31:0:16
+libwim_la_LDFLAGS = $(AM_LDFLAGS) -version-info 41:0:26
 
 libwim_la_LIBADD =             \
        $(PTHREAD_LIBS)         \
-       $(LIBXML2_LIBS)         \
        $(LIBNTFS_3G_LIBS)      \
        $(LIBFUSE_LIBS)         \
        $(LIBRT_LIBS)           \
-       $(LIBCRYPTO_LIBS)       \
        $(PLATFORM_LIBS)
 
-if ENABLE_SSSE3_SHA1
-libwim_la_SOURCES += src/sha1-ssse3.asm
-libwim_la_LIBADD += src/sha1-ssse3.lo
-
-src/sha1-ssse3.lo:src/sha1-ssse3.asm
-       $(LIBTOOL) --mode=compile $(srcdir)/build-aux/nasm_lt.sh \
-       $(NASM) $(NAFLAGS) $(NASM_PLATFORM_FLAGS)                           \
-       -DINTEL_SHA1_UPDATE_FUNCNAME=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_ssse3       \
-       -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_default  \
-       $< -o $@
-endif
-EXTRA_DIST += build-aux/nasm_lt.sh
-
 ##############################################################################
 #                              Programs                                     #
 ##############################################################################
@@ -341,7 +326,8 @@ EXTRA_DIST +=                                       \
        tests/security_descriptor_1.base64      \
        tests/security_descriptor_1.bin         \
        tests/security_descriptor_2.base64      \
-       tests/security_descriptor_2.bin
+       tests/security_descriptor_2.bin         \
+       tests/wims
 
 if WINDOWS_NATIVE_BUILD
 # Tests are run manually for Windows builds.
diff --git a/NEWS b/NEWS
deleted file mode 100644 (file)
index c720f6c..0000000
--- a/NEWS
+++ /dev/null
@@ -1,1113 +0,0 @@
-Version 1.13.1:
-       Fixed a crash or incorrect output during LZMS compression with a
-       compression level greater than 50 and a chunk size greater than 64 MiB.
-       This affected wimlib v1.8.0 and later.  In the unlikely event that you
-       used all these non-default compression settings in combination, e.g.
-       'wimcapture --solid --solid-compress=LZMS:100 --solid-chunk-size=128M',
-       run 'wimverify' on your archives to verify your data is intact.
-
-Version 1.13.0:
-       On Windows, wimlib now supports capturing and applying extended
-       attributes (EAs).  It is compatible with DISM with the /EA option,
-       available since Windows 10 version 1607.  wimlib's EA support is on by
-       default and works on older versions of Windows too.
-
-       Partially fixed a bug where [ExclusionException] entries didn't take
-       effect when the containing directory is matched by [ExclusionList].  It
-       now works when the [ExclusionException] patterns are absolute.  For
-       example, listing "/dir/file" in [ExclusionException] now works even if
-       "/dir" is matched by [ExclusionList].
-
-       Added a '--create' option to 'wimappend' which makes it create the WIM
-       file (like 'wimcapture') if it doesn't exist yet.
-
-       Added an '--include-integrity' option to various wimlib-imagex commands.
-       '--include-integrity' is like '--check', but it will just include an
-       integrity table in the output WIM(s), while skipping verification of any
-       existing integrity tables.  This can be useful to avoid unwanted
-       verification of large WIM files, e.g. WIMs given by '--delta-from'.
-
-       'wimextract' now reads a pathlist file from standard input when "@-" is
-       given as an argument.
-
-       wimsplit (API: wimlib_split()) now correctly handles a dot in the path
-       to the first split WIM part, prior to the filename extension.
-
-       'wimlib-imagex --version' now shows the version of the library it is
-       actually using (in case it is different from wimlib-imagex's version).
-
-Version 1.12.0:
-       Fixed a bug that was causing the LZMS decompressor to be miscompiled
-       with GCC 7 (this broke extracting "solid" archives).
-
-       The Windows 10 Recycle Bin directory (\$RECYCLE.BIN) has been added to
-       the default exclusion list.
-
-       Added a '--quiet' option to wimlib-imagex.
-
-       The 'mkwinpeimg' script now also looks for the syslinux BIOS modules in
-       the directory /usr/lib/syslinux/modules/bios.
-
-       Files with timestamps before the year 1970 are now extracted correctly
-       to UNIX-style filesystems, are displayed correctly by
-       'wimdir --detailed', and show up correctly in mounted WIM images.
-
-       Files with timestamps after the year 2038 are now displayed correctly by
-       the 32-bit Windows build of wimlib.
-
-Version 1.11.0:
-       Fixed a data corruption bug (incorrect compression) when storing an
-       already highly-compressed file in an LZX-compressed WIM with a chunk
-       size greater than or equal to 64K.  Note that this is not the default
-       setting and such WIMs are not supported by Microsoft's WIM software, so
-       only users who used the --chunk-size option to wimlib-imagex or the
-       wimlib_set_output_chunk_size() API function may have been affected.
-       This bug was introduced in wimlib v1.10.0.  See
-       https://wimlib.net/forums/viewtopic.php?f=1&t=300 for more details.
-
-       On all platforms, sparse files are now extracted as sparse.
-
-       Sparse files captured from UNIX-style filesystems are now marked as
-       sparse in the resulting WIM image.
-
-       Added support for storing Linux-style extended attributes in WIM images.
-       When the --unix-data option is used on Linux, wimlib-imagex now captures
-       and applies extended attributes, in addition to the already-supported
-       standard UNIX file permissions (owner/group/mode) and special files.
-
-       --delta-from is now supported by wimappend.  (Previously it was only
-       supported by wimcapture.)
-
-       On Windows, improved the way in which files deduplicated with Windows'
-       Data Deduplication feature are captured.
-
-       The development files needed to link with wimlib using Visual Studio are
-       now included in the Windows release archives.
-
-       wimlib.h can now be included by Visual Studio without errors.
-
-       The example programs can now be compiled in C++ mode, and they also now
-       work on Windows.
-
-       Updated 'mkwinpeimg' to work correctly on images that have a "windows"
-       (lower case) directory rather than a "Windows" (upper case) directory.
-
-       Fixed configuring with --enable-ssse3-sha1 from release tarball
-       (the file nasm_lt.sh was missing).
-
-       Made some documentation improvements.
-
-Version 1.10.0:
-       The LZX compression ratio has been slightly improved.  The default mode,
-       LZX level 50, is now almost as good as the old LZX level 100, while
-       being nearly the same speed as before.
-
-       Decompression performance has been slightly improved.
-
-       Filenames are now always listed in NTFS collation order.
-
-       On UNIX-like systems, wimlib can now process Windows filenames that are
-       not valid Unicode due to the presence of unpaired surrogates.
-
-       On UNIX-like systems, wimlib now always assumes UTF-8 encoding with the
-       addition of surrogate codepoints.  Consequently, the environmental
-       variable WIMLIB_IMAGEX_USE_UTF8 and the flag
-       WIMLIB_INIT_FLAG_ASSUME_UTF8 no longer have any effect.
-
-       wimlib no longer depends on iconv.
-
-       Reduced memory usage slightly.
-
-       When a WIM image is applied in NTFS-3G mode, security descriptors are
-       now created in NTFS v3.0 format when supported by the volume.
-
-       Workarounds for bugs in libntfs-3g version 2013.1.13 and earlier have
-       been removed.  Users are advised to upgrade to a later version of
-       libntfs-3g.
-
-       On Windows, wimlib now supports case-sensitive filename extraction when
-       supported by the underlying operating system and filesystem (operating
-       system support requires a registry setting).
-
-Version 1.9.2:
-       On UNIX, wimlib can now overwrite readonly files when extracting.
-
-       On Windows, fixed a bug where wimlib could leave a null DACL (a.k.a. "no
-       NTFS permissions") set on some existing directories after extraction.
-
-       On Windows, when applying a WIM image in "WIMBoot mode" when the WOF
-       driver is not loaded, wimlib can now correctly register a new WIM file
-       with the target volume when the target volume previously had had WIM
-       files unregistered.
-
-       Added a new testing program.
-
-       Clarified the main license text and updated public domain dedications
-       for certain files to be more thorough.
-
-Version 1.9.1:
-       Object IDs are now saved and restored on Windows and in NTFS-3G mode.
-
-       Reduced memory usage when exporting large numbers of WIM images.
-
-       Non UTF-8 locales are now detected correctly.
-
-       Addressed compiler warnings and enabled "silent" make rules by default.
-
-       Windows-specific updates:
-               Fixed a bug where duplicate backslashes could be generated in
-               link targets when extracting symbolic links and junctions.
-
-               Fixed a bug where the .cmd shortcuts for wimlib-imagex wouldn't
-               work if their full path contained a space.
-
-               Fixed bugs related to scanning SMB filesystems.
-
-               Added warning message about known issue with WindowsApps folder.
-
-               Added instructions for building from source on Windows.
-
-               VSS support is no longer marked "experimental".
-
-               Added missing license file for libdivsufsort-lite.
-
-Version 1.9.0:
-       Added experimental support for Windows VSS (Volume Shadow Copy Service).
-       The new '--snapshot' argument to 'wimcapture' makes wimlib automatically
-       create and use a temporary VSS snapshot when capturing a WIM image.
-
-       Implemented setting of Windows-specific XML information, such as
-       architecture, system root, and version details.  This information is now
-       automatically set in newly captured WIM images, when appropriate.
-
-       Improved performance of directory tree scans on Windows.
-
-       On Windows, to improve capture performance, wimlib now sometimes opens
-       files by inode number rather than by path.  This is enabled for
-       wimcapture and wimappend, but for now other applications have to opt-in.
-
-       The progress messages printed by wimlib-imagex while writing WIM files
-       have been slightly tweaked.
-
-       Progress information for directory tree scans now counts all hard links.
-       Also, on Windows "\\?\" is no longer stripped from the current path.
-
-       Added a new '--image-property' option to 'wimcapture', 'wimappend', and
-       'wiminfo'.  This option lets you assign values to elements in a WIM
-       file's XML document by name.
-
-       The wimlib_get_image_property() and wimlib_set_image_property() API
-       functions now support numerically indexed elements.
-
-       Fixed a bug where, on Windows, wimlib would change the security
-       descriptor of the target directory of an extraction even when the
-       '--no-acls' option was specified.
-
-Version 1.8.3:
-       Fixed a bug with libntfs-3g extraction present since v1.8.1.  Sometimes,
-       some Microsoft software would not correctly recognize data in the
-       resulting filesystem.
-
-       Made some small improvements to the compression algorithms:
-               LZX compression ratio was slightly improved.
-               XPRESS compression ratio and speed was slightly improved.
-               LZMS compression speed was slightly improved.
-
-       Improved handling of WIM XML data.  wimlib no longer drops unrecognized
-       elements when exporting images.  In addition, two API functions were
-       added for better access to elements in the XML document:
-       wimlib_get_image_property() and wimlib_set_image_property().
-
-       Added support for (unsafe) in-place compaction of WIM files.
-
-       Improved performance of image export by reusing metadata resources
-       instead of always rebuilding and recompressing them.
-
-       Improved performance of wimlib_update_image() by delaying the update to
-       the WIM's XML document until a write is requested.
-
-       On Windows, the target of an extraction may now be a reparse point
-       (which will be dereferenced).
-
-       On Windows, wimlib now correctly restores non-Microsoft reparse points.
-       However, this remains broken in NTFS-3g mode due to a libntfs-3g bug.
-
-       On Windows, wimlib now has improved performance when archiving files
-       from a filesystem backed by a WIM (a "WIMBoot" setup).
-
-       Several improvements to System Compression (compact mode) support:
-
-               wof.sys (or wofadk.sys) is now automatically attached to the
-               target volume if needed.
-
-               Compact-mode extractions now work correctly with wofadk.sys on
-               older versions of Windows.
-
-               For compatibility with the Windows bootloader, the requested
-               compression format now is overridden on certain files.
-
-       Other minor bugfixes.
-
-Version 1.8.2:
-       This release primarily contains various minor bug fixes and
-       improvements, including:
-
-               Improved handling of deep directory structures.
-
-               Fixed a bug where on 32-bit systems, the library could enter an
-               infinite loop if a WIM file was malformed in a specific way.
-
-               Added a workaround for a case where libntfs-3g may report
-               duplicate streams in an NTFS file.
-
-               Windows symbolic links and junctions in mounted WIM images are
-               now automatically rewritten to be valid in the mounted location.
-
-               Reparse point fixes: correctly handle the "ReparseReserved"
-               field, and correctly handle "empty" (data-less) reparse points.
-
-               On Windows, wimlib now acquires SeManageVolumePrivilege, which
-               is needed to create externally backed files using the
-               "wofadk.sys" driver.
-
-               Improved validation of filenames.
-
-               Improved LZMS decompression speed.
-
-               The configure script now honors alternate pkg-config settings.
-
-               Links have been updated to point to the new website.
-
-       In addition, experimental support has been added for compressing
-       extracted files using System Compression on Windows 10.  This
-       functionality is available through the new '--compact' option to
-       'wimapply' and 'wimextract' as well as new library flags.
-
-Version 1.8.1:
-       Fixed a bug in the LZX decompressor: malicious input data could cause
-       out of bounds writes to memory (since wimlib v1.2.2).
-
-       The output of the 'wiminfo' command now consolidates various boolean
-       flags (such as "Relative path junction") into a single line.
-
-       A file can now have both an unnamed data stream ("file contents") and a
-       reparse point stream.  Such files can exist as a result of the use of
-       certain Windows features, such as offline storage, including "OneDrive".
-       wimlib will now store and restore both streams on Windows as well as in
-       NTFS-3g mode.  Microsoft's WIMGAPI also has this behavior.
-
-       On Windows, named data streams of encrypted files are no longer stored
-       twice in WIM archives.
-
-       On Windows, named data streams are now correctly extracted to existing
-       "readonly" directories.  Before, an error would be reported.
-
-       On Windows, it is now possible to do a "WIMBoot mode" extraction with
-       non-standalone WIMs such as delta WIMs.
-
-       On Windows, when doing an extraction in "WIMBoot mode", files larger
-       than 4 gigabytes are now never extracted as externally backed.  This
-       works around a bug in Microsoft's "WOF" driver.
-
-       The '--enable-verify-compression' configure option has been removed.  If
-       you want to verify a WIM file, use the 'wimverify' program.
-
-       The way the "file count", "directory count", "total bytes", and "hard
-       link bytes" image statistics (stored in the WIM XML data) is calculated
-       has been slightly changed.
-
-       In mounted WIM images, the disk usage provided for each file (st_blocks)
-       is now the compressed size rather than the uncompressed size.
-
-       The performance of the NTFS-3g and Windows capture modes has been
-       slightly improved.
-
-       On UNIX-like systems, symbolic links whose targets contain the backslash
-       character are now handled correctly (losslessly).
-
-Version 1.8.0:
-       Improved the LZX compressor.  It is now 15-20% faster than before and
-       provides a slightly better compression ratio.
-
-       Improved the LZMS compressor.  It now provides a compression ratio
-       slightly better than WIMGAPI while still being faster and using slightly
-       less memory.
-
-       The compression chunk size in solid resources, e.g. when capturing or
-       exporting a WIM file using the '--solid' option, now defaults to 64 MiB
-       (67108864 bytes) instead of 32 MiB (33554432 bytes).  This provides a
-       better compression ratio and is the same value that WIMGAPI uses.  The
-       memory usage is less than 50% higher than wimlib v1.7.4 and is slightly
-       lower than WIMGAPI's memory usage, but if it is too much, it is still
-       possible to choose a lower value, e.g. with the '--solid-chunk-size'
-       option to wimlib-imagex.
-
-       The '--chunk-size' and '--solid-chunk-size' options to wimlib-imagex now
-       accept the 'K', 'M', and 'G' suffixes.
-
-       Files are now sorted by name extension when creating a solid WIM file.
-
-       Fixed various issues related to capture/apply of EFS-encrypted files on
-       Windows.
-
-       The file list printed by 'wimdir' is now sorted by the platform-specific
-       case sensitivity setting, rather than always case sensitively.  This
-       also affects the library function wimlib_iterate_dir_tree().
-
-       On Windows, some error and warning messages have been improved.
-
-Version 1.7.4:
-       The Windows binary distribution no longer contains third party DLLs.
-       These dependencies are instead compiled directly into the libwim DLL.
-
-       Added more fixes for wimlib on non-x86 architectures such as ARM.
-
-       Extracting files to a Windows PE in-memory filesystem no longer fails if
-       the target files do not yet exist.
-
-       Improved the performance of XPRESS compression and LZMS decompression.
-
-       Enabled SSSE3 accelerated SHA-1 computation in x86_64 Windows builds.
-       It will automatically be faster on newer Intel and AMD processors.
-
-       Removed the --with-imagex-progname and --enable-more-assertions
-       configure options.
-
-Version 1.7.3:
-       Fix for very slow export from solid WIM / ESD files.
-
-       Fix for LZX and LZMS algorithms on non-x86 architectures, such as ARM.
-
-       New progress message: WIMLIB_PROGRESS_MSG_HANDLE_ERROR.  Applications
-       may use this to treat some types of errors as non-fatal.
-
-       The library now permits making in-memory changes to a WIMStruct backed
-       by a read-only WIM file.
-
-       Fixes for "WIMBoot" extraction mode (Windows only):
-
-               When not using the WOF driver, extraction no longer fails if the
-               disk containing the WIM file has too many partitions.
-
-               When matching patterns in [PrepopulateList], all hard links of
-               each file are now considered.
-
-               The system registry files are now automatically treated as being
-               in [PrepopulateList].
-
-               Added a hack to try to work around an intermittent bug in
-               Microsoft's WOF (Windows Overlay Filesystem) driver.
-
-Version 1.7.2:
-       Made more improvements to the XPRESS, LZX, and LZMS compressors.
-
-       A number of improvements to the Windows port:
-
-               Fixes for setting short filenames.
-
-               Faster "WIMBoot" extraction.
-
-               Updated and slimmed down the dependent DLLs.
-
-               ACL inheritence bits are now restored.
-
-               Mandatory integrity labels are now backed up and restored.
-
-       Added a workaround for an issue where in rare cases, wimlib could create
-       a compressed data stream that could not be read correctly by Windows
-       after an extraction in "WIMBoot" mode.
-
-       Library changes:
-               Added file count progress data for
-               WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE and
-               WIMLIB_PROGRESS_MSG_EXTRACT_METADATA.
-
-               Added support for testing file exclusions via the user-provided
-               progress function.
-
-               Some documentation improvements.
-
-       Made some clarifications to the license text in the COPYING file.
-
-Version 1.7.1:
-       Made more improvements to the XPRESS, LZX, and LZMS compressors.
-
-       The default compression mode for wimcapture is now LZX compression in
-       its default mode, which is the same as '--compress=maximum'.
-
-       You can now specify an optional integer compression level to the
-       '--compress' option; e.g. '--compress=lzx:75'.
-
-       Made a minor change to the LZMS compressor and decompressor to fix an
-       incompatibility with the Microsoft implementation.  In the unlikely
-       event that you created an LZMS-compressed WIM with wimlib v1.7.0 or
-       earlier and a checksum error is reported when extracting files from it
-       with wimlib v1.7.1, decompress it with v1.7.0 then compress it with
-       v1.7.1.
-
-       Added 'verify' subcommand to wimlib-imagex.
-
-       Notable library changes:
-
-               Custom compressor parameters have been removed from the library
-               in favor of the simpler level-based API.
-
-               Decompressor parameters have been removed entirely.
-
-               Library users can now specify a custom file for warning and
-               error messages to be sent to, rather than the default of
-               standard error.
-
-               New progress messages:
-               WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE,
-               WIMLIB_PROGRESS_MSG_EXTRACT_METADATA.
-
-               New function: wimlib_verify_wim().
-
-Version 1.7.0:
-       Improved compression, decompression, and extraction performance.
-
-       Improved compatibility with version 3584 WIM / ESD files:
-           - Add support for reading and writing multiple solid blocks per
-             archive, which WIMGAPI/DISM can create when appending an image.
-           - Correctly create solid blocks larger than 4 GiB.
-
-       'add' commands passed to wimupdate will now replace existing
-       nondirectory files by default.  Use the --no-replace option to get the
-       old behavior.
-
-       The license for the library now contains an exception that allows using
-       it under the LGPL.  See the COPYING file for details.
-
-       In reparse-point fixup mode (the default for capture), symbolic links
-       and junctions that point outside the tree being captured are no longer
-       excluded from capture.
-
-       Added support for "WIMBoot" capture and extraction.  See the
-       documentation for the new '--wimboot' option to wimcapture and wimapply
-       for more information.
-
-       On UNIX-like systems, you can now backup and restore device nodes, named
-       pipes, and sockets.  In addition, 32-bit user and group IDs are now
-       supported.
-
-       The way that UNIX data is stored in WIM files has been changed.  If you
-       captured any WIMs with the --unix-data option, to upgrade them you'll
-       need to apply them with --unix-data using wimlib-imagex v1.6.2, then
-       re-capture them with --unix-data using this version.
-
-       wimlib now understands tagged metadata items, such as object IDs, that
-       can be stored in WIM directory entries.
-
-       Removed the --hardlink and --symlink options to wimapply, since I don't
-       think they are too useful and they got in the way of improving the code.
-
-       WIMs will now retain their GUIDs when rebuilt (e.g. with wimoptimize).
-
-       The 'mkwinpeimg' script now supports writing the ISO image to standard
-       output.
-
-       The <ARCH> element in WIM XML data is now exported correctly.
-
-       On Windows, sparse file attributes are no longer set on extracted files.
-       Oddly enough, this actually saves disk space in some cases.
-
-       On UNIX, configuring with --disable-xattr or --enable-xattr is no longer
-       supported.  Mounting WIM images now always requires extended attribute
-       support.  Use --without-fuse to disable support for mounting WIM images;
-       this will also disable the need for extended attribute support.
-
-       Configuring with --enable-ssse3-sha1 now works correctly.
-
-       The shared library version has been bumped up.  The main
-       incompatibilities are:
-
-               - WIMLIB_COMPRESSION_TYPE_XPRESS is now 1 and
-                 WIMLIB_COMPRESSION_TYPE_LZX is now 2 (so it's the same as
-                 WIMGAPI).
-
-               - User-provided progress functions are now registered using a
-                 separate function, wimlib_register_progress_function().  The
-                 'progress_func' argument to many functions no longer exists.
-
-               - The return value from user-provided progress functions is now
-                 significant.
-
-               - A context argument has been added to the prototype of
-                 user-provided progress functions.
-
-               - 'struct wimlib_capture_config' has been removed.  The library
-                 now takes the path to the configuration file directly.  This
-                 affects wimlib_add_image(), wimlib_add_image_multisource(),
-                 and wimlib_update_image().  However, a NULL value passed in
-                 the argument retains the same meaning.
-
-               - Removed deprecated functions: some (de)compression functions,
-                 wimlib_extract_files(), and wimlib_print_metadata().
-
-               - Removed extraction flags: WIMLIB_EXTRACT_FLAG_HARDLINK,
-                 WIMLIB_EXTRACT_FLAG_SYMLINK, WIMLIB_EXTRACT_FLAG_FILE_ORDER,
-                 and WIMLIB_EXTRACT_FLAG_SEQUENTIAL.
-
-               - Removed some progress messages:
-                 WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
-                 WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
-                 WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END.  Numbering
-                 stays the same.
-
-               - Removed some error codes.  Numbering stays the same.
-
-               - Replaced WIMLIB_UNMOUNT_FLAG_LAZY with
-                 WIMLIB_UNMOUNT_FLAG_FORCE.
-
-               - WIM paths passed to progress functions now have a leading
-                 slash.
-
-Version 1.6.2:
-       Case-insensitive comparisons of strings (e.g. filenames) containing
-       UTF-16 codepoints above 32767 are now done correctly.
-
-       Fixed build failure on Mac OS X.
-
-       wimunmount now provides the '--new-image' option to cause changes to a
-       read-write mounted image to be committed as a new image rather than as
-       an update of the mounted image.  (The corresponding new library flag is
-       WIMLIB_UNMOUNT_FLAG_NEW_IMAGE.)
-
-       The LZMS ("recovery") compression chunk size, or "dictionary size", may
-       now be up to 1 GiB (1,073,741,824 bytes).
-
-       The performance of LZX ("maximum") and LZMS ("recovery") compression
-       with large chunk sizes has been slightly improved.
-
-Version 1.6.1:
-       Stored files with size exactly 4 GiB (4,294,967,296 bytes) are now
-       decompressed correctly.
-
-       Fixed a bug in the LZX compressor introduced in v1.5.3.  The bug
-       occurred in an unlikely case, and due to validity checks it did not
-       affect successfully created archives.
-
-       Fixed a minor compatibility issue with the LZMS compressor and
-       decompressor.  This is *not* the default compression type and was only
-       introduced in v1.6.0.  In the unlikely event that you created an
-       LZMS-compressed WIM with v1.6.0 and a checksum error is reported when
-       applying it with v1.6.1, decompress it with v1.6.0 then compress it with
-       v1.6.1.
-
-       Memory usage for LZMS and LZX compression has been decreased.
-
-       wimextract now allows wildcard characters in paths specified on the
-       command line.  Also, the '--strict-wildcards' option has been removed
-       and replaced with the inverse option '--nullglob'.  See the
-       documentation for wimextract for more details and changes.
-
-       The wimlib_extract_files() function is now considered deprecated in
-       favor of wimlib_extract_paths().
-
-       Fixed more permissions problems when extracting files on Windows.
-
-       A new '--no-attributes' option has been added to wimapply and
-       wimextract.  The library flag is WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES.
-
-       The default chunk size is now set correctly when changing the
-       compression type of a WIM, for example with 'wimoptimize'.
-
-       The '--metadata' option to wiminfo has been replaced with the
-       '--detailed' option to wimdir.
-
-       In relevant wimlib-imagex commands, '--solid' may now be used as an
-       alias for '--pack-streams'.
-
-Version 1.6.0:
-       Support for extracting and updating the new version 3584 WIMs has been
-       added.  These WIMs typically pack many streams ("files") together into a
-       single compressed resource, thereby saving space.  This degrades the
-       performance of random access (such as that which occurs on a mounted
-       image), but optimizations have been implemented for extraction.  These
-       new WIM files also typically use a new compression format (LZMS), which
-       is similar to LZMA and can offer a better compression ratio than LZX.
-       These new WIM files can be created using `wimcapture' with
-       the '--compress=lzms --pack-streams' options.  Note: this new WIM format
-       is used by the Windows 8 web downloader, but important segments of the
-       raw '.esd' files are encrypted, so wimlib will not be able to extract
-       such files until they are first decrypted.
-
-       wimlib now supports extracting files and directories from a WIM image
-       based on a "listfile" that itself contains the list of paths to extract.
-       For `wimextract', the syntax is to specify @LISTFILE instead of a PATH,
-       and for the library itself, the new APIs are wimlib_extract_pathlist()
-       and wimlib_extract_paths().  Path globs containing wildcard characters
-       are supported.
-
-       For searching WIM files, wimlib now has configurable case sensitivity.
-       The default on Windows is still case-insensitive and the default on
-       UNIX-like systems is still case-sensitive, but this can be overridden
-       on either platform through flags to wimlib_global_init().  For
-       `wimlib-imagex', the environmental variable WIMLIB_IMAGEX_IGNORE_CASE
-       can be set to 1 or 0 for case-insensitive or case-sensitive behavior,
-       respectively.
-
-       Support for compression chunk sizes greater than the default of 32768
-       bytes has been added.  A larger chunk size typically results in a better
-       compression ratio.  However, the MS implementation is seemingly not
-       compatible with all chunk sizes, especially for LZX compression, so the
-       defaults remain unchanged, with the exception of the new LZMS-compressed
-       WIMs, which use a larger chunk size by default.
-
-       The compression/decompression API exported by wimlib has been changed.
-       Now one set of functions handles all supported compression formats.
-
-       `wimcapture' and `wimappend' will now display the progress of scanning
-       the directory tree to capture, in addition to the progress of writing
-       data to the WIM.  The '--verbose' option no longer does anything.  The
-       library API change for this is the addition of several members to
-       `struct wimlib_progress_info_scan' available to progress callbacks.
-
-       `mkwinpeimg' now correctly handles the '--start-script' option when the
-       start script is not in the working directory.
-
-       Sequential extraction, previously requested by using
-       WIMLIB_EXTRACT_FLAG_SEQUENTIAL, is now the default.
-       WIMLIB_EXTRACT_FLAG_FILE_ORDER can be used to get the old default
-       behavior (extract in file order).
-
-Version 1.5.3:
-       The new LZX compressor added in v1.5.2 has been improved and is now
-       enabled by default, except when `wimcapture' or `wimappend' is run
-       *without* the '--compress' option, in which case the faster LZX
-       compressor is used (the same as before).  This behavior is reasonably
-       consistent with ImageX which actually uses "fast" (XPRESS) compression
-       by default.  In those cases, use '--compress=maximum' to explicitly
-       capture a WIM image using the new (slower but better) LZX compressor.
-
-       The '--compress-slow' option still exists to `wimlib-imagex optimize',
-       but its new behavior is to tweak the new LZX compressor even more to
-       produce an even better compression ratio at the cost of more time spent
-       compressing.
-
-       `wimlib-imagex optimize' now supports the '--compress=TYPE' option,
-       which recompresses the WIM file using the specified compression TYPE.
-       The new library API function used for this is
-       wimlib_set_output_compression_type().
-
-       Added the wimlib_get_xml_data() function to allow library clients to
-       easily retrieve the raw XML data from a WIM file if needed.
-
-       Fixed a bug that could cause an error code to be incorrectly returned
-       when writing XML data containing a <WINDOWS> element.
-
-       Mounted WIM images will now correctly show the default file stream even
-       if appears in the alternate data stream entries of the corresponding WIM
-       directory entry.
-
-Version 1.5.2:
-       Added a new experimental LZX compressor which can be enabled by passing
-       '--compress-slow' to `wimlib-imagex capture' or `wimlib-imagex
-       optimize'.  (The latter is only applicable if the WIM is already
-       LZX-compressed and the '--recompress' option is also given.)  The
-       experimental compressor is much slower but compresses the data slightly
-       more --- currently usually to within a fraction of a percent of the
-       results from WIMGAPI/ImageX.
-
-       A workaround has been added for compatibility with versions of WinPE
-       that interpret alternate data stream entries in the boot WIM
-       incorrectly.
-
-       An alignment bug that caused a crash in the LZX decompressor on some
-       builds was fixed.
-
-       wimlib now attempts to clear the WIM_HDR_FLAG_WRITE_IN_PROGRESS flag in
-       the WIM header when restoring the previous state of a WIM it failed to
-       successfully update.
-
-       Added a workaround to avoid an access denied error on Windows when
-       replacing a WIM file that another process has opened.
-
-Version 1.5.1:
-       wimlib can now open WinPE WIMs from WAIK v2.1, which had a quirk that
-       needed to be handled.
-
-       A bug in the interpretation of negative IMAGE indices in the
-       --update-of=[WIMFILE:]IMAGE option to `wimlib-imagex capture' and
-       `wimlib-imagex append' has been fixed.
-
-       A workaround has been added to successfully apply security descriptors
-       with empty DACLs when the NTFS-3g apply mode is being used with NTFS-3g
-       2013.1.13 or earlier.
-
-       `wimlib-imagex capture' can now accept the '--delta-from' option
-       multiple times.
-
-Version 1.5.0:
-       Added support for "pipable" WIMs.  Pipable WIMs allow capturing images
-       to standard output and applying images from standard input, but they are
-       not compatible with Microsoft's software and are not created by default.
-       See the documentation for --pipable flag of `wimlib-imagex capture' for
-       more information.
-
-       To better support incremental backups, added support for declaring an
-       image as a modified form of another image.  See the documentation for
-       the '--update-of' option of `wimlib-imagex append' and `wimlib-imagex
-       capture'.
-
-       Added supported for "delta" WIMs.  See the documentation for the
-       '--delta-from' option of `wimlib-imagex capture'.
-
-       The library support for managing split WIMs has been changed to support
-       other arrangements, such as delta WIMs, and be easier to use.  This
-       change is visible in `wimlib-imagex', which also can now accept the
-       '--ref' option multiple times, and also now supports "delta" WIMs as
-       mentioned above.
-
-       wimlib now preserves WIM integrity tables by default, even if
-       WIMLIB_WRITE_FLAG_CHECK_INTEGRITY is not specified.  This changes the
-       behavior of `wimlib-imagex' whenever the WIM being operated on contains
-       an integrity table and the '--check' option is not specified.
-
-       `wimlib-imagex capture' now creates LZX-compressed WIMs by default
-       (when --compress is not specified).  This provides the best compression
-       ratio by default, which is usually what is desired, at a cost of some
-       speed.
-
-       `wimlib-imagex' now supports being invoked as wimCOMMAND, where COMMAND
-       is the command as in `wimlib-imagex COMMAND'; for example, it can be
-       invoked as `wimapply' as an alternative to `wimlib-imagex apply'.  The
-       appropriate hard links are created in UNIX installations of
-       `wimlib-imagex', while for the Windows distribution of `wimlib-imagex',
-       batch files that emulate this behavior are generated.
-
-       Security descriptors are now extracted correctly on Windows.
-
-       Fixed archiving DOS names in NTFS-3g capture mode.
-
-       The extraction code has been rewritten and it will now be easier to
-       support new features on all supported backends (currently Win32, UNIX,
-       and NTFS-3g).  For example, hard-linked extraction mode (--hardlink) is
-       now supported on all backends, not just UNIX.
-
-       `mkwinpeimg' now supports grabbing files from the WAIK supplement rather
-       than the WAIK itself.
-
-       wimlib_global_init() now, by default, attempts to acquire additional
-       privileges on Windows, so library clients need not do this.
-
-       This update bumps the shared library version number up to 9, since it is
-       not binary compatibible with previous releases.
-
-Version 1.4.2:
-       Fixed bug in `wimlib-imagex export' that made it impossible to export an
-       image from a WIM that is readonly at the filesystem level.
-
-       Return error code rather than segfaulting when trying to list files from
-       a non-first part of a split WIM.
-
-       Joining a WIM will now preserve the RP_FIX and READONLY flags.
-
-Version 1.4.1:
-       On Windows, paths given to wimlib-imagex are now treated case
-       insensitively.
-
-       Improved behavior regarding invalid filenames; in particular, on
-       Windows, wimlib-imagex will, when extracting, now omit (with an option
-       to override this default) filenames differing only in case, or filenames
-       containing characters not valid on Windows.
-
-       On Windows, wimlib now supports capturing and extracting long paths
-       (longer than the so-called MAX_PATH).
-
-       On Windows, `wimlib-imagex update' now acquires proper privileges when
-       running as an Administrator.
-
-       `wimlib-imagex update' will now complain if no image is specified when
-       trying to update a multi-image WIM.
-
-       `wimlib-imagex update' now supports specifying a single update command
-       directly on the command line using the --command option.
-
-       wimlib-imagex will now choose different units for progress messages,
-       depending on the amount of data that needs to be processed.
-
-       `wimlib-imagex append' will now generate a unique WIM image name if no
-       name is specified and the defaulted name already exists in the WIM.
-
-       wimlib now allows you to create unnamed WIM images, which can then only
-       be referred to by index.
-
-       wimlib now allows you to explicitly declare you want write access to a
-       WIM by providing the WIMLIB_OPEN_FLAG_WRITE_ACCESS flag to
-       wimlib_open_wim().
-
-       wimlib now respects the WIM_HDR_FLAG_READONLY flag when set in the WIM
-       header.
-
-       Progress callbacks have been added to wimlib's wimlib_update_image()
-       function.
-
-       Added wimlib_get_wim_info(), wimlib_set_wim_info(),
-       wimlib_iterate_dir_tree(), and wimlib_iterate_lookup_table() functions
-       to the library.
-
-       NTFS-3g capture now only warns about two conditions previously treated
-       as errors.
-
-       Fixed a couple issues with using wimlib-imagex on UDF filesystems on
-       Windows.
-
-       wimlib now correctly detects and returns an error when reading a WIM
-       image with a cyclic directory structure.  (Fun fact: such a WIM will
-       crash Microsoft's software.)
-
-Version 1.4.0:
-       Added new "extract" and "update" subcommands to wimlib-imagex, along
-       with associated APIs in the library.  These commands are intended mainly
-       for Windows use but can be used on UNIX as well.
-
-       Many documentation improvements.
-
-       Fixed a bug in the Windows build where relative symbolic links were not
-       captured in reparse-point fixup mode.
-
-       Fixed a bug in the Windows build where file handles were left open to
-       the WIM file, causing `wimlib_imagex optimize' to fail in some cases.
-
-       Fixed a bug in the Windows build of wimlib-imagex where globbing
-       split-WIM parts could cause the program to crash.
-
-       Fixed a bug where the creation time of WIM images would be shown instead
-       of the last modification time.
-
-       With the Windows build it is now possible to restore a WIM containing
-       symbolic links as a non-Administrator; however you will receive warnings
-       about not being able to extract the symbolic links.
-
-Version 1.3.3:
-       Capturing a WIM image should now be significantly faster in most cases
-       due to improved use of the operating system's cache and avoiding reading
-       files twice whenever possible.
-
-       The Windows build should now work on Windows XP.
-
-       The Windows build now supports capturing and restoring hidden,
-       compressed, sparse, and encrypted files.
-
-       The Windows build now supports capturing and applying WIM images from
-       filesystems other than NTFS (with some reduced functionality).
-
-       The Windows build now extracts short names correctly.
-
-       Added support for "reparse-point" fixups (i.e. fixing up of symbolic
-       links).  See docs for --rpfix and --norpfix flags of `wimlib-imagex
-       capture' and `wimlib-imagex apply'.
-
-       The performance of splitting and joining WIMs should be slightly
-       improved.
-
-       The LZX and XPRESS compression and decompression functions are now
-       exported from the library.
-
-Version 1.3.2:
-       Improvements and bugfixes for the Windows build.
-
-       Added --strict-acls options.
-
-       Fixed the way that wimlib determines the order of images in the WIM.
-
-Version 1.3.1:
-       Since wimlib can now be used on Windows, wimlib's implementation of
-       ImageX has been renamed to wimlib-imagex to avoid confusion with
-       Microsoft's implementation of ImageX, which would have the same file
-       name ("imagex.exe").  If you really don't like this you can pass the
-       --with-imagex-progname option to `configure' to choose a different name,
-       or even simply rename the binary yourself (but the former way will
-       configure the man pages to use the chosen name).
-
-       Various bugs fixed in the Windows build.  Mainly to do with capturing
-       and restoring alternate data streams correctly in weird cases, and
-       requesting the correct privileges when opening files.  Also added the
-       --noacls options to wimlib-imagex capture, append, and apply.
-
-       Windows build again: FindFirstStreamW() and FindNextStreamW() are now
-       dynamically loaded, so this may make the library compatible with Windows
-       XP (however, there may still be other problems).
-
-Version 1.3.0:
-       Added experimental support for native Windows builds.  Binaries can be
-       downloaded from the SourceForge page.
-
-       --source-list option added to `imagex capture' and `imagex append'.
-
-       Better support for different character encodings.
-
-Version 1.2.6:
-       Storing UNIX file owners, groups, and modes in WIM images is now
-       possible using `imagex capture' with the --unix-data flag.
-
-       Minor bug fixes and documentation fixes.
-
-Version 1.2.5:
-       NTFS capture: Fixed capturing duplicate reparse points.
-
-       NTFS capture: Capture first unnamed stream if there are more than one
-       (print warning instead of error).
-
-       Allow multiple test cases to execute concurrently (e.g. make -j2 check).
-
-Version 1.2.4:
-       Added --arch switch to mkwinpeimg script to support getting AMD64 WinPE
-       from the WAIK.
-
-       Update to work with ntfs-3g version 2013.1.13.
-
-Version 1.2.3:
-       Fixed truncating file to shorter but non-zero length on read-write
-       mounted WIM image.
-
-       Various code cleanups and minor documentation fixes.
-
-Version 1.2.2:
-       LZX and XPRESS decompression have received some additional optimizations
-       and should now be even faster.  (Although, they were already pretty
-       fast--- much faster than typical I/O speeds.)
-
-       Fixed a bug introduced in v1.2.1 that would cause a directory tree
-       containing hard links to be captured incorrectly in some cases.
-
-Version 1.2.1:
-       By default, unmounting a read-write mounted WIM with 'imagex unmount
-       --commit' will now change the WIM in-place without needing to write the
-       entire WIM again.  Use 'imagex unmount --commit --rebuild' to get the
-       old behavior.
-
-       'imagex unmount' no longer has a hard-coded limit of 10 minutes to wait
-       for a response from the daemon servicing the mounted WIM.  Instead,
-       every second 'imagex unmount' will check if the daemon is still alive,
-       and keep waiting if so, otherwise terminate with an error.
-
-       'imagex unmount --commit' on a read-write mounted WIM will now print
-       progress information regarding the writing of new or modified streams
-       the WIM, just like when capturing or appending a WIM.
-
-       A small change has been made to XPRESS compression and it should improve
-       the compression ratio slightly.
-
-       A change was made that may improve performance slightly when applying a
-       WIM image to a NTFS volume.
-
-       Microsoft has managed to introduce even more bugs into their software,
-       and now the WIMs for Windows 8 have incorrect (too low) reference counts
-       for some streams.  This is unsafe because such streams can be removed
-       when they are in actuality still referenced in the WIM (perhaps by a
-       different image).  wimlib will now work around this problem by fixing
-       the stream reference counts.  This is only done when wimlib_delete_image() is
-       called ('imagex delete') or when wimlib_mount_image() is called with
-       WIMLIB_MOUNT_FLAG_READWRITE ('imagex mountrw').  Please note that this
-       requires reading the metadata for all images in the WIM, so this will
-       make these operations noticably slower on WIMs with multiple images.
-
-       Various other bugfixes.
-
-Version 1.2.0:
-       Appending images to a WIM is now be done by default without re-building
-       the whole WIM.  Use the --rebuild flag to get the old behavior (which
-       was to re-build the entire WIM when a new image is appended).
-
-       A new command `imagex optimize' is now available to manually re-build a
-       WIM that has wasted space due to repeated appends.
-
-       Progress information has been improved, and now arbitrary callback
-       functions can be used to show the progress of a WIM operation.
-
-       A possible bug with changing the bootable image of a WIM was fixed.
-
-       Some advisory locking is now done to prevent two processes from
-       modifying a WIM at the same time (but only in some cases).  For example,
-       you cannot mount two images from a WIM read-write at the same time.
-
-       Some functions have been reorganized:
-               * wimlib_mount() renamed to wimlib_mount_image().
-               * wimlib_unmount() renamed to wimlib_unmount_image().
-               * wimlib_overwrite_xml_and_header() removed as
-               wimlib_overwrite() suffices now.
-               * wimlib_apply_image_to_ntfs_volume() removed as
-               wimlib_extract_image() suffices now.
-               * wimlib_add_image_from_ntfs_volume() removed as
-               * wimlib_add_image() suffices now.
-
-       Previously, the soname of libwim.so has been 0.0.0, despite many
-       interface changes.  The soname is now updated to 1.0.0 and will now be
-       updated each release.
-
-Version 1.1.0:
-       Resources will now be compressed using multiple threads by default.
-       (This applies to `imagex capture', `imagex append', and `imagex
-       export').
-
-       Some performance improvements in mounted WIMs.
-
-       More progress information is shown when capturing a WIM.
-
-Version 1.0.4:
-       Lots of minor fixes, code cleanups, and some documentation updates.
-       Nothing in particular is really noteworthy.
-
-Version 1.0.3:
-       LZX and XPRESS compression improvements.
-
-       Fixed calculation of Directory Count, File Count, Total Bytes, and Hard
-       Link Bytes of the WIM.
-
-Version 1.0.2:
-       Fixed bug when capturing NTFS file with multiple named data streams.
-
-       Internally, we are now using inode structures, even though these don't
-       appear literally in the WIM file.  This simplifies some of the code
-       (mainly for WIM mounting) and likely fixed a few problems, although it
-       needs more testing.
-
-Version 1.0.1:
-       Fixed problem when exporting images from XPRESS to LZX compressed WIM or
-       vice versa
-
-Version 1.0.0:
-       Enough changes to call it version 1.0.0!
-
-       Capturing a WIM directly from a NTFS volume, and applying a WIM directly
-       to a NTFS volume, is now supported.
-
-       Hard links and symbolic links have much improved support.  They are
-       supported for WIM capture, WIM application, and mounted WIMs (you can
-       even make them on read-write mounted WIMs).
-
-       Alternate data streams are now supported on mounted WIMs through an
-       xattr or a Windows-style stream interface.  Also they are supported when
-       capturing a WIM from NTFS or applying a WIM to NTFS.
-
-       Split WIMs are better supported.  You may now apply an image directly
-       from a split WIM, mount an image from a split WIM read-only, or export
-       an image from a split WIM.
-
-       Using a capture configuration file is now supported (but not fully yet).
-
-       SHA1 message digests are checked in more places, so we can make sure
-       applied and captured data is correct.
-
-       Man pages have been updated and consolidated.
-
-Version 0.7.2:
-       Fixed segfault when unmounting read-only WIM.
-
-Version 0.7.1:
-       Support for joining and splitting WIMs.
-       Also, security data is now preserved by default.
-
-Version 0.6.3:
-       Can now build with older gcc and system headers, like on CentOS 5.
-
-Version 0.6.2:
-       Fixed bug that made it impossible to overwrite files in read-write
-       mount.
-
-Version 0.6.1:
-       Write byte-order mark before WIM XML data.  (imagex.exe requires this to
-       be there.)
diff --git a/NEWS.md b/NEWS.md
new file mode 100644 (file)
index 0000000..13d310d
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,1266 @@
+# wimlib release notes
+
+## Version 1.14.4
+
+- Fixed potential crash when writing WIM XML data, introduced in v1.14.0.
+
+- Improved some documentation.
+
+- Fixed the Windows build script to avoid an unnecessary DLL dependency when
+  building with MSYS2 MINGW32 or MSYS2 MINGW64.
+
+## Version 1.14.3
+
+- Fixed a bug introduced in v1.14.0 where non-ASCII characters stopped being
+  accepted in image names and descriptions.  This bug only affected UNIX-like
+  systems that use `signed char`, e.g. x86 Linux systems.
+
+## Version 1.14.2
+
+- Fixed a bug introduced in v1.14.0 where a crash would sometimes occur if a
+  Delphi application or Visual Studio compiled application called into the
+  32-bit x86 build of the library.
+
+- Fixed an issue where some WIM images written by wimlib weren't accepted by
+  some MS software versions.  wimlib-written WIM images containing directory
+  reparse points (e.g. junctions) weren't accepted by some versions of the
+  Windows 8 setup wizard.  Also, recent versions of DISM had stopped accepting
+  wimlib-written WIM images containing directories with named data streams.
+
+- Commands passed to wimupdate on standard input are now interpreted as UTF-8 or
+  UTF-16LE (autodetected), just like wimcapture config files and wimextract path
+  list files.  Previously, on Windows the Windows code page was used instead of
+  UTF-8, which made it hard to specify non-ASCII file paths in wimupdate
+  commands.  The same change also applies to wimcapture source list files.
+
+## Version 1.14.1
+
+- Fixed a bug introduced in v1.14.0 where wimlib would crash on older CPUs.
+
+## Version 1.14.0
+
+- Removed libxml2 and libcrypto (OpenSSL) as dependencies of wimlib.  Also
+  removed winpthreads as a dependency of wimlib on Windows.
+
+- Upgraded the support for mounting WIM images on Linux from fuse2 to fuse3.
+  fuse2 is no longer supported.
+
+- Converted the README, README.WINDOWS, and NEWS files to Markdown.
+
+- Simplified the process of building wimlib for Windows.  See README.WINDOWS for
+  the updated instructions, which use MSYS2 instead of Cygwin.  Windows ARM64
+  builds are now supported (experimentally) as well.
+
+- Improved performance on CPUs that have SHA-1 instructions in cases where
+  wimlib wasn't using OpenSSL, e.g. the Windows binaries.
+
+- Fixed a bug in `wimsplit` where it didn't accept part sizes of 4 GiB or larger
+  on Windows and on 32-bit platforms.
+
+- `wimupdate` now supports the `--ref` option.  It should be specified when
+  updating a delta WIM to avoid two minor issues.
+
+- `wimoptimize` now has better default behavior when converting to and from
+  solid archives, i.e. WIM <=> ESD.  It now is consistent with `wimcapture` and
+  `wimexport`.  For WIM => ESD, `wimoptimize --solid` now works.  Before, the
+  needed command was `wimoptimize --solid --compress=LZMS --chunk-size=128K`.
+  For ESD => WIM, `wimoptimize --compress=LZX` now works.  Before, the needed
+  command was `wimoptimize --compress=LZX --chunk-size=32K`.
+
+- Removed support for Windows XP.
+
+- Added a GitHub Actions workflow that tests wimlib.
+
+## Version 1.13.6
+
+- `wimsplit` no longer prints a success message on failure.
+
+- `wimlib_iterate_dir_tree()` no longer hashes files that haven't yet been
+  written to the WIM file.
+
+- Reduced the maximum number of file descriptors that wimlib can use when
+  extracting files from a WIM image on macOS.
+
+- The files that used the CC0 public domain dedication now use the MIT license
+  instead.
+
+- Removed some configuration options (`--disable-assertions`,
+  `--disable-error-messages`, and `--disable-multithreaded-compression`) that
+  probably weren't being used by anyone.
+
+## Version 1.13.5
+
+- Exporting "all" images from a WIM file no longer fails if multiple
+  images in that WIM file have the same name.
+
+- wimlib now warns rather than aborts if two files have the same SHA-1
+  hash but different sizes.
+
+- Fixed build errors with the latest version of MinGW-w64.
+
+## Version 1.13.4
+
+- wimsplit now prints progress messages regularly rather than just once per WIM
+  part.
+
+- Added support for a data recovery mode which causes files to be extracted even
+  if they are corrupted.  The option is `--recover-data` for `wimapply` and
+  `wimextract`, and `WIMLIB_EXTRACT_FLAG_RECOVER_DATA` for the library.  Note
+  that this option won't help with all types of corruption; some types of
+  corruption will still cause a fatal error.
+
+## Version 1.13.3
+
+- On Windows, improved performance of capturing an entire drive in some cases.
+
+- On Windows, fixed leaking a directory handle (or triggering a SEH exception
+  when running under a debugger) when referencing WIM files.
+
+- On Windows, when applying a Windows OS image using the `--compact` flag,
+  bootloader files can now be compressed with stronger compression algorithms if
+  the version of Windows is recent enough to support it.
+
+- Clarified the license text.
+
+## Version 1.13.2
+
+- Prevented miscompilation with gcc 10 at -O3 due to [a gcc
+  bug](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94994).
+
+- Avoided some compiler warnings with gcc 9 and later.
+
+- The `mkwinpeimg` script now also looks for syslinux modules in
+  `/usr/share/syslinux`, to handle where openSUSE installs them.
+
+- Removed obsolete packaging files from the source tree.
+
+## Version 1.13.1
+
+- Fixed a crash or incorrect output during LZMS compression with a compression
+  level greater than 50 and a chunk size greater than 64 MiB.  This affected
+  wimlib v1.8.0 and later.  In the unlikely event that you used all these
+  non-default compression settings in combination, e.g. `wimcapture --solid
+  --solid-compress=LZMS:100 --solid-chunk-size=128M`, run `wimverify` on your
+  archives to verify your data is intact.
+
+## Version 1.13.0
+
+- On Windows, wimlib now supports capturing and applying extended attributes
+  (EAs).  It is compatible with DISM with the `/EA` option, available since
+  Windows 10 version 1607.  wimlib's EA support is on by default and works on
+  older versions of Windows too.
+
+- Partially fixed a bug where `[ExclusionException]` entries didn't take effect
+  when the containing directory is matched by `[ExclusionList]`.  It now works
+  when the `[ExclusionException]` patterns are absolute.  For example, listing
+  `/dir/file` in `[ExclusionException]` now works even if `/dir` is matched by
+  `[ExclusionList]`.
+
+- Added a `--create` option to `wimappend` which makes it create the WIM
+  file (like `wimcapture`) if it doesn't exist yet.
+
+- Added an `--include-integrity` option to various `wimlib-imagex` commands.
+  `--include-integrity` is like `--check`, but it will just include an integrity
+  table in the output WIM(s), while skipping verification of any existing
+  integrity tables.  This can be useful to avoid unwanted verification of large
+  WIM files, e.g. WIMs given by `--delta-from`.
+
+- `wimextract` now reads a pathlist file from standard input when `@-` is given
+  as an argument.
+
+- `wimsplit` (API: `wimlib_split()`) now correctly handles a dot in the path to
+  the first split WIM part, prior to the filename extension.
+
+- `wimlib-imagex --version` now shows the version of the library it is actually
+  using (in case it is different from `wimlib-imagex`'s version).
+
+## Version 1.12.0
+
+- Fixed a bug that was causing the LZMS decompressor to be miscompiled with GCC
+  7 (this broke extracting "solid" archives).
+
+- The Windows 10 Recycle Bin directory (`\$RECYCLE.BIN`) has been added to the
+  default exclusion list.
+
+- Added a `--quiet` option to `wimlib-imagex`.
+
+- The `mkwinpeimg` script now also looks for the syslinux BIOS modules in the
+  directory `/usr/lib/syslinux/modules/bios`.
+
+- Files with timestamps before the year 1970 are now extracted correctly to
+  UNIX-style filesystems, are displayed correctly by `wimdir --detailed`, and
+  show up correctly in mounted WIM images.
+
+- Files with timestamps after the year 2038 are now displayed correctly by the
+  32-bit Windows build of wimlib.
+
+## Version 1.11.0
+
+- Fixed a data corruption bug (incorrect compression) when storing an already
+  highly-compressed file in an LZX-compressed WIM with a chunk size greater than
+  or equal to 64K.  Note that this is not the default setting and such WIMs are
+  not supported by Microsoft's WIM software, so only users who used the
+  `--chunk-size` option to `wimlib-imagex` or the
+  `wimlib_set_output_chunk_size()` API function may have been affected.  This
+  bug was introduced in wimlib v1.10.0.  See [this forum
+  thread](https://wimlib.net/forums/viewtopic.php?f=1&t=300) for more details.
+
+- On all platforms, sparse files are now extracted as sparse.
+
+- Sparse files captured from UNIX-style filesystems are now marked as sparse in
+  the resulting WIM image.
+
+- Added support for storing Linux-style extended attributes in WIM images.  When
+  the `--unix-data` option is used on Linux, `wimlib-imagex` now captures and
+  applies extended attributes, in addition to the already-supported standard
+  UNIX file permissions (owner/group/mode) and special files.
+
+- `--delta-from` is now supported by `wimappend`.  (Previously it was only
+  supported by `wimcapture`.)
+
+- On Windows, improved the way in which files deduplicated with Windows' Data
+  Deduplication feature are captured.
+
+- The development files needed to link with wimlib using Visual Studio are now
+  included in the Windows release archives.
+
+- `wimlib.h` can now be included by Visual Studio without errors.
+
+- The example programs can now be compiled in C++ mode, and they also now work
+  on Windows.
+
+- Updated `mkwinpeimg` to work correctly on images that have a `windows`
+  (lower case) directory rather than a `Windows` (upper case) directory.
+
+- Fixed configuring with `--enable-ssse3-sha1` from release tarball (the file
+  `nasm_lt.sh` was missing).
+
+- Made some documentation improvements.
+
+## Version 1.10.0
+
+- The LZX compression ratio has been slightly improved.  The default mode, LZX
+  level 50, is now almost as good as the old LZX level 100, while being nearly
+  the same speed as before.
+
+- Decompression performance has been slightly improved.
+
+- Filenames are now always listed in NTFS collation order.
+
+- On UNIX-like systems, wimlib can now process Windows filenames that are
+  not valid Unicode due to the presence of unpaired surrogates.
+
+- On UNIX-like systems, wimlib now always assumes UTF-8 encoding with the
+  addition of surrogate codepoints.  Consequently, the environmental variable
+  `WIMLIB_IMAGEX_USE_UTF8` and the flag `WIMLIB_INIT_FLAG_ASSUME_UTF8` no longer
+  have any effect.
+
+- wimlib no longer depends on iconv.
+
+- Reduced memory usage slightly.
+
+- When a WIM image is applied in NTFS-3G mode, security descriptors are now
+  created in NTFS v3.0 format when supported by the volume.
+
+- Workarounds for bugs in libntfs-3g version 2013.1.13 and earlier have been
+  removed.  Users are advised to upgrade to a later version of libntfs-3g.
+
+- On Windows, wimlib now supports case-sensitive filename extraction when
+  supported by the underlying operating system and filesystem (operating system
+  support requires a registry setting).
+
+## Version 1.9.2
+
+- On UNIX, wimlib can now overwrite readonly files when extracting.
+
+- On Windows, fixed a bug where wimlib could leave a null DACL (a.k.a. "no NTFS
+  permissions") set on some existing directories after extraction.
+
+- On Windows, when applying a WIM image in "WIMBoot mode" when the WOF driver is
+  not loaded, wimlib can now correctly register a new WIM file with the target
+  volume when the target volume previously had had WIM files unregistered.
+
+- Added a new testing program.
+
+- Clarified the main license text and updated public domain dedications for
+  certain files to be more thorough.
+
+## Version 1.9.1
+
+- Object IDs are now saved and restored on Windows and in NTFS-3G mode.
+
+- Reduced memory usage when exporting large numbers of WIM images.
+
+- Non UTF-8 locales are now detected correctly.
+
+- Addressed compiler warnings and enabled "silent" make rules by default.
+
+- Windows-specific updates:
+
+  - Fixed a bug where duplicate backslashes could be generated in link targets
+    when extracting symbolic links and junctions.
+
+  - Fixed a bug where the `.cmd` shortcuts for `wimlib-imagex` wouldn't work if
+    their full path contained a space.
+
+  - Fixed bugs related to scanning SMB filesystems.
+
+  - Added warning message about known issue with WindowsApps folder.
+
+  - Added instructions for building from source on Windows.
+
+  - VSS support is no longer marked "experimental".
+
+  - Added missing license file for libdivsufsort-lite.
+
+## Version 1.9.0
+
+- Added experimental support for Windows VSS (Volume Shadow Copy Service).  The
+  new `--snapshot` argument to `wimcapture` makes wimlib automatically create
+  and use a temporary VSS snapshot when capturing a WIM image.
+
+- Implemented setting of Windows-specific XML information, such as architecture,
+  system root, and version details.  This information is now automatically set
+  in newly captured WIM images, when appropriate.
+
+- Improved performance of directory tree scans on Windows.
+
+- On Windows, to improve capture performance, wimlib now sometimes opens files
+  by inode number rather than by path.  This is enabled for `wimcapture` and
+  `wimappend`, but for now other applications have to opt-in.
+
+- The progress messages printed by `wimlib-imagex` while writing WIM files have
+  been slightly tweaked.
+
+- Progress information for directory tree scans now counts all hard links.
+  Also, on Windows `\\?\` is no longer stripped from the current path.
+
+- Added a new `--image-property` option to `wimcapture`, `wimappend`, and
+  `wiminfo`.  This option lets you assign values to elements in a WIM file's XML
+  document by name.
+
+- The `wimlib_get_image_property()` and `wimlib_set_image_property()` API
+  functions now support numerically indexed elements.
+
+- Fixed a bug where, on Windows, wimlib would change the security descriptor of
+  the target directory of an extraction even when the `--no-acls` option was
+  specified.
+
+## Version 1.8.3
+
+- Fixed a bug with libntfs-3g extraction present since v1.8.1.  Sometimes, some
+  Microsoft software would not correctly recognize data in the resulting
+  filesystem.
+
+- Made some small improvements to the compression algorithms:
+  - LZX compression ratio was slightly improved.
+  - XPRESS compression ratio and speed was slightly improved.
+  - LZMS compression speed was slightly improved.
+
+- Improved handling of WIM XML data.  wimlib no longer drops unrecognized
+  elements when exporting images.  In addition, two API functions were added for
+  better access to elements in the XML document: `wimlib_get_image_property()`
+  and `wimlib_set_image_property()`.
+
+- Added support for (unsafe) in-place compaction of WIM files.
+
+- Improved performance of image export by reusing metadata resources
+  instead of always rebuilding and recompressing them.
+
+- Improved performance of `wimlib_update_image()` by delaying the update to the
+  WIM's XML document until a write is requested.
+
+- On Windows, the target of an extraction may now be a reparse point
+  (which will be dereferenced).
+
+- On Windows, wimlib now correctly restores non-Microsoft reparse points.
+  However, this remains broken in NTFS-3G mode due to a libntfs-3g bug.
+
+- On Windows, wimlib now has improved performance when archiving files
+  from a filesystem backed by a WIM (a "WIMBoot" setup).
+
+- Several improvements to System Compression (compact mode) support:
+
+  - `wof.sys` (or `wofadk.sys`) is now automatically attached to the target
+    volume if needed.
+
+  - Compact-mode extractions now work correctly with `wofadk.sys` on older
+    versions of Windows.
+
+  - For compatibility with the Windows bootloader, the requested compression
+    format now is overridden on certain files.
+
+- Other minor bugfixes.
+
+## Version 1.8.2
+
+- This release primarily contains various minor bug fixes and improvements,
+  including:
+
+  - Improved handling of deep directory structures.
+
+  - Fixed a bug where on 32-bit systems, the library could enter an infinite
+    loop if a WIM file was malformed in a specific way.
+
+  - Added a workaround for a case where libntfs-3g may report duplicate streams
+    in an NTFS file.
+
+  - Windows symbolic links and junctions in mounted WIM images are now
+    automatically rewritten to be valid in the mounted location.
+
+  - Reparse point fixes: correctly handle the "ReparseReserved" field, and
+    correctly handle "empty" (data-less) reparse points.
+
+  - On Windows, wimlib now acquires SeManageVolumePrivilege, which is needed to
+    create externally backed files using the `wofadk.sys` driver.
+
+  - Improved validation of filenames.
+
+  - Improved LZMS decompression speed.
+
+  - The configure script now honors alternate pkg-config settings.
+
+  - Links have been updated to point to the new website.
+
+- In addition, experimental support has been added for compressing extracted
+  files using System Compression on Windows 10.  This functionality is available
+  through the new `--compact` option to `wimapply` and `wimextract` as well as
+  new library flags.
+
+## Version 1.8.1
+
+- Fixed a bug in the LZX decompressor: malicious input data could cause out of
+  bounds writes to memory (since wimlib v1.2.2).
+
+- The output of the `wiminfo` command now consolidates various boolean flags
+  (such as "Relative path junction") into a single line.
+
+- A file can now have both an unnamed data stream ("file contents") and a
+  reparse point stream.  Such files can exist as a result of the use of certain
+  Windows features, such as offline storage, including "OneDrive".  wimlib will
+  now store and restore both streams on Windows as well as in NTFS-3G mode.
+  Microsoft's WIMGAPI also has this behavior.
+
+- On Windows, named data streams of encrypted files are no longer stored twice
+  in WIM archives.
+
+- On Windows, named data streams are now correctly extracted to existing
+  "readonly" directories.  Before, an error would be reported.
+
+- On Windows, it is now possible to do a "WIMBoot mode" extraction with
+  non-standalone WIMs such as delta WIMs.
+
+- On Windows, when doing an extraction in "WIMBoot mode", files larger
+  than 4 gigabytes are now never extracted as externally backed.  This
+  works around a bug in Microsoft's "WOF" driver.
+
+- The `--enable-verify-compression` configure option has been removed.  If you
+  want to verify a WIM file, use the `wimverify` program.
+
+- The way the "file count", "directory count", "total bytes", and "hard link
+  bytes" image statistics (stored in the WIM XML data) is calculated has been
+  slightly changed.
+
+- In mounted WIM images, the disk usage provided for each file (`st_blocks`) is
+  now the compressed size rather than the uncompressed size.
+
+- The performance of the NTFS-3G and Windows capture modes has been slightly
+  improved.
+
+- On UNIX-like systems, symbolic links whose targets contain the backslash
+  character are now handled correctly (losslessly).
+
+## Version 1.8.0
+
+- Improved the LZX compressor.  It is now 15-20% faster than before and provides
+  a slightly better compression ratio.
+
+- Improved the LZMS compressor.  It now provides a compression ratio slightly
+  better than WIMGAPI while still being faster and using slightly less memory.
+
+- The compression chunk size in solid resources, e.g. when capturing or
+  exporting a WIM file using the `--solid` option, now defaults to 64 MiB
+  (67108864 bytes) instead of 32 MiB (33554432 bytes).  This provides a better
+  compression ratio and is the same value that WIMGAPI uses.  The memory usage
+  is less than 50% higher than wimlib v1.7.4 and is slightly lower than
+  WIMGAPI's memory usage, but if it is too much, it is still possible to choose
+  a lower value, e.g. with the `--solid-chunk-size` option to `wimlib-imagex`.
+
+- The `--chunk-size` and `--solid-chunk-size` options to `wimlib-imagex` now
+  accept the 'K', 'M', and 'G' suffixes.
+
+- Files are now sorted by name extension when creating a solid WIM file.
+
+- Fixed various issues related to capture/apply of EFS-encrypted files on
+  Windows.
+
+- The file list printed by `wimdir` is now sorted by the platform-specific
+  case sensitivity setting, rather than always case sensitively.  This
+  also affects the library function `wimlib_iterate_dir_tree()`.
+
+- On Windows, some error and warning messages have been improved.
+
+## Version 1.7.4
+
+- The Windows binary distribution no longer contains third party DLLs.  These
+  dependencies are instead compiled directly into the libwim DLL.
+
+- Added more fixes for wimlib on non-x86 architectures such as ARM.
+
+- Extracting files to a Windows PE in-memory filesystem no longer fails if
+  the target files do not yet exist.
+
+- Improved the performance of XPRESS compression and LZMS decompression.
+
+- Enabled SSSE3 accelerated SHA-1 computation in `x86_64` Windows builds.  It
+  will automatically be faster on newer Intel and AMD processors.
+
+- Removed the `--with-imagex-progname` and `--enable-more-assertions` configure
+  options.
+
+## Version 1.7.3
+
+- Fix for very slow export from solid WIM / ESD files.
+
+- Fix for LZX and LZMS algorithms on non-x86 architectures, such as ARM.
+
+- New progress message: `WIMLIB_PROGRESS_MSG_HANDLE_ERROR`.  Applications may
+  use this to treat some types of errors as non-fatal.
+
+- The library now permits making in-memory changes to a WIMStruct backed by a
+  read-only WIM file.
+
+- Fixes for "WIMBoot" extraction mode (Windows only):
+
+  - When not using the WOF driver, extraction no longer fails if the disk
+    containing the WIM file has too many partitions.
+
+  - When matching patterns in `[PrepopulateList]`, all hard links of each file
+    are now considered.
+
+  - The system registry files are now automatically treated as being in
+    `[PrepopulateList]`.
+
+  - Added a hack to try to work around an intermittent bug in Microsoft's WOF
+    (Windows Overlay Filesystem) driver.
+
+## Version 1.7.2
+
+- Made more improvements to the XPRESS, LZX, and LZMS compressors.
+
+- A number of improvements to the Windows port:
+
+  - Fixes for setting short filenames.
+
+  - Faster "WIMBoot" extraction.
+
+  - Updated and slimmed down the dependent DLLs.
+
+  - ACL inheritence bits are now restored.
+
+  - Mandatory integrity labels are now backed up and restored.
+
+- Added a workaround for an issue where in rare cases, wimlib could create a
+  compressed data stream that could not be read correctly by Windows after an
+  extraction in "WIMBoot" mode.
+
+- Library changes:
+
+  - Added file count progress data for
+    `WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE` and
+    `WIMLIB_PROGRESS_MSG_EXTRACT_METADATA`.
+
+  - Added support for testing file exclusions via the user-provided progress
+    function.
+
+  - Some documentation improvements.
+
+- Made some clarifications to the license text in the COPYING file.
+
+## Version 1.7.1
+
+- Made more improvements to the XPRESS, LZX, and LZMS compressors.
+
+- The default compression mode for wimcapture is now LZX compression in its
+  default mode, which is the same as `--compress=maximum`.
+
+- You can now specify an optional integer compression level to the
+  `--compress` option; e.g. `--compress=lzx:75`.
+
+- Made a minor change to the LZMS compressor and decompressor to fix an
+  incompatibility with the Microsoft implementation.  In the unlikely event that
+  you created an LZMS-compressed WIM with wimlib v1.7.0 or earlier and a
+  checksum error is reported when extracting files from it with wimlib v1.7.1,
+  decompress it with v1.7.0 then compress it with v1.7.1.
+
+- Added `verify` subcommand to `wimlib-imagex`.
+
+- Notable library changes:
+
+  - Custom compressor parameters have been removed from the library in favor of
+    the simpler level-based API.
+
+  - Decompressor parameters have been removed entirely.
+
+  - Library users can now specify a custom file for warning and error messages
+    to be sent to, rather than the default of standard error.
+
+  - New progress messages: `WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE`,
+    `WIMLIB_PROGRESS_MSG_EXTRACT_METADATA`.
+
+    New function: `wimlib_verify_wim()`.
+
+## Version 1.7.0
+
+- Improved compression, decompression, and extraction performance.
+
+- Improved compatibility with version 3584 WIM / ESD files:
+
+  - Add support for reading and writing multiple solid blocks per archive, which
+    WIMGAPI/DISM can create when appending an image.
+
+  - Correctly create solid blocks larger than 4 GiB.
+
+- `add` commands passed to wimupdate will now replace existing nondirectory
+  files by default.  Use the `--no-replace` option to get the old behavior.
+
+- The license for the library now contains an exception that allows using it
+  under the LGPL.  See the COPYING file for details.
+
+- In reparse-point fixup mode (the default for capture), symbolic links and
+  junctions that point outside the tree being captured are no longer excluded
+  from capture.
+
+- Added support for "WIMBoot" capture and extraction.  See the documentation for
+  the new `--wimboot` option to wimcapture and wimapply for more information.
+
+- On UNIX-like systems, you can now backup and restore device nodes, named
+  pipes, and sockets.  In addition, 32-bit user and group IDs are now supported.
+
+- The way that UNIX data is stored in WIM files has been changed.  If you
+  captured any WIMs with the `--unix-data` option, to upgrade them you'll need
+  to apply them with `--unix-data` using `wimlib-imagex` v1.6.2, then re-capture
+  them with `--unix-data` using this version.
+
+- wimlib now understands tagged metadata items, such as object IDs, that
+  can be stored in WIM directory entries.
+
+- Removed the `--hardlink` and `--symlink` options to wimapply, since I don't
+  think they are too useful and they got in the way of improving the code.
+
+- WIMs will now retain their GUIDs when rebuilt (e.g. with wimoptimize).
+
+- The `mkwinpeimg` script now supports writing the ISO image to standard output.
+
+- The `<ARCH>` element in WIM XML data is now exported correctly.
+
+- On Windows, sparse file attributes are no longer set on extracted files.
+  Oddly enough, this actually saves disk space in some cases.
+
+- On UNIX, configuring with `--disable-xattr` or `--enable-xattr` is no longer
+  supported.  Mounting WIM images now always requires extended attribute
+  support.  Use `--without-fuse` to disable support for mounting WIM images;
+  this will also disable the need for extended attribute support.
+
+- Configuring with `--enable-ssse3-sha1` now works correctly.
+
+- The shared library version has been bumped up.  The main incompatibilities
+  are:
+
+  - `WIMLIB_COMPRESSION_TYPE_XPRESS` is now 1 and `WIMLIB_COMPRESSION_TYPE_LZX`
+    is now 2 (so it's the same as WIMGAPI).
+
+  - User-provided progress functions are now registered using a separate
+    function, `wimlib_register_progress_function()`.  The `progress_func`
+    argument to many functions no longer exists.
+
+  - The return value from user-provided progress functions is now significant.
+
+  - A context argument has been added to the prototype of user-provided progress
+    functions.
+
+  - `struct wimlib_capture_config` has been removed.  The library now takes the
+    path to the configuration file directly.  This affects `wimlib_add_image()`,
+    `wimlib_add_image_multisource()`, and `wimlib_update_image()`.  However, a
+    NULL value passed in the argument retains the same meaning.
+
+  - Removed deprecated functions: some (de)compression functions,
+    `wimlib_extract_files()`, and `wimlib_print_metadata()`.
+
+  - Removed extraction flags: `WIMLIB_EXTRACT_FLAG_HARDLINK`,
+    `WIMLIB_EXTRACT_FLAG_SYMLINK`, `WIMLIB_EXTRACT_FLAG_FILE_ORDER`, and
+    `WIMLIB_EXTRACT_FLAG_SEQUENTIAL`.
+
+  - Removed some progress messages: `WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS`,
+    `WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN`,
+    `WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END`.  Numbering stays the same.
+
+  - Removed some error codes.  Numbering stays the same.
+
+  - Replaced `WIMLIB_UNMOUNT_FLAG_LAZY` with `WIMLIB_UNMOUNT_FLAG_FORCE`.
+
+  - WIM paths passed to progress functions now have a leading slash.
+
+## Version 1.6.2
+
+- Case-insensitive comparisons of strings (e.g. filenames) containing UTF-16
+  codepoints above 32767 are now done correctly.
+
+- Fixed build failure on Mac OS X.
+
+- `wimunmount` now provides the `--new-image` option to cause changes to a
+  read-write mounted image to be committed as a new image rather than as an
+  update of the mounted image.  (The corresponding new library flag is
+  `WIMLIB_UNMOUNT_FLAG_NEW_IMAGE`.)
+
+- The LZMS ("recovery") compression chunk size, or "dictionary size", may now be
+  up to 1 GiB (1,073,741,824 bytes).
+
+- The performance of LZX ("maximum") and LZMS ("recovery") compression with
+  large chunk sizes has been slightly improved.
+
+## Version 1.6.1
+
+- Stored files with size exactly 4 GiB (4,294,967,296 bytes) are now
+  decompressed correctly.
+
+- Fixed a bug in the LZX compressor introduced in v1.5.3.  The bug occurred in
+  an unlikely case, and due to validity checks it did not affect successfully
+  created archives.
+
+- Fixed a minor compatibility issue with the LZMS compressor and decompressor.
+  This is *not* the default compression type and was only introduced in v1.6.0.
+  In the unlikely event that you created an LZMS-compressed WIM with v1.6.0 and
+  a checksum error is reported when applying it with v1.6.1, decompress it with
+  v1.6.0 then compress it with v1.6.1.
+
+- Memory usage for LZMS and LZX compression has been decreased.
+
+- wimextract now allows wildcard characters in paths specified on the command
+  line.  Also, the `--strict-wildcards` option has been removed and replaced
+  with the inverse option `--nullglob`.  See the documentation for wimextract
+  for more details and changes.
+
+- The `wimlib_extract_files()` function is now considered deprecated in favor of
+  `wimlib_extract_paths()`.
+
+- Fixed more permissions problems when extracting files on Windows.
+
+- A new `--no-attributes` option has been added to `wimapply` and `wimextract`.
+  The library flag is `WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES`.
+
+- The default chunk size is now set correctly when changing the compression type
+  of a WIM, for example with `wimoptimize`.
+
+- The `--metadata` option to `wiminfo` has been replaced with the `--detailed`
+  option to `wimdir`.
+
+- In relevant `wimlib-imagex` commands, `--solid` may now be used as an alias
+  for `--pack-streams`.
+
+## Version 1.6.0
+
+- Support for extracting and updating the new version 3584 WIMs has been added.
+  These WIMs typically pack many streams ("files") together into a single
+  compressed resource, thereby saving space.  This degrades the performance of
+  random access (such as that which occurs on a mounted image), but
+  optimizations have been implemented for extraction.  These new WIM files also
+  typically use a new compression format (LZMS), which is similar to LZMA and
+  can offer a better compression ratio than LZX.  These new WIM files can be
+  created using `wimcapture` with the `--compress=lzms --pack-streams` options.
+  Note: this new WIM format is used by the Windows 8 web downloader, but
+  important segments of the raw `.esd` files are encrypted, so wimlib will not
+  be able to extract such files until they are first decrypted.
+
+- wimlib now supports extracting files and directories from a WIM image based on
+  a "listfile" that itself contains the list of paths to extract.  For
+  `wimextract`, the syntax is to specify `@LISTFILE` instead of a `PATH`, and
+  for the library itself, the new APIs are `wimlib_extract_pathlist()` and
+  `wimlib_extract_paths()`.  Path globs containing wildcard characters are
+  supported.
+
+- For searching WIM files, wimlib now has configurable case sensitivity.  The
+  default on Windows is still case-insensitive and the default on UNIX-like
+  systems is still case-sensitive, but this can be overridden on either platform
+  through flags to `wimlib_global_init()`.  For `wimlib-imagex`, the
+  environmental variable `WIMLIB_IMAGEX_IGNORE_CASE` can be set to 1 or 0 for
+  case-insensitive or case-sensitive behavior, respectively.
+
+- Support for compression chunk sizes greater than the default of 32768
+  bytes has been added.  A larger chunk size typically results in a better
+  compression ratio.  However, the MS implementation is seemingly not
+  compatible with all chunk sizes, especially for LZX compression, so the
+  defaults remain unchanged, with the exception of the new LZMS-compressed
+  WIMs, which use a larger chunk size by default.
+
+- The compression/decompression API exported by wimlib has been changed.  Now
+  one set of functions handles all supported compression formats.
+
+- `wimcapture` and `wimappend` will now display the progress of scanning the
+  directory tree to capture, in addition to the progress of writing data to the
+  WIM.  The `--verbose` option no longer does anything.  The library API change
+  for this is the addition of several members to `struct
+  wimlib_progress_info_scan` available to progress callbacks.
+
+- `mkwinpeimg` now correctly handles the `--start-script` option when the start
+  script is not in the working directory.
+
+- Sequential extraction, previously requested by using
+  `WIMLIB_EXTRACT_FLAG_SEQUENTIAL`, is now the default.
+  `WIMLIB_EXTRACT_FLAG_FILE_ORDER` can be used to get the old default behavior
+  (extract in file order).
+
+## Version 1.5.3
+
+- The new LZX compressor added in v1.5.2 has been improved and is now enabled by
+  default, except when `wimcapture` or `wimappend` is run *without* the
+  `--compress` option, in which case the faster LZX compressor is used (the same
+  as before).  This behavior is reasonably consistent with ImageX which actually
+  uses "fast" (XPRESS) compression by default.  In those cases, use
+  `--compress=maximum` to explicitly capture a WIM image using the new (slower
+  but better) LZX compressor.
+
+  The `--compress-slow` option still exists to `wimlib-imagex optimize`, but its
+  new behavior is to tweak the new LZX compressor even more to produce an even
+  better compression ratio at the cost of more time spent compressing.
+
+- `wimlib-imagex optimize` now supports the `--compress=TYPE` option, which
+  recompresses the WIM file using the specified compression TYPE.  The new
+  library API function used for this is `wimlib_set_output_compression_type()`.
+
+- Added the `wimlib_get_xml_data()` function to allow library clients to easily
+  retrieve the raw XML data from a WIM file if needed.
+
+- Fixed a bug that could cause an error code to be incorrectly returned when
+  writing XML data containing a `<WINDOWS>` element.
+
+- Mounted WIM images will now correctly show the default file stream even if
+  appears in the alternate data stream entries of the corresponding WIM
+  directory entry.
+
+## Version 1.5.2
+
+- Added a new experimental LZX compressor which can be enabled by passing
+  `--compress-slow` to `wimlib-imagex capture` or `wimlib-imagex optimize`.
+  (The latter is only applicable if the WIM is already LZX-compressed and the
+  `--recompress` option is also given.)  The experimental compressor is much
+  slower but compresses the data slightly more --- currently usually to within a
+  fraction of a percent of the results from WIMGAPI/ImageX.
+
+- A workaround has been added for compatibility with versions of WinPE that
+  interpret alternate data stream entries in the boot WIM incorrectly.
+
+- An alignment bug that caused a crash in the LZX decompressor on some builds
+  was fixed.
+
+- wimlib now attempts to clear the `WIM_HDR_FLAG_WRITE_IN_PROGRESS` flag in the
+  WIM header when restoring the previous state of a WIM it failed to
+  successfully update.
+
+- Added a workaround to avoid an access denied error on Windows when replacing a
+  WIM file that another process has opened.
+
+## Version 1.5.1
+
+- wimlib can now open WinPE WIMs from WAIK v2.1, which had a quirk that needed
+  to be handled.
+
+- A bug in the interpretation of negative `IMAGE` indices in the
+  `--update-of=[WIMFILE:]IMAGE` option to `wimlib-imagex capture` and
+  `wimlib-imagex append` has been fixed.
+
+- A workaround has been added to successfully apply security descriptors with
+  empty DACLs when the NTFS-3G apply mode is being used with NTFS-3G 2013.1.13
+  or earlier.
+
+- `wimlib-imagex capture` can now accept the `--delta-from` option multiple
+  times.
+
+## Version 1.5.0
+
+- Added support for "pipable" WIMs.  Pipable WIMs allow capturing images to
+  standard output and applying images from standard input, but they are not
+  compatible with Microsoft's software and are not created by default.  See the
+  documentation for `--pipable` flag of `wimlib-imagex capture` for more
+  information.
+
+- To better support incremental backups, added support for declaring an image as
+  a modified form of another image.  See the documentation for the `--update-of`
+  option of `wimlib-imagex append` and `wimlib-imagex capture`.
+
+- Added supported for "delta" WIMs.  See the documentation for the
+  `--delta-from` option of `wimlib-imagex capture`.
+
+- The library support for managing split WIMs has been changed to support other
+  arrangements, such as delta WIMs, and be easier to use.  This change is
+  visible in `wimlib-imagex`, which also can now accept the `--ref` option
+  multiple times, and also now supports "delta" WIMs as mentioned above.
+
+- wimlib now preserves WIM integrity tables by default, even if
+  `WIMLIB_WRITE_FLAG_CHECK_INTEGRITY` is not specified.  This changes the
+  behavior of `wimlib-imagex` whenever the WIM being operated on contains
+  an integrity table and the `--check` option is not specified.
+
+- `wimlib-imagex capture` now creates LZX-compressed WIMs by default (when
+  `--compress` is not specified).  This provides the best compression ratio by
+  default, which is usually what is desired, at a cost of some speed.
+
+- `wimlib-imagex` now supports being invoked as `wimCOMMAND`, where `COMMAND` is
+  the command as in `wimlib-imagex COMMAND`; for example, it can be invoked as
+  `wimapply` as an alternative to `wimlib-imagex apply`.  The appropriate hard
+  links are created in UNIX installations of `wimlib-imagex`, while for the
+  Windows distribution of `wimlib-imagex`, batch files that emulate this
+  behavior are generated.
+
+- Security descriptors are now extracted correctly on Windows.
+
+- Fixed archiving DOS names in NTFS-3G capture mode.
+
+- The extraction code has been rewritten and it will now be easier to support
+  new features on all supported backends (currently Win32, UNIX, and NTFS-3G).
+  For example, hard-linked extraction mode (`--hardlink`) is now supported on
+  all backends, not just UNIX.
+
+- `mkwinpeimg` now supports grabbing files from the WAIK supplement rather
+  than the WAIK itself.
+
+- `wimlib_global_init()` now, by default, attempts to acquire additional
+  privileges on Windows, so library clients need not do this.
+
+- This update bumps the shared library version number up to 9, since it is not
+  binary compatibible with previous releases.
+
+## Version 1.4.2
+
+- Fixed bug in `wimlib-imagex export` that made it impossible to export an image
+  from a WIM that is readonly at the filesystem level.
+
+- Return error code rather than segfaulting when trying to list files from a
+  non-first part of a split WIM.
+
+- Joining a WIM will now preserve the `RP_FIX` and `READONLY` flags.
+
+## Version 1.4.1
+
+- On Windows, paths given to `wimlib-imagex` are now treated case insensitively.
+
+- Improved behavior regarding invalid filenames; in particular, on Windows,
+  `wimlib-imagex` will, when extracting, now omit (with an option to override
+  this default) filenames differing only in case, or filenames containing
+  characters not valid on Windows.
+
+- On Windows, wimlib now supports capturing and extracting long paths (longer
+  than the so-called `MAX_PATH`).
+
+- On Windows, `wimlib-imagex update` now acquires proper privileges when running
+  as an Administrator.
+
+- `wimlib-imagex update` will now complain if no image is specified when trying
+  to update a multi-image WIM.
+
+- `wimlib-imagex update` now supports specifying a single update command
+  directly on the command line using the `--command` option.
+
+- `wimlib-imagex` will now choose different units for progress messages,
+  depending on the amount of data that needs to be processed.
+
+- `wimlib-imagex append` will now generate a unique WIM image name if no name is
+  specified and the defaulted name already exists in the WIM.
+
+- wimlib now allows you to create unnamed WIM images, which can then only be
+  referred to by index.
+
+- wimlib now allows you to explicitly declare you want write access to a WIM by
+  providing the `WIMLIB_OPEN_FLAG_WRITE_ACCESS` flag to `wimlib_open_wim()`.
+
+- wimlib now respects the `WIM_HDR_FLAG_READONLY` flag when set in the WIM header.
+
+- Progress callbacks have been added to wimlib's `wimlib_update_image()`
+  function.
+
+- Added `wimlib_get_wim_info()`, `wimlib_set_wim_info()`,
+  `wimlib_iterate_dir_tree()`, and `wimlib_iterate_lookup_table()` functions to
+  the library.
+
+- NTFS-3G capture now only warns about two conditions previously treated as
+  errors.
+
+- Fixed a couple issues with using `wimlib-imagex` on UDF filesystems on
+  Windows.
+
+- wimlib now correctly detects and returns an error when reading a WIM image
+  with a cyclic directory structure.  (Fun fact: such a WIM will crash
+  Microsoft's software.)
+
+## Version 1.4.0
+
+- Added new "extract" and "update" subcommands to `wimlib-imagex`, along with
+  associated APIs in the library.  These commands are intended mainly for
+  Windows use but can be used on UNIX as well.
+
+- Many documentation improvements.
+
+- Fixed a bug in the Windows build where relative symbolic links were not
+  captured in reparse-point fixup mode.
+
+- Fixed a bug in the Windows build where file handles were left open to the WIM
+  file, causing `wimlib-imagex optimize` to fail in some cases.
+
+- Fixed a bug in the Windows build of `wimlib-imagex` where globbing split-WIM
+  parts could cause the program to crash.
+
+- Fixed a bug where the creation time of WIM images would be shown instead of
+  the last modification time.
+
+- With the Windows build it is now possible to restore a WIM containing symbolic
+  links as a non-Administrator; however you will receive warnings about not
+  being able to extract the symbolic links.
+
+## Version 1.3.3
+
+- Capturing a WIM image should now be significantly faster in most cases due to
+  improved use of the operating system's cache and avoiding reading files twice
+  whenever possible.
+
+- The Windows build should now work on Windows XP.
+
+- The Windows build now supports capturing and restoring hidden, compressed,
+  sparse, and encrypted files.
+
+- The Windows build now supports capturing and applying WIM images from
+  filesystems other than NTFS (with some reduced functionality).
+
+- The Windows build now extracts short names correctly.
+
+- Added support for "reparse-point" fixups (i.e. fixing up of symbolic links).
+  See docs for `--rpfix` and `--norpfix` flags of `wimlib-imagex capture` and
+  `wimlib-imagex apply`.
+
+- The performance of splitting and joining WIMs should be slightly improved.
+
+- The LZX and XPRESS compression and decompression functions are now exported
+  from the library.
+
+## Version 1.3.2
+
+- Improvements and bugfixes for the Windows build.
+
+- Added `--strict-acls` options.
+
+- Fixed the way that wimlib determines the order of images in the WIM.
+
+## Version 1.3.1
+
+- Since wimlib can now be used on Windows, wimlib's implementation of ImageX has
+  been renamed to `wimlib-imagex` to avoid confusion with Microsoft's
+  implementation of ImageX, which would have the same file name (`imagex.exe`).
+  If you really don't like this you can pass the `--with-imagex-progname` option
+  to `configure` to choose a different name, or even simply rename the binary
+  yourself (but the former way will configure the man pages to use the chosen
+  name).
+
+- Various bugs fixed in the Windows build.  Mainly to do with capturing and
+  restoring alternate data streams correctly in weird cases, and requesting the
+  correct privileges when opening files.  Also added the `--noacls` options to
+  `wimlib-imagex` capture, append, and apply.
+
+- Windows build again: `FindFirstStreamW()` and `FindNextStreamW()` are now
+  dynamically loaded, so this may make the library compatible with Windows XP
+  (however, there may still be other problems).
+
+## Version 1.3.0
+
+- Added experimental support for native Windows builds.  Binaries can be
+  downloaded from the SourceForge page.
+
+- `--source-list` option added to `imagex capture` and `imagex append`.
+
+- Better support for different character encodings.
+
+## Version 1.2.6
+
+- Storing UNIX file owners, groups, and modes in WIM images is now
+- possible using `imagex capture` with the `--unix-data` flag.
+
+- Minor bug fixes and documentation fixes.
+
+## Version 1.2.5
+
+- NTFS capture: Fixed capturing duplicate reparse points.
+
+- NTFS capture: Capture first unnamed stream if there are more than one (print
+  warning instead of error).
+
+- Allow multiple test cases to execute concurrently (e.g. `make -j2 check`).
+
+## Version 1.2.4
+
+- Added `--arch` switch to mkwinpeimg script to support getting AMD64 WinPE from
+  the WAIK.
+
+- Update to work with ntfs-3g version 2013.1.13.
+
+## Version 1.2.3
+
+- Fixed truncating file to shorter but non-zero length on read-write mounted WIM
+  image.
+
+- Various code cleanups and minor documentation fixes.
+
+## Version 1.2.2
+
+- LZX and XPRESS decompression have received some additional optimizations and
+  should now be even faster.  (Although, they were already pretty fast --- much
+  faster than typical I/O speeds.)
+
+- Fixed a bug introduced in v1.2.1 that would cause a directory tree containing
+  hard links to be captured incorrectly in some cases.
+
+## Version 1.2.1
+
+- By default, unmounting a read-write mounted WIM with `imagex unmount --commit`
+  will now change the WIM in-place without needing to write the entire WIM
+  again.  Use `imagex unmount --commit --rebuild` to get the old behavior.
+
+- `imagex unmount` no longer has a hard-coded limit of 10 minutes to wait for a
+  response from the daemon servicing the mounted WIM.  Instead, every second
+  `imagex unmount` will check if the daemon is still alive, and keep waiting if
+  so, otherwise terminate with an error.
+
+- `imagex unmount --commit` on a read-write mounted WIM will now print progress
+  information regarding the writing of new or modified streams the WIM, just
+  like when capturing or appending a WIM.
+
+- A small change has been made to XPRESS compression and it should improve the
+  compression ratio slightly.
+
+- A change was made that may improve performance slightly when applying a WIM
+  image to a NTFS volume.
+
+- Microsoft has managed to introduce even more bugs into their software, and now
+  the WIMs for Windows 8 have incorrect (too low) reference counts for some
+  streams.  This is unsafe because such streams can be removed when they are in
+  actuality still referenced in the WIM (perhaps by a different image).  wimlib
+  will now work around this problem by fixing the stream reference counts.  This
+  is only done when `wimlib_delete_image()` is called (`imagex delete`) or when
+  `wimlib_mount_image()` is called with `WIMLIB_MOUNT_FLAG_READWRITE` (`imagex
+  mountrw`).  Please note that this requires reading the metadata for all images
+  in the WIM, so this will make these operations noticably slower on WIMs with
+  multiple images.
+
+- Various other bugfixes.
+
+## Version 1.2.0
+
+- Appending images to a WIM is now be done by default without re-building the
+  whole WIM.  Use the `--rebuild` flag to get the old behavior (which was to
+  re-build the entire WIM when a new image is appended).
+
+- A new command `imagex optimize` is now available to manually re-build a WIM
+  that has wasted space due to repeated appends.
+
+- Progress information has been improved, and now arbitrary callback functions
+  can be used to show the progress of a WIM operation.
+
+- A possible bug with changing the bootable image of a WIM was fixed.
+
+- Some advisory locking is now done to prevent two processes from modifying a
+  WIM at the same time (but only in some cases).  For example, you cannot mount
+  two images from a WIM read-write at the same time.
+
+- Some functions have been reorganized:
+  - `wimlib_mount()` renamed to `wimlib_mount_image()`.
+  - `wimlib_unmount()` renamed to `wimlib_unmount_image()`.
+  - `wimlib_overwrite_xml_and_header()` removed as `wimlib_overwrite()` suffices
+    now.
+  - `wimlib_apply_image_to_ntfs_volume()` removed as `wimlib_extract_image()`
+    suffices now.
+  - `wimlib_add_image_from_ntfs_volume()` removed as `wimlib_add_image()`
+    suffices now.
+
+- Previously, the soname of libwim.so has been 0.0.0, despite many interface
+  changes.  The soname is now updated to 1.0.0 and will now be updated each
+  release.
+
+## Version 1.1.0
+
+- Resources will now be compressed using multiple threads by default.  (This
+  applies to `imagex capture`, `imagex append`, and `imagex export`).
+
+- Some performance improvements in mounted WIMs.
+
+- More progress information is shown when capturing a WIM.
+
+## Version 1.0.4
+
+- Lots of minor fixes, code cleanups, and some documentation updates.  Nothing
+  in particular is really noteworthy.
+
+## Version 1.0.3
+
+- LZX and XPRESS compression improvements.
+
+- Fixed calculation of Directory Count, File Count, Total Bytes, and Hard Link
+  Bytes of the WIM.
+
+## Version 1.0.2
+
+- Fixed bug when capturing NTFS file with multiple named data streams.
+
+- Internally, we are now using inode structures, even though these don't appear
+  literally in the WIM file.  This simplifies some of the code (mainly for WIM
+  mounting) and likely fixed a few problems, although it needs more testing.
+
+## Version 1.0.1
+
+- Fixed problem when exporting images from XPRESS to LZX compressed WIM or vice
+  versa
+
+## Version 1.0.0
+
+- Enough changes to call it version 1.0.0!
+
+- Capturing a WIM directly from a NTFS volume, and applying a WIM directly to a
+  NTFS volume, is now supported.
+
+- Hard links and symbolic links have much improved support.  They are supported
+  for WIM capture, WIM application, and mounted WIMs (you can even make them on
+  read-write mounted WIMs).
+
+- Alternate data streams are now supported on mounted WIMs through an xattr or a
+  Windows-style stream interface.  Also they are supported when capturing a WIM
+  from NTFS or applying a WIM to NTFS.
+
+- Split WIMs are better supported.  You may now apply an image directly from a
+  split WIM, mount an image from a split WIM read-only, or export an image from
+  a split WIM.
+
+- Using a capture configuration file is now supported (but not fully yet).
+
+- SHA1 message digests are checked in more places, so we can make sure applied
+  and captured data is correct.
+
+- Man pages have been updated and consolidated.
+
+## Version 0.7.2
+
+- Fixed segfault when unmounting read-only WIM.
+
+## Version 0.7.1
+
+- Support for joining and splitting WIMs.
+
+- Also, security data is now preserved by default.
+
+## Version 0.6.3
+
+- Can now build with older gcc and system headers, like on CentOS 5.
+
+## Version 0.6.2
+
+- Fixed bug that made it impossible to overwrite files in read-write mount.
+
+## Version 0.6.1
+
+- Write byte-order mark before WIM XML data.  (`imagex.exe` requires this to be
+  there.)
diff --git a/README b/README
deleted file mode 100644 (file)
index 0d105ce..0000000
--- a/README
+++ /dev/null
@@ -1,283 +0,0 @@
-                                  INTRODUCTION
-
-This is wimlib version 1.13.1 (May 2019).  wimlib is a C library for
-creating, modifying, extracting, and mounting files in the Windows Imaging
-Format (WIM files).  wimlib and its command-line frontend 'wimlib-imagex'
-provide a free and cross-platform alternative to Microsoft's WIMGAPI, ImageX,
-and DISM.
-
-                                  INSTALLATION
-
-To install wimlib and wimlib-imagex on UNIX-like systems, you can compile from
-source (e.g. './configure && make && sudo make install').  Alternatively, check
-if a package has already been prepared for your operating system.  Example files
-for Debian and RPM packaging are in the debian/ and rpm/ directories.
-
-To install wimlib and wimlib-imagex on Windows, just download and extract the
-ZIP file containing the latest binaries.  See README.WINDOWS for more details.
-
-All official wimlib releases are available from https://wimlib.net.
-
-                                   WIM FILES
-
-A Windows Imaging (WIM) file is an archive designed primarily for archiving
-Windows filesystems.  However, it can be used on other platforms as well, with
-some limitations.  Like some other archive formats such as ZIP, files in WIM
-archives may be compressed.  WIM archives support multiple compression formats,
-including LZX, XPRESS, and LZMS.  All these formats are supported by wimlib.
-
-A WIM archive contains one or more "images", each of which is a logically
-independent directory tree.  Each image has a 1-based index and usually a name.
-
-WIM archives provide data deduplication at the level of full file contents.  In
-other words, each unique "file contents" is only stored once in the archive,
-regardless of how many files have that contents across all images.
-
-A WIM archive may be either stand-alone or split into multiple parts.
-
-An update of the WIM format --- first added by Microsoft for Windows 8 ---
-supports solid-mode compression.  This refers to files being compressed together
-(e.g. as in a .tar.xz or .7z archive) rather than separately (e.g. as in a .zip
-archive).  This usually produces a much better compression ratio.  Solid
-archives are sometimes called "ESD files" by Microsoft and may have the ".esd"
-file extension rather than ".wim".  They are supported in wimlib since v1.6.0.
-
-                             IMAGEX IMPLEMENTATION
-
-wimlib itself is a C library, and it provides a documented public API (See:
-https://wimlib.net/apidoc) for other programs to use.  However, it is also
-distributed with a command-line program called "wimlib-imagex" that uses this
-library to implement an imaging tool similar to Microsoft's ImageX.
-wimlib-imagex supports almost all the capabilities of Microsoft's ImageX as well
-as additional capabilities.  wimlib-imagex works on both UNIX-like systems and
-Windows, although some features differ between the platforms.
-
-Run `wimlib-imagex' with no arguments to see an overview of the available
-commands and their syntax.  Note that the commands have both long and short
-forms, e.g. `wimlib-imagex apply' is equivalent to `wimapply'.  For additional
-documentation:
-
-  * If you have installed wimlib-imagex on a UNIX-like system, you will find
-    further documentation in the man pages; run `man wimlib-imagex' to get
-    started.
-
-  * If you have downloaded the Windows binary distribution, you will find the
-    documentation for wimlib-imagex in PDF format in the "doc" directory.  Note
-    that although the documentation is written in the style of UNIX manual
-    pages, it does document Windows-specific behavior when relevant.
-
-                                  COMPRESSION
-
-wimlib (and wimlib-imagex) can create XPRESS, LZX, and LZMS compressed WIM
-archives.  wimlib's compression codecs usually outperform and outcompress their
-closed-source Microsoft equivalents.  Multiple compression levels and chunk
-sizes as well as solid mode compression are supported.  Compression is
-multithreaded by default.  Detailed benchmark results and descriptions of the
-algorithms used can be found at https://wimlib.net/compression.html.
-
-                                  NTFS SUPPORT
-
-WIM images may contain data, such as named data streams and
-compression/encryption flags, that are best represented on the NTFS filesystem
-used on Windows.  Also, WIM images may contain security descriptors which are
-specific to Windows and cannot be represented on other operating systems.
-wimlib handles this NTFS-specific or Windows-specific data in a
-platform-dependent way:
-
-  * In the Windows version of wimlib and wimlib-imagex, NTFS-specific and
-    Windows-specific data are supported natively.
-
-  * In the UNIX version of wimlib and wimlib-imagex, NTFS-specific and
-    Windows-specific data are ordinarily ignored; however, there is also special
-    support for capturing and extracting images directly to/from unmounted NTFS
-    volumes.  This was made possible with the help of libntfs-3g from the
-    NTFS-3G project.
-
-For both platforms the code for NTFS capture and extraction is complete enough
-that it is possible to apply an image from the "install.wim" contained in recent
-Windows installation media (Vista or later) directly to an NTFS filesystem, and
-then boot Windows from it after preparing the Boot Configuration Data.  In
-addition, a Windows installation can be captured (or backed up) into a WIM file,
-and then re-applied later.
-
-                                   WINDOWS PE
-
-wimlib can also be used to create customized images of Windows PE on either
-UNIX-like systems or Windows.  Windows PE (Preinstallation Environment) is a
-lightweight version of Windows that runs entirely from memory and can be used to
-perform maintenance or to install Windows.  It is the operating system that runs
-when you boot from the Windows installation media.
-
-A copy of Windows PE can be found on the installation media for Windows (Vista
-or later) as the file `sources/boot.wim', or in the Windows Automated
-Installation Kit (WAIK), which is free to download from Microsoft.
-
-A shell script `mkwinpeimg' is provided with wimlib on UNIX-like systems to
-simplify the process of creating and customizing a bootable Windows PE image,
-sourcing the needed files from the Windows installation media or from the WAIK.
-
-                                  DEPENDENCIES
-
-This section documents the dependencies of wimlib and the programs distributed
-with it, when building for a UNIX-like system from source.  If you have
-downloaded the Windows binary distribution of wimlib and wimlib-imagex then all
-dependencies were already included and this section is irrelevant.
-
-* libxml2 (required)
-       This is a commonly used free library to read and write XML documents.
-       Almost all Linux distributions should include this; however, you may
-       need to install the header files, which might be in a package named
-       "libxml2-dev" or similar.  For more information see http://xmlsoft.org/.
-
-* libfuse (optional but recommended)
-       Unless configured --without-fuse, wimlib requires a non-ancient version
-       of libfuse.  Most Linux distributions already include this, but make
-       sure you have the libfuse package installed, and also libfuse-dev if
-       your distribution distributes header files separately.  FUSE also
-       requires a kernel module.  If the kernel module is available it should
-       automatically be loaded if you try to mount a WIM image.  For more
-       information see http://fuse.sourceforge.net/.
-
-* libntfs-3g (optional but recommended)
-       Unless configured --without-ntfs-3g, wimlib requires the library and
-       headers for libntfs-3g to be installed.  The minimum required version is
-       2011-4-12, but newer versions contain important bug fixes.
-
-* OpenSSL / libcrypto (optional)
-       wimlib can use the SHA-1 message digest implementation from libcrypto
-       (usually provided by OpenSSL) instead of compiling in yet another SHA-1
-       implementation.
-
-* cdrkit (optional)
-* mtools (optional)
-* syslinux (optional)
-* cabextract (optional)
-       The `mkwinpeimg' shell script will look for several other programs
-       depending on what options are given to it.  Depending on your Linux
-       distribution, you may already have these programs installed, or they may
-       be in the software repository.  Making an ISO filesystem requires
-       `mkisofs' from `cdrkit' (http://www.cdrkit.org).  Making a disk image
-       requires `mtools' (http://www.gnu.org/software/mtools) and `syslinux'
-       (http://www.syslinux.org).  Retrieving files from the Windows Automated
-       Installation Kit requires `cabextract' (http://www.cabextract.org.uk).
-
-                                 CONFIGURATION
-
-This section documents the most important options that may be passed to the
-"configure" script when building from source:
-
---without-ntfs-3g
-       If libntfs-3g is not available or is not version 2011-4-12 or later,
-       wimlib can be built without it, in which case it will not be possible to
-       capture or apply WIM images directly from/to NTFS volumes.
-
-       The default is --with-ntfs-3g when building for any UNIX-like system,
-       and --without-ntfs-3g when building for Windows.
-
---without-fuse
-       The --without-fuse option disables support for mounting WIM images.
-       This removes dependencies on libfuse and librt.  The wimmount,
-       wimmountrw, and wimunmount commands will not work.
-
-       The default is --with-fuse when building for Linux, and --without-fuse
-       otherwise.
-
---without-libcrypto
-       Build in functions for SHA-1 rather than using external SHA-1 functions
-       from libcrypto (usually provided by OpenSSL).
-
-       The default is to use libcrypto if it is found on your system.
-
-                                  PORTABILITY
-
-wimlib works on both UNIX-like systems (Linux, Mac OS X, FreeBSD, etc.) and
-Windows (XP and later).
-
-As much code as possible is shared among all supported platforms, but there
-necessarily are some differences in what features are supported on each platform
-and how they are implemented.  Most notable is that file tree scanning and
-extraction are implemented separately for Windows, UNIX, and UNIX (NTFS-3G
-mode), to ensure a fast and feature-rich implementation of each platform/mode.
-
-wimlib is mainly used on x86 and x86_64 CPUs, but it should also work on a
-number of other GCC-supported 32-bit or 64-bit architectures.  It has been
-tested on the ARM and MIPS architectures.
-
-Currently, gcc and clang are the only supported compilers.  A few nonstandard
-extensions are used in the code.
-
-                                   REFERENCES
-
-The WIM file format is partially specified in a document that can be found in
-the Microsoft Download Center.  However, this document really only provides an
-overview of the format and is not a formal specification.  It also does not
-cover later extensions of the format, such as solid resources.
-
-With regards to the supported compression formats:
-
-- Microsoft has official documentation for XPRESS that is of reasonable quality.
-- Microsoft has official documentation for LZX, but in two different documents,
-  neither of which is completely applicable to its use in the WIM format, and
-  the first of which contains multiple errors.
-- There does not seem to be any official documentation for LZMS, so my comments
-  and code in src/lzms_decompress.c may in fact be the best documentation
-  available for this particular compression format.
-
-The algorithms used by wimlib's compression and decompression codecs are
-inspired by a variety of sources, including open source projects and computer
-science papers.
-
-The code in ntfs-3g_apply.c and ntfs-3g_capture.c uses the NTFS-3G library,
-which is a library for reading and writing to NTFS filesystems (the filesystem
-used by recent versions of Windows).  See
-http://www.tuxera.com/community/open-source-ntfs-3g/ for more information.
-
-A limited number of other free programs can handle some parts of the WIM
-file format:
-
-  * 7-Zip is able to extract and create WIMs (as well as files in many
-    other archive formats).  However, wimlib is designed specifically to handle
-    WIM files and provides features previously only available in Microsoft's
-    implementation, such as the ability to mount WIMs read-write as well as
-    read-only, the ability to create compressed WIMs, the correct handling of
-    security descriptors and hard links, support for LZMS compression, and
-    support for solid archives.
-  * ImagePyX (https://github.com/maxpat78/ImagePyX) is a Python program that
-    provides some capabilities of wimlib-imagex, with the help of external
-    compression codecs.
-
-If you are looking for an archive format that provides features similar to WIM
-but was designed primarily for UNIX, you may want to consider SquashFS
-(http://squashfs.sourceforge.net/).  However, you may find that wimlib works
-surprisingly well on UNIX.  It will store hard links and symbolic links, and it
-supports storing standard UNIX file permissions (owners, groups, and modes);
-special files such as device nodes and FIFOs; and extended attributes.
-Actually, I use it to back up my own files on Linux!
-
-                                    HISTORY
-
-wimlib was originally a project started by Carl Thijssen for use on Linux in the
-Ultimate Deployment Appliance (http://www.ultimatedeployment.org/).  Since then
-the code has been entirely rewritten and improved (main author: Eric Biggers).
-Windows support has been available since version 1.3.0 (March 2013).  A list of
-version-to-version changes can be found in the NEWS file.
-
-                                    NOTICES
-
-wimlib is free software that comes with NO WARRANTY, to the extent permitted by
-law.  See the COPYING file for more details.
-
-Bug reports, suggestions, and other contributions are appreciated and may be
-sent via email to ebiggers3@gmail.com or posted to https://wimlib.net/forums.
-
-wimlib is independently developed and does not contain any code, data, or files
-copyrighted by Microsoft.  It is not known to be affected by any patents.
-
-On UNIX-like systems, if you do not want wimlib to be dynamically linked with
-libcrypto (OpenSSL), configure with --without-libcrypto.  This replaces the SHA1
-implementation with built-in code and there will be no difference in
-functionality.
-
-Note: copyright years may be listed using range notation, e.g., 2012-2016,
-indicating that every year in the range, inclusive, is a copyrightable year that
-would otherwise be listed individually.
diff --git a/README.WINDOWS b/README.WINDOWS
deleted file mode 100644 (file)
index d778fb5..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-
-                                  INTRODUCTION
-
-wimlib is free and open source software that is available on both UNIX-like
-systems and Windows.  This file provides additional information specifically
-about the Windows version of wimlib and the command line tool "wimlib-imagex"
-that is distributed with it.  It does not obsolete the generic README.txt, which
-you should read too.
-
-                              WINDOWS DISTRIBUTION
-
-The Windows distribution of wimlib is a ZIP file containing the following items:
-
-  * wimlib-imagex.exe, a command-line tool to deal with WIM (.wim), split WIM
-    (.swm), and ESD (.esd) files that is inspired by Microsoft's ImageX and
-    DISM.  This is a ready-to-run executable and not an installer.
-
-  * Very short batch scripts (e.g. wimapply.cmd) which are shortcuts to the
-    corresponding wimlib-imagex commands (e.g. `wimlib-imagex apply').
-
-  * The library itself in DLL format (libwim-15.dll).  wimlib-imagex.exe
-    requires this to run.
-
-  * The documentation, including this file, the generic README.txt, and
-    PDF documentation for wimlib-imagex in the 'doc' directory.
-
-  * License files for all software included.  These are all free software
-    licenses.  COPYING.txt is the main license, and it refers to
-    COPYING.GPLv3.txt and COPYING.LGPLv3.txt.  The other licenses are for
-    third-party software included in the library.
-
-  * Development files in the 'devel' directory.  These are only needed if you
-    are developing C or C++ applications that use wimlib.
-
-Note that there are separate ZIP files for 32-bit (i686) and 64-bit (x86_64)
-binaries.  They are both fully supported, but you should prefer the 64-bit
-binaries when possible as they can be noticeably faster.
-
-                                 WIMLIB-IMAGEX
-
-wimlib-imagex supports most features of Microsoft's ImageX as well as some
-features that are supported by DISM but not by ImageX.  wimlib-imagex also
-supports some features that neither ImageX nor DISM support.  Some of the
-advantages of wimlib-imagex compared to ImageX and DISM are:
-
-  * wimlib-imagex provides "extract" and "update" commands which allow
-    you to quickly work with WIM images without mounting them.
-
-  * wimlib-imagex provides an easy-to-use "optimize" command which removes
-    wasted space from a WIM file and optionally recompresses it with stronger
-    compression.
-
-  * wimlib includes advanced implementations of all compression algorithms used
-    in WIM files.  They usually outperform and outcompress their Microsoft
-    equivalents.
-
-  * wimlib-imagex supports solid WIM files and LZMS compression, for example as
-    used in ESD (.esd) files.  (These are partially supported by recent DISM
-    versions but not by ImageX.)
-
-  * wimlib-imagex supports imaging a live Windows system.  Just use the
-    --snapshot option.
-
-  * In many cases, wimlib-imagex has simpler command-line syntax than either
-    ImageX or DISM.
-
-  * Whenever possible, wimlib-imagex includes improved documentation and
-    informational output compared to Microsoft's software.
-
-  * wimlib and wimlib-imagex are free software, so you can modify and/or audit
-    the source code.
-
-However, some limitations of wimlib-imagex compared to ImageX and DISM are:
-
-  * On Windows, wimlib-imagex does not support mounting WIM images.
-
-  * wimlib-imagex has no awareness of Windows "packages".
-
-                                ADDITIONAL NOTES
-
-It's recommended to use wimlib-imagex in scripts to avoid having to
-interactively enter commands.  However, note that wimlib-imagex is largely just
-a command-line front-end for wimlib, and it's possible to use wimlib's API in
-other front-ends or applications.  Currently there is no official graphical user
-interface available for wimlib or wimlib-imagex.  However, an unofficial, beta,
-Windows-only graphical user interface that provides a thin wrapper around
-wimlib-imagex can be downloaded at
-http://reboot.pro/files/file/485-wimlib-imagex-command-line-compiler/.
-
-                              BUILDING FROM SOURCE
-
-As with other open source software, advanced users may choose to build wimlib
-from source, potentially with customizations.  Although wimlib's build system is
-designed for UNIX-like systems and is easiest to use on Linux, it's possible to
-build Windows binaries on Windows using Cygwin with MinGW.  To do this, follow
-the instructions below.  For the sake of example, I'll assume you are building a
-64-bit version of wimlib v1.13.0.
-
-Run the Cygwin installer, available from https://www.cygwin.com/setup-x86.exe.
-When you get to the package selection screen, choose the following additional
-packages from category "Devel":
-
-    - make
-    - mingw64-x86_64-binutils
-    - mingw64-x86_64-gcc-g++
-    - mingw64-x86_64-libxml2
-    - mingw64-x86_64-pkg-config
-    - mingw64-x86_64-winpthreads
-
-Download wimlib's source code from https://wimlib.net/downloads/wimlib-1.13.0.tar.gz.
-
-Start a Cygwin terminal and run the following commands:
-
-    cd /cygdrive/c/Users/example/Downloads # (or wherever you downloaded the source to)
-    tar xf wimlib-1.13.0.tar.gz
-    cd wimlib-1.13.0
-    ./configure --host=x86_64-w64-mingw32
-    make
-
-If successful, the new binaries "libwim-15.dll" and "wimlib-imagex.exe" will
-have been produced in the .libs directory.
-
-By default the binaries are built with debug symbols.  If desired, you can use
-x86_64-w64-mingw32-strip to strip them.
-
-libwim-15.dll will be linked to several other DLLs which you will need as well:
-
-    - libwinpthread-1.dll
-    - libxml2-2.dll, which also requires:
-        - iconv.dll
-        - liblzma-5.dll
-        - zlib1.dll
-
-These DLLs can be found in "C:\cygwin\usr\x86_64-w64-mingw32\sys-root\mingw\bin"
-and must be placed alongside libwim-15.dll for it to run portably.  But see
-below for an alternative.
-
-Building 32-bit binaries is very similar, but you'll need to replace "x86_64"
-with "i686" everywhere in the above instructions, and libwim-15.dll will also
-depend on libgcc_s_sjlj-1.dll.  Note that you can build both 32-bit and 64-bit
-binaries from the same Cygwin installation, provided that you install both the
-mingw64-i686-* and mingw64-x86_64-* packages; and you can run the Cygwin setup
-program to install more packages at any time.
-
-In the official binary releases from wimlib.net, libwim-15.dll's dependent
-libraries are linked in statically rather than dynamically, so it does not
-depend on any DLLs other than standard Windows DLLs.  If you want to do this,
-install the following additional Cygwin packages:
-
-    - p7zip         (category "Archiver")
-    - autoconf      (category "Devel")
-    - automake      (category "Devel")
-    - git           (category "Devel")
-    - libtool       (category "Devel")
-    - nasm          (category "Devel")
-    - pkg-config    (category "Devel")
-    - ghostscript   (category "Graphics")
-    - wget          (category "Web")
-
-Then, in a Cygwin terminal, clone the git repository, checkout the wimlib
-version you want, bootstrap the repository, and run the Windows release script:
-
-    git clone git://wimlib.net/wimlib
-    cd wimlib
-    git checkout v1.13.0
-    ./bootstrap
-    ./tools/make-windows-release x86_64
-
-The release script will download and build libxml2 and winpthreads as static
-libraries, then build wimlib, then do some final tasks and bundle the resulting
-files up into a ZIP archive.  If successful you'll end up with a file like
-"wimlib-1.13.0-windows-x86_64-bin.zip", just like the official releases.  For
-32-bit binaries just use "i686" instead of "x86_64".
diff --git a/README.WINDOWS.md b/README.WINDOWS.md
new file mode 100644 (file)
index 0000000..aa3cf32
--- /dev/null
@@ -0,0 +1,133 @@
+# Introduction
+
+wimlib is free and open source software that is available on both UNIX-like
+systems and Windows.  This file provides additional information specifically
+about the Windows version of wimlib and the command line tool `wimlib-imagex`
+that is distributed with it.  It does not obsolete the generic
+[README](README.md), which you should read too.
+
+# Windows distribution
+
+The Windows distribution of wimlib is a ZIP file containing the following items:
+
+- `wimlib-imagex.exe`, a command-line tool to deal with WIM (.wim), split WIM
+  (.swm), and ESD (.esd) files that is inspired by Microsoft's ImageX and DISM.
+  This is a ready-to-run executable and not an installer.
+
+- Very short batch scripts (e.g. `wimapply.cmd`) which are shortcuts to the
+  corresponding `wimlib-imagex` commands (e.g. `wimlib-imagex apply`).
+
+- The library itself in DLL format (`libwim-15.dll`).  `wimlib-imagex.exe`
+  requires this to run.
+
+- The documentation, including this file, the generic README, and PDF
+  documentation for `wimlib-imagex` in the `doc` folder.
+
+- License files for all software included.  These are all free software
+  licenses.  `COPYING.txt` is the main license, and it refers to
+  `COPYING.GPLv3.txt` and `COPYING.LGPLv3.txt`.  The other licenses are for
+  third-party software included in the library.
+
+- Development files in the `devel` folder.  These are only needed if you are
+  developing C or C++ applications that use wimlib.
+
+Note that there are separate ZIP files for 32-bit `i686` and 64-bit `x86_64`
+binaries.  They are both fully supported, but you should prefer the 64-bit
+binaries when possible as they can be noticeably faster.
+
+# wimlib-imagex
+
+`wimlib-imagex` supports most features of Microsoft's ImageX as well as some
+features that are supported by DISM but not by ImageX.  wimlib-imagex also
+supports some features that neither ImageX nor DISM support.  Some of the
+advantages of `wimlib-imagex` compared to ImageX and DISM are:
+
+- `wimlib-imagex` provides "extract" and "update" commands which allow you to
+  quickly work with WIM images without mounting them.
+
+- `wimlib-imagex` provides an easy-to-use "optimize" command which removes
+  wasted space from a WIM file and optionally recompresses it with stronger
+  compression.
+
+- wimlib includes advanced implementations of all compression algorithms used in
+  WIM files.  They usually outperform and outcompress their Microsoft
+  equivalents.
+
+- `wimlib-imagex` supports solid WIM files and LZMS compression, for example as
+  used in ESD (.esd) files.  (These are partially supported by recent DISM
+  versions but not by ImageX.)
+
+- `wimlib-imagex` supports imaging a live Windows system.  Just use the
+  `--snapshot` option.
+
+- In many cases, `wimlib-imagex` has simpler command-line syntax than either
+  ImageX or DISM.
+
+- Whenever possible, `wimlib-imagex` includes improved documentation and
+  informational output compared to Microsoft's software.
+
+- wimlib and `wimlib-imagex` are free software, so you can modify and/or audit
+  the source code.
+
+However, some limitations of `wimlib-imagex` compared to ImageX and DISM are:
+
+- On Windows, `wimlib-imagex` does not support mounting WIM images.
+
+- `wimlib-imagex` has no awareness of Windows "packages".
+
+# Additional notes
+
+It's recommended to use `wimlib-imagex` in scripts to avoid having to
+interactively enter commands.  However, note that `wimlib-imagex` is largely
+just a command-line front-end for wimlib, and it's possible to use wimlib's API
+in other front-ends or applications.  Currently there is no official graphical
+user interface available for wimlib or `wimlib-imagex`.  However,
+[Wimlib-clc](https://reboot.pro/files/file/588-wimlib-clc/) is an unofficial,
+Windows-only graphical user interface for `wimlib-imagex`.
+
+# Building from source
+
+As with other open source software, advanced users may choose to build wimlib
+from source, potentially with customizations.  Currently, wimlib depends on
+MinGW-w64 for its Windows support; Visual Studio is not supported.  The Windows
+binaries can be cross-compiled on Linux, or built on Windows using MSYS2 or
+Cygwin.  The following instructions show the MSYS2 method.
+
+First, install MSYS2 by running the installer from
+[msys2.org](https://www.msys2.org).
+
+Then, open any MSYS2 shell and run the following command:
+
+    pacman -Syu --noconfirm
+
+After that, open any MSYS2 shell again and run the following commands:
+
+    pacman -Syu --noconfirm git
+    git clone https://wimlib.net/git/wimlib
+
+Note: By default the git repository will be on the `master` branch, which is the
+latest development snapshot.  Optionally, you can check out a specific version,
+e.g. `cd wimlib && git checkout v1.14.4`.  For old versions, please refer to the
+documentation for that version, as things may have changed.  It is also possible
+to use a release tarball (e.g. `wimlib-1.14.4.tar.gz`) instead of the git repo.
+
+Finally, to actually do a build, close the MSYS2 shell you have open, then open
+one of the following from the Start menu:
+
+- "MSYS2 MINGW64" - for `x86_64` binaries, built with gcc
+- "MSYS2 CLANG64" - for `x86_64` binaries, built with clang
+- "MSYS2 MINGW32" - for `i686` binaries, built with gcc
+- "MSYS2 CLANG32" - for `i686` binaries, built with clang
+- "MSYS2 CLANGARM64" - for ARM64 binaries (EXPERIMENTAL)
+
+(If unsure, use "MSYS2 MINGW64".)  Then run the following commands:
+
+    cd wimlib
+    tools/windows-build.sh --install-prerequisites
+
+The script will automatically download and install the packages needed to build
+wimlib in the chosen MSYS2 environment, then build wimlib.  The output will be
+in a folder named similarly to `wimlib-1.14.4-windows-x86_64-bin`.  Note that
+your "home" folder within MSYS2 is `C:\msys64\home\%USERNAME%` by default.
+Therefore, the full path to the output folder will be similar to
+`C:\msys64\home\%USERNAME%\wimlib\wimlib-1.14.4-windows-x86_64-bin`.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..49b6f62
--- /dev/null
+++ b/README.md
@@ -0,0 +1,281 @@
+# Introduction
+
+This is wimlib version 1.14.4 (February 2024).  wimlib is a C library for
+creating, modifying, extracting, and mounting files in the Windows Imaging
+Format (WIM files).  wimlib and its command-line frontend `wimlib-imagex`
+provide a free and cross-platform alternative to Microsoft's WIMGAPI, ImageX,
+and DISM.
+
+For the release notes, see the [NEWS file](NEWS.md).
+
+# Table of Contents
+
+- [Installation](#installation)
+  - [Windows](#windows)
+  - [UNIX-like systems](#unix-like-systems)
+    - [Installing distro package](#installing-distro-package)
+    - [Building from source](#building-from-source)
+- [WIM files](#wim-files)
+- [ImageX implementation](#imagex-implementation)
+- [Compression](#compression)
+- [NTFS support](#ntfs-support)
+- [Windows PE](#windows-pe)
+- [Portability](#portability)
+- [References](#references)
+- [History](#history)
+- [Notices](#notices)
+
+# Installation
+
+## Windows
+
+To install wimlib and `wimlib-imagex` on Windows, just download and extract the
+ZIP file containing the latest binaries.  All official releases are available
+from [wimlib.net](https://wimlib.net).
+
+For more details, including directions for how to build from source on Windows
+if desired, see [README.WINDOWS.md](README.WINDOWS.md).
+
+## UNIX-like systems
+
+### Installing distro package
+
+To install wimlib and `wimlib-imagex` on UNIX-like systems, first consider just
+installing the package provided by your operating system, if there is one.
+
+For example, on Ubuntu and other Debian based systems, run:
+
+    sudo apt install wimtools
+
+On Fedora and other Red Hat based systems, run:
+
+    sudo dnf install wimlib-utils
+
+On Arch Linux, run:
+
+    sudo pacman -S wimlib
+
+### Building from source
+
+To build from source instead, first install the development files for libfuse3
+and libntfs-3g, if they're available for your operating system.  For example, on
+Ubuntu, run:
+
+    sudo apt install libfuse3-dev ntfs-3g-dev
+
+Then, if you're building from the git repository instead of from a release
+tarball, install additional build dependencies and run the bootstrap script:
+
+    sudo apt install autoconf automake libtool pkgconf
+    ./bootstrap
+
+Finally, configure, build, and install the software:
+
+    ./configure
+    make
+    sudo make install
+
+In addition to the standard options, the configure script accepts the following
+options:
+
+- `--without-fuse`:  Disables support for mounting WIM images.  The `wimmount`,
+  `wimmountrw`, and `wimunmount` commands won't work.  This removes the
+  dependency on libfuse3.
+
+- `--without-ntfs-3g`:  Disables support for capturing or applying WIM images
+  directly from/to NTFS volumes.  This removes the dependency on libntfs-3g.
+
+The `mkwinpeimg` shell script also has some optional dependencies that you can
+choose to install:
+
+- `cdrkit` (for making ISO filesystems)
+- `mtools` (for making disk images)
+- `syslinux` (for making disk images)
+- `cabextract` (for extracting files from the Windows Automated Installation Kit)
+
+Mounting WIM images also requires the FUSE kernel module.  When you try to mount
+a WIM image, the FUSE kernel module should be automatically loaded.  Mounting
+WIM images is only supported on Linux.
+
+# WIM files
+
+A Windows Imaging (WIM) file is an archive designed primarily for archiving
+Windows filesystems.  However, it can be used on other platforms as well, with
+some limitations.  Like some other archive formats such as ZIP, files in WIM
+archives may be compressed.  WIM archives support multiple compression formats,
+including LZX, XPRESS, and LZMS.  All these formats are supported by wimlib.
+
+A WIM archive contains one or more "images", each of which is a logically
+independent directory tree.  Each image has a 1-based index and usually a name.
+
+WIM archives provide data deduplication at the level of full file contents.  In
+other words, each unique "file contents" is only stored once in the archive,
+regardless of how many files have that contents across all images.
+
+A WIM archive may be either stand-alone or split into multiple parts.
+
+An update of the WIM format --- first added by Microsoft for Windows 8 ---
+supports solid-mode compression.  This refers to files being compressed together
+(e.g. as in a .tar.xz or .7z archive) rather than separately (e.g. as in a .zip
+archive).  This usually produces a much better compression ratio.  Solid
+archives are sometimes called "ESD files" by Microsoft and may have the ".esd"
+file extension rather than ".wim".  They are supported in wimlib since v1.6.0.
+
+# ImageX implementation
+
+wimlib itself is a C library, and it provides a [documented public
+API](https://wimlib.net/apidoc) for other programs to use.  However, it is also
+distributed with a command-line program called `wimlib-imagex` that uses this
+library to implement an imaging tool similar to Microsoft's `ImageX`.
+`wimlib-imagex` supports almost all the capabilities of Microsoft's `ImageX` as
+well as additional capabilities.  `wimlib-imagex` works on both UNIX-like
+systems and Windows, although some features differ between the platforms.
+
+Run `wimlib-imagex` with no arguments to see an overview of the available
+commands and their syntax.  Note that the commands have both long and short
+forms, e.g. `wimlib-imagex apply` is equivalent to `wimapply`.  For additional
+documentation:
+
+- If you have installed `wimlib-imagex` on a UNIX-like system, you will find
+  further documentation in the man pages; run `man wimlib-imagex` to get
+  started.
+
+- If you have downloaded the Windows binary distribution, you will find the
+  documentation for `wimlib-imagex` in PDF format in the `doc` directory.  Note
+  that although the documentation is written in the style of UNIX manual pages,
+  it does document Windows-specific behavior when relevant.
+
+# Compression
+
+wimlib (and `wimlib-imagex`) can create XPRESS, LZX, and LZMS compressed WIM
+archives.  wimlib's compression codecs usually outperform and outcompress their
+closed-source Microsoft equivalents.  Multiple compression levels and chunk
+sizes as well as solid mode compression are supported.  Compression is
+multithreaded by default.  Detailed benchmark results and descriptions of the
+algorithms used can be found at
+[wimlib.net](https://wimlib.net/compression.html).
+
+# NTFS support
+
+WIM images may contain data, such as named data streams and
+compression/encryption flags, that are best represented on the NTFS filesystem
+used on Windows.  Also, WIM images may contain security descriptors which are
+specific to Windows and cannot be represented on other operating systems.
+wimlib handles this NTFS-specific or Windows-specific data in a
+platform-dependent way:
+
+- In the Windows version of wimlib and `wimlib-imagex`, NTFS-specific and
+  Windows-specific data are supported natively.
+
+- In the UNIX version of wimlib and `wimlib-imagex`, NTFS-specific and
+  Windows-specific data are ordinarily ignored; however, there is also special
+  support for capturing and extracting images directly to/from unmounted NTFS
+  volumes.  This was made possible with the help of libntfs-3g from the NTFS-3G
+  project.
+
+For both platforms the code for NTFS capture and extraction is complete enough
+that it is possible to apply an image from the `install.wim` contained in recent
+Windows installation media (Vista or later) directly to an NTFS filesystem, and
+then boot Windows from it after preparing the Boot Configuration Data.  In
+addition, a Windows installation can be captured (or backed up) into a WIM file,
+and then re-applied later.
+
+# Windows PE
+
+wimlib can also be used to create customized images of Windows PE on either
+UNIX-like systems or Windows.  Windows PE (Preinstallation Environment) is a
+lightweight version of Windows that runs entirely from memory and can be used to
+perform maintenance or to install Windows.  It is the operating system that runs
+when you boot from the Windows installation media.
+
+A copy of Windows PE can be found on the installation media for Windows (Vista
+or later) as the file `sources/boot.wim`, or in the Windows Automated
+Installation Kit (WAIK), which is free to download from Microsoft.
+
+A shell script `mkwinpeimg` is provided with wimlib on UNIX-like systems to
+simplify the process of creating and customizing a bootable Windows PE image,
+sourcing the needed files from the Windows installation media or from the WAIK.
+
+# Portability
+
+wimlib works on both UNIX-like systems (Linux, Mac OS X, FreeBSD, etc.) and
+Windows (Vista and later).
+
+As much code as possible is shared among all supported platforms, but there
+necessarily are some differences in what features are supported on each platform
+and how they are implemented.  Most notable is that file tree scanning and
+extraction are implemented separately for Windows, UNIX, and UNIX (NTFS-3G
+mode), to ensure a fast and feature-rich implementation of each platform/mode.
+
+wimlib is mainly used on x86 and x86\_64 CPUs, but it should also work on a
+number of other GCC-supported 32-bit or 64-bit architectures.  It has been
+tested on the ARM and MIPS architectures.
+
+Currently, gcc and clang are the only supported compilers.  A few nonstandard
+extensions are used in the code.
+
+# References
+
+The WIM file format is partially specified in a document that can be found in
+the Microsoft Download Center.  However, this document really only provides an
+overview of the format and is not a formal specification.  It also does not
+cover later extensions of the format, such as solid resources.
+
+With regards to the supported compression formats:
+
+- Microsoft has official documentation for XPRESS that is of reasonable quality.
+- Microsoft has official documentation for LZX, but in two different documents,
+  neither of which is completely applicable to its use in the WIM format, and
+  the first of which contains multiple errors.
+- There does not seem to be any official documentation for LZMS, so my comments
+  and code in `src/lzms_decompress.c` may in fact be the best documentation
+  available for this particular compression format.
+
+The algorithms used by wimlib's compression and decompression codecs are
+inspired by a variety of sources, including open source projects and computer
+science papers.
+
+The code in `ntfs-3g_apply.c` and `ntfs-3g_capture.c` uses the [NTFS-3G
+library](https://github.com/tuxera/ntfs-3g), which is a library for reading and
+writing to NTFS filesystems (the filesystem used by recent versions of Windows).
+
+A limited number of other free programs can handle some parts of the WIM
+file format:
+
+- 7-Zip is able to extract and create WIMs (as well as files in many other
+  archive formats).  However, wimlib is designed specifically to handle WIM
+  files and provides features previously only available in Microsoft's
+  implementation, such as the ability to mount WIMs read-write as well as
+  read-only, the ability to create compressed WIMs, the correct handling of
+  security descriptors and hard links, and support for LZMS compression.
+
+- [`ImagePyX`](https://github.com/maxpat78/ImagePyX) is a Python program that
+  provides some capabilities of `wimlib-imagex`, with the help of external
+  compression codecs.
+
+If you are looking for an archive format that provides features similar to WIM
+but was designed primarily for UNIX, you may want to consider
+[SquashFS](https://docs.kernel.org/filesystems/squashfs.html).  However, you may
+find that wimlib works surprisingly well on UNIX.  It will store hard links and
+symbolic links, and it supports storing standard UNIX file permissions (owners,
+groups, and modes); special files such as device nodes and FIFOs; and extended
+attributes.  Actually, I use it to back up my own files on Linux!
+
+# History
+
+wimlib was originally a project started by Carl Thijssen for use on Linux in the
+[Ultimate Deployment Appliance](https://www.ultimatedeployment.org).  Since then
+the code has been entirely rewritten and improved (main author: Eric Biggers).
+Windows support has been available since version 1.3.0 (March 2013).  A list of
+version-to-version changes can be found in the [NEWS file](NEWS.md).
+
+# Notices
+
+wimlib is free software that comes with NO WARRANTY, to the extent permitted by
+law.  For full details, see the [COPYING file](COPYING).
+
+Bug reports, suggestions, and other contributions are appreciated and should be
+posted to [the forums](https://wimlib.net/forums/).
+
+wimlib is independently developed and does not contain any code, data, or files
+copyrighted by Microsoft.  It is not known to be affected by any patents.
index 618a22f59b3a88561750734ee5cfa5b0103f34ce..7c590a31ea7d35bc4823ef148f27ee1ee71f01e8 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -1,4 +1,3 @@
 #!/bin/sh
 
-touch build-aux/config.rpath
 autoreconf -i
diff --git a/build-aux/nasm_lt.sh b/build-aux/nasm_lt.sh
deleted file mode 100755 (executable)
index 6cd7329..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#! /bin/sh
-command=""
-infile=""
-o_opt=no
-pic=no
-while [ $# -gt 0 ]; do
-    case "$1" in
-        -DPIC|-fPIC|-fpic|-Kpic|-KPIC)
-            if [ "$pic" != "yes" ] ; then
-                command="$command -DPIC"
-                pic=yes
-            fi
-            ;;
-        -f|-fbin|-faout|-faoutb|-fcoff|-felf|-felf64|-fas86| \
-        -fobj|-fwin32|-fwin64|-frdf|-fieee|-fmacho|-fmacho64)
-            # it's a file format specifier for nasm.
-            command="$command $1"
-            ;;
-        -f*)
-            # maybe a code-generation flag for gcc.
-            ;;
-        -[Ii]*)
-            incdir=`echo "$1" | sed 's/^-[Ii]//'`
-            if [ "x$incdir" = x -a "x$2" != x ] ; then
-                case "$2" in
-                    -*) ;;
-                    *) incdir="$2"; shift;;
-                esac
-            fi
-            if [ "x$incdir" != x ] ; then
-                # In the case of NASM, the trailing slash is necessary.
-                incdir=`echo "$incdir" | sed 's%/*$%/%'`
-                command="$command -I$incdir"
-            fi
-            ;;
-        -o*)
-            o_opt=yes
-            command="$command $1"
-            ;;
-        *.asm)
-            infile=$1
-            command="$command $1"
-            ;;
-        *)
-            command="$command $1"
-            ;;
-    esac
-    shift
-done
-if [ "$o_opt" != yes ] ; then
-    # By default, NASM creates an output file
-    # in the same directory as the input file.
-    outfile="-o `echo $infile | sed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.o"
-    command="$command $outfile"
-fi
-echo $command
-exec $command
index 642c19b27a1a4996710d56a957641bf7af9654c8..21fec5bfe59481b21cabc77e6420a258ca2d2611 100644 (file)
@@ -1,6 +1,7 @@
 ###############################################################################
 
-AC_INIT([wimlib], [1.13.1], [ebiggers3@gmail.com])
+AC_INIT([wimlib], m4_esyscmd_s([tools/get-version-number.sh]),
+       [https://wimlib.net/forums/])
 AC_CONFIG_SRCDIR([src/wim.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
@@ -9,6 +10,7 @@ AM_SILENT_RULES([yes])
 AC_C_BIGENDIAN
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 LT_INIT
+PKG_PROG_PKG_CONFIG
 
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_FILES([Makefile] [doc/Doxyfile] [wimlib.pc])
@@ -35,7 +37,7 @@ mingw*)
        # Native Windows
        WINDOWS_NATIVE_BUILD="yes"
        # -D__MINGW_USE_VC2005_COMPAT: make time_t 64-bit on 32-bit Windows.
-       PLATFORM_CPPFLAGS="-D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D_CRT_NON_CONFORMING_SWPRINTFS -D__MINGW_USE_VC2005_COMPAT"
+       PLATFORM_CPPFLAGS="-D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D_CRT_NON_CONFORMING_SWPRINTFS -D__MINGW_USE_VC2005_COMPAT -D_WIN32_WINNT=0x0600"
        PLATFORM_CFLAGS="-municode -mno-ms-bitfields"
        PLATFORM_LDFLAGS="-no-undefined"
        WITH_NTFS_3G_DEFAULT="no"
@@ -93,23 +95,14 @@ AC_CHECK_MEMBER([struct stat.st_mtim],
                [],
                [#include <sys/stat.h>])
 
-# Check for possible support for the Linux getrandom() system call
-AC_CHECK_DECL([__NR_getrandom],
-             [AC_DEFINE([HAVE_NR_GETRANDOM], [1], [Define to 1 if the system
-              headers define a system call number for getrandom()])],
-             [],
-             [#include <sys/syscall.h>])
-
 ###############################################################################
 #                           Required libraries                               #
 ###############################################################################
 
 # ------------------------------ pthreads -------------------------------------
-AX_PTHREAD([], [AC_MSG_ERROR(["cannot find pthreads library"])])
-
-# ------------------------------ libxml2 --------------------------------------
-PKG_CHECK_MODULES([LIBXML2], [libxml-2.0])
-PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libxml-2.0"
+if test "$WINDOWS_NATIVE_BUILD" != "yes"; then
+       AX_PTHREAD([], [AC_MSG_ERROR(["cannot find pthreads library"])])
+fi
 
 ###############################################################################
 #                        Configuration options                               #
@@ -135,7 +128,9 @@ if test "$WITH_NTFS_3G" = "yes"; then
                 NTFS volume while preserving NTFS-specific data such as
                 security descriptors and named data streams.  Either install
                 libntfs-3g, or configure --without-ntfs-3g to disable this
-                feature.])])
+                feature.  If your operating system packages development files
+                separately, the package you need to install may be called
+                ntfs-3g-dev, ntfs-3g-devel, or similar.])])
        PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libntfs-3g"
        AC_DEFINE([WITH_NTFS_3G], [1], [Define to 1 if using NTFS-3G support])
 fi
@@ -146,7 +141,7 @@ AM_CONDITIONAL([WITH_NTFS_3G], [test "$WITH_NTFS_3G" = "yes"])
 AC_MSG_CHECKING([whether to include support for mounting WIMs])
 AC_ARG_WITH([fuse],
            [AS_HELP_STRING([--without-fuse],
-                           [build without libfuse.  This will disable the
+                           [build without libfuse3.  This will disable the
                             ability to mount WIM images.])],
            [WITH_FUSE=$withval],
            [WITH_FUSE=$WITH_FUSE_DEFAULT])
@@ -154,11 +149,13 @@ AC_MSG_RESULT([$WITH_FUSE])
 
 if test "$WITH_FUSE" = "yes"; then
 
-       PKG_CHECK_MODULES([LIBFUSE], [fuse], [],
-               [AC_MSG_ERROR([Cannot find libfuse!
-               Without libfuse, wimlib cannot include support for mounting WIM
-               images.  Either install libfuse, or configure --without-fuse to
-               disable this feature.])])
+       PKG_CHECK_MODULES([LIBFUSE], [fuse3], [],
+               [AC_MSG_ERROR([Cannot find libfuse3!
+               Without libfuse3, wimlib cannot include support for mounting WIM
+               images.  Either install libfuse3, or configure --without-fuse to
+               disable this feature.  If your operating system packages
+               development files separately, the package you need to install
+               may be called libfuse3-dev, fuse-devel, or similar.])])
        PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES fuse"
        AC_DEFINE([WITH_FUSE], [1], [Define to 1 if using FUSE support])
 
@@ -174,87 +171,8 @@ if test "$WITH_FUSE" = "yes"; then
 fi
 AM_CONDITIONAL([WITH_FUSE], [test "$WITH_FUSE" = "yes"])
 
-# ------------------------ SHA-1 implementation ---------------------------------
-
-AC_MSG_CHECKING([whether to use SSSE3-accelerated SHA-1])
-AC_ARG_ENABLE([ssse3-sha1],
-             [AS_HELP_STRING([--enable-ssse3-sha1],
-                             [Include SSSE3-accelerated SHA-1 implementation by
-                              Intel.  This implies --without-libcrypto.])],
-             [ENABLE_SSSE3_SHA1=$enableval],
-             [ENABLE_SSSE3_SHA1=no])
-AC_MSG_RESULT([$ENABLE_SSSE3_SHA1])
-
-if test "$ENABLE_SSSE3_SHA1" = "yes" ; then
-       AC_DEFINE([ENABLE_SSSE3_SHA1], [1],
-                 [Define to 1 if using SSSE3 implementation of SHA-1])
-       AC_PROG_NASM
-       NASM_SYMBOL_PREFIX=""
-       NASM_PLATFORM_FLAGS=""
-       if test "$WINDOWS_NATIVE_BUILD" = "yes"; then
-               NASM_PLATFORM_FLAGS="-DWIN_ABI"
-       fi
-       case "$host_os" in
-       darwin* | rhapsody* | nextstep* | openstep* | macos*)
-               NASM_SYMBOL_PREFIX="_"
-               ;;
-       esac
-       AC_SUBST([NASM_PLATFORM_FLAGS], [$NASM_PLATFORM_FLAGS])
-       AC_SUBST([NASM_SYMBOL_PREFIX], [$NASM_SYMBOL_PREFIX])
-else
-       AC_MSG_CHECKING([whether to use SHA-1 implementation from system libcrypto])
-       AC_ARG_WITH([libcrypto],
-                   [AS_HELP_STRING([--without-libcrypto],
-                                   [build in the SHA-1 algorithm, rather than
-                                    use external libcrypto from OpenSSL
-                                    (default is autodetect)])],
-                   [WITH_LIBCRYPTO=$withval],
-                   [WITH_LIBCRYPTO=auto])
-       AC_MSG_RESULT([$WITH_LIBCRYPTO])
-       if test "$WITH_LIBCRYPTO" != "no"; then
-               PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [
-                       PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libcrypto"
-                       AC_DEFINE([WITH_LIBCRYPTO], [1],
-                                 [Define to 1 if using libcrypto SHA-1])
-               ], [AC_MSG_WARN([Cannot find libcrypto: using stand-alone SHA-1 code instead])])
-       fi
-fi
-AM_CONDITIONAL([ENABLE_SSSE3_SHA1], [test "$ENABLE_SSSE3_SHA1" = "yes"])
-
 # ----------------------------- Other options ---------------------------------
 
-AC_MSG_CHECKING([whether to include error messages])
-AC_ARG_ENABLE([error_messages],
-       AS_HELP_STRING([--disable-error-messages], [do not compile in error messsages]),
-       [ENABLE_ERROR_MESSAGES=$enableval],
-       [ENABLE_ERROR_MESSAGES=yes])
-AC_MSG_RESULT([$ENABLE_ERROR_MESSAGES])
-if test "$ENABLE_ERROR_MESSAGES" = "yes"; then
-       AC_DEFINE([ENABLE_ERROR_MESSAGES], [1], [Define to 1 if including error messages])
-fi
-
-AC_MSG_CHECKING([whether to include assertions])
-AC_ARG_ENABLE([assertions],
-       AS_HELP_STRING([--disable-assertions], [do not include assertions]),
-       [ENABLE_ASSERTIONS=$enableval],
-       [ENABLE_ASSERTIONS=yes])
-AC_MSG_RESULT([$ENABLE_ASSERTIONS])
-if test "$ENABLE_ASSERTIONS" = "yes"; then
-       AC_DEFINE([ENABLE_ASSERTIONS], [1], [Define to 1 if including assertions])
-fi
-
-AC_MSG_CHECKING([whether to include support for multi-threaded compression])
-AC_ARG_ENABLE([multithreaded-compression],
-       AS_HELP_STRING([--disable-multithreaded-compression],
-                      [disable support for multithreaded compression]),
-       [ENABLE_MULTITHREADED_COMPRESSION=$enableval],
-       [ENABLE_MULTITHREADED_COMPRESSION=yes])
-AC_MSG_RESULT([$ENABLE_MULTITHREADED_COMPRESSION])
-if test "$ENABLE_MULTITHREADED_COMPRESSION" = "yes"; then
-       AC_DEFINE([ENABLE_MULTITHREADED_COMPRESSION], [1],
-                 [Define to 1 to support multithreaded compression])
-fi
-
 AC_ARG_WITH(pkgconfigdir,
             [  --with-pkgconfigdir=DIR      pkgconfig file in DIR @<:@LIBDIR/pkgconfig@:>@],
             [pkgconfigdir=$withval],
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644 (file)
index 7bc1580..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-wimlib (1.13.1-1) unstable; urgency=low
-
-  * Update to v1.13.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Mon, 06 May 2019 19:23:40 -0700
-
-wimlib (1.13.0-1) unstable; urgency=low
-
-  * Update to v1.13.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Thu, 22 Nov 2018 17:04:35 -0800
-
-wimlib (1.12.0-1) unstable; urgency=low
-
-  * Update to v1.12.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 29 Jul 2017 12:01:11 -0700
-
-wimlib (1.11.0-1) unstable; urgency=low
-
-  * Update to v1.11.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 17 Jan 2017 19:41:32 -0800
-
-wimlib (1.10.0-1) unstable; urgency=low
-
-  * Update to v1.10.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 19 Aug 2016 20:12:30 -0700
-
-wimlib (1.9.2-1) unstable; urgency=low
-
-  * Update to v1.9.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 31 May 2016 20:40:27 -0500
-
-wimlib (1.9.1-1) unstable; urgency=low
-
-  * Update to v1.9.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 11 Mar 2016 21:54:00 -0600
-
-wimlib (1.9.0-1) unstable; urgency=low
-
-  * Update to v1.9.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 30 Jan 2016 23:58:54 -0600
-
-wimlib (1.8.3-1) unstable; urgency=low
-
-  * Update to v1.8.3
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 14 Nov 2015 13:29:29 -0600
-
-wimlib (1.8.2-1) unstable; urgency=low
-
-  * Update to v1.8.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 22 Aug 2015 09:19:10 -0500
-
-wimlib (1.8.1-1) unstable; urgency=low
-
-  * Update to v1.8.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 16 May 2015 12:35:15 -0500
-
-wimlib (1.8.0-1) unstable; urgency=low
-
-  * Update to v1.8.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 24 Feb 2015 22:54:49 -0600
-
-wimlib (1.7.4-1) unstable; urgency=low
-
-  * Update to v1.7.4
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 02 Jan 2015 20:01:28 -0600
-
-wimlib (1.7.3-1) unstable; urgency=low
-
-  * Update to v1.7.3
-
- -- Eric Biggers <ebiggers3@gmail.com>  Thu, 13 Nov 2014 18:11:27 -0600
-
-wimlib (1.7.2-1) unstable; urgency=low
-
-  * Update to v1.7.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Thu, 02 Oct 2014 20:24:27 -0500
-
-wimlib (1.7.1-1) unstable; urgency=low
-
-  * Update to v1.7.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Wed, 06 Aug 2014 22:34:49 -0500
-
-wimlib (1.7.0-1) unstable; urgency=low
-
-  * Update to v1.7.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 07 Jun 2014 21:34:56 -0500
-
-wimlib (1.6.2-1) unstable; urgency=low
-
-  * Update to v1.6.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 14 Mar 2014 15:59:59 -0500
-
-wimlib (1.6.1-1) unstable; urgency=low
-
-  * Update to v1.6.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sun, 12 Jan 2014 23:13:44 -0600
-
-wimlib (1.6.0-1) unstable; urgency=low
-
-  * Update to v1.6.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 28 Dec 2013 11:13:54 -0600
-
-wimlib (1.5.3-1) unstable; urgency=low
-
-  * Update to v1.5.3
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sun, 08 Dec 2013 00:34:21 -0600
-
-wimlib (1.5.2-1) unstable; urgency=low
-
-  * Update to v1.5.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sun, 17 Nov 2013 17:10:43 -0600
-
-wimlib (1.5.1-1) unstable; urgency=low
-
-  * Update to v1.5.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 11 Oct 2013 09:34:20 -0500
-
-wimlib (1.5.0-1) unstable; urgency=low
-
-  * Update to v1.5.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 16 Aug 2013 20:51:08 -0500
-
-wimlib (1.4.2-1) unstable; urgency=low
-
-  * Update to v1.4.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 11 Jun 2013 19:46:09 -0500
-
-wimlib (1.4.1-1) precise; urgency=low
-
-  * Update to v1.4.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Mon, 20 May 2013 14:01:44 -0500
-
-wimlib (1.4.0-1) unstable; urgency=low
-
-  * Update to v1.4.0; moved wimlib-imagex and mkwinpeimg to "wimtools"
-    package.
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sun, 12 May 2013 22:59:55 -0500
-
-wimlib (1.3.3-1) unstable; urgency=low
-
-  * Update to v1.3.3
-
- -- Eric Biggers <ebiggers3@gmail.com>  Mon, 08 Apr 2013 00:16:50 -0500
-
-wimlib (1.3.2-1) unstable; urgency=low
-
-  * Update to v1.3.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sat, 23 Mar 2013 20:00:14 -0500
-
-wimlib (1.3.1-1) unstable; urgency=low
-
-  * Update to v1.3.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 22 Mar 2013 01:05:27 -0500
-
-wimlib (1.3.0-1) unstable; urgency=low
-
-  * Update to v1.3.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sun, 10 Mar 2013 18:46:29 -0700
-
-wimlib (1.2.6-1) unstable; urgency=low
-
-  * Update to v1.2.6
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 05 Mar 2013 14:26:59 -0600
-
-wimlib (1.2.5-1) unstable; urgency=low
-
-  * Update to v1.2.5
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 05 Feb 2013 20:26:47 -0600
-
-wimlib (1.2.4-1) unstable; urgency=low
-
-  * Update to v1.2.4
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 29 Jan 2013 20:19:29 -0600
-
-wimlib (1.2.3-1) unstable; urgency=low
-
-  * Update to v1.2.3
-
- -- Eric Biggers <ebiggers3@gmail.com>  Mon, 31 Dec 2012 14:33:36 -0600
-
-wimlib (1.2.2-1) unstable; urgency=low
-
-  * Update to v1.2.2
-
- -- Eric Biggers <ebiggers3@gmail.com>  Fri, 21 Dec 2012 12:39:22 -0600
-
-wimlib (1.2.1-1) unstable; urgency=low
-
-  * Update to v1.2.1
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 18 Dec 2012 11:25:02 -0600
-
-wimlib (1.2.0-1) unstable; urgency=low
-
-  * Update to v1.2.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Thu, 22 Nov 2012 14:35:33 -0600
-
-wimlib (1.1.0-1) unstable; urgency=low
-
-  * Update to v1.1.0
-
- -- Eric Biggers <ebiggers3@gmail.com>  Sun, 18 Nov 2012 13:49:54 -0600
-
-wimlib (1.0.4-1) unstable; urgency=low
-
-  * Update to v1.0.4
-
- -- Eric Biggers <ebiggers3@gmail.com>  Mon, 29 Oct 2012 03:57:13 +0000
-
-wimlib (0.6.3-1) unstable; urgency=low
-
-  * Initial release of Debian package
-
- -- Eric Biggers <ebiggers3@gmail.com>  Tue, 01 May 2012 23:48:40 -0500
diff --git a/debian/compat b/debian/compat
deleted file mode 100644 (file)
index 7f8f011..0000000
+++ /dev/null
@@ -1 +0,0 @@
-7
diff --git a/debian/control b/debian/control
deleted file mode 100644 (file)
index e8c627e..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-Source: wimlib
-Priority: optional
-Maintainer: Eric Biggers <ebiggers3@gmail.com>
-Build-Depends: debhelper (>= 8.9.7), autotools-dev, pkg-config,
-               libfuse-dev, libxml2-dev, libssl-dev,
-               ntfs-3g-dev (>= 2011.4.12), attr
-Build-Depends-Indep: doxygen
-Standards-Version: 3.9.3
-Section: libs
-Homepage: https://wimlib.net
-Vcs-Git: git://wimlib.net/wimlib
-
-Package: wimlib15
-Section: libs
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Suggests: wimtools
-Description: Library to extract, create, modify, and mount WIM files
- wimlib is a C library for extracting, creating, modifying, and mounting WIM
- (Windows Imaging) files.  WIM is an archive format designed primarily for
- archiving Windows filesystems.  It features single-instancing and LZ77-based
- compression, and is used by Microsoft to distribute and deploy Windows Vista and
- later.  wimlib is an independent implementation of an API for handling WIM
- files, available on both UNIX-like systems and Windows, that provides features
- similar to Microsoft's WIMGAPI, as well as additional features such as support
- for pipable WIM files and programatically making changes to WIM images without
- mounting them.
-
-
-Package: wimtools
-Section: utils
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Suggests: genisoimage, mtools, syslinux, cabextract
-Description: Tools to extract, create, modify, and mount WIM files
- Tools to extract, create, modify, and mount WIM (Windows Imaging) files.  WIM is
- an archive format designed primarily for archiving Windows filesystems.  It
- features single-instancing and LZ77-based compression and is used by Microsoft
- to distribute and deploy Windows Vista and later.  WIM files are normally
- created by using the `imagex.exe' utility on Windows, but this package contains
- a free implementation of ImageX called "wimlib-imagex" that is designed to work
- on both UNIX-like systems and Windows.
- .
- In addition to the usual extract/create/update support, wimlib-imagex allows you
- to mount WIM images readonly or read-write, and it even allows you to extract or
- create a WIM image directly to/from an unmounted NTFS volume.  This makes it
- possible to, from Linux, back up or deploy a Windows OS directly to or from a
- WIM file, such as the install.wim distributed on the Windows installation media.
- .
- This package also contains a script to make a customized Windows PE image based
- on the capabilities provided by wimlib-imagex.
-
-Package: wimlib-dev
-Section: libdevel
-Architecture: any
-Depends: wimlib15 (= ${binary:Version}), ${misc:Depends}
-Suggests: wimlib-doc
-Description: wimlib - development files
- Development files for wimlib
-
-Package: wimlib-doc
-Section: doc
-Architecture: all
-Depends: ${misc:Depends}
-Description: wimlib - API documentation
- API documentation for wimlib
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644 (file)
index 05c487e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-Format: http://dep.debian.net/deps/dep5
-Upstream-Name: wimlib
-Source: https://wimlib.net
-
-Files: *
-Copyright: 2012-2016 Eric Biggers <ebiggers3@gmail.com>
-License: GPLv3+ with exception
-  Unless otherwise specified, wimlib and its associated programs, scripts,
-  documentation, and other files may be redistributed and/or modified under the
-  terms of the GNU General Public License; either version 3 of the License, or (at
-  your option) any later version.  There is NO WARRANTY, to the extent permitted
-  by law.  See the file COPYING.GPLv3 for more details.
-  .
-  Alternatively, when not prohibited by conflict with a third-party software
-  license, the library portion of wimlib may be redistributed and/or modified
-  under the terms of the GNU Lesser General Public License; either version 3 of
-  the License, or (at your option) any later version.  There is NO WARRANTY, to
-  the extent permitted by law.  See the file COPYING.LGPLv3 for more details.
-
-Files: debian/*
-License: Public domain
-  The Debian packaging scripts are free to be redistributed and/or modified with
-  no restrictions.
diff --git a/debian/rules b/debian/rules
deleted file mode 100755 (executable)
index 27b9b9e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-export DH_VERBOSE=1
-
-%:
-       dh $@
-
-override_dh_auto_build-indep:
-       set -e; if type doxygen >/dev/null 2>/dev/null; \
-       then \
-               cd doc && doxygen; \
-       fi
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644 (file)
index 89ae9db..0000000
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (native)
diff --git a/debian/watch b/debian/watch
deleted file mode 100644 (file)
index 0d727f6..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# See uscan(1) for format
-
-# Compulsory line, this is a version 3 file
-version=3
-
-https://wimlib.net/downloads/wimlib-(.*)\.tar\.gz
diff --git a/debian/wimlib-dev.install b/debian/wimlib-dev.install
deleted file mode 100644 (file)
index aff5182..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-usr/include/wimlib.h
-usr/lib/libwim.a
-usr/lib/libwim.so
-usr/lib/pkgconfig/wimlib.pc
diff --git a/debian/wimlib-doc.docs b/debian/wimlib-doc.docs
deleted file mode 100644 (file)
index 3abb624..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-doc/html
-NEWS
-README
diff --git a/debian/wimlib-doc.examples b/debian/wimlib-doc.examples
deleted file mode 100644 (file)
index e39721e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-examples/*
diff --git a/debian/wimlib15.install b/debian/wimlib15.install
deleted file mode 100644 (file)
index 8161387..0000000
+++ /dev/null
@@ -1 +0,0 @@
-usr/lib/libwim.so.*
diff --git a/debian/wimtools.docs b/debian/wimtools.docs
deleted file mode 100644 (file)
index 50bd824..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-NEWS
-README
diff --git a/debian/wimtools.install b/debian/wimtools.install
deleted file mode 100644 (file)
index 68671de..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/bin/*
-usr/share/man/man1/*
index 4081a8d38828404a7d50839b50925eb859c9e2db..96fe747b4e505801bcde6c88e4245ca827f53c01 100644 (file)
@@ -1,4 +1,4 @@
-.TH MKWINPEIMG "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH MKWINPEIMG "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 mkwinpeimg \- Make a customized bootable image of Windows PE
 .SH SYNOPSIS
@@ -118,6 +118,6 @@ mkwinpeimg --start-script=install.cmd --windows-dir=/media/windows /var/tftpboot
 Microsoft's licenses may limit the things that Windows PE can be used for, and
 they may limit your rights to redistribute customized versions of Windows PE.
 .SH REPORTING BUGS
-Report bugs to ebiggers3@gmail.com.
+Report bugs to \fIhttps://wimlib.net/forums/\fR.
 .SH SEE ALSO
 .BR wimlib-imagex (1)
index b0e45cf44a4412a578ba5d68fd7d77d6172c5db3..448fafda7ad511907bc958ea9a5d36064ca480f3 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMAPPLY "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMAPPLY "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimapply \- Apply a WIM image
 .SH SYNOPSIS
@@ -217,7 +217,7 @@ image from a WIM file available on a HTTP server to an NTFS volume on /dev/sda1,
 run something like:
 .PP
 .RS
-wget -O - http://myserver/mywim.wim | wimapply - 1 /dev/sda1
+wget -O - https://myserver/mywim.wim | wimapply - 1 /dev/sda1
 .RE
 .PP
 Pipable WIMs may also be split into multiple parts, just like normal WIMs.  To
@@ -355,15 +355,22 @@ files matching any of the patterns in this section will not be compressed.
 In addition, wimlib has a hardcoded list of files for which it knows, for
 compatibility with the Windows bootloader, to override the requested compression
 format.
+.TP
+\fB--recover-data\fR
+If a file is corrupted (its stored hash doesn't match its actual hash, or some
+parts of it can't be decompressed), extract the corrupted file anyway with a
+warning, rather than aborting with an error.  This may be useful to recover data
+if a WIM archive was corrupted.  Note that recovering data is not guaranteed to
+succeed, as it depends on the type of corruption that occurred.
 .SH NOTES
 \fIData integrity\fR: WIM files include checksums of file data.  To detect
 accidental (non-malicious) data corruption, wimlib calculates the checksum of
 every file it extracts and issues an error if it does not have the expected
-value.  (This default behavior seems equivalent to the \fB/verify\fR option of
-ImageX.)  In addition, a WIM file can include an integrity table (extra
-checksums) over the raw data of the entire WIM file.  For performance reasons
-wimlib does not check the integrity table by default, but the \fB--check\fR
-option can be passed to make it do so.
+value, unless the \fB--recover-data\fR option is given.  (This default behavior
+seems equivalent to the \fB/verify\fR option of ImageX.)  In addition, a WIM
+file can include an integrity table (extra checksums) over the raw data of the
+entire WIM file.  For performance reasons wimlib does not check the integrity
+table by default, but the \fB--check\fR option can be passed to make it do so.
 .PP
 \fIESD files\fR: wimlib can extract files from solid-compressed WIMs, or "ESD"
 (.esd) files, just like from normal WIM (.wim) files.  However, Microsoft
index 3d2bf39782e6adce2b940e1da53f92253b56bab0..fe237b06efa41b63f40d86ab63c5544c583f726c 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMCAPTURE "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMCAPTURE "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimcapture, wimappend \- Capture or append a WIM image
 .SH SYNOPSIS
index ae07ff779e61a208db146bafc1d5706358a6b5c4..9c323a46b8e6dee28852e213d1476516ccefb534 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMDELETE "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMDELETE "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimdelete \- Delete an image from a WIM archive
 .SH SYNOPSIS
index 8c42aa64acca111fe9bd3f30464d7fd076c3b946..541c6aac2337db9b7b57a35c7b444b098e08d23e 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMDIR "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMDIR "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimdir \- List the files contained in a WIM image
 .SH SYNOPSIS
index 24f68ff569a075c2c1f02469307ccb3450b8fed3..e99558fd02a3a0e84cbb050723ee89fc75bf2dc8 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMEXPORT "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMEXPORT "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimexport \- Export image(s) from a WIM archive
 .SH SYNOPSIS
index ab0f4b0ab2f6642892243311aebc013adaac8b85..6fae7e04eaaad9fbe3653868a105857de06fc613 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMEXTRACT "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMEXTRACT "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimextract \- Extract files from a WIM image
 .SH SYNOPSIS
@@ -152,6 +152,9 @@ See the documentation for this option to \fBwimapply\fR(1).
 .TP
 \fB--compact\fR=\fIFORMAT\fR
 See the documentation for this option to \fBwimapply\fR(1).
+.TP
+\fB--recover-data\fR
+See the documentation for this option to \fBwimapply\fR(1).
 .SH NOTES
 See \fBwimapply\fR(1) for information about what data and metadata are extracted
 on UNIX-like systems versus on Windows.
index ecb3f0932874e00fd046b30f52387f65d49a95de..9df6fbc1ad7160c49fa2c884d5d9b5351c514585 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMINFO "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMINFO "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wiminfo \- Display or change information about a WIM file or image
 .SH SYNOPSIS
index c4817f805deafdc580e74cfc1609ad698657cbeb..4e32457bbb6841966f61ae412912ac704497c0e6 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMJOIN "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMJOIN "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimjoin\- Join a split WIM into a standalone WIM
 .SH SYNOPSIS
index 73eacf867c8f7aa5f2f02477aacb3ce8da8d4273..f75be19908cc2a4f1bdd1a895b9bcc7f6d6360ad 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMLIB-IMAGEX 1 "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMLIB-IMAGEX 1 "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimlib-imagex \- Extract, create, modify, or mount a WIM archive
 .SH SYNOPSIS
@@ -224,7 +224,7 @@ wimlib-imagex may be redistributed and/or modified under the terms of the GNU
 General Public License; either version 3 of the License, or (at your option) any
 later version.  There is NO WARRANTY, to the extent permitted by law.
 .SH REPORTING BUGS
-Report bugs to ebiggers3@gmail.com or to \fIhttps://wimlib.net/forums/\fR.
+Report bugs to \fIhttps://wimlib.net/forums/\fR.
 Feedback and suggestions are also welcome.
 .SH SEE ALSO
 .BR wimappend (1),
index a1ab531f589db11c56fe7770723db715eccfe81e..964849653333e15421a4556e6b16e8ac3a7c9105 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMMOUNT "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMMOUNT "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimmount, wimmountrw, wimunmount \- Mount or unmount a WIM image
 .SH SYNOPSIS
index bf859a69b8c69fd9cea208b0053e8abeba6c0f47..4e20884f170f4e919b090178ad0c8ab684f1d5b0 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMOPTIMIZE "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMOPTIMIZE "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimoptimize \- Optimize a WIM archive
 .SH SYNOPSIS
@@ -124,6 +124,14 @@ wimoptimize install.wim --solid
 .br
 mv install.wim install.esd
 .RE
+.PP
+Turn 'install.esd' back into 'install.wim':
+.RS
+.PP
+wimoptimize install.esd --compress=LZX
+.br
+mv install.esd install.wim
+.RE
 .SH SEE ALSO
 .BR wimlib-imagex (1)
 .BR wimexport (1)
index 9f95fedcdb5ca241005631536f1bdda483162904..d77d84cbb5d681b3eea44f14d82b6bc28d05fc0a 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMSPLIT "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMSPLIT "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimsplit \- Split a WIM archive into multiple parts
 .SH SYNOPSIS
index f074be12213a60155ef533d36a427295bea9e548..f1c82ba4e1104e5513ebe8918217028e1d8b6940 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMUPDATE "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMUPDATE "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimupdate \- Update a WIM image
 .SH SYNOPSIS
@@ -14,10 +14,11 @@ contains only one image.  You can use \fBwiminfo\fR(1) to list the images
 contained in \fIWIMFILE\fR.
 .PP
 The modifications to perform on the WIM image are specified as a sequence of
-commands, one per line, read in a text file from standard input.  It is
-recommended that standard input be redirected from a file (\fICMDFILE\fR), as
-shown above, rather than typing in commands interactively.  Alternatively, to
-specify a command directly on the command line, see the \fB--command\fR option.
+commands, one per line, read in a text file (UTF-8 or UTF-16LE encoded; plain
+ASCII is also fine) from standard input.  It is recommended that standard input
+be redirected from a file (\fICMDFILE\fR), as shown above, rather than typing in
+commands interactively.  Alternatively, to specify a command directly on the
+command line, see the \fB--command\fR option.
 .SH AVAILABLE COMMANDS
 This section documents the commands that may appear in the \fICMDFILE\fR
 described above.
@@ -163,6 +164,13 @@ Compact the WIM archive in-place and append any new data, eliminating "holes".
 This is efficient, but in general this option should \fInot\fR be used because a
 failed or interrupted compaction will corrupt the WIM archive.  For more
 information, see the documentation for this option in \fBwimoptimize\fR(1).
+.TP
+\fB--ref\fR="\fIGLOB\fR"
+File glob of WIM(s) on which the delta WIM is based.  Updating split WIMs is not
+allowed, but updating delta WIMs is allowed.  When updating a delta WIM, the
+WIM(s) on which the delta WIM is based should be specified using this option.
+(It isn't a hard requirement, but it's needed for data deduplication to work
+fully and for the TOTALBYTES statistic to be correctly updated.)
 .SH NOTES
 \fBwimupdate\fR can be viewed as redundant with \fBwimmountrw\fR, since a WIM
 image can also be updated by mounting it read-write.  However, \fBwimupdate\fR
@@ -220,7 +228,7 @@ $ wimupdate boot.wim 2 < update_commands.txt
 .PP
 Add some files and directories to a WIM image.  Note that the first path of each
 \fBadd\fR command specifies the files to add, while the second path of each
-\fBadd\fR command specify the locations at which to to add them inside the WIM
+\fBadd\fR command specify the locations at which to add them inside the WIM
 image:
 .PP
 .RS
index 597ba1ce81cc660abcf8b10a7af9254b3acb40fe..e77af954a0eec6cb70fe171161570af17fcb3b8c 100644 (file)
@@ -1,4 +1,4 @@
-.TH WIMVERIFY "1" "May 2019" "wimlib 1.13.1" "User Commands"
+.TH WIMVERIFY "1" "February 2024" "wimlib 1.14.4" "User Commands"
 .SH NAME
 wimverify \- Verify a WIM archive
 .SH SYNOPSIS
index cd6d340967ee4fca79029a3f1ae654884254f86b..ef65ba1bb9b0b4b41ff07518c75570a92b7cf746 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * applywim.c - A program to extract the first image from a WIM file.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2013-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 <wimlib.h>
index ae99acb4d5651151965799ab4f7fbfcaeabc8d7e..922995c13387ee44a1e94a800ae558c3058f08c8 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * capturewim.c - A program to capture a directory tree into a WIM file.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 <wimlib.h>
index af4010bf1107f45d5e4e6a1f49a0f158cd42b059..419386f89b26437c67812a96e723e22ef40a18ac 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * compressfile.c - compression API example
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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.
  */
 
 /*
index e5a03b44512b4bd5e7a3223fb64d25237f0ff8dc..561557a1e132ce3fd204153ccd0f11bf74b93f86 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * decompressfile.c - decompression API example
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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.
  */
 
 /*
index 0483d5e73542b1c53042016f7e5ea3bd2cd94f94..a929886fa21de7ac1f9f2c8ba68e77230cd36af0 100644 (file)
@@ -2,21 +2,28 @@
  * updatewim.c - A program to add a file or directory tree to the first image of
  * a WIM file.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 <wimlib.h>
index 35d2236f8994cf3186958deac7c992f78927d811..84f1e2eca6742dafb8bad725a02beaf6efdfcd39 100644 (file)
 /**
  * @mainpage
  *
- * This is the documentation for the library interface of wimlib 1.13.1, a C
+ * This is the documentation for the library interface of wimlib 1.14.4, a C
  * library for creating, modifying, extracting, and mounting files in the
  * Windows Imaging (WIM) format.  This documentation is intended for developers
  * only.  If you have installed wimlib and want to know how to use the @b
  * wimlib-imagex program, please see the manual pages and also the <a
- * href="https://wimlib.net/git/?p=wimlib;a=blob;f=README">README file</a>.
+ * href="https://wimlib.net/git/?p=wimlib;a=blob;f=README.md">README file</a>.
  *
  * @section sec_installing Installing
  *
@@ -30,9 +30,8 @@
  * @subsection Windows
  *
  * Download the Windows binary distribution with the appropriate architecture
- * (i686 or x86_64 --- also called "x86" and "amd64" respectively) from
- * https://wimlib.net.  Link your program with libwim-15.dll.  If needed by your
- * programming language or development environment, the import library
+ * from https://wimlib.net.  Link your program with libwim-15.dll.  If needed by
+ * your programming language or development environment, the import library
  * libwim.lib and C/C++ header wimlib.h can be found in the directory "devel" in
  * the ZIP file.
  *
 #include <stdint.h>
 #include <time.h>
 
+#ifdef BUILDING_WIMLIB
+  /*
+   * On i386, gcc assumes that the stack is 16-byte aligned at function entry.
+   * However, some compilers (e.g. MSVC) and programming languages (e.g. Delphi)
+   * only guarantee 4-byte alignment when calling functions.  This is mainly an
+   * issue on Windows, but it can occur on Linux too.  Work around this ABI
+   * incompatibility by realigning the stack pointer when entering the library.
+   * This prevents crashes in SSE/AVX code.
+   */
+#  if defined(__GNUC__) && defined(__i386__)
+#    define WIMLIB_ALIGN_STACK  __attribute__((force_align_arg_pointer))
+#  else
+#    define WIMLIB_ALIGN_STACK
+#  endif
+#  ifdef _WIN32
+#    define WIMLIBAPI __declspec(dllexport) WIMLIB_ALIGN_STACK
+#  else
+#    define WIMLIBAPI __attribute__((visibility("default"))) WIMLIB_ALIGN_STACK
+#  endif
+#else
+#  define WIMLIBAPI
+#endif
+
 /** @addtogroup G_general
  * @{ */
 
 #define WIMLIB_MAJOR_VERSION 1
 
 /** Minor version of the library (for example, the 2 in 1.2.5). */
-#define WIMLIB_MINOR_VERSION 13
+#define WIMLIB_MINOR_VERSION 14
 
 /** Patch version of the library (for example, the 5 in 1.2.5). */
-#define WIMLIB_PATCH_VERSION 1
+#define WIMLIB_PATCH_VERSION 4
 
 #ifdef __cplusplus
 extern "C" {
@@ -649,7 +671,8 @@ enum wimlib_progress_msg {
         * to ::wimlib_progress_info.write_streams.  This message may be
         * received many times while the WIM file is being written or appended
         * to with wimlib_write(), wimlib_overwrite(), or wimlib_write_to_fd().
-        */
+        * Since wimlib v1.13.4 it will also be received when a split WIM part
+        * is being written by wimlib_split().  */
        WIMLIB_PROGRESS_MSG_WRITE_STREAMS = 12,
 
        /** Per-image metadata is about to be written to the WIM file.  @p info
@@ -825,7 +848,8 @@ union wimlib_progress_info {
                /** The number of bytes of file data that have been written so
                 * far.  This starts at 0 and ends at @p total_bytes.  This
                 * number is the uncompressed size; the actual size may be lower
-                * due to compression.  */
+                * due to compression.  See @p completed_compressed_bytes for
+                * the compressed size.  */
                uint64_t completed_bytes;
 
                /** The number of distinct file data "blobs" that have been
@@ -848,6 +872,10 @@ union wimlib_progress_info {
 
                /** This is currently broken and will always be 0.  */
                uint32_t completed_parts;
+
+               /** Since wimlib v1.13.4: Like @p completed_bytes, but counts
+                * the compressed size.  */
+               uint64_t completed_compressed_bytes;
        } write_streams;
 
        /** Valid on messages ::WIMLIB_PROGRESS_MSG_SCAN_BEGIN,
@@ -1393,20 +1421,23 @@ struct wimlib_wim_info {
  *    sha1_hash.  This case can only occur with wimlib_iterate_dir_tree(), never
  *    wimlib_iterate_lookup_table().
  *
- * 2. Otherwise we know the sha1_hash, the uncompressed_size, the
- *    reference_count, and the is_metadata flag.  In addition:
+ * 2. Otherwise we know the uncompressed_size, the reference_count, and the
+ *    is_metadata flag.  In addition:
  *
  *    A. If the blob is located in a non-solid WIM resource, then we also know
- *       the compressed_size and offset.
+ *       the sha1_hash, compressed_size, and offset.
  *
  *    B. If the blob is located in a solid WIM resource, then we also know the
- *       offset, raw_resource_offset_in_wim, raw_resource_compressed_size, and
- *       raw_resource_uncompressed_size.  But the "offset" is actually the
- *       offset in the uncompressed solid resource rather than the offset from
- *       the beginning of the WIM file.
+ *       sha1_hash, offset, raw_resource_offset_in_wim,
+ *       raw_resource_compressed_size, and raw_resource_uncompressed_size.  But
+ *       the "offset" is actually the offset in the uncompressed solid resource
+ *       rather than the offset from the beginning of the WIM file.
  *
- *    C. If the blob is *not* located in any type of WIM resource, then we don't
- *       know any additional information.
+ *    C. If the blob is *not* located in any type of WIM resource, for example
+ *       if it's in a external file that was scanned by wimlib_add_image(), then
+ *       we usually won't know any more information.  The sha1_hash might be
+ *       known, and prior to wimlib v1.13.6 it always was; however, in wimlib
+ *       v1.13.6 and later, the sha1_hash might not be known in this case.
  *
  * Unknown or irrelevant fields are left zeroed.
  */
@@ -1426,7 +1457,8 @@ struct wimlib_resource_entry {
         * of this blob within that solid resource when uncompressed.  */
        uint64_t offset;
 
-       /** The SHA-1 message digest of the blob's uncompressed contents.  */
+       /** If this blob is located in a WIM resource, then this is the SHA-1
+        * message digest of the blob's uncompressed contents.  */
        uint8_t sha1_hash[20];
 
        /** If this blob is located in a WIM resource, then this is the part
@@ -1922,6 +1954,10 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
  * wimlib_extract_paths() when passed multiple paths.  */
 #define WIMLIB_EXTRACT_FLAG_NTFS                       0x00000001
 
+/** Since wimlib v1.13.4: Don't consider corrupted files to be an error.  Just
+ * extract them in whatever form we can.  */
+#define WIMLIB_EXTRACT_FLAG_RECOVER_DATA               0x00000002
+
 /** UNIX-like systems only:  Extract UNIX-specific metadata captured with
  * ::WIMLIB_ADD_FLAG_UNIX_DATA.  */
 #define WIMLIB_EXTRACT_FLAG_UNIX_DATA                  0x00000020
@@ -2624,7 +2660,7 @@ enum wimlib_error_code {
  * @retval ::WIMLIB_ERR_IMAGE_NAME_COLLISION
  *     The WIM already contains an image with the requested name.
  */
-extern int
+WIMLIBAPI int
 wimlib_add_empty_image(WIMStruct *wim,
                       const wimlib_tchar *name,
                       int *new_idx_ret);
@@ -2680,7 +2716,7 @@ wimlib_add_empty_image(WIMStruct *wim,
  * In addition, if ::WIMLIB_ADD_FLAG_VERBOSE is specified in @p add_flags, it
  * will receive ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY.
  */
-extern int
+WIMLIBAPI int
 wimlib_add_image(WIMStruct *wim,
                 const wimlib_tchar *source,
                 const wimlib_tchar *name,
@@ -2697,7 +2733,7 @@ wimlib_add_image(WIMStruct *wim,
  * same as wimlib_add_image().  See the documentation for <b>wimcapture</b> for
  * full details on how this mode works.
  */
-extern int
+WIMLIBAPI int
 wimlib_add_image_multisource(WIMStruct *wim,
                             const struct wimlib_capture_source *sources,
                             size_t num_sources,
@@ -2714,7 +2750,7 @@ wimlib_add_image_multisource(WIMStruct *wim,
  * This just builds an appropriate ::wimlib_add_command and passes it to
  * wimlib_update_image().
  */
-extern int
+WIMLIBAPI int
 wimlib_add_tree(WIMStruct *wim, int image,
                const wimlib_tchar *fs_source_path,
                const wimlib_tchar *wim_target_path, int add_flags);
@@ -2738,7 +2774,7 @@ wimlib_add_tree(WIMStruct *wim, int image,
  * @param wim_ret
  *     On success, a pointer to the new ::WIMStruct is written to the memory
  *     location pointed to by this parameter.  This ::WIMStruct must be freed
- *     using using wimlib_free() when finished with it.
+ *     using wimlib_free() when finished with it.
  *
  * @return 0 on success; a ::wimlib_error_code value on failure.
  *
@@ -2747,7 +2783,7 @@ wimlib_add_tree(WIMStruct *wim, int image,
  * @retval ::WIMLIB_ERR_NOMEM
  *     Insufficient memory to allocate a new ::WIMStruct.
  */
-extern int
+WIMLIBAPI int
 wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret);
 
 /**
@@ -2778,7 +2814,7 @@ wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret);
  * If this function fails when @p image was ::WIMLIB_ALL_IMAGES, then it's
  * possible that some but not all of the images were deleted.
  */
-extern int
+WIMLIBAPI int
 wimlib_delete_image(WIMStruct *wim, int image);
 
 /**
@@ -2789,7 +2825,7 @@ wimlib_delete_image(WIMStruct *wim, int image);
  * This just builds an appropriate ::wimlib_delete_command and passes it to
  * wimlib_update_image().
  */
-extern int
+WIMLIBAPI int
 wimlib_delete_path(WIMStruct *wim, int image,
                   const wimlib_tchar *path, int delete_flags);
 
@@ -2861,7 +2897,7 @@ wimlib_delete_path(WIMStruct *wim, int image,
  * indicate failure (for different reasons) to read the metadata resource for an
  * image in @p src_wim that needed to be exported.
  */
-extern int
+WIMLIBAPI int
 wimlib_export_image(WIMStruct *src_wim, int src_image,
                    WIMStruct *dest_wim,
                    const wimlib_tchar *dest_name,
@@ -2980,7 +3016,7 @@ wimlib_export_image(WIMStruct *src_wim, int src_image,
  * ::WIMLIB_PROGRESS_MSG_EXTRACT_METADATA messages, then
  * ::WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
  */
-extern int
+WIMLIBAPI int
 wimlib_extract_image(WIMStruct *wim, int image,
                     const wimlib_tchar *target, int extract_flags);
 
@@ -3022,7 +3058,7 @@ wimlib_extract_image(WIMStruct *wim, int image,
  * @retval ::WIMLIB_ERR_NOT_PIPABLE
  *     The WIM being piped over @p pipe_fd is a normal WIM, not a pipable WIM.
  */
-extern int
+WIMLIBAPI int
 wimlib_extract_image_from_pipe(int pipe_fd,
                               const wimlib_tchar *image_num_or_name,
                               const wimlib_tchar *target, int extract_flags);
@@ -3036,7 +3072,7 @@ wimlib_extract_image_from_pipe(int pipe_fd,
  * ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, in addition to
  * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN.
  */
-extern int
+WIMLIBAPI int
 wimlib_extract_image_from_pipe_with_progress(int pipe_fd,
                                             const wimlib_tchar *image_num_or_name,
                                             const wimlib_tchar *target,
@@ -3063,7 +3099,7 @@ wimlib_extract_image_from_pipe_with_progress(int pipe_fd,
  * cannot read the path list file (e.g. ::WIMLIB_ERR_OPEN, ::WIMLIB_ERR_STAT,
  * ::WIMLIB_ERR_READ).
  */
-extern int
+WIMLIBAPI int
 wimlib_extract_pathlist(WIMStruct *wim, int image,
                        const wimlib_tchar *target,
                        const wimlib_tchar *path_list_file,
@@ -3138,7 +3174,7 @@ wimlib_extract_pathlist(WIMStruct *wim, int image,
  * If a progress function is registered with @p wim, then it will receive
  * ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS.
  */
-extern int
+WIMLIBAPI int
 wimlib_extract_paths(WIMStruct *wim,
                     int image,
                     const wimlib_tchar *target,
@@ -3160,7 +3196,7 @@ wimlib_extract_paths(WIMStruct *wim,
  * @retval ::WIMLIB_ERR_WRITE
  *     Failed to write the data to the requested file.
  */
-extern int
+WIMLIBAPI int
 wimlib_extract_xml_data(WIMStruct *wim, FILE *fp);
 
 /**
@@ -3175,7 +3211,7 @@ wimlib_extract_xml_data(WIMStruct *wim, FILE *fp);
  * @param wim
  *     Pointer to the ::WIMStruct to release.  If @c NULL, no action is taken.
  */
-extern void
+WIMLIBAPI void
 wimlib_free(WIMStruct *wim);
 
 /**
@@ -3191,7 +3227,7 @@ wimlib_free(WIMStruct *wim);
  *     "None", "LZX", or "XPRESS".  If the value was unrecognized, then
  *     the resulting string will be "Invalid".
  */
-extern const wimlib_tchar *
+WIMLIBAPI const wimlib_tchar *
 wimlib_get_compression_type_string(enum wimlib_compression_type ctype);
 
 /**
@@ -3207,7 +3243,7 @@ wimlib_get_compression_type_string(enum wimlib_compression_type ctype);
  *     the value was unrecognized, then the resulting string will be "Unknown
  *     error".
  */
-extern const wimlib_tchar *
+WIMLIBAPI const wimlib_tchar *
 wimlib_get_error_string(enum wimlib_error_code code);
 
 /**
@@ -3216,7 +3252,7 @@ wimlib_get_error_string(enum wimlib_error_code code);
  * Get the description of the specified image.  Equivalent to
  * <tt>wimlib_get_image_property(wim, image, "DESCRIPTION")</tt>.
  */
-extern const wimlib_tchar *
+WIMLIBAPI const wimlib_tchar *
 wimlib_get_image_description(const WIMStruct *wim, int image);
 
 /**
@@ -3227,7 +3263,7 @@ wimlib_get_image_description(const WIMStruct *wim, int image);
  * wimlib_get_image_name() will return an empty string if the image is unnamed
  * whereas wimlib_get_image_property() may return @c NULL in that case.
  */
-extern const wimlib_tchar *
+WIMLIBAPI const wimlib_tchar *
 wimlib_get_image_name(const WIMStruct *wim, int image);
 
 /**
@@ -3258,7 +3294,7 @@ wimlib_get_image_name(const WIMStruct *wim, int image);
  *     no such property.  The string may not remain valid after later library
  *     calls, so the caller should duplicate it if needed.
  */
-extern const wimlib_tchar *
+WIMLIBAPI const wimlib_tchar *
 wimlib_get_image_property(const WIMStruct *wim, int image,
                          const wimlib_tchar *property_name);
 
@@ -3273,7 +3309,7 @@ wimlib_get_image_property(const WIMStruct *wim, int image,
  * 20) | (WIMLIB_MINOR_VERSION << 10) | WIMLIB_PATCH_VERSION)</c> for the
  * corresponding header file.
  */
-extern uint32_t
+WIMLIBAPI uint32_t
 wimlib_get_version(void);
 
 /**
@@ -3283,7 +3319,7 @@ wimlib_get_version(void);
  * PACKAGE_VERSION string that was set at build time.  (This allows a beta
  * release to be distinguished from an official release.)
  */
-extern const wimlib_tchar *
+WIMLIBAPI const wimlib_tchar *
 wimlib_get_version_string(void);
 
 /**
@@ -3300,7 +3336,7 @@ wimlib_get_version_string(void);
  *
  * @return 0
  */
-extern int
+WIMLIBAPI int
 wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info);
 
 /**
@@ -3330,7 +3366,7 @@ wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info);
  * @retval ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE
  *     Failed to read the XML document from the WIM file.
  */
-extern int
+WIMLIBAPI int
 wimlib_get_xml_data(WIMStruct *wim, void **buf_ret, size_t *bufsize_ret);
 
 /**
@@ -3351,7 +3387,7 @@ wimlib_get_xml_data(WIMStruct *wim, void **buf_ret, size_t *bufsize_ret);
  *     ::WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES were specified in @p
  *     init_flags, but the corresponding privileges could not be acquired.
  */
-extern int
+WIMLIBAPI int
 wimlib_global_init(int init_flags);
 
 /**
@@ -3360,7 +3396,7 @@ wimlib_global_init(int init_flags);
  * Cleanup function for wimlib.  You are not required to call this function, but
  * it will release any global resources allocated by the library.
  */
-extern void
+WIMLIBAPI void
 wimlib_global_cleanup(void);
 
 /**
@@ -3379,7 +3415,7 @@ wimlib_global_cleanup(void);
  *     if there is no image named @p name in @p wim.  If @p name is @c NULL or
  *     the empty string, then @c false is returned.
  */
-extern bool
+WIMLIBAPI bool
 wimlib_image_name_in_use(const WIMStruct *wim, const wimlib_tchar *name);
 
 /**
@@ -3426,7 +3462,7 @@ wimlib_image_name_in_use(const WIMStruct *wim, const wimlib_tchar *name);
  * indicate failure (for different reasons) to read the metadata resource for an
  * image over which iteration needed to be done.
  */
-extern int
+WIMLIBAPI int
 wimlib_iterate_dir_tree(WIMStruct *wim, int image, const wimlib_tchar *path,
                        int flags,
                        wimlib_iterate_dir_tree_callback_t cb, void *user_ctx);
@@ -3460,7 +3496,7 @@ wimlib_iterate_dir_tree(WIMStruct *wim, int image, const wimlib_tchar *path,
  * @return 0 if all calls to @p cb returned 0; otherwise the first nonzero value
  * that was returned from @p cb.
  */
-extern int
+WIMLIBAPI int
 wimlib_iterate_lookup_table(WIMStruct *wim, int flags,
                            wimlib_iterate_lookup_table_callback_t cb,
                            void *user_ctx);
@@ -3501,7 +3537,7 @@ wimlib_iterate_lookup_table(WIMStruct *wim, int flags,
  * an easy-to-use wrapper around this that has some advantages (e.g.  extra
  * sanity checks).
  */
-extern int
+WIMLIBAPI int
 wimlib_join(const wimlib_tchar * const *swms,
            unsigned num_swms,
            const wimlib_tchar *output_path,
@@ -3519,7 +3555,7 @@ wimlib_join(const wimlib_tchar * const *swms,
  * ::WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY messages when each of the split WIM
  * parts is opened.
  */
-extern int
+WIMLIBAPI int
 wimlib_join_with_progress(const wimlib_tchar * const *swms,
                          unsigned num_swms,
                          const wimlib_tchar *output_path,
@@ -3528,6 +3564,24 @@ wimlib_join_with_progress(const wimlib_tchar * const *swms,
                          wimlib_progress_func_t progfunc,
                          void *progctx);
 
+/**
+ * @ingroup G_general
+ *
+ * Load a UTF-8 or UTF-16LE encoded text file into memory.
+ *
+ * @param path
+ *     The path to the file, or NULL or "-" to use standard input.
+ * @param tstr_ret
+ *     On success, a buffer containing the file's text as a "wimlib_tchar"
+ *     string is returned here.  The buffer must be freed using free().
+ * @param tstr_nchars_ret
+ *     On success, the length of the text in "wimlib_tchar"s is returned here.
+ *
+ * @return 0 on success; a ::wimlib_error_code value on failure.
+ */
+WIMLIBAPI int
+wimlib_load_text_file(const wimlib_tchar *path,
+                     wimlib_tchar **tstr_ret, size_t *tstr_nchars_ret);
 
 /**
  * @ingroup G_mounting_wim_images
@@ -3604,7 +3658,7 @@ wimlib_join_with_progress(const wimlib_tchar * const *swms,
  * To unmount the image, call wimlib_unmount_image().  This may be done in a
  * different process.
  */
-extern int
+WIMLIBAPI int
 wimlib_mount_image(WIMStruct *wim,
                   int image,
                   const wimlib_tchar *dir,
@@ -3623,8 +3677,8 @@ wimlib_mount_image(WIMStruct *wim,
  * @param wim_ret
  *     On success, a pointer to a new ::WIMStruct backed by the specified
  *     on-disk WIM file is written to the memory location pointed to by this
- *     parameter.  This ::WIMStruct must be freed using using wimlib_free()
- *     when finished with it.
+ *     parameter.  This ::WIMStruct must be freed using wimlib_free() when
+ *     finished with it.
  *
  * @return 0 on success; a ::wimlib_error_code value on failure.
  *
@@ -3680,7 +3734,7 @@ wimlib_mount_image(WIMStruct *wim,
  * @retval ::WIMLIB_ERR_XML
  *     The XML data of the WIM was invalid.
  */
-extern int
+WIMLIBAPI int
 wimlib_open_wim(const wimlib_tchar *wim_file,
                int open_flags,
                WIMStruct **wim_ret);
@@ -3696,7 +3750,7 @@ wimlib_open_wim(const wimlib_tchar *wim_file,
  * progress function will receive ::WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY
  * messages while checking the WIM file's integrity.
  */
-extern int
+WIMLIBAPI int
 wimlib_open_wim_with_progress(const wimlib_tchar *wim_file,
                              int open_flags,
                              WIMStruct **wim_ret,
@@ -3762,7 +3816,7 @@ wimlib_open_wim_with_progress(const wimlib_tchar *wim_file,
  * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, and
  * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_END.
  */
-extern int
+WIMLIBAPI int
 wimlib_overwrite(WIMStruct *wim, int write_flags, unsigned num_threads);
 
 /**
@@ -3785,7 +3839,7 @@ wimlib_overwrite(WIMStruct *wim, int write_flags, unsigned num_threads);
  * This function is deprecated; use wimlib_get_xml_data() or
  * wimlib_get_image_property() to query image information instead.
  */
-extern void
+WIMLIBAPI void
 wimlib_print_available_images(const WIMStruct *wim, int image);
 
 /**
@@ -3793,7 +3847,7 @@ wimlib_print_available_images(const WIMStruct *wim, int image);
  *
  * Print the header of the WIM file (intended for debugging only).
  */
-extern void
+WIMLIBAPI void
 wimlib_print_header(const WIMStruct *wim);
 
 /**
@@ -3837,7 +3891,7 @@ wimlib_print_header(const WIMStruct *wim);
  * This function can additionally return most values that can be returned by
  * wimlib_open_wim().
  */
-extern int
+WIMLIBAPI int
 wimlib_reference_resource_files(WIMStruct *wim,
                                const wimlib_tchar * const *resource_wimfiles_or_globs,
                                unsigned count,
@@ -3864,7 +3918,7 @@ wimlib_reference_resource_files(WIMStruct *wim,
  *
  * @return 0 on success; a ::wimlib_error_code value on failure.
  */
-extern int
+WIMLIBAPI int
 wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims,
                           unsigned num_resource_wims, int ref_flags);
 
@@ -3930,7 +3984,7 @@ wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims,
  * indicate failure (for different reasons) to read the metadata resource for
  * the template image.
  */
-extern int
+WIMLIBAPI int
 wimlib_reference_template_image(WIMStruct *wim, int new_image,
                                WIMStruct *template_wim, int template_image,
                                int flags);
@@ -3950,7 +4004,7 @@ wimlib_reference_template_image(WIMStruct *wim, int new_image,
  *     The value which will be passed as the third argument to calls to @p
  *     progfunc.
  */
-extern void
+WIMLIBAPI void
 wimlib_register_progress_function(WIMStruct *wim,
                                  wimlib_progress_func_t progfunc,
                                  void *progctx);
@@ -3964,7 +4018,7 @@ wimlib_register_progress_function(WIMStruct *wim,
  * This just builds an appropriate ::wimlib_rename_command and passes it to
  * wimlib_update_image().
  */
-extern int
+WIMLIBAPI int
 wimlib_rename_path(WIMStruct *wim, int image,
                   const wimlib_tchar *source_path, const wimlib_tchar *dest_path);
 
@@ -3998,7 +4052,7 @@ wimlib_rename_path(WIMStruct *wim, int image,
  *     images, an unnamed image must be specified by index to eliminate the
  *     ambiguity.)
  */
-extern int
+WIMLIBAPI int
 wimlib_resolve_image(WIMStruct *wim,
                     const wimlib_tchar *image_name_or_num);
 
@@ -4014,12 +4068,9 @@ wimlib_resolve_image(WIMStruct *wim,
  * This also enables error messages, as if by a call to
  * wimlib_set_print_errors(true).
  *
- * @return 0 on success; a ::wimlib_error_code value on failure.
- *
- * @retval ::WIMLIB_ERR_UNSUPPORTED
- *     wimlib was compiled using the <c>--without-error-messages</c> option.
+ * @return 0
  */
-extern int
+WIMLIBAPI int
 wimlib_set_error_file(FILE *fp);
 
 /**
@@ -4035,10 +4086,8 @@ wimlib_set_error_file(FILE *fp);
  *
  * @retval ::WIMLIB_ERR_OPEN
  *     The file named by @p path could not be opened for appending.
- * @retval ::WIMLIB_ERR_UNSUPPORTED
- *     wimlib was compiled using the <c>--without-error-messages</c> option.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_error_file_by_name(const wimlib_tchar *path);
 
 /**
@@ -4049,7 +4098,7 @@ wimlib_set_error_file_by_name(const wimlib_tchar *path);
  *
  * Note that "description" is misspelled in the name of this function.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_image_descripton(WIMStruct *wim, int image,
                            const wimlib_tchar *description);
 
@@ -4060,7 +4109,7 @@ wimlib_set_image_descripton(WIMStruct *wim, int image,
  * (usually something like "Core" or "Ultimate").  Equivalent to
  * <tt>wimlib_set_image_property(wim, image, "FLAGS", flags)</tt>.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_image_flags(WIMStruct *wim, int image, const wimlib_tchar *flags);
 
 /**
@@ -4069,7 +4118,7 @@ wimlib_set_image_flags(WIMStruct *wim, int image, const wimlib_tchar *flags);
  * Change the name of a WIM image.  Equivalent to
  * <tt>wimlib_set_image_property(wim, image, "NAME", name)</tt>.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name);
 
 /**
@@ -4109,7 +4158,7 @@ wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name);
  *     @p property_name has an unsupported format, or @p property_name included
  *     a bracketed index that was too high.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_image_property(WIMStruct *wim, int image,
                          const wimlib_tchar *property_name,
                          const wimlib_tchar *property_value);
@@ -4142,7 +4191,7 @@ wimlib_set_image_property(WIMStruct *wim, int image,
  *
  * @return 0
  */
-extern int
+WIMLIBAPI int
 wimlib_set_memory_allocator(void *(*malloc_func)(size_t),
                            void (*free_func)(void *),
                            void *(*realloc_func)(void *, size_t));
@@ -4172,7 +4221,7 @@ wimlib_set_memory_allocator(void *(*malloc_func)(size_t),
  *     @p chunk_size was not 0 or a supported chunk size for the currently
  *     selected output compression type.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size);
 
 /**
@@ -4181,7 +4230,7 @@ wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size);
  * Similar to wimlib_set_output_chunk_size(), but set the chunk size for writing
  * solid resources.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_output_pack_chunk_size(WIMStruct *wim, uint32_t chunk_size);
 
 /**
@@ -4203,7 +4252,7 @@ wimlib_set_output_pack_chunk_size(WIMStruct *wim, uint32_t chunk_size);
  * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE
  *     @p ctype did not specify a valid compression type.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_output_compression_type(WIMStruct *wim,
                                   enum wimlib_compression_type ctype);
 
@@ -4213,7 +4262,7 @@ wimlib_set_output_compression_type(WIMStruct *wim,
  * Similar to wimlib_set_output_compression_type(), but set the compression type
  * for writing solid resources.  This cannot be ::WIMLIB_COMPRESSION_TYPE_NONE.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_output_pack_compression_type(WIMStruct *wim,
                                        enum wimlib_compression_type ctype);
 
@@ -4234,12 +4283,9 @@ wimlib_set_output_pack_compression_type(WIMStruct *wim,
  *     @c true if messages are to be printed; @c false if messages are not to
  *     be printed.
  *
- * @return 0 on success; a ::wimlib_error_code value on failure.
- *
- * @retval ::WIMLIB_ERR_UNSUPPORTED
- *     wimlib was compiled using the <c>--without-error-messages</c> option.
+ * @return 0
  */
-extern int
+WIMLIBAPI int
 wimlib_set_print_errors(bool show_messages);
 
 /**
@@ -4265,7 +4311,7 @@ wimlib_set_print_errors(bool show_messages);
  *     ::wimlib_wim_info.boot_index did not specify 0 or a valid 1-based image
  *     index in the WIM.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info,
                    int which);
 
@@ -4305,9 +4351,11 @@ wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info,
  * If a progress function is registered with @p wim, then for each split WIM
  * part that is written it will receive the messages
  * ::WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART and
- * ::WIMLIB_PROGRESS_MSG_SPLIT_END_PART.
+ * ::WIMLIB_PROGRESS_MSG_SPLIT_END_PART.  Since wimlib v1.13.4 it will also
+ * receive ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS messages while writing each part;
+ * these messages will report the progress of the current part only.
  */
-extern int
+WIMLIBAPI int
 wimlib_split(WIMStruct *wim,
             const wimlib_tchar *swm_name,
             uint64_t part_size,
@@ -4350,7 +4398,7 @@ wimlib_split(WIMStruct *wim,
  * ::WIMLIB_PROGRESS_MSG_END_VERIFY_IMAGE, and
  * ::WIMLIB_PROGRESS_MSG_VERIFY_STREAMS.
  */
-extern int
+WIMLIBAPI int
 wimlib_verify_wim(WIMStruct *wim, int verify_flags);
 
 /**
@@ -4386,7 +4434,7 @@ wimlib_verify_wim(WIMStruct *wim, int verify_flags);
  * by using the @c umount or @c fusermount programs.  However, you need to call
  * this function if you want changes to be committed.
  */
-extern int
+WIMLIBAPI int
 wimlib_unmount_image(const wimlib_tchar *dir, int unmount_flags);
 
 /**
@@ -4397,7 +4445,7 @@ wimlib_unmount_image(const wimlib_tchar *dir, int unmount_flags);
  * message.  In addition, if changes are committed from a read-write mount, the
  * progress function will receive ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS messages.
  */
-extern int
+WIMLIBAPI int
 wimlib_unmount_image_with_progress(const wimlib_tchar *dir,
                                   int unmount_flags,
                                   wimlib_progress_func_t progfunc,
@@ -4493,7 +4541,7 @@ wimlib_unmount_image_with_progress(const wimlib_tchar *dir,
  * indicate failure (for different reasons) to read the metadata resource for an
  * image that needed to be updated.
  */
-extern int
+WIMLIBAPI int
 wimlib_update_image(WIMStruct *wim,
                    int image,
                    const struct wimlib_update_command *cmds,
@@ -4561,7 +4609,7 @@ wimlib_update_image(WIMStruct *wim,
  * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, and
  * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_END.
  */
-extern int
+WIMLIBAPI int
 wimlib_write(WIMStruct *wim,
             const wimlib_tchar *path,
             int image,
@@ -4588,7 +4636,7 @@ wimlib_write(WIMStruct *wim,
  *     @p fd was not seekable, but ::WIMLIB_WRITE_FLAG_PIPABLE was not
  *     specified in @p write_flags.
  */
-extern int
+WIMLIBAPI int
 wimlib_write_to_fd(WIMStruct *wim,
                   int fd,
                   int image,
@@ -4646,7 +4694,7 @@ struct wimlib_decompressor;
  * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE
  *     @p ctype was neither a supported compression type nor -1.
  */
-extern int
+WIMLIBAPI int
 wimlib_set_default_compression_level(int ctype, unsigned int compression_level);
 
 /**
@@ -4657,7 +4705,7 @@ wimlib_set_default_compression_level(int ctype, unsigned int compression_level);
  * compression type is invalid, or the @p max_block_size for that compression
  * type is invalid.
  */
-extern uint64_t
+WIMLIBAPI uint64_t
 wimlib_get_compressor_needed_memory(enum wimlib_compression_type ctype,
                                    size_t max_block_size,
                                    unsigned int compression_level);
@@ -4732,7 +4780,7 @@ wimlib_get_compressor_needed_memory(enum wimlib_compression_type ctype,
  * @retval ::WIMLIB_ERR_NOMEM
  *     Insufficient memory to allocate the compressor.
  */
-extern int
+WIMLIBAPI int
 wimlib_create_compressor(enum wimlib_compression_type ctype,
                         size_t max_block_size,
                         unsigned int compression_level,
@@ -4758,7 +4806,7 @@ wimlib_create_compressor(enum wimlib_compression_type ctype,
  *     The size of the compressed data, in bytes, or 0 if the data could not be
  *     compressed to @p compressed_size_avail or fewer bytes.
  */
-extern size_t
+WIMLIBAPI size_t
 wimlib_compress(const void *uncompressed_data, size_t uncompressed_size,
                void *compressed_data, size_t compressed_size_avail,
                struct wimlib_compressor *compressor);
@@ -4769,7 +4817,7 @@ wimlib_compress(const void *uncompressed_data, size_t uncompressed_size,
  * @param compressor
  *     The compressor to free.  If @c NULL, no action is taken.
  */
-extern void
+WIMLIBAPI void
 wimlib_free_compressor(struct wimlib_compressor *compressor);
 
 /**
@@ -4805,7 +4853,7 @@ wimlib_free_compressor(struct wimlib_compressor *compressor);
  * @retval ::WIMLIB_ERR_NOMEM
  *     Insufficient memory to allocate the decompressor.
  */
-extern int
+WIMLIBAPI int
 wimlib_create_decompressor(enum wimlib_compression_type ctype,
                           size_t max_block_size,
                           struct wimlib_decompressor **decompressor_ret);
@@ -4837,7 +4885,7 @@ wimlib_create_decompressor(enum wimlib_compression_type ctype,
  * as the @p uncompressed_size parameter.  If this is not done correctly,
  * decompression may fail or the data may be decompressed incorrectly.
  */
-extern int
+WIMLIBAPI int
 wimlib_decompress(const void *compressed_data, size_t compressed_size,
                  void *uncompressed_data, size_t uncompressed_size,
                  struct wimlib_decompressor *decompressor);
@@ -4848,7 +4896,7 @@ wimlib_decompress(const void *compressed_data, size_t compressed_size,
  * @param decompressor
  *     The decompressor to free.  If @c NULL, no action is taken.
  */
-extern void
+WIMLIBAPI void
 wimlib_free_decompressor(struct wimlib_decompressor *decompressor);
 
 
index 2592fae6fd90da5bcd040de7b46313bcf7aa3d39..d801ab0c2c69acc05eadbaa0447db45a1b7c39df 100644 (file)
@@ -81,7 +81,12 @@ struct apply_ctx {
 /* Maximum number of UNIX file descriptors, NTFS attributes, or Windows file
  * handles that can be opened simultaneously to extract a blob to multiple
  * destinations.  */
+#ifndef __APPLE__
 #define MAX_OPEN_FILES 512
+#else /* !__APPLE__ */
+/* With macOS, reduce to 128 because the default value for ulimit -n is 256 */
+#define MAX_OPEN_FILES 128
+#endif /* __APPLE__ */
 
 static inline int
 extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg)
@@ -89,7 +94,7 @@ extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg)
        return call_progress(ctx->progfunc, msg, &ctx->progress, ctx->progctx);
 }
 
-extern int
+int
 do_file_extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg);
 
 #define COUNT_PER_FILE_PROGRESS 256
@@ -103,10 +108,10 @@ maybe_do_file_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg)
        return 0;
 }
 
-extern int
+int
 start_file_structure_phase(struct apply_ctx *ctx, u64 end_file_count);
 
-extern int
+int
 start_file_metadata_phase(struct apply_ctx *ctx, u64 end_file_count);
 
 /* Report that a file was created, prior to blob extraction.  */
@@ -123,10 +128,10 @@ report_file_metadata_applied(struct apply_ctx *ctx)
        return maybe_do_file_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA);
 }
 
-extern int
+int
 end_file_structure_phase(struct apply_ctx *ctx);
 
-extern int
+int
 end_file_metadata_phase(struct apply_ctx *ctx);
 
 static inline int
@@ -135,7 +140,7 @@ report_apply_error(struct apply_ctx *ctx, int error_code, const tchar *path)
        return report_error(ctx->progfunc, ctx->progctx, error_code, path);
 }
 
-extern bool
+bool
 detect_sparse_region(const void *data, size_t size, size_t *len_ret);
 
 static inline bool
@@ -158,7 +163,7 @@ maybe_detect_sparse_region(const void *data, size_t size, size_t *len_ret,
             dentry != NULL;                                            \
             dentry = dentry->d_next_extraction_alias)
 
-extern int
+int
 extract_blob_list(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs);
 
 /*
@@ -263,7 +268,7 @@ struct apply_operations {
        bool single_tree_only;
 };
 
-#ifdef __WIN32__
+#ifdef _WIN32
   extern const struct apply_operations win32_apply_ops;
 #else
   extern const struct apply_operations unix_apply_ops;
index 5da01bf7972e2b88fc51954f1a31caafca12c8a8..5c8fe650846565eab7b40b47b4f3976c47e51ab4 100644 (file)
@@ -1,11 +1,7 @@
 #ifndef _WIMLIB_ASSERT_H
 #define _WIMLIB_ASSERT_H
 
-#ifdef ENABLE_ASSERTIONS
 #include <assert.h>
-#  define wimlib_assert(expr) assert(expr)
-#else
-#  define wimlib_assert(expr)
-#endif
+#define wimlib_assert(expr)    assert(expr)
 
 #endif /* _WIMLIB_ASSERT_H */
index b7a9bb1b27fb3958bc8734cb160f36944d66981c..5ae11ebbad72624be12c716725626d4e7cc9d9dd 100644 (file)
@@ -2,21 +2,28 @@
  * avl_tree.h - intrusive, nonrecursive AVL tree data structure (self-balancing
  *             binary search tree), header file
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _AVL_TREE_H_
@@ -64,7 +71,7 @@ avl_get_parent(const struct avl_tree_node *node)
 }
 
 /* (Internal use only)  */
-extern void
+void
 avl_tree_rebalance_after_insert(struct avl_tree_node **root_ptr,
                                struct avl_tree_node *inserted);
 
@@ -246,27 +253,27 @@ avl_tree_insert(struct avl_tree_node **root_ptr,
 
 /* Removes an item from the specified AVL tree.
  * See implementation for details.  */
-extern void
+void
 avl_tree_remove(struct avl_tree_node **root_ptr, struct avl_tree_node *node);
 
 /* Nonrecursive AVL tree traversal functions  */
 
-extern struct avl_tree_node *
+struct avl_tree_node *
 avl_tree_first_in_order(const struct avl_tree_node *root);
 
-extern struct avl_tree_node *
+struct avl_tree_node *
 avl_tree_last_in_order(const struct avl_tree_node *root);
 
-extern struct avl_tree_node *
+struct avl_tree_node *
 avl_tree_next_in_order(const struct avl_tree_node *node);
 
-extern struct avl_tree_node *
+struct avl_tree_node *
 avl_tree_prev_in_order(const struct avl_tree_node *node);
 
-extern struct avl_tree_node *
+struct avl_tree_node *
 avl_tree_first_in_postorder(const struct avl_tree_node *root);
 
-extern struct avl_tree_node *
+struct avl_tree_node *
 avl_tree_next_in_postorder(const struct avl_tree_node *prev,
                           const struct avl_tree_node *prev_parent);
 
index 1fc30f6c69a1b9a03e0987e215ffe5abe5900340..e5c4872e40b4035cd987ad70bcc8e0026be73fbf 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * bitops.h - inline functions for bit manipulation
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_BITOPS_H
 static forceinline unsigned
 bsr32(u32 v)
 {
-#ifdef compiler_bsr32
-       return compiler_bsr32(v);
-#else
-       unsigned bit = 0;
-       while ((v >>= 1) != 0)
-               bit++;
-       return bit;
-#endif
+       return 31 - __builtin_clz(v);
 }
 
 static forceinline unsigned
 bsr64(u64 v)
 {
-#ifdef compiler_bsr64
-       return compiler_bsr64(v);
-#else
-       unsigned bit = 0;
-       while ((v >>= 1) != 0)
-               bit++;
-       return bit;
-#endif
+       return 63 - __builtin_clzll(v);
 }
 
 static forceinline unsigned
@@ -75,27 +68,13 @@ bsrw(machine_word_t v)
 static forceinline unsigned
 bsf32(u32 v)
 {
-#ifdef compiler_bsf32
-       return compiler_bsf32(v);
-#else
-       unsigned bit;
-       for (bit = 0; !(v & 1); bit++, v >>= 1)
-               ;
-       return bit;
-#endif
+       return __builtin_ctz(v);
 }
 
 static forceinline unsigned
 bsf64(u64 v)
 {
-#ifdef compiler_bsf64
-       return compiler_bsf64(v);
-#else
-       unsigned bit;
-       for (bit = 0; !(v & 1); bit++, v >>= 1)
-               ;
-       return bit;
-#endif
+       return __builtin_ctzll(v);
 }
 
 static forceinline unsigned
index 610db2419e2f9b96c8c0b079f8830d937848109f..2c9aab15c7572c02613f7f0d90002270a8a8a16a 100644 (file)
@@ -40,7 +40,7 @@ enum blob_location {
        BLOB_IN_NTFS_VOLUME,
 #endif
 
-#ifdef __WIN32__
+#ifdef _WIN32
        /* Windows only: the blob's data is available in the file (or named data
         * stream) specified by @windows_file.  The data might be only properly
         * accessible through the Windows API.  */
@@ -141,6 +141,9 @@ struct blob_descriptor {
        /* 1 iff the SHA-1 message digest of this blob is unknown.  */
        u16 unhashed : 1;
 
+       /* 1 iff this blob has failed its checksum.  */
+       u16 corrupted : 1;
+
        /* Temporary fields used when writing blobs; set as documented for
         * prepare_blob_list_for_write().  */
        u16 unique_size : 1;
@@ -262,79 +265,79 @@ struct blob_descriptor {
        };
 };
 
-extern struct blob_table *
-new_blob_table(size_t capacity) _malloc_attribute;
+struct blob_table *
+new_blob_table(size_t capacity);
 
-extern void
+void
 free_blob_table(struct blob_table *table);
 
-extern int
+int
 read_blob_table(WIMStruct *wim);
 
-extern int
+int
 write_blob_table_from_blob_list(struct list_head *blob_list,
                                struct filedes *out_fd,
                                u16 part_number,
                                struct wim_reshdr *out_reshdr,
                                int write_resource_flags);
 
-extern struct blob_descriptor *
-new_blob_descriptor(void) _malloc_attribute;
+struct blob_descriptor *
+new_blob_descriptor(void);
 
-extern struct blob_descriptor *
-clone_blob_descriptor(const struct blob_descriptor *blob) _malloc_attribute;
+struct blob_descriptor *
+clone_blob_descriptor(const struct blob_descriptor *blob);
 
-extern void
+void
 blob_decrement_refcnt(struct blob_descriptor *blob, struct blob_table *table);
 
-extern void
+void
 blob_subtract_refcnt(struct blob_descriptor *blob, struct blob_table *table,
                     u32 count);
 
 #ifdef WITH_FUSE
-extern void
+void
 blob_decrement_num_opened_fds(struct blob_descriptor *blob);
 #endif
 
-extern void
+void
 blob_release_location(struct blob_descriptor *blob);
 
-extern void
+void
 free_blob_descriptor(struct blob_descriptor *blob);
 
-extern void
+void
 blob_table_insert(struct blob_table *table, struct blob_descriptor *blob);
 
-extern void
+void
 blob_table_unlink(struct blob_table *table, struct blob_descriptor *blob);
 
-extern struct blob_descriptor *
+struct blob_descriptor *
 lookup_blob(const struct blob_table *table, const u8 *hash);
 
-extern int
+int
 for_blob_in_table(struct blob_table *table,
                  int (*visitor)(struct blob_descriptor *, void *), void *arg);
 
-extern int
+int
 for_blob_in_table_sorted_by_sequential_order(struct blob_table *table,
                                             int (*visitor)(struct blob_descriptor *, void *),
                                             void *arg);
 
 struct wimlib_resource_entry;
 
-extern void
+void
 blob_to_wimlib_resource_entry(const struct blob_descriptor *blob,
                              struct wimlib_resource_entry *wentry);
 
-extern int
+int
 sort_blob_list(struct list_head *blob_list, size_t list_head_offset,
               int (*compar)(const void *, const void*));
 
-extern int
+int
 sort_blob_list_by_sequential_order(struct list_head *blob_list,
                                   size_t list_head_offset);
 
-extern int
+int
 cmp_blobs_by_sequential_order(const void *p1, const void *p2);
 
 static inline const struct blob_extraction_target *
@@ -381,41 +384,41 @@ static inline bool
 blob_is_in_file(const struct blob_descriptor *blob)
 {
        return blob->blob_location == BLOB_IN_FILE_ON_DISK
-#ifdef __WIN32__
+#ifdef _WIN32
            || blob->blob_location == BLOB_IN_WINDOWS_FILE
 #endif
           ;
 }
 
-#ifdef __WIN32__
-extern const wchar_t *
+#ifdef _WIN32
+const wchar_t *
 get_windows_file_path(const struct windows_file *file);
 #endif
 
 static inline const tchar *
 blob_file_path(const struct blob_descriptor *blob)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        if (blob->blob_location == BLOB_IN_WINDOWS_FILE)
                return get_windows_file_path(blob->windows_file);
 #endif
        return blob->file_on_disk;
 }
 
-extern struct blob_descriptor *
+struct blob_descriptor *
 new_blob_from_data_buffer(const void *buffer, size_t size,
                          struct blob_table *blob_table);
 
-extern struct blob_descriptor *
+struct blob_descriptor *
 after_blob_hashed(struct blob_descriptor *blob,
                  struct blob_descriptor **back_ptr,
-                 struct blob_table *blob_table);
+                 struct blob_table *blob_table, struct wim_inode *inode);
 
-extern int
+int
 hash_unhashed_blob(struct blob_descriptor *blob, struct blob_table *blob_table,
                   struct blob_descriptor **blob_ret);
 
-extern struct blob_descriptor **
+struct blob_descriptor **
 retrieve_pointer_to_unhashed_blob(struct blob_descriptor *blob);
 
 static inline void
index 05bd07d9daf540acfd2ad8abdcbe17a8aa8000d8..14e9c5ca9c1501d0b83d3f7d871fc270a6ee773e 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * bt_matchfinder.h - Lempel-Ziv matchfinding with a hash table of binary trees
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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.
  *
  * ----------------------------------------------------------------------------
  *
@@ -58,8 +65,7 @@
 
 #include <string.h>
 
-#include "wimlib/lz_extend.h"
-#include "wimlib/lz_hash.h"
+#include "wimlib/matchfinder_common.h"
 
 #define BT_MATCHFINDER_HASH3_ORDER 15
 #define BT_MATCHFINDER_HASH3_WAYS  2
@@ -135,28 +141,27 @@ TEMPLATED(bt_right_child)(struct TEMPLATED(bt_matchfinder) *mf, u32 node)
 }
 
 /* The minimum permissible value of 'max_len' for bt_matchfinder_get_matches()
- * and bt_matchfinder_skip_position().  There must be sufficiently many bytes
+ * and bt_matchfinder_skip_byte().  There must be sufficiently many bytes
  * remaining to load a 32-bit integer from the *next* position.  */
 #define BT_MATCHFINDER_REQUIRED_NBYTES 5
 
 /* Advance the binary tree matchfinder by one byte, optionally recording
  * matches.  @record_matches should be a compile-time constant.  */
 static forceinline struct lz_match *
-TEMPLATED(bt_matchfinder_advance_one_byte)(struct TEMPLATED(bt_matchfinder) * const restrict mf,
-                                          const u8 * const restrict in_begin,
+TEMPLATED(bt_matchfinder_advance_one_byte)(struct TEMPLATED(bt_matchfinder) * const mf,
+                                          const u8 * const in_begin,
                                           const ptrdiff_t cur_pos,
                                           const u32 max_len,
                                           const u32 nice_len,
                                           const u32 max_search_depth,
-                                          u32 next_hashes[const restrict static 2],
-                                          u32 * const restrict best_len_ret,
-                                          struct lz_match * restrict lz_matchptr,
+                                          u32 * const next_hashes,
+                                          u32 * const best_len_ret,
+                                          struct lz_match *lz_matchptr,
                                           const bool record_matches)
 {
        const u8 *in_next = in_begin + cur_pos;
        u32 depth_remaining = max_search_depth;
-       u32 next_seq4;
-       u32 next_seq3;
+       u32 next_hashseq;
        u32 hash3;
        u32 hash4;
 #ifdef BT_MATCHFINDER_HASH2_ORDER
@@ -175,14 +180,13 @@ TEMPLATED(bt_matchfinder_advance_one_byte)(struct TEMPLATED(bt_matchfinder) * co
        u32 len;
        u32 best_len = 3;
 
-       next_seq4 = load_u32_unaligned(in_next + 1);
-       next_seq3 = loaded_u32_to_u24(next_seq4);
+       next_hashseq = get_unaligned_le32(in_next + 1);
 
        hash3 = next_hashes[0];
        hash4 = next_hashes[1];
 
-       next_hashes[0] = lz_hash(next_seq3, BT_MATCHFINDER_HASH3_ORDER);
-       next_hashes[1] = lz_hash(next_seq4, BT_MATCHFINDER_HASH4_ORDER);
+       next_hashes[0] = lz_hash(next_hashseq & 0xFFFFFF, BT_MATCHFINDER_HASH3_ORDER);
+       next_hashes[1] = lz_hash(next_hashseq, BT_MATCHFINDER_HASH4_ORDER);
        prefetchw(&mf->hash3_tab[next_hashes[0]]);
        prefetchw(&mf->hash4_tab[next_hashes[1]]);
 
@@ -294,8 +298,8 @@ TEMPLATED(bt_matchfinder_advance_one_byte)(struct TEMPLATED(bt_matchfinder) * co
  * @in_begin
  *     Pointer to the beginning of the input buffer.
  * @cur_pos
- *     The current position in the input buffer (the position of the sequence
- *     being matched against).
+ *     The current position in the input buffer relative to @in_begin (the
+ *     position of the sequence being matched against).
  * @max_len
  *     The maximum permissible match length at this position.  Must be >=
  *     BT_MATCHFINDER_REQUIRED_NBYTES.
@@ -330,7 +334,7 @@ TEMPLATED(bt_matchfinder_get_matches)(struct TEMPLATED(bt_matchfinder) *mf,
                                      u32 max_len,
                                      u32 nice_len,
                                      u32 max_search_depth,
-                                     u32 next_hashes[static 2],
+                                     u32 next_hashes[2],
                                      u32 *best_len_ret,
                                      struct lz_match *lz_matchptr)
 {
@@ -353,12 +357,12 @@ TEMPLATED(bt_matchfinder_get_matches)(struct TEMPLATED(bt_matchfinder) *mf,
  * must do hashing and tree re-rooting.
  */
 static forceinline void
-TEMPLATED(bt_matchfinder_skip_position)(struct TEMPLATED(bt_matchfinder) *mf,
-                                       const u8 *in_begin,
-                                       ptrdiff_t cur_pos,
-                                       u32 nice_len,
-                                       u32 max_search_depth,
-                                       u32 next_hashes[static 2])
+TEMPLATED(bt_matchfinder_skip_byte)(struct TEMPLATED(bt_matchfinder) *mf,
+                                   const u8 *in_begin,
+                                   ptrdiff_t cur_pos,
+                                   u32 nice_len,
+                                   u32 max_search_depth,
+                                   u32 next_hashes[2])
 {
        u32 best_len;
        TEMPLATED(bt_matchfinder_advance_one_byte)(mf,
index 265e1362438a39c9238fe9d67657d65cf2d59f27..237ece5018b8c2de4ff3cd0af11635afd7e4e88b 100644 (file)
@@ -63,12 +63,10 @@ struct chunk_compressor {
 
 /* Functions that return implementations of the chunk_compressor interface.  */
 
-#ifdef ENABLE_MULTITHREADED_COMPRESSION
 int
 new_parallel_chunk_compressor(int out_ctype, u32 out_chunk_size,
                              unsigned num_threads, u64 max_memory,
                              struct chunk_compressor **compressor_ret);
-#endif
 
 int
 new_serial_chunk_compressor(int out_ctype, u32 out_chunk_size,
index 6a87e8912338a759b7516db26109a99d2529cb0f..77cd27f8864ebfe0bdc0b6a4f5fc322647556ca7 100644 (file)
@@ -3,21 +3,28 @@
  *
  * Compiler-specific definitions.  Currently, only GCC and clang are supported.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2013-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_COMPILER_H
 #  define __has_builtin(builtin)       0
 #endif
 
-/* Declare that the annotated function should be exported from the shared
- * library (or DLL).  */
-#ifdef __WIN32__
-#  define WIMLIBAPI __declspec(dllexport)
-#else
-#  define WIMLIBAPI __attribute__((visibility("default")))
-#endif
-
 /* Declare that the annotated function should always be inlined.  This might be
  * desirable in highly tuned code, e.g. compression codecs.  */
 #define forceinline            inline __attribute__((always_inline))
 /* Prefetch into L1 cache for write.  */
 #define prefetchw(addr)                __builtin_prefetch((addr), 1)
 
-/* Declare that the members of the annotated struct are tightly packed, and the
- * struct itself may be misaligned.  */
-#define _packed_attribute      __attribute__((packed))
-
-/* Declare that the annotated variable, or variables of the annotated type, are
- * to be aligned on n-byte boundaries.  */
-#define _aligned_attribute(n)  __attribute__((aligned(n)))
-
-/* Declare that pointers to the annotated type may alias other pointers.  */
-#define _may_alias_attribute   __attribute__((may_alias))
-
-/* Hint that the annotated function is rarely called.  */
-#if GCC_PREREQ(4, 4) || __has_attribute(cold)
-#  define _cold_attribute      __attribute__((cold))
-#else
-#  define _cold_attribute
-#endif
-
-/* Hint that the annotated function is malloc-like: any non-null pointer it
- * returns will not alias any pointer previously in use by the program.  */
-#define _malloc_attribute      __attribute__((malloc))
-
 /* Hint that the annotated function takes a printf()-like format string and
  * arguments.  This is currently disabled on Windows because MinGW does not
  * support this attribute on functions taking wide-character strings.  */
-#ifdef __WIN32__
+#ifdef _WIN32
 #  define _format_attribute(type, format_str, format_start)
 #else
 #  define _format_attribute(type, format_str, format_start)    \
                        __attribute__((format(type, format_str, format_start)))
 #endif
 
-/* Hint that the annotated function is intentionally not used.  This might be
- * the case if the function contains only static assertions.  */
-#define _unused_attribute      __attribute__((unused))
-
-/* Endianness definitions.  Either CPU_IS_BIG_ENDIAN or CPU_IS_LITTLE_ENDIAN is
- * set to 1.  The other is set to 0.  Note that newer gcc supports
+/* Endianness definitions.  Either CPU_IS_BIG_ENDIAN() or CPU_IS_LITTLE_ENDIAN()
+ * evaluates to 1.  The other evaluates to 0.  Note that newer gcc supports
  * __BYTE_ORDER__ for easily determining the endianness; older gcc doesn't.  In
  * the latter case we fall back to a configure-time check.  */
 #ifdef __BYTE_ORDER__
-#  define CPU_IS_BIG_ENDIAN    (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#  define CPU_IS_BIG_ENDIAN()  (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
 #elif defined(HAVE_CONFIG_H)
 #  include "config.h"
 #  ifdef WORDS_BIGENDIAN
-#    define CPU_IS_BIG_ENDIAN 1
+#    define CPU_IS_BIG_ENDIAN()        1
 #  else
-#    define CPU_IS_BIG_ENDIAN 0
+#    define CPU_IS_BIG_ENDIAN()        0
 #  endif
 #endif
-#define CPU_IS_LITTLE_ENDIAN (!CPU_IS_BIG_ENDIAN)
+#define CPU_IS_LITTLE_ENDIAN() (!CPU_IS_BIG_ENDIAN())
 
 /* UNALIGNED_ACCESS_IS_FAST should be defined to 1 if unaligned memory accesses
  * can be performed efficiently on the target platform.  */
-#if defined(__x86_64__) || defined(__i386__) || defined(__ARM_FEATURE_UNALIGNED)
+#if defined(__x86_64__) || defined(__i386__) || \
+       defined(__ARM_FEATURE_UNALIGNED) || defined(__powerpc64__)
 #  define UNALIGNED_ACCESS_IS_FAST 1
 #else
 #  define UNALIGNED_ACCESS_IS_FAST 0
 #endif
 
-/* Get the type of the specified expression.  */
-#define typeof     __typeof__
-
 /* Get the minimum of two variables, without multiple evaluation.  */
-#ifndef min
-#  define min(a, b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); \
-                       (_a < _b) ? _a : _b; })
-#endif
+#undef min
+#define min(a, b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); \
+                   (_a < _b) ? _a : _b; })
+#undef MIN
+#define MIN(a, b)      min((a), (b))
 
 /* Get the maximum of two variables, without multiple evaluation.  */
-#ifndef max
-#  define max(a, b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); \
-                       (_a > _b) ? _a : _b; })
-#endif
+#undef max
+#define max(a, b)  ({ typeof(a) _a = (a); typeof(b) _b = (b); \
+                   (_a > _b) ? _a : _b; })
+#undef MAX
+#define MAX(a, b)      max((a), (b))
+
+/* Get the maximum of three variables, without multiple evaluation.  */
+#undef max3
+#define max3(a, b, c)  max(max((a), (b)), (c))
 
 /* Swap the values of two variables, without multiple evaluation.  */
 #ifndef swap
 #  define swap(a, b) ({ typeof(a) _a = (a); (a) = (b); (b) = _a; })
 #endif
-
-/* (Optional) Efficiently swap the bytes of a 16-bit integer.  */
-#if GCC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16)
-#  define compiler_bswap16 __builtin_bswap16
-#endif
-
-/* (Optional) Efficiently swap the bytes of a 32-bit integer.  */
-#if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap32)
-#  define compiler_bswap32 __builtin_bswap32
-#endif
-
-/* (Optional) Efficiently swap the bytes of a 64-bit integer.  */
-#if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap64)
-#  define compiler_bswap64 __builtin_bswap64
-#endif
-
-/* (Optional) Find Last Set bit and Find First Set bit macros.  */
-#define compiler_bsr32(n)      (31 - __builtin_clz(n))
-#define compiler_bsr64(n)      (63 - __builtin_clzll(n))
-#define compiler_bsf32(n)      __builtin_ctz(n)
-#define compiler_bsf64(n)      __builtin_ctzll(n)
+#define SWAP(a, b)     swap((a), (b))
 
 /* Optional definitions for checking with 'sparse'.  */
 #ifdef __CHECKER__
index 7913a138734925aaae2a01a965390e33948fe7f7..f2ed4109e8712741785defc100659d36b8e066e8 100644 (file)
@@ -9,11 +9,11 @@
 
 #include "wimlib/types.h"
 
-extern void
-make_canonical_huffman_code(unsigned num_syms,
-                           unsigned max_codeword_len,
-                           const u32 freq_tab[restrict],
-                           u8 lens[restrict],
-                           u32 codewords[restrict]);
+#define MAX_NUM_SYMS           799     /* LZMS_MAX_NUM_SYMS */
+#define MAX_CODEWORD_LEN       16
+
+void
+make_canonical_huffman_code(unsigned num_syms, unsigned max_codeword_len,
+                           const u32 freqs[], u8 lens[], u32 codewords[]);
 
 #endif /* _WIMLIB_COMPRESS_COMMON_H */
diff --git a/include/wimlib/cpu_features.h b/include/wimlib/cpu_features.h
new file mode 100644 (file)
index 0000000..ee6ef48
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _WIMLIB_CPU_FEATURES_H
+#define _WIMLIB_CPU_FEATURES_H
+
+#include "wimlib/types.h"
+
+#define X86_CPU_FEATURE_SSSE3          0x00000001
+#define X86_CPU_FEATURE_SSE4_1         0x00000002
+#define X86_CPU_FEATURE_SSE4_2         0x00000004
+#define X86_CPU_FEATURE_AVX            0x00000008
+#define X86_CPU_FEATURE_BMI2           0x00000010
+#define X86_CPU_FEATURE_SHA            0x00000020
+
+#define ARM_CPU_FEATURE_SHA1           0x00000001
+
+#if (defined(__i386__) || defined(__x86_64__)) || \
+    (defined(__aarch64__) && defined(__linux__)) || \
+    (defined(__aarch64__) && defined(__APPLE__)) || \
+    (defined(__aarch64__) && defined(_WIN32))
+
+#define CPU_FEATURES_ENABLED   1
+extern u32 cpu_features;
+
+void init_cpu_features(void);
+
+#else
+
+#define CPU_FEATURES_ENABLED   0
+#define cpu_features 0
+
+static inline void
+init_cpu_features(void)
+{
+}
+
+#endif
+
+#endif /* _WIMLIB_CPU_FEATURES_H */
index 65eb2e4bd79dc906752735e0cfeeb227cdd9fb7f..46fce94fda6224716b0411baed52b1371c8ea98d 100644 (file)
@@ -3,21 +3,28 @@
  *
  * Header for decompression code shared by multiple compression formats.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2012-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_DECOMPRESS_COMMON_H
@@ -396,7 +403,7 @@ read_huffsym(struct input_bitstream *is, const u16 decode_table[],
 #define DECODE_TABLE(name, num_syms, table_bits, max_codeword_len) \
        u16 name[DECODE_TABLE_SIZE((num_syms), (table_bits), \
                                   (max_codeword_len))] \
-               _aligned_attribute(DECODE_TABLE_ALIGNMENT)
+               __attribute__((aligned(DECODE_TABLE_ALIGNMENT)))
 
 /*
  * Declare the temporary "working_space" array needed for building the decode
@@ -405,7 +412,7 @@ read_huffsym(struct input_bitstream *is, const u16 decode_table[],
 #define DECODE_TABLE_WORKING_SPACE(name, num_syms, max_codeword_len)   \
        u16 name[2 * ((max_codeword_len) + 1)  + (num_syms)];
 
-extern int
+int
 make_huffman_decode_table(u16 decode_table[], unsigned num_syms,
                          unsigned table_bits, const u8 lens[],
                          unsigned max_codeword_len, u16 working_space[]);
index 64ff6c30a4fd9fd072a019fe1d2ee7da3f314b75..d7dc138e0bf10ba0d2016465d676f01133793e43 100644 (file)
@@ -123,10 +123,10 @@ will_extract_dentry(const struct wim_dentry *dentry)
        return dentry->d_extraction_list_node.next != NULL;
 }
 
-extern size_t
+size_t
 dentry_out_total_length(const struct wim_dentry *dentry);
 
-extern int
+int
 for_dentry_in_tree(struct wim_dentry *root,
                   int (*visitor)(struct wim_dentry *, void *), void *args);
 
@@ -162,10 +162,10 @@ for_dentry_in_tree(struct wim_dentry *root,
 #define dentry_any_child(parent) \
        inode_any_child((parent)->d_inode)
 
-extern struct wim_dentry *
+struct wim_dentry *
 dentry_get_first_ci_match(struct wim_dentry *dentry);
 
-extern struct wim_dentry *
+struct wim_dentry *
 dentry_get_next_ci_match(struct wim_dentry *dentry,
                         struct wim_dentry *ci_match);
 
@@ -176,75 +176,75 @@ dentry_get_next_ci_match(struct wim_dentry *dentry,
             (ci_match);                                                \
             (ci_match) = dentry_get_next_ci_match((dentry), (ci_match)))
 
-extern void
+void
 calculate_subdir_offsets(struct wim_dentry *root, u64 *subdir_offset_p);
 
-extern int
+int
 dentry_set_name(struct wim_dentry *dentry, const tchar *name);
 
-extern int
+int
 dentry_set_name_utf16le(struct wim_dentry *dentry, const utf16lechar *name,
                        size_t name_nbytes);
 
-extern struct wim_dentry *
+struct wim_dentry *
 get_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type);
 
-extern struct wim_dentry *
+struct wim_dentry *
 get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name,
                           CASE_SENSITIVITY_TYPE case_type);
 
-extern struct wim_dentry *
+struct wim_dentry *
 get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry,
                                   const utf16lechar *name,
                                   size_t name_nbytes,
                                   CASE_SENSITIVITY_TYPE case_type);
 
-extern struct wim_dentry *
+struct wim_dentry *
 get_parent_dentry(WIMStruct *wim, const tchar *path,
                  CASE_SENSITIVITY_TYPE case_type);
 
-extern int
+int
 calculate_dentry_full_path(struct wim_dentry *dentry);
 
-extern tchar *
+tchar *
 dentry_full_path(struct wim_dentry *dentry);
 
-extern int
+int
 new_dentry_with_new_inode(const tchar *name, bool set_timestamps,
                          struct wim_dentry **dentry_ret);
 
-extern int
+int
 new_dentry_with_existing_inode(const tchar *name, struct wim_inode *inode,
                               struct wim_dentry **dentry_ret);
 
-extern int
+int
 new_filler_directory(struct wim_dentry **dentry_ret);
 
-extern void
+void
 free_dentry(struct wim_dentry *dentry);
 
-extern void
+void
 free_dentry_tree(struct wim_dentry *root, struct blob_table *blob_table);
 
-extern void
+void
 unlink_dentry(struct wim_dentry *dentry);
 
-extern struct wim_dentry *
+struct wim_dentry *
 dentry_add_child(struct wim_dentry *parent, struct wim_dentry *child);
 
 struct update_command_journal;
 
-extern int
+int
 rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to,
-               CASE_SENSITIVITY_TYPE case_type,
+               CASE_SENSITIVITY_TYPE case_type, bool noreplace,
                struct update_command_journal *j);
 
 
-extern int
+int
 read_dentry_tree(const u8 *buf, size_t buf_len,
                 u64 root_offset, struct wim_dentry **root_ret);
 
-extern u8 *
+u8 *
 write_dentry_tree(struct wim_dentry *root, u8 *p);
 
 static inline bool
index 8ec81a9f652d6e882b9dadea2eae15d6964be95b..c82bda18e3a039667d502e3b04d6465d679d9590 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "wimlib/types.h"
 
-extern void
+void
 divsufsort(const u8 *T, u32 *SA, u32 n, u32 *tmp);
 
 #define DIVSUFSORT_TMP_LEN (256 + (256 * 256))
index 9216ca2c291cb8eafdb53c47579832e2aac3a5de..da83b9d3a87ec4cef9ee1b6495b92b8277eab083 100644 (file)
@@ -9,11 +9,11 @@
 
 /* String conversion functions */
 
-extern int
+int
 utf8_to_utf16le(const char *in, size_t in_nbytes,
                utf16lechar **out_ret, size_t *out_nbytes_ret);
 
-extern int
+int
 utf16le_to_utf8(const utf16lechar *in, size_t in_nbytes,
                char **out_ret, size_t *out_nbytes_ret);
 
@@ -121,28 +121,28 @@ utf16le_put_tstr(const tchar *s)
 
 extern u16 upcase[65536];
 
-extern void
+void
 init_upcase(void);
 
-extern int
+int
 cmp_utf16le_strings(const utf16lechar *s1, size_t n1,
                    const utf16lechar *s2, size_t n2,
                    bool ignore_case);
 
-extern int
+int
 cmp_utf16le_strings_z(const utf16lechar *s1, const utf16lechar *s2,
                      bool ignore_case);
 
-extern utf16lechar *
+utf16lechar *
 utf16le_dupz(const void *s, size_t size);
 
-extern utf16lechar *
+utf16lechar *
 utf16le_dup(const utf16lechar *s);
 
-extern size_t
+size_t
 utf16le_len_bytes(const utf16lechar *s);
 
-extern size_t
+size_t
 utf16le_len_chars(const utf16lechar *s);
 
 #endif /* _WIMLIB_ENCODING_H */
index 38e2bc2c042ab64c9b642b6aab712196a763cc79..d72d50a940ecc0db2d7809f5c3b72b996ea9eb91 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * endianness.h - macros and inline functions for endianness conversion
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_ENDIANNESS_H
@@ -57,8 +64,8 @@
 
 static forceinline u16 do_bswap16(u16 n)
 {
-#ifdef compiler_bswap16
-       return compiler_bswap16(n);
+#if GCC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16)
+       return __builtin_bswap16(n);
 #else
        return bswap16_const(n);
 #endif
@@ -66,8 +73,8 @@ static forceinline u16 do_bswap16(u16 n)
 
 static forceinline u32 do_bswap32(u32 n)
 {
-#ifdef compiler_bswap32
-       return compiler_bswap32(n);
+#if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap32)
+       return __builtin_bswap32(n);
 #else
        return bswap32_const(n);
 #endif
@@ -75,8 +82,8 @@ static forceinline u32 do_bswap32(u32 n)
 
 static forceinline u64 do_bswap64(u64 n)
 {
-#ifdef compiler_bswap64
-       return compiler_bswap64(n);
+#if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap64)
+       return __builtin_bswap64(n);
 #else
        return bswap64_const(n);
 #endif
@@ -86,7 +93,7 @@ static forceinline u64 do_bswap64(u64 n)
 #define bswap32(n) (__builtin_constant_p(n) ? bswap32_const(n) : do_bswap32(n))
 #define bswap64(n) (__builtin_constant_p(n) ? bswap64_const(n) : do_bswap64(n))
 
-#if CPU_IS_BIG_ENDIAN
+#if CPU_IS_BIG_ENDIAN()
 #  define cpu_to_le16(n) ((_force_attr le16)bswap16(n))
 #  define cpu_to_le32(n) ((_force_attr le32)bswap32(n))
 #  define cpu_to_le64(n) ((_force_attr le64)bswap64(n))
index 06222a06f03050fd4c16cdd798a01ec2561ed1c1..062acacf6f5146c78aa1b51854fd3d693c7cb293 100644 (file)
@@ -7,44 +7,27 @@
 #include "wimlib/compiler.h"
 #include "wimlib/types.h"
 
-static inline int _format_attribute(printf, 1, 2)
-dummy_tprintf(const tchar *format, ...)
-{
-       return 0;
-}
-
-#ifdef ENABLE_ERROR_MESSAGES
-extern void
-wimlib_error(const tchar *format, ...)
-       _format_attribute(printf, 1, 2) _cold_attribute;
-
-extern void
-wimlib_error_with_errno(const tchar *format, ...)
-               _format_attribute(printf, 1, 2) _cold_attribute;
-
-extern void
-wimlib_warning(const tchar *format, ...)
-               _format_attribute(printf, 1, 2) _cold_attribute;
-
-extern void
-wimlib_warning_with_errno(const tchar *format, ...)
-               _format_attribute(printf, 1, 2) _cold_attribute;
-#  define ERROR(format, ...)                   wimlib_error(T(format), ## __VA_ARGS__)
-#  define ERROR_WITH_ERRNO(format, ...)                wimlib_error_with_errno(T(format), ## __VA_ARGS__)
-#  define WARNING(format, ...)                 wimlib_warning(T(format), ## __VA_ARGS__)
-#  define WARNING_WITH_ERRNO(format, ...)      wimlib_warning_with_errno(T(format), ## __VA_ARGS__)
+void _format_attribute(printf, 1, 2) __attribute__((cold))
+wimlib_error(const tchar *format, ...);
+
+void _format_attribute(printf, 1, 2) __attribute__((cold))
+wimlib_error_with_errno(const tchar *format, ...);
+
+void _format_attribute(printf, 1, 2) __attribute__((cold))
+wimlib_warning(const tchar *format, ...);
+
+void _format_attribute(printf, 1, 2) __attribute__((cold))
+wimlib_warning_with_errno(const tchar *format, ...);
+
+#define ERROR(format, ...)             wimlib_error(T(format), ## __VA_ARGS__)
+#define ERROR_WITH_ERRNO(format, ...)  wimlib_error_with_errno(T(format), ## __VA_ARGS__)
+#define WARNING(format, ...)           wimlib_warning(T(format), ## __VA_ARGS__)
+#define WARNING_WITH_ERRNO(format, ...)        wimlib_warning_with_errno(T(format), ## __VA_ARGS__)
+
 extern bool wimlib_print_errors;
 extern FILE *wimlib_error_file;
-#else /* ENABLE_ERROR_MESSAGES */
-#  define wimlib_print_errors 0
-#  define wimlib_error_file NULL
-#  define ERROR(format, ...)                   dummy_tprintf(T(format), ## __VA_ARGS__)
-#  define ERROR_WITH_ERRNO(format, ...)                dummy_tprintf(T(format), ## __VA_ARGS__)
-#  define WARNING(format, ...)                 dummy_tprintf(T(format), ## __VA_ARGS__)
-#  define WARNING_WITH_ERRNO(format, ...)      dummy_tprintf(T(format), ## __VA_ARGS__)
-#endif /* !ENABLE_ERROR_MESSAGES */
-
-extern void
+
+void
 print_byte_field(const u8 *field, size_t len, FILE *out);
 
 #endif /* _WIMLIB_ERROR_H */
index 61ae78b9e270aee622376346a68e0eff7ad18af0..8dbdb12025186be663929ad0ac69648d27ec502b 100644 (file)
@@ -14,26 +14,26 @@ struct filedes {
        off_t offset;
 };
 
-extern int
+int
 full_read(struct filedes *fd, void *buf, size_t n);
 
-extern int
+int
 full_pread(struct filedes *fd, void *buf, size_t nbyte, off_t offset);
 
-extern int
+int
 full_write(struct filedes *fd, const void *buf, size_t n);
 
-extern int
+int
 full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset);
 
-#ifndef __WIN32__
+#ifndef _WIN32
 #  define O_BINARY 0
 #endif
 
-extern off_t
+off_t
 filedes_seek(struct filedes *fd, off_t offset);
 
-extern bool
+bool
 filedes_is_seekable(struct filedes *fd);
 
 static inline void filedes_init(struct filedes *fd, int raw_fd)
index 1027ec7cb316af092298c0e6d3b47c6219669430..d5ab39995d844a9ba65556c3f9f3ace5bb4b2471 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _WIMLIB_GLOB_H
 #define _WIMLIB_GLOB_H
 
-#ifndef __WIN32__
+#ifndef _WIN32
 #  include <glob.h>
 #else
 #include <stddef.h>
@@ -14,12 +14,12 @@ typedef struct {
 } glob_t;
 
 /* WARNING: this is a reduced functionality replacement */
-extern int
+int
 win32_wglob(const wchar_t *pattern, int flags,
            int (*errfunc)(const wchar_t *epath, int eerrno),
            glob_t *pglob);
 
-extern void globfree(glob_t *pglob);
+void globfree(glob_t *pglob);
 
 #define        GLOB_ERR        0x1 /* Return on read errors.  */
 #define        GLOB_NOSORT     0x2 /* Don't sort the names.  */
@@ -29,6 +29,6 @@ extern void globfree(glob_t *pglob);
 #define        GLOB_ABORTED    2       /* Read error.  */
 #define        GLOB_NOMATCH    3       /* No matches found.  */
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
 
 #endif /* _WIMLIB_GLOB_H */
index b0c8c65a44acf09ad4efba0889b275ce0a4b4b46..a96aa6511184ed7d99560b09c21f1a0664a1c394 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * hc_matchfinder.h - Lempel-Ziv matchfinding with a hash table of linked lists
  *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * Copyright 2022 Eric Biggers
+ *
+ * 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.
  *
  * ---------------------------------------------------------------------------
  *
@@ -81,7 +88,7 @@
  * chain for length 3+ matches, the algorithm just checks for one close length 3
  * match, then focuses on finding length 4+ matches.
  *
- * The longest_match() and skip_positions() functions are inlined into the
+ * The longest_match() and skip_bytes() functions are inlined into the
  * compressors that use them.  This isn't just about saving the overhead of a
  * function call.  These functions are intended to be called from the inner
  * loops of compressors, where giving the compiler more control over register
 
 #include <string.h>
 
-#include "wimlib/lz_extend.h"
-#include "wimlib/lz_hash.h"
-#include "wimlib/unaligned.h"
+#include "wimlib/matchfinder_common.h"
 
-#define HC_MATCHFINDER_HASH3_ORDER     14
-#define HC_MATCHFINDER_HASH4_ORDER     15
+#define HC_MATCHFINDER_HASH3_ORDER     15
+#define HC_MATCHFINDER_HASH4_ORDER     16
 
 /* TEMPLATED functions and structures have MF_SUFFIX appended to their name.  */
 #undef TEMPLATED
@@ -162,9 +167,9 @@ TEMPLATED(hc_matchfinder_init)(struct TEMPLATED(hc_matchfinder) *mf)
  *     The matchfinder structure.
  * @in_begin
  *     Pointer to the beginning of the input buffer.
- * @cur_pos
- *     The current position in the input buffer (the position of the sequence
- *     being matched against).
+ * @in_next
+ *     Pointer to the next position in the input buffer, i.e. the sequence
+ *     being matched against.
  * @best_len
  *     Require a match longer than this length.
  * @max_len
@@ -185,25 +190,25 @@ TEMPLATED(hc_matchfinder_init)(struct TEMPLATED(hc_matchfinder) *mf)
  * 'best_len' was found.
  */
 static forceinline u32
-TEMPLATED(hc_matchfinder_longest_match)(struct TEMPLATED(hc_matchfinder) * const restrict mf,
-                                       const u8 * const restrict in_begin,
-                                       const ptrdiff_t cur_pos,
+TEMPLATED(hc_matchfinder_longest_match)(struct TEMPLATED(hc_matchfinder) * const mf,
+                                       const u8 * const in_begin,
+                                       const u8 * const in_next,
                                        u32 best_len,
                                        const u32 max_len,
                                        const u32 nice_len,
                                        const u32 max_search_depth,
-                                       u32 next_hashes[const restrict static 2],
-                                       u32 * const restrict offset_ret)
+                                       u32 * const next_hashes,
+                                       u32 * const offset_ret)
 {
-       const u8 *in_next = in_begin + cur_pos;
        u32 depth_remaining = max_search_depth;
        const u8 *best_matchptr = in_next;
        mf_pos_t cur_node3, cur_node4;
        u32 hash3, hash4;
-       u32 next_seq3, next_seq4;
+       u32 next_hashseq;
        u32 seq4;
        const u8 *matchptr;
        u32 len;
+       u32 cur_pos = in_next - in_begin;
 
        if (unlikely(max_len < 5)) /* can we read 4 bytes from 'in_next + 1'? */
                goto out;
@@ -226,10 +231,9 @@ TEMPLATED(hc_matchfinder_longest_match)(struct TEMPLATED(hc_matchfinder) * const
        mf->next_tab[cur_pos] = cur_node4;
 
        /* Compute the next hash codes.  */
-       next_seq4 = load_u32_unaligned(in_next + 1);
-       next_seq3 = loaded_u32_to_u24(next_seq4);
-       next_hashes[0] = lz_hash(next_seq3, HC_MATCHFINDER_HASH3_ORDER);
-       next_hashes[1] = lz_hash(next_seq4, HC_MATCHFINDER_HASH4_ORDER);
+       next_hashseq = get_unaligned_le32(in_next + 1);
+       next_hashes[0] = lz_hash(next_hashseq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER);
+       next_hashes[1] = lz_hash(next_hashseq, HC_MATCHFINDER_HASH4_ORDER);
        prefetchw(&mf->hash3_tab[next_hashes[0]]);
        prefetchw(&mf->hash4_tab[next_hashes[1]]);
 
@@ -339,54 +343,49 @@ out:
  *     The matchfinder structure.
  * @in_begin
  *     Pointer to the beginning of the input buffer.
- * @cur_pos
- *     The current position in the input buffer (the position of the sequence
- *     being matched against).
- * @end_pos
- *     The length of the input buffer.
+ * @in_next
+ *     Pointer to the next position in the input buffer.
+ * @in_end
+ *     Pointer to the end of the input buffer.
+ * @count
+ *     The number of bytes to advance.  Must be > 0.
  * @next_hashes
  *     The precomputed hash codes for the sequence beginning at @in_next.
  *     These will be used and then updated with the precomputed hashcodes for
  *     the sequence beginning at @in_next + @count.
- * @count
- *     The number of bytes to advance.  Must be > 0.
- *
- * Returns @in_next + @count.
  */
-static forceinline const u8 *
-TEMPLATED(hc_matchfinder_skip_positions)(struct TEMPLATED(hc_matchfinder) * const restrict mf,
-                                        const u8 * const restrict in_begin,
-                                        const ptrdiff_t cur_pos,
-                                        const ptrdiff_t end_pos,
-                                        const u32 count,
-                                        u32 next_hashes[const restrict static 2])
+static forceinline void
+TEMPLATED(hc_matchfinder_skip_bytes)(struct TEMPLATED(hc_matchfinder) * const mf,
+                                    const u8 * const in_begin,
+                                    const u8 *in_next,
+                                    const u8 * const in_end,
+                                    const u32 count,
+                                    u32 * const next_hashes)
 {
-       const u8 *in_next = in_begin + cur_pos;
-       const u8 * const stop_ptr = in_next + count;
-
-       if (likely(count + 5 <= end_pos - cur_pos)) {
-               u32 hash3, hash4;
-               u32 next_seq3, next_seq4;
-
-               hash3 = next_hashes[0];
-               hash4 = next_hashes[1];
-               do {
-                       mf->hash3_tab[hash3] = in_next - in_begin;
-                       mf->next_tab[in_next - in_begin] = mf->hash4_tab[hash4];
-                       mf->hash4_tab[hash4] = in_next - in_begin;
-
-                       next_seq4 = load_u32_unaligned(++in_next);
-                       next_seq3 = loaded_u32_to_u24(next_seq4);
-                       hash3 = lz_hash(next_seq3, HC_MATCHFINDER_HASH3_ORDER);
-                       hash4 = lz_hash(next_seq4, HC_MATCHFINDER_HASH4_ORDER);
-
-               } while (in_next != stop_ptr);
-
-               prefetchw(&mf->hash3_tab[hash3]);
-               prefetchw(&mf->hash4_tab[hash4]);
-               next_hashes[0] = hash3;
-               next_hashes[1] = hash4;
-       }
+       u32 cur_pos;
+       u32 hash3, hash4;
+       u32 next_hashseq;
+       u32 remaining = count;
 
-       return stop_ptr;
+       if (unlikely(count + 5 > in_end - in_next))
+               return;
+
+       cur_pos = in_next - in_begin;
+       hash3 = next_hashes[0];
+       hash4 = next_hashes[1];
+       do {
+               mf->hash3_tab[hash3] = cur_pos;
+               mf->next_tab[cur_pos] = mf->hash4_tab[hash4];
+               mf->hash4_tab[hash4] = cur_pos;
+
+               next_hashseq = get_unaligned_le32(++in_next);
+               hash3 = lz_hash(next_hashseq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER);
+               hash4 = lz_hash(next_hashseq, HC_MATCHFINDER_HASH4_ORDER);
+               cur_pos++;
+       } while (--remaining);
+
+       prefetchw(&mf->hash3_tab[hash3]);
+       prefetchw(&mf->hash4_tab[hash4]);
+       next_hashes[0] = hash3;
+       next_hashes[1] = hash4;
 }
index 894bd22591897491f2089ce75c4bd0dacb64939f..49ccf0b2fd5b8eee5410332fb8ee0e49b2bb1a36 100644 (file)
@@ -102,17 +102,22 @@ struct wim_header_disk {
         * if the WIM has no integrity table.
         *
         * Note the integrity_table_reshdr here is 4-byte aligned even though it
-        * would ordinarily be 8-byte aligned--- hence, the _packed_attribute on
-        * this structure is essential.  */
+        * would ordinarily be 8-byte aligned--- hence, the
+        * __attribute__((packed)) on this structure is essential.  */
        struct wim_reshdr_disk integrity_table_reshdr;
 
        /* +0x94: Unused bytes.  */
        u8 unused[60];
 
        /* +0xd0 (208)  */
-} _packed_attribute;
-
-#define MAX_IMAGES (((INT_MAX < INT32_MAX) ? INT_MAX : INT32_MAX) - 1)
+} __attribute__((packed));
+
+/*
+ * Arbitrarily limit the maximum number of images to 65535, to prevent huge
+ * memory allocations when processing fuzzed files.  This can be increased if
+ * ever needed (up to INT_MAX - 1).
+ */
+#define MAX_IMAGES     65535
 
 /* In-memory representation of a WIM header.  See `struct wim_header_disk' for
  * field descriptions.  */
index 4497775077415e337687136bf23f1068c46a0b27..bdf2ad188dcf62ea89b7d9d701525240723788d9 100644 (file)
@@ -229,12 +229,12 @@ struct wim_inode {
 /* Optional extra data for a WIM inode  */
 struct wim_inode_extra {
        size_t size;    /* Size of the extra data in bytes  */
-       u8 data[] _aligned_attribute(8); /* The extra data  */
+       u8 data[] __attribute__((aligned(8))); /* The extra data  */
 };
 
 /*
  * The available reparse tags are documented at
- * http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx.
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4
  * Here we only define the ones of interest to us.
  */
 #define WIM_IO_REPARSE_TAG_MOUNT_POINT         0xA0000003
@@ -264,7 +264,7 @@ struct wim_inode_extra {
 #define FILE_ATTRIBUTE_ENCRYPTED           0x00004000
 #define FILE_ATTRIBUTE_VIRTUAL             0x00010000
 
-extern struct wim_inode *
+struct wim_inode *
 new_inode(struct wim_dentry *dentry, bool set_timestamps);
 
 /* Iterate through each alias of the specified inode.  */
@@ -280,14 +280,14 @@ new_inode(struct wim_dentry *dentry, bool set_timestamps);
 #define inode_any_full_path(inode) \
        dentry_full_path(inode_any_dentry(inode))
 
-extern void
+void
 d_associate(struct wim_dentry *dentry, struct wim_inode *inode);
 
-extern void
+void
 d_disassociate(struct wim_dentry *dentry);
 
 #ifdef WITH_FUSE
-extern void
+void
 inode_dec_num_opened_fds(struct wim_inode *inode);
 #endif
 
@@ -330,11 +330,11 @@ inode_has_security_descriptor(const struct wim_inode *inode)
        return inode->i_security_id >= 0;
 }
 
-extern struct wim_inode_stream *
+struct wim_inode_stream *
 inode_get_stream(const struct wim_inode *inode, int stream_type,
                 const utf16lechar *stream_name);
 
-extern struct wim_inode_stream *
+struct wim_inode_stream *
 inode_get_unnamed_stream(const struct wim_inode *inode, int stream_type);
 
 static inline struct wim_inode_stream *
@@ -343,29 +343,29 @@ inode_get_unnamed_data_stream(const struct wim_inode *inode)
        return inode_get_unnamed_stream(inode, STREAM_TYPE_DATA);
 }
 
-extern struct wim_inode_stream *
+struct wim_inode_stream *
 inode_add_stream(struct wim_inode *inode, int stream_type,
                 const utf16lechar *stream_name, struct blob_descriptor *blob);
 
-extern void
+void
 inode_replace_stream_blob(struct wim_inode *inode,
                          struct wim_inode_stream *strm,
                          struct blob_descriptor *new_blob,
                          struct blob_table *blob_table);
 
-extern bool
+bool
 inode_replace_stream_data(struct wim_inode *inode,
                          struct wim_inode_stream *strm,
                          const void *data, size_t size,
                          struct blob_table *blob_table);
 
-extern bool
+bool
 inode_add_stream_with_data(struct wim_inode *inode,
                           int stream_type, const utf16lechar *stream_name,
                           const void *data, size_t size,
                           struct blob_table *blob_table);
 
-extern void
+void
 inode_remove_stream(struct wim_inode *inode, struct wim_inode_stream *strm,
                    struct blob_table *blob_table);
 
@@ -394,40 +394,40 @@ stream_is_named_data_stream(const struct wim_inode_stream *strm)
        return strm->stream_type == STREAM_TYPE_DATA && stream_is_named(strm);
 }
 
-extern bool
+bool
 inode_has_named_data_stream(const struct wim_inode *inode);
 
-extern int
+int
 inode_resolve_streams(struct wim_inode *inode, struct blob_table *table,
                      bool force);
 
-extern int
+int
 blob_not_found_error(const struct wim_inode *inode, const u8 *hash);
 
-extern struct blob_descriptor *
+struct blob_descriptor *
 stream_blob(const struct wim_inode_stream *strm, const struct blob_table *table);
 
-extern struct blob_descriptor *
+struct blob_descriptor *
 inode_get_blob_for_unnamed_data_stream(const struct wim_inode *inode,
                                       const struct blob_table *blob_table);
 
-extern struct blob_descriptor *
+struct blob_descriptor *
 inode_get_blob_for_unnamed_data_stream_resolved(const struct wim_inode *inode);
 
-extern const u8 *
+const u8 *
 stream_hash(const struct wim_inode_stream *strm);
 
-extern const u8 *
+const u8 *
 inode_get_hash_of_unnamed_data_stream(const struct wim_inode *inode);
 
-extern void
+void
 inode_ref_blobs(struct wim_inode *inode);
 
-extern void
+void
 inode_unref_blobs(struct wim_inode *inode, struct blob_table *blob_table);
 
 /* inode_fixup.c  */
-extern int
+int
 dentry_tree_fix_inodes(struct wim_dentry *root, struct hlist_head *inode_list);
 
 #endif /* _WIMLIB_INODE_H  */
index 4d4fab5ec5678100d7dac4212d5e865985eb8d32..781a87062a6f8081e9d2c6786758aadf1c99f93c 100644 (file)
@@ -28,22 +28,22 @@ hash_inode(const struct wim_inode_table *table, u64 ino, u64 devno)
        return (hash_u64(ino) + devno) & (table->capacity - 1);
 }
 
-extern int
+int
 init_inode_table(struct wim_inode_table *table, size_t capacity);
 
-extern int
+int
 inode_table_new_dentry(struct wim_inode_table *table, const tchar *name,
                       u64 ino, u64 devno, bool noshare,
                       struct wim_dentry **dentry_ret);
 
-extern void
+void
 enlarge_inode_table(struct wim_inode_table *table);
 
-extern void
+void
 inode_table_prepare_inode_list(struct wim_inode_table *table,
                               struct hlist_head *head);
 
-extern void
+void
 destroy_inode_table(struct wim_inode_table *table);
 
 #endif /* _WIMLIB_INODE_TABLE_H  */
index 48f24e319a4c2b448e4d18b17d40e8cec4d7d75c..755cddda34db6bea5abd774330f26af9f208c780 100644 (file)
 
 struct integrity_table;
 
-extern int
+int
 read_integrity_table(WIMStruct *wim, u64 num_checked_bytes,
                     struct integrity_table **table_ret);
 
 #define free_integrity_table(table) FREE(table)
 
-extern int
+int
 write_integrity_table(WIMStruct *wim,
                      off_t new_blob_table_end,
                      off_t old_blob_table_end,
                      struct integrity_table *old_table);
 
-extern int
+int
 check_wim_integrity(WIMStruct *wim);
 
 #endif /* _WIMLIB_INTEGRITY_H */
index 6391a481476b234398740f546675f25589eb4a32..1cfa444f2c3099e181bfd60bf9b83ae513bcf28e 100644 (file)
@@ -4,21 +4,28 @@
  * A match-finder for Lempel-Ziv compression based on bottom-up construction and
  * traversal of the Longest Common Prefix (LCP) interval tree.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _LCPIT_MATCHFINDER_H
@@ -45,24 +52,24 @@ struct lz_match {
        u32 offset;
 };
 
-extern u64
+u64
 lcpit_matchfinder_get_needed_memory(size_t max_bufsize);
 
-extern bool
+bool
 lcpit_matchfinder_init(struct lcpit_matchfinder *mf, size_t max_bufsize,
                       u32 min_match_len, u32 nice_match_len);
 
-extern void
+void
 lcpit_matchfinder_load_buffer(struct lcpit_matchfinder *mf, const u8 *T, u32 n);
 
-extern u32
+u32
 lcpit_matchfinder_get_matches(struct lcpit_matchfinder *mf,
                               struct lz_match *matches);
 
-extern void
+void
 lcpit_matchfinder_skip_bytes(struct lcpit_matchfinder *mf, u32 count);
 
-extern void
+void
 lcpit_matchfinder_destroy(struct lcpit_matchfinder *mf);
 
 #endif /* _LCPIT_MATCHFINDER_H */
diff --git a/include/wimlib/lz_extend.h b/include/wimlib/lz_extend.h
deleted file mode 100644 (file)
index 26f0ce5..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * lz_extend.h - fast match extension for Lempel-Ziv matchfinding
- *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
- */
-
-#ifndef _WIMLIB_LZ_EXTEND_H
-#define _WIMLIB_LZ_EXTEND_H
-
-#include "wimlib/bitops.h"
-#include "wimlib/unaligned.h"
-
-/*
- * Return the number of bytes at @matchptr that match the bytes at @strptr, up
- * to a maximum of @max_len.  Initially, @len bytes are matched.
- */
-static forceinline u32
-lz_extend(const u8 * const strptr, const u8 * const matchptr,
-         u32 len, const u32 max_len)
-{
-       while (UNALIGNED_ACCESS_IS_FAST && len + WORDBYTES <= max_len) {
-               machine_word_t v = load_word_unaligned(matchptr + len) ^
-                                  load_word_unaligned(strptr + len);
-               if (v != 0) {
-                       if (CPU_IS_LITTLE_ENDIAN)
-                               len += bsfw(v) >> 3;
-                       else
-                               len += (WORDBITS - 1 - bsrw(v)) >> 3;
-                       return len;
-               }
-               len += WORDBYTES;
-       }
-
-       while (len < max_len && matchptr[len] == strptr[len])
-               len++;
-       return len;
-}
-
-#endif /* _WIMLIB_LZ_EXTEND_H */
diff --git a/include/wimlib/lz_hash.h b/include/wimlib/lz_hash.h
deleted file mode 100644 (file)
index f761815..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * lz_hash.h - hashing for Lempel-Ziv matchfinding
- *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
- */
-
-#ifndef _LZ_HASH_H
-#define _LZ_HASH_H
-
-#include "wimlib/types.h"
-
-/*
- * The hash function: given a sequence prefix held in the low-order bits of a
- * 32-bit value, multiply by a carefully-chosen large constant.  Discard any
- * bits of the product that don't fit in a 32-bit value, but take the
- * next-highest @num_bits bits of the product as the hash value, as those have
- * the most randomness.
- */
-static forceinline u32
-lz_hash(u32 seq, unsigned num_bits)
-{
-       return (u32)(seq * 0x1E35A7BD) >> (32 - num_bits);
-}
-
-#endif /* _LZ_HASH_H */
index b5071469f443bf3227657ac7ba0c72023fe30b27..b2d59d8a2e1b7f1ce4dbc61e573568443bb543e6 100644 (file)
@@ -19,7 +19,7 @@ extern const u8 lzms_extra_offset_bits[LZMS_MAX_NUM_OFFSET_SYMS];
 extern const u32 lzms_length_slot_base[LZMS_NUM_LENGTH_SYMS + 1];
 extern const u8 lzms_extra_length_bits[LZMS_NUM_LENGTH_SYMS];
 
-extern unsigned
+unsigned
 lzms_get_slot(u32 value, const u32 slot_base_tab[], unsigned num_slots);
 
 /* Return the offset slot for the specified offset  */
@@ -36,7 +36,7 @@ lzms_get_length_slot(u32 length)
        return lzms_get_slot(length, lzms_length_slot_base, LZMS_NUM_LENGTH_SYMS);
 }
 
-extern unsigned
+unsigned
 lzms_get_num_offset_slots(size_t uncompressed_size);
 
 
@@ -67,7 +67,7 @@ struct lzms_probabilites {
                                               [LZMS_NUM_DELTA_REP_PROBS];
 };
 
-extern void
+void
 lzms_init_probabilities(struct lzms_probabilites *probs);
 
 /* Given a decoded or encoded bit, update the probability entry.  */
@@ -130,14 +130,14 @@ lzms_get_probability(const struct lzms_probability_entry *prob_entry)
        return prob;
 }
 
-extern void
+void
 lzms_init_symbol_frequencies(u32 freqs[], unsigned num_syms);
 
-extern void
+void
 lzms_dilute_symbol_frequencies(u32 freqs[], unsigned num_syms);
 
 /* Pre/post-processing  */
-extern void
+void
 lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo);
 
 #endif /* _LZMS_COMMON_H */
index f93221d5b16c84b075b4e865bf3d7b1b63805274..80d53813388c57e8f7a9e730b6d1090a6a36f655 100644 (file)
@@ -14,16 +14,16 @@ extern const s32 lzx_offset_slot_base[LZX_MAX_OFFSET_SLOTS + 1];
 
 extern const u8 lzx_extra_offset_bits[LZX_MAX_OFFSET_SLOTS];
 
-extern unsigned
+unsigned
 lzx_get_window_order(size_t max_bufsize);
 
-extern unsigned
+unsigned
 lzx_get_num_main_syms(unsigned window_order);
 
-extern void
+void
 lzx_preprocess(u8 *data, u32 size);
 
-extern void
+void
 lzx_postprocess(u8 *data, u32 size);
 
 #endif /* _LZX_COMMON_H */
diff --git a/include/wimlib/matchfinder_common.h b/include/wimlib/matchfinder_common.h
new file mode 100644 (file)
index 0000000..bbb61f6
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * matchfinder_common.h - common code for Lempel-Ziv matchfinding
+ *
+ * Copyright 2022 Eric Biggers
+ *
+ * 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 _WIMLIB_MATCHFINDER_COMMON_H
+#define _WIMLIB_MATCHFINDER_COMMON_H
+
+#include "wimlib/bitops.h"
+#include "wimlib/unaligned.h"
+
+/*
+ * Given a 32-bit value that was loaded with the platform's native endianness,
+ * return a 32-bit value whose high-order 8 bits are 0 and whose low-order 24
+ * bits contain the first 3 bytes, arranged in octets in a platform-dependent
+ * order, at the memory location from which the input 32-bit value was loaded.
+ */
+static forceinline u32
+loaded_u32_to_u24(u32 v)
+{
+       if (CPU_IS_LITTLE_ENDIAN())
+               return v & 0xFFFFFF;
+       else
+               return v >> 8;
+}
+
+/*
+ * Load the next 3 bytes from @p into the 24 low-order bits of a 32-bit value.
+ * The order in which the 3 bytes will be arranged as octets in the 24 bits is
+ * platform-dependent.  At least 4 bytes (not 3) must be available at @p.
+ */
+static forceinline u32
+load_u24_unaligned(const u8 *p)
+{
+#if UNALIGNED_ACCESS_IS_FAST
+       return loaded_u32_to_u24(load_u32_unaligned(p));
+#else
+       if (CPU_IS_LITTLE_ENDIAN())
+               return ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16);
+       else
+               return ((u32)p[2] << 0) | ((u32)p[1] << 8) | ((u32)p[0] << 16);
+#endif
+}
+
+/*
+ * The hash function: given a sequence prefix held in the low-order bits of a
+ * 32-bit value, multiply by a carefully-chosen large constant.  Discard any
+ * bits of the product that don't fit in a 32-bit value, but take the
+ * next-highest @num_bits bits of the product as the hash value, as those have
+ * the most randomness.
+ */
+static forceinline u32
+lz_hash(u32 seq, unsigned num_bits)
+{
+       return (u32)(seq * 0x1E35A7BD) >> (32 - num_bits);
+}
+
+/*
+ * Return the number of bytes at @matchptr that match the bytes at @strptr, up
+ * to a maximum of @max_len.  Initially, @start_len bytes are matched.
+ */
+static forceinline unsigned
+lz_extend(const u8 * const strptr, const u8 * const matchptr,
+         const unsigned start_len, const unsigned max_len)
+{
+       unsigned len = start_len;
+       machine_word_t v_word;
+
+       if (UNALIGNED_ACCESS_IS_FAST) {
+
+               if (likely(max_len - len >= 4 * WORDBYTES)) {
+
+               #define COMPARE_WORD_STEP                               \
+                       v_word = load_word_unaligned(&matchptr[len]) ^  \
+                                load_word_unaligned(&strptr[len]);     \
+                       if (v_word != 0)                                \
+                               goto word_differs;                      \
+                       len += WORDBYTES;                               \
+
+                       COMPARE_WORD_STEP
+                       COMPARE_WORD_STEP
+                       COMPARE_WORD_STEP
+                       COMPARE_WORD_STEP
+               #undef COMPARE_WORD_STEP
+               }
+
+               while (len + WORDBYTES <= max_len) {
+                       v_word = load_word_unaligned(&matchptr[len]) ^
+                                load_word_unaligned(&strptr[len]);
+                       if (v_word != 0)
+                               goto word_differs;
+                       len += WORDBYTES;
+               }
+       }
+
+       while (len < max_len && matchptr[len] == strptr[len])
+               len++;
+       return len;
+
+word_differs:
+       if (CPU_IS_LITTLE_ENDIAN())
+               len += (bsfw(v_word) >> 3);
+       else
+               len += (WORDBITS - 1 - bsrw(v_word)) >> 3;
+       return len;
+}
+
+#endif /* _WIMLIB_MATCHFINDER_COMMON_H */
index 030b0b12dd644daaa8298a2d700fbef1b2566da1..a3d28048055da2202ab3f62c3d9e813e2e136a56 100644 (file)
@@ -150,16 +150,16 @@ can_unload_image(const struct wim_image_metadata *imd)
 #define image_for_each_unhashed_blob_safe(blob, tmp, imd) \
        list_for_each_entry_safe(blob, tmp, &(imd)->unhashed_blobs, unhashed_list)
 
-extern void
+void
 put_image_metadata(struct wim_image_metadata *imd);
 
-extern int
+int
 append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd);
 
-extern struct wim_image_metadata *
+struct wim_image_metadata *
 new_empty_image_metadata(void);
 
-extern struct wim_image_metadata *
+struct wim_image_metadata *
 new_unloaded_image_metadata(struct blob_descriptor *metadata_blob);
 
 #endif /* _WIMLIB_METADATA_H */
index 7909e43e73f0e0fe0111e9841a4be09152c2040a..03fc04b6b15748927beae0ed13b2ce0f9c73e24c 100644 (file)
@@ -9,17 +9,18 @@ struct blob_descriptor;
 struct consume_chunk_callback;
 struct ntfs_location;
 
-extern int
+int
 read_ntfs_attribute_prefix(const struct blob_descriptor *blob, u64 size,
-                          const struct consume_chunk_callback *cb);
+                          const struct consume_chunk_callback *cb,
+                          bool recover_data);
 
-extern struct ntfs_location *
+struct ntfs_location *
 clone_ntfs_location(const struct ntfs_location *loc);
 
-extern void
+void
 free_ntfs_location(struct ntfs_location *loc);
 
-extern int
+int
 cmp_ntfs_locations(const struct ntfs_location *loc1,
                   const struct ntfs_location *loc2);
 
index 71878bf7e9875db80a0642fea1a6dcc0ce04be3b..b49dbd2898ccf2507787443b1fc60a294da08472 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "wimlib/types.h"
 
-extern int
+int
 read_path_list_file(const tchar *listfile,
                    tchar ***paths_ret, size_t *num_paths_ret,
                    void **mem_ret);
index e6c331776d3825755415d55406f7c704f3de09f7..4faf970370fd32868fe227be8d67bcbdf06c33a5 100644 (file)
@@ -10,14 +10,14 @@ path_basename(const tchar *path);
 const tchar *
 path_basename_with_len(const tchar *path, size_t len);
 
-extern const tchar *
+const tchar *
 path_stream_name(const tchar *path);
 
-extern void
+void
 do_canonicalize_path(const tchar *in, tchar *out);
 
-extern tchar *
-canonicalize_wim_path(const tchar *wim_path) _malloc_attribute;
+tchar *
+canonicalize_wim_path(const tchar *wim_path);
 
 /* is_any_path_separator() - characters treated as path separators in WIM path
  * specifications and capture configuration files (the former will be translated
@@ -32,7 +32,7 @@ canonicalize_wim_path(const tchar *wim_path) _malloc_attribute;
  * Currently needs to be '/' on UNIX for the WIM mounting code to work properly.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  define OS_PREFERRED_PATH_SEPARATOR L'\\'
 #  define is_any_path_separator(c) ((c) == L'/' || (c) == L'\\')
 #else
index b0e299958fc7c48c0e951aff3bf91c6643e89a1c..3f3add77096035987b8af7a6e249e70dcc58726d 100644 (file)
@@ -19,10 +19,10 @@ struct wim_dentry;
  */
 #define MATCH_ANCESTORS                0x02
 
-extern bool
+bool
 match_path(const tchar *path, const tchar *pattern, int match_flags);
 
-extern int
+int
 expand_path_pattern(struct wim_dentry *root, const tchar *pattern,
                    int (*consume_dentry)(struct wim_dentry *, void *),
                    void *ctx);
index 6d39b7e49804fb0089c8234985aa51e978108a00..538e358f79b86441779b8892773526efe1d73247 100644 (file)
@@ -30,7 +30,7 @@ call_progress(wimlib_progress_func_t progfunc,
        return 0;
 }
 
-extern int
+int
 report_error(wimlib_progress_func_t progfunc,
             void *progctx, int error_code, const tchar *path);
 
@@ -61,7 +61,7 @@ static inline tchar *
 progress_get_streamless_path(const tchar *path)
 {
        tchar *cookie = NULL;
-#ifdef __WIN32__
+#ifdef _WIN32
        cookie = (wchar_t *)path_stream_name(path);
        if (cookie)
                *--cookie = L'\0'; /* Overwrite the colon  */
@@ -74,7 +74,7 @@ progress_get_streamless_path(const tchar *path)
 static inline tchar *
 progress_get_win32_path(const tchar *path)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        if (!wcsncmp(path, L"\\??\\", 4)) {
                ((wchar_t *)path)[1] = L'\\';
                return (wchar_t *)&path[1];
@@ -87,7 +87,7 @@ progress_get_win32_path(const tchar *path)
 static inline void
 progress_put_win32_path(tchar *cookie)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        if (cookie)
                *cookie = L'?';
 #endif
@@ -97,7 +97,7 @@ progress_put_win32_path(tchar *cookie)
 static inline void
 progress_put_streamless_path(tchar *cookie)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        if (cookie)
                *cookie = L':';
 #endif
index 7f306313c92a58c46ddf9d351040212ce3a415cb..27fcd1ab42d17c087f94cccca1f9dbe6fe3b78f6 100644 (file)
@@ -16,25 +16,25 @@ enum hive_status {
        HIVE_ITERATION_STOPPED,
 };
 
-extern enum hive_status
+enum hive_status
 hive_validate(const void *hive_mem, size_t hive_size);
 
-extern enum hive_status
+enum hive_status
 hive_get_string(const struct regf *regf, const tchar *key_name,
                const tchar *value_name, tchar **value_ret);
 
-extern enum hive_status
+enum hive_status
 hive_get_number(const struct regf *regf, const tchar *key_name,
                const tchar *value_name, s64 *value_ret);
 
-extern enum hive_status
+enum hive_status
 hive_list_subkeys(const struct regf *regf, const tchar *key_name,
                  tchar ***subkeys_ret);
 
-extern void
+void
 hive_free_subkeys_list(tchar **subkeys);
 
-extern const char *
+const char *
 hive_status_to_string(enum hive_status status);
 
 #endif /* _WIMLIB_REGISTRY_H */
index dce19a22130087ec1cc9475a88b060aab513dbdb..972780dfc23fed60607c1b04e85159a50a4b1c78 100644 (file)
@@ -14,8 +14,8 @@ struct blob_table;
  * On-disk format of a reparse point buffer.  See:
  *     https://msdn.microsoft.com/en-us/library/dd541671.aspx
  *
- * Note: we are not using _packed_attribute for this structure, so only cast to
- * this if properly aligned!
+ * Note: we are not using __attribute__((packed)) for this structure, so only
+ * cast to this if properly aligned!
  */
 struct reparse_buffer_disk {
        le32 rptag;
@@ -49,7 +49,7 @@ struct reparse_buffer_disk {
 
 #define REPARSE_DATA_MAX_SIZE (REPARSE_POINT_MAX_SIZE - REPARSE_DATA_OFFSET)
 
-static _unused_attribute void
+static void __attribute__((unused))
 check_reparse_buffer_disk(void)
 {
        STATIC_ASSERT(offsetof(struct reparse_buffer_disk, rpdata) == 8);
@@ -86,25 +86,25 @@ link_is_relative_symlink(const struct link_reparse_point *link)
               (link->symlink_flags & SYMBOLIC_LINK_RELATIVE);
 }
 
-extern void
+void
 complete_reparse_point(struct reparse_buffer_disk *rpbuf,
                       const struct wim_inode *inode, u16 blob_size);
 
-extern int
+int
 parse_link_reparse_point(const struct reparse_buffer_disk *rpbuf, u16 rpbuflen,
                         struct link_reparse_point *link);
 
-extern int
+int
 make_link_reparse_point(const struct link_reparse_point *link,
                        struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_ret);
 
-#ifndef __WIN32__
-extern int
+#ifndef _WIN32
+int
 wim_inode_readlink(const struct wim_inode *inode, char *buf, size_t bufsize,
                   const struct blob_descriptor *blob,
                   const char *altroot, size_t altroot_len);
 
-extern int
+int
 wim_inode_set_symlink(struct wim_inode *inode, const char *target,
                      struct blob_table *blob_table);
 #endif
index 05ceed70b88b7b24e96f45296e324698cd14321c..8924b80716232b0e7489fd6ea8d9f7273954323f 100644 (file)
@@ -72,7 +72,7 @@ struct wim_reshdr_disk {
 
        /* Uncompressed size of the resource, in bytes.  */
        le64 uncompressed_size;
-} _packed_attribute;
+} __attribute__((packed));
 
 /* In-memory version of a WIM resource header (`struct wim_reshdr_disk').  */
 struct wim_reshdr {
@@ -119,20 +119,20 @@ zero_reshdr(struct wim_reshdr *reshdr)
        memset(reshdr, 0, sizeof(struct wim_reshdr));
 }
 
-extern void
+void
 wim_reshdr_to_desc(const struct wim_reshdr *reshdr, WIMStruct *wim,
                   struct wim_resource_descriptor *rdesc);
 
-extern void
+void
 wim_reshdr_to_desc_and_blob(const struct wim_reshdr *reshdr, WIMStruct *wim,
                            struct wim_resource_descriptor *rdesc,
                            struct blob_descriptor *blob);
 
-extern void
+void
 get_wim_reshdr(const struct wim_reshdr_disk *disk_reshdr,
               struct wim_reshdr *reshdr);
 
-extern void
+void
 put_wim_reshdr(const struct wim_reshdr *reshdr,
               struct wim_reshdr_disk *disk_reshdr);
 
@@ -158,7 +158,7 @@ struct alt_chunk_table_header_disk {
 
        /* This header is directly followed by a table of compressed sizes of
         * the chunks (4 bytes per entry).  */
-} _packed_attribute;
+} __attribute__((packed));
 
 static inline unsigned int
 get_chunk_entry_size(u64 res_size, bool is_alt)
@@ -171,25 +171,25 @@ get_chunk_entry_size(u64 res_size, bool is_alt)
 
 /* Functions to read blobs  */
 
-extern int
+int
 read_partial_wim_blob_into_buf(const struct blob_descriptor *blob,
                               u64 offset, size_t size, void *buf);
 
-extern int
+int
 read_blob_into_buf(const struct blob_descriptor *blob, void *buf);
 
-extern int
+int
 read_blob_into_alloc_buf(const struct blob_descriptor *blob, void **buf_ret);
 
-extern int
+int
 wim_reshdr_to_data(const struct wim_reshdr *reshdr, WIMStruct *wim,
                   void **buf_ret);
 
-extern int
+int
 wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim,
                   u8 hash[SHA1_HASH_SIZE]);
 
-extern int
+int
 skip_wim_resource(const struct wim_resource_descriptor *rdesc);
 
 /*
@@ -273,37 +273,39 @@ call_end_blob(struct blob_descriptor *blob, int status,
 #define VERIFY_BLOB_HASHES             0x1
 #define COMPUTE_MISSING_BLOB_HASHES    0x2
 #define BLOB_LIST_ALREADY_SORTED       0x4
+#define RECOVER_DATA                   0x8
 
-extern int
+int
 read_blob_list(struct list_head *blob_list, size_t list_head_offset,
               const struct read_blob_callbacks *cbs, int flags);
 
-extern int
+int
 read_blob_with_cbs(struct blob_descriptor *blob,
-                  const struct read_blob_callbacks *cbs);
+                  const struct read_blob_callbacks *cbs, bool recover_data);
 
-extern int
+int
 read_blob_with_sha1(struct blob_descriptor *blob,
-                   const struct read_blob_callbacks *cbs);
+                   const struct read_blob_callbacks *cbs, bool recover_data);
 
-extern int
+int
 extract_blob_prefix_to_fd(struct blob_descriptor *blob, u64 size,
                          struct filedes *fd);
 
-extern int
-extract_blob_to_fd(struct blob_descriptor *blob, struct filedes *fd);
+int
+extract_blob_to_fd(struct blob_descriptor *blob, struct filedes *fd,
+                  bool recover_data);
 
 /* Miscellaneous blob functions.  */
 
-extern int
+int
 sha1_blob(struct blob_descriptor *blob);
 
 /* Functions to read/write metadata resources.  */
 
-extern int
+int
 read_metadata_resource(struct wim_image_metadata *imd);
 
-extern int
+int
 write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags);
 
 /* Definitions specific to pipable WIM resources.  */
@@ -319,12 +321,12 @@ struct pwm_blob_hdr {
        u8 hash[SHA1_HASH_SIZE];        /* +16  */
        le32 flags;                     /* +36  */
                                        /* +40  */
-} _packed_attribute;
+} __attribute__((packed));
 
 /* Header that precedes each chunk of a compressed resource in a pipable WIM.
  */
 struct pwm_chunk_hdr {
        le32 compressed_size;
-} _packed_attribute;
+} __attribute__((packed));
 
 #endif /* _WIMLIB_RESOURCE_H */
index fbe304e8d41734958f4e66cb8c0b8f09e9fa9fcc..cfd7835e2067f17aae0d6deb2e336fb76b2daf25 100644 (file)
@@ -73,25 +73,25 @@ struct scan_params {
 
 /* scan.c */
 
-extern int
+int
 do_scan_progress(struct scan_params *params, int status,
                 const struct wim_inode *inode);
 
-extern int
+int
 mangle_pat(tchar *pat, const tchar *path, unsigned long line_no);
 
-extern int
+int
 read_capture_config(const tchar *config_file, const void *buf,
                    size_t bufsize, struct capture_config *config);
 
-extern void
+void
 destroy_capture_config(struct capture_config *config);
 
-extern bool
+bool
 match_pattern_list(const tchar *path, const struct string_list *list,
                   int match_flags);
 
-extern int
+int
 try_exclude(const struct scan_params *params);
 
 typedef int (*scan_tree_t)(struct wim_dentry **, const tchar *,
@@ -99,28 +99,28 @@ typedef int (*scan_tree_t)(struct wim_dentry **, const tchar *,
 
 #ifdef WITH_NTFS_3G
 /* ntfs-3g_capture.c */
-extern int
+int
 ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret,
                          const tchar *device, struct scan_params *params);
 #endif
 
-#ifdef __WIN32__
+#ifdef _WIN32
 /* win32_capture.c */
-extern int
+int
 win32_build_dentry_tree(struct wim_dentry **root_ret,
                        const tchar *root_disk_path,
                        struct scan_params *params);
 #define platform_default_scan_tree win32_build_dentry_tree
 #else
 /* unix_capture.c */
-extern int
+int
 unix_build_dentry_tree(struct wim_dentry **root_ret,
                       const tchar *root_disk_path, struct scan_params *params);
 #define platform_default_scan_tree unix_build_dentry_tree
 #endif
 
 #ifdef ENABLE_TEST_SUPPORT
-extern int
+int
 generate_dentry_tree(struct wim_dentry **root_ret,
                     const tchar *root_disk_path, struct scan_params *params);
 #endif
@@ -134,21 +134,21 @@ report_scan_error(struct scan_params *params, int error_code)
                            params->cur_path);
 }
 
-extern bool
+bool
 should_ignore_filename(const tchar *name, int name_nchars);
 
-extern void
+void
 attach_scanned_tree(struct wim_dentry *parent, struct wim_dentry *child,
                    struct blob_table *blob_table);
 
-extern int
+int
 pathbuf_init(struct scan_params *params, const tchar *root_path);
 
-extern const tchar *
+const tchar *
 pathbuf_append_name(struct scan_params *params, const tchar *name,
                    size_t name_nchars, size_t *orig_path_nchars_ret);
 
-extern void
+void
 pathbuf_truncate(struct scan_params *params, size_t nchars);
 
 #endif /* _WIMLIB_SCAN_H */
index 5c864ddd7754e4fbe853d90a1a6bb31d9df1584f..3e817750ab6de9d17a349533582f00dac99e4174 100644 (file)
@@ -35,34 +35,34 @@ struct wim_security_data {
        u8 **descriptors;
 };
 
-extern void
+void
 rollback_new_security_descriptors(struct wim_sd_set *sd_set);
 
-extern void
+void
 destroy_sd_set(struct wim_sd_set *sd_set);
 
-extern s32
+s32
 sd_set_add_sd(struct wim_sd_set *sd_set, const char descriptor[],
              size_t size);
 
-extern int
+int
 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd);
 
-extern struct wim_security_data *
+struct wim_security_data *
 new_wim_security_data(void);
 
-extern int
+int
 read_wim_security_data(const u8 *buf, size_t buf_len,
                       struct wim_security_data **sd_ret);
 
-extern u8 *
+u8 *
 write_wim_security_data(const struct wim_security_data * restrict sd,
                        u8 * restrict p);
 
-extern void
+void
 print_wim_security_data(const struct wim_security_data *sd);
 
-extern void
+void
 free_wim_security_data(struct wim_security_data *sd);
 
 #endif /* _WIMLIB_SECURITY_H */
index 0355fd2c2717b2d2969d7ee43f95f04e62a3b461..f5c5a34c5bdf0cefe1504a271e96aeb092dfd018 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * security_descriptor.h - declarations for Windows security descriptor format
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2013-2015 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_SECURITY_DESCRIPTOR_H
@@ -51,7 +58,7 @@ typedef struct {
        /* Offset of Discretionary Access Control List (DACL) in security
         * descriptor, or 0 if no DACL is present  */
        le32 dacl_offset;
-} _packed_attribute wimlib_SECURITY_DESCRIPTOR_RELATIVE;
+} __attribute__((packed)) wimlib_SECURITY_DESCRIPTOR_RELATIVE;
 
 #define wimlib_SE_OWNER_DEFAULTED              0x0001
 #define wimlib_SE_GROUP_DEFAULTED              0x0002
@@ -78,7 +85,7 @@ typedef struct {
        u8  identifier_authority[6];
 
        le32 sub_authority[];
-} _packed_attribute wimlib_SID;
+} __attribute__((packed)) wimlib_SID;
 
 /* Header of a Windows NT access control list  */
 typedef struct {
@@ -97,7 +104,7 @@ typedef struct {
 
        /* padding  */
        le16 sbz2;
-} _packed_attribute wimlib_ACL;
+} __attribute__((packed)) wimlib_ACL;
 
 #define wimlib_ACCESS_ALLOWED_ACE_TYPE         0
 #define wimlib_ACCESS_DENIED_ACE_TYPE          1
@@ -113,27 +120,27 @@ typedef struct {
 
        /* Size of the access control entry, including this header  */
        le16 size;
-} _packed_attribute wimlib_ACE_HEADER;
+} __attribute__((packed)) wimlib_ACE_HEADER;
 
 /* Windows NT access control entry to grant rights to a user or group  */
 typedef struct {
        wimlib_ACE_HEADER hdr;
        le32 mask;
        wimlib_SID sid;
-} _packed_attribute wimlib_ACCESS_ALLOWED_ACE;
+} __attribute__((packed)) wimlib_ACCESS_ALLOWED_ACE;
 
 /* Windows NT access control entry to deny rights to a user or group  */
 typedef struct {
        wimlib_ACE_HEADER hdr;
        le32 mask;
        wimlib_SID sid;
-} _packed_attribute wimlib_ACCESS_DENIED_ACE;
+} __attribute__((packed)) wimlib_ACCESS_DENIED_ACE;
 
 /* Windows NT access control entry to audit access to the object  */
 typedef struct {
        wimlib_ACE_HEADER hdr;
        le32 mask;
        wimlib_SID sid;
-} _packed_attribute wimlib_SYSTEM_AUDIT_ACE;
+} __attribute__((packed)) wimlib_SYSTEM_AUDIT_ACE;
 
 #endif /* _WIMLIB_SECURITY_DESCRIPTOR_H */
index d14a9c1e577b7239aaa53ce6c8bae5d557e54075..91f84d9168baca324b879127a1b1be722c314a49 100644 (file)
@@ -1,21 +1,28 @@
 /*
  * sha1.h
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022-2023 Eric Biggers
  *
- * Written in 2013-2015 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_SHA1_H
 #include "wimlib/types.h"
 #include "wimlib/util.h"
 
-#define SHA1_HASH_SIZE 20
+#define SHA1_HASH_SIZE 20
+#define SHA1_BLOCK_SIZE        64
+
+struct sha1_ctx {
+       u64 bytecount;
+       u32 h[5];
+       u8 buffer[SHA1_BLOCK_SIZE];
+};
+
+void
+sha1_init(struct sha1_ctx *ctx);
+
+void
+sha1_update(struct sha1_ctx *ctx, const void *data, size_t len);
+
+void
+sha1_final(struct sha1_ctx *ctx, u8 hash[SHA1_HASH_SIZE]);
+
+void
+sha1(const void *data, size_t len, u8 hash[SHA1_HASH_SIZE]);
 
 extern const u8 zero_hash[SHA1_HASH_SIZE];
 
-extern void
-sprint_hash(const u8 hash[SHA1_HASH_SIZE], tchar strbuf[SHA1_HASH_SIZE * 2 + 1]);
+#define SHA1_HASH_STRING_LEN   (2 * SHA1_HASH_SIZE + 1)
+void
+sprint_hash(const u8 hash[SHA1_HASH_SIZE], tchar strbuf[SHA1_HASH_STRING_LEN]);
 
 static inline void
 copy_hash(u8 dest[SHA1_HASH_SIZE], const u8 src[SHA1_HASH_SIZE])
@@ -54,43 +81,7 @@ hashes_equal(const u8 h1[SHA1_HASH_SIZE], const u8 h2[SHA1_HASH_SIZE])
 static inline bool
 is_zero_hash(const u8 *hash)
 {
-       return (hash == zero_hash || hashes_equal(hash, zero_hash));
-}
-
-#ifdef WITH_LIBCRYPTO
-
-#include <openssl/sha.h>
-
-#define sha1_init     SHA1_Init
-#define sha1_update   SHA1_Update
-#define sha1_final    SHA1_Final
-
-static inline void
-sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE])
-{
-       SHA1(buffer, len, hash);
+       return hash == zero_hash || hashes_equal(hash, zero_hash);
 }
 
-#else /* WITH_LIBCRYPTO */
-
-typedef struct {
-       u64 bytecount;
-       u32 state[5];
-       u8 buffer[64];
-} SHA_CTX;
-
-extern void
-sha1_init(SHA_CTX *ctx);
-
-extern void
-sha1_update(SHA_CTX *ctx, const void *data, size_t len);
-
-extern void
-sha1_final(u8 hash[SHA1_HASH_SIZE], SHA_CTX *ctx);
-
-extern void
-sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE]);
-
-#endif /* !WITH_LIBCRYPTO */
-
 #endif /* _WIMLIB_SHA1_H */
index cf37a88084a1dd8de78cba01c8cee4ef35e5240c..051e445f19a6d96b40d7beafca710b8366012e4c 100644 (file)
@@ -3,7 +3,7 @@
 
 struct list_head;
 
-extern int
+int
 sort_blob_list_for_solid_compression(struct list_head *blob_list);
 
 #endif /* _WIMLIB_SOLID_H */
index dfbae30bb9839d7c676b6b2937ed01b608feb1b4..815fa187925aaa00ea60715e21b36a976a250487 100644 (file)
@@ -20,11 +20,11 @@ struct wim_inode;
  */
 #define TAG_WIMLIB_LINUX_XATTRS                0x337DD874
 
-extern void *
+void *
 inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len,
                      u32 *actual_len_ret);
 
-extern bool
+bool
 inode_set_tagged_item(struct wim_inode *inode, u32 tag,
                      const void *data, u32 len);
 
index 8c19a01a7c8a926e26b146c82930442470dea45a..8104921ddaf5dea7ad04e7d10ed85e20a526288d 100644 (file)
@@ -3,6 +3,7 @@
 
 #ifdef ENABLE_TEST_SUPPORT
 
+#include "wimlib.h"
 #include "wimlib/types.h"
 
 #define WIMLIB_ERR_IMAGES_ARE_DIFFERENT                        200
 #define WIMLIB_CMP_FLAG_UNIX_MODE      0x00000001
 #define WIMLIB_CMP_FLAG_NTFS_3G_MODE   0x00000002
 #define WIMLIB_CMP_FLAG_WINDOWS_MODE   0x00000004
+#define WIMLIB_CMP_FLAG_EXT4           0x00000008
 
-extern int
+WIMLIBAPI void
+wimlib_seed_random(u64 seed);
+
+WIMLIBAPI int
 wimlib_compare_images(WIMStruct *wim1, int image1,
                      WIMStruct *wim2, int image2, int cmp_flags);
 
+WIMLIBAPI int
+wimlib_parse_and_write_xml_doc(const tchar *in, tchar **out_ret);
+
+WIMLIBAPI int
+wimlib_utf8_to_utf16le(const char *in, size_t in_nbytes,
+                      utf16lechar **out_ret, size_t *out_nbytes_ret);
+
+WIMLIBAPI int
+wimlib_utf16le_to_utf8(const utf16lechar *in, size_t in_nbytes,
+                      char **out_ret, size_t *out_nbytes_ret);
+
 #endif /* ENABLE_TEST_SUPPORT */
 
 #endif /* _WIMLIB_TEST_SUPPORT_H */
index d79f8b11b78b954fadc374095fdd0b2f690d9adb..df606561aa7d3aa775f24fb6d2edcb28b7ed4f35 100644 (file)
@@ -27,7 +27,7 @@ struct text_file_section {
 #define LOAD_TEXT_FILE_NO_WARNINGS   0x00000002
 #define LOAD_TEXT_FILE_ALLOW_STDIN   0x00000004
 
-extern int
+int
 load_text_file(const tchar *path, const void *buf, size_t bufsize,
               void **mem_ret,
               const struct text_file_section *pos_sections,
diff --git a/include/wimlib/threads.h b/include/wimlib/threads.h
new file mode 100644 (file)
index 0000000..2e624a4
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef _WIMLIB_THREADS_H
+#define _WIMLIB_THREADS_H
+
+#include <stdbool.h>
+
+#ifdef _WIN32
+
+struct thread {
+       void *win32_thread;
+       void *(*thrproc)(void *);
+       void *arg;
+};
+
+struct mutex { void *win32_crit; };
+#define MUTEX_INITIALIZER { NULL }
+
+struct condvar { void *win32_cond; };
+
+#else /* _WIN32 */
+
+#include <pthread.h>
+
+struct thread { pthread_t pthread; };
+
+struct mutex { pthread_mutex_t pthread_mutex; };
+#define MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
+
+struct condvar { pthread_cond_t pthread_cond; };
+
+#endif /* !_WIN32 */
+
+bool thread_create(struct thread *t, void *(*thrproc)(void *), void *arg);
+void thread_join(struct thread *t);
+bool mutex_init(struct mutex *m);
+void mutex_destroy(struct mutex *m);
+void mutex_lock(struct mutex *m);
+void mutex_unlock(struct mutex *m);
+bool condvar_init(struct condvar *c);
+void condvar_destroy(struct condvar *c);
+void condvar_wait(struct condvar *c, struct mutex *m);
+void condvar_signal(struct condvar *c);
+void condvar_broadcast(struct condvar *c);
+
+#endif /* _WIMLIB_THREADS_H */
index 37666c9aeb3329747aa55ca5348212b7132ee2d7..e30e06210c6d8706e88626c270de3c158cc11237 100644 (file)
 
 struct wimlib_timespec;
 
-extern time_t
+time_t
 wim_timestamp_to_time_t(u64 timestamp);
 
-extern void
+void
 wim_timestamp_to_wimlib_timespec(u64 timestamp, struct wimlib_timespec *wts,
                                 s32 *high_part_ret);
 
-extern struct timeval
+struct timeval
 wim_timestamp_to_timeval(u64 timestamp);
 
-extern struct timespec
+struct timespec
 wim_timestamp_to_timespec(u64 timestamp);
 
-extern u64
+u64
 time_t_to_wim_timestamp(time_t t);
 
-extern u64
+u64
 timeval_to_wim_timestamp(const struct timeval *tv);
 
-extern u64
+u64
 timespec_to_wim_timestamp(const struct timespec *ts);
 
-extern u64
+u64
 now_as_wim_timestamp(void);
 
-extern void
+void
 wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len);
 
 #endif /* _WIMLIB_TIMESTAMP_H */
index 473f5e2578f4b88e7907ae4466f7e26371506c22..5c6aa33b75da3aa6d7601841e12eea17385fe878 100644 (file)
@@ -1,45 +1,52 @@
 /*
  * unaligned.h - inline functions for unaligned memory accesses
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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 _WIMLIB_UNALIGNED_H
 #define _WIMLIB_UNALIGNED_H
 
+#include <string.h>
+
 #include "wimlib/compiler.h"
 #include "wimlib/endianness.h"
 #include "wimlib/types.h"
 
 #define DEFINE_UNALIGNED_TYPE(type)                            \
-struct type##_unaligned {                                      \
-       type v;                                                 \
-} _packed_attribute _may_alias_attribute;                      \
-                                                               \
 static forceinline type                                                \
 load_##type##_unaligned(const void *p)                         \
 {                                                              \
-       return ((const struct type##_unaligned *)p)->v;         \
+       type v;                                                 \
+       memcpy(&v, p, sizeof(v));                               \
+       return v;                                               \
 }                                                              \
                                                                \
 static forceinline void                                                \
-store_##type##_unaligned(type val, void *p)                    \
+store_##type##_unaligned(type v, void *p)                      \
 {                                                              \
-       ((struct type##_unaligned *)p)->v = val;                \
+       memcpy(p, &v, sizeof(v));                               \
 }
 
 DEFINE_UNALIGNED_TYPE(u16);
@@ -76,6 +83,16 @@ get_unaligned_le32(const u8 *p)
                        ((u32)p[1] << 8) | p[0];
 }
 
+static forceinline u32
+get_unaligned_be32(const u8 *p)
+{
+       if (UNALIGNED_ACCESS_IS_FAST)
+               return be32_to_cpu(load_be32_unaligned(p));
+       else
+               return ((u32)p[0] << 24) | ((u32)p[1] << 16) |
+                       ((u32)p[2] << 8) | p[3];
+}
+
 static forceinline void
 put_unaligned_le16(u16 v, u8 *p)
 {
@@ -100,42 +117,17 @@ put_unaligned_le32(u32 v, u8 *p)
        }
 }
 
-/*
- * Given a 32-bit value that was loaded with the platform's native endianness,
- * return a 32-bit value whose high-order 8 bits are 0 and whose low-order 24
- * bits contain the first 3 bytes, arranged in octets in a platform-dependent
- * order, at the memory location from which the input 32-bit value was loaded.
- */
-static forceinline u32
-loaded_u32_to_u24(u32 v)
-{
-       if (CPU_IS_LITTLE_ENDIAN)
-               return v & 0xFFFFFF;
-       else
-               return v >> 8;
-}
-
-/*
- * Load the next 3 bytes from the memory location @p into the 24 low-order bits
- * of a 32-bit value.  The order in which the 3 bytes will be arranged as octets
- * in the 24 bits is platform-dependent.  At least LOAD_U24_REQUIRED_NBYTES
- * bytes must be available at @p; note that this may be more than 3.
- */
-static forceinline u32
-load_u24_unaligned(const u8 *p)
+static forceinline void
+put_unaligned_be32(u32 v, u8 *p)
 {
-#if UNALIGNED_ACCESS_IS_FAST
-#  define LOAD_U24_REQUIRED_NBYTES 4
-       return loaded_u32_to_u24(load_u32_unaligned(p));
-#else
-#  define LOAD_U24_REQUIRED_NBYTES 3
-#  if CPU_IS_BIG_ENDIAN
-       return ((u32)p[2] << 0) | ((u32)p[1] << 8) | ((u32)p[0] << 16);
-#  else
-       return ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16);
-#  endif
-#endif
+       if (UNALIGNED_ACCESS_IS_FAST) {
+               store_be32_unaligned(cpu_to_be32(v), p);
+       } else {
+               p[0] = (u8)(v >> 24);
+               p[1] = (u8)(v >> 16);
+               p[2] = (u8)(v >> 8);
+               p[3] = (u8)(v >> 0);
+       }
 }
 
-
 #endif /* _WIMLIB_UNALIGNED_H */
index ede0fdcec3cf1cc5c46091083db7eeb06c55b36f..11aa67b376798b9bcd6bb9af22b2f6e3ec4b89ab 100644 (file)
@@ -12,10 +12,10 @@ struct wimlib_unix_data {
 
 struct wim_inode;
 
-extern bool
+bool
 inode_has_unix_data(const struct wim_inode *inode);
 
-extern bool
+bool
 inode_get_unix_data(const struct wim_inode *inode,
                    struct wimlib_unix_data *unix_data);
 
@@ -26,7 +26,7 @@ inode_get_unix_data(const struct wim_inode *inode,
 
 #define UNIX_DATA_ALL  0xF
 
-extern bool
+bool
 inode_set_unix_data(struct wim_inode *inode,
                    struct wimlib_unix_data *unix_data, int which);
 
index 12b1b120a46ef0d1be9ee47e975ec1e928ced870..6c9ee061efadd6c694bee17a066bad857ba4d7f6 100644 (file)
  * Memory allocation
  *******************/
 
-extern void *
-wimlib_malloc(size_t size) _malloc_attribute;
+void *
+wimlib_malloc(size_t size);
 
-extern void
+void
 wimlib_free_memory(void *p);
 
-extern void *
+void *
 wimlib_realloc(void *ptr, size_t size);
 
-extern void *
-wimlib_calloc(size_t nmemb, size_t size) _malloc_attribute;
+void *
+wimlib_calloc(size_t nmemb, size_t size);
 
-extern char *
-wimlib_strdup(const char *str) _malloc_attribute;
+char *
+wimlib_strdup(const char *str);
 
-#ifdef __WIN32__
-extern wchar_t *
-wimlib_wcsdup(const wchar_t *str) _malloc_attribute;
+#ifdef _WIN32
+wchar_t *
+wimlib_wcsdup(const wchar_t *str);
 #endif
 
-extern void *
-wimlib_aligned_malloc(size_t size, size_t alignment) _malloc_attribute;
+void *
+wimlib_aligned_malloc(size_t size, size_t alignment);
 
-extern void
+void
 wimlib_aligned_free(void *ptr);
 
-extern void *
-memdup(const void *mem, size_t size) _malloc_attribute;
+void *
+memdup(const void *mem, size_t size);
 
 #define MALLOC         wimlib_malloc
 #define FREE           wimlib_free_memory
@@ -85,7 +85,7 @@ memdup(const void *mem, size_t size) _malloc_attribute;
  *******************/
 
 #ifndef HAVE_MEMPCPY
-extern void *
+void *
 mempcpy(void *dst, const void *src, size_t n);
 #endif
 
@@ -93,10 +93,10 @@ mempcpy(void *dst, const void *src, size_t n);
  * Random number generation
  **************************/
 
-extern void
+void
 get_random_bytes(void *p, size_t n);
 
-extern void
+void
 get_random_alnum_chars(tchar *p, size_t n);
 
 /************************
index 67f55e3bb0ed24df599161cf7fd314ef01b80028..666f4b02638f82b612bd6bf6ba8df20918e14c39 100644 (file)
@@ -96,6 +96,9 @@ struct WIMStruct {
         * Otherwise, this field is invalid (!filedes_valid(&out_fd)).  */
        struct filedes out_fd;
 
+       /* The size of the backing file, or 0 if unknown */
+       u64 file_size;
+
        /*
         * This is the cached decompressor for this WIM file, or NULL if no
         * decompressor is cached yet.  Normally, all the compressed data in a
@@ -185,47 +188,47 @@ static inline bool wim_is_pipable(const WIMStruct *wim)
        return (wim->hdr.magic == PWM_MAGIC);
 }
 
-extern void
+void
 wim_decrement_refcnt(WIMStruct *wim);
 
-extern bool
+bool
 wim_has_solid_resources(const WIMStruct *wim);
 
-extern int
+int
 read_wim_header(WIMStruct *wim, struct wim_header *hdr);
 
-extern int
+int
 write_wim_header(const struct wim_header *hdr, struct filedes *out_fd,
                 off_t offset);
 
-extern int
+int
 write_wim_header_flags(u32 hdr_flags, struct filedes *out_fd);
 
-extern int
+int
 select_wim_image(WIMStruct *wim, int image);
 
-extern void
+void
 deselect_current_wim_image(WIMStruct *wim);
 
-extern int
+int
 for_image(WIMStruct *wim, int image, int (*visitor)(WIMStruct *));
 
-extern int
+int
 wim_checksum_unhashed_blobs(WIMStruct *wim);
 
-extern int
+int
 delete_wim_image(WIMStruct *wim, int image);
 
 /* Internal open flags (pass to open_wim_as_WIMStruct(), not wimlib_open_wim())
  */
 #define WIMLIB_OPEN_FLAG_FROM_PIPE     0x80000000
 
-extern int
+int
 open_wim_as_WIMStruct(const void *wim_filename_or_fd, int open_flags,
                      WIMStruct **wim_ret,
                      wimlib_progress_func_t progfunc, void *progctx);
 
-extern int
+int
 can_modify_wim(WIMStruct *wim);
 
 #endif /* _WIMLIB_WIM_H */
index 10857d5b64680be085e6d5afc508613e2092a94d..97071290695ac784470c9f42981f58f6e726dba9 100644 (file)
@@ -8,13 +8,13 @@
 
 struct blob_descriptor;
 
-extern int
+int
 wimboot_alloc_data_source_id(const wchar_t *wim_path,
                             const u8 guid[GUID_SIZE], int image,
                             const wchar_t *target, u64 *data_source_id_ret,
                             bool *wof_running_ret);
 
-extern bool
+bool
 wimboot_set_pointer(HANDLE h,
                    const struct blob_descriptor *blob,
                    u64 data_source_id,
index 94511a872b28f10072dcd9e6c36876f1fa0110bf..ed54e6f5bd853cd9a603100ca2c6c39873186080 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef _WIMLIB_WIN32_H
 #define _WIMLIB_WIN32_H
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #include "wimlib/types.h"
 
@@ -13,56 +13,57 @@ struct blob_descriptor;
 struct consume_chunk_callback;
 struct windows_file;
 
-extern struct windows_file *
+struct windows_file *
 clone_windows_file(const struct windows_file *file);
 
-extern void
+void
 free_windows_file(struct windows_file *file);
 
-extern int
+int
 cmp_windows_files(const struct windows_file *file1,
                  const struct windows_file *file2);
 
-extern int
+int
 read_windows_file_prefix(const struct blob_descriptor *blob, u64 size,
-                        const struct consume_chunk_callback *cb);
+                        const struct consume_chunk_callback *cb,
+                        bool recover_data);
 
-extern int
+int
 win32_global_init(int init_flags);
 
-extern void
+void
 win32_global_cleanup(void);
 
-extern int
+int
 fsync(int fd);
 
-extern tchar *
+tchar *
 realpath(const tchar *path, tchar *resolved_path);
 
-extern int
+int
 win32_rename_replacement(const tchar *oldpath, const tchar *newpath);
 
-extern int
+int
 win32_truncate_replacement(const tchar *path, off_t size);
 
-extern int
+int
 win32_strerror_r_replacement(int errnum, tchar *buf, size_t buflen);
 
-extern FILE *
+FILE *
 win32_open_logfile(const wchar_t *path);
 
-extern ssize_t
+ssize_t
 win32_read(int fd, void *buf, size_t count);
 
-extern ssize_t
+ssize_t
 win32_write(int fd, const void *buf, size_t count);
 
-extern ssize_t
+ssize_t
 win32_pread(int fd, void *buf, size_t count, off_t offset);
 
-extern ssize_t
+ssize_t
 win32_pwrite(int fd, const void *buf, size_t count, off_t offset);
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
 
 #endif /* _WIMLIB_WIN32_H */
index 70114c8dac7aa86938c3e5bac0db6f0d9d73a352..46faf6ac6dd086059f1822af18b117c9ffcf9f7c 100644 (file)
@@ -30,7 +30,9 @@ typedef struct _RTL_RELATIVE_NAME_U {
        PRTLP_CURDIR_REF CurDirRef;
 } RTL_RELATIVE_NAME_U, *PRTL_RELATIVE_NAME_U;
 
-#define FSCTL_SET_PERSISTENT_VOLUME_STATE 0x90238
+#ifndef FSCTL_SET_PERSISTENT_VOLUME_STATE
+#define FSCTL_SET_PERSISTENT_VOLUME_STATE \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 142, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 #define PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED 0x00000001
 
@@ -40,6 +42,7 @@ typedef struct _FILE_FS_PERSISTENT_VOLUME_INFORMATION {
        ULONG Version;
        ULONG Reserved;
 } FILE_FS_PERSISTENT_VOLUME_INFORMATION, *PFILE_FS_PERSISTENT_VOLUME_INFORMATION;
+#endif /* FSCTL_SET_PERSISTENT_VOLUME_STATE */
 
 /* ntdll functions  */
 
@@ -133,28 +136,28 @@ extern NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
 
 /* Other utility functions */
 
-extern int
+int
 win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path);
 
-extern int
+int
 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7]);
 
-extern bool
+bool
 win32_try_to_attach_wof(const wchar_t *drive);
 
-extern void
-win32_warning(DWORD err, const wchar_t *format, ...) _cold_attribute;
+void __attribute__((cold))
+win32_warning(DWORD err, const wchar_t *format, ...);
 
-extern void
-win32_error(DWORD err, const wchar_t *format, ...) _cold_attribute;
+void __attribute__((cold))
+win32_error(DWORD err, const wchar_t *format, ...);
 
-extern void
-winnt_warning(NTSTATUS status, const wchar_t *format, ...) _cold_attribute;
+void __attribute__((cold))
+winnt_warning(NTSTATUS status, const wchar_t *format, ...);
 
-extern void
-winnt_error(NTSTATUS status, const wchar_t *format, ...) _cold_attribute;
+void __attribute__((cold))
+winnt_error(NTSTATUS status, const wchar_t *format, ...);
 
-extern NTSTATUS
+NTSTATUS
 winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size,
            void *out, u32 out_size_avail, u32 *actual_out_size_ret);
 
index 6e9c197717e7376b39dccef978728f5293a9e650..54244064b485d1088c03abdab9e264093963dd1d 100644 (file)
@@ -14,7 +14,7 @@ struct vss_snapshot {
        size_t refcnt;
 };
 
-extern void
+void
 vss_delete_snapshot(struct vss_snapshot *snapshot);
 
 /* Acquire a reference to the specified VSS snapshot.  */
@@ -35,11 +35,11 @@ vss_put_snapshot(struct vss_snapshot *snapshot)
                vss_delete_snapshot(snapshot);
 }
 
-extern int
+int
 vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
                    struct vss_snapshot **snapshot_ret);
 
-extern void
+void
 vss_global_cleanup(void);
 
 #endif /* _WIMLIB_WIN32_VSS_H */
index d0fcf03a3ca4797727846fa183a817bcaedcebed..6f5017be45c1db4a2e8eb4d21ec8ba836c887695 100644 (file)
@@ -2,25 +2,30 @@
  * wof.h
  *
  * Definitions for the Windows Overlay Filesystem filter (WOF) ioctls, as well
- * some definitions for associated undocumented data structures.  See
- * http://msdn.microsoft.com/en-us/library/windows/hardware/ff540367(v=vs.85).aspx
- * for more information about the documented ioctls.
- *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * some definitions for associated undocumented data structures.
+ *
+ * Copyright 2022 Eric Biggers
+ *
+ * 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 _WOF_H_
@@ -29,6 +34,8 @@
 #include "wimlib/compiler.h"
 #include "wimlib/types.h"
 
+#ifdef _WIN32
+
 /*
  * The Windows Overlay Filesystem filter (WOF, a.k.a. wof.sys) is a filesystem
  * filter driver, available in Windows 8.1 and later, which allows files to be
  */
 
 
-/* Current version of the WOF driver/protocol  */
-#define WOF_CURRENT_VERSION            1
-
-/* Specifies the WIM backing provider  */
-#define WOF_PROVIDER_WIM               1
-
-/* Specifies the "file" backing provider (a.k.a. System Compression)  */
-#define WOF_PROVIDER_FILE              2
+/*----------------------------------------------------------------------------*
+ *                          WOF ioctl definitions                             *
+ *----------------------------------------------------------------------------*/
 
-/* The current version of the WIM backing provider  */
-#define WIM_PROVIDER_CURRENT_VERSION   1
-
-/* The current version of the file backing provider  */
-#define FILE_PROVIDER_CURRENT_VERSION  1
-
-/* Identifies a backing provider for a specific overlay service version.  */
-struct wof_external_info {
-
-       /* Version of the overlay service supported by the backing provider.
-        * Set to WOF_CURRENT_VERSION.  */
-       u32 version;
-
-       /* Identifier for the backing provider.  Example value:
-        * WOF_PROVIDER_WIM.  */
-       u32 provider;
-};
+#ifndef WOF_CURRENT_VERSION
+/* Identifies a file backing provider and the overlay service version it supports.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_wof_external_info */
+typedef struct _WOF_EXTERNAL_INFO {
+#define WOF_CURRENT_VERSION 1
+       DWORD Version;
+       DWORD Provider;
+} WOF_EXTERNAL_INFO, *PWOF_EXTERNAL_INFO;
+#endif /* WOF_CURRENT_VERSION */
 
+/* WIM provider ("WIMBoot") */
+#ifndef WOF_PROVIDER_WIM
+#define WOF_PROVIDER_WIM 1
+/*
+ * The identifier and status information for the Windows Image File (WIM)
+ * external backing provider.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_wim_provider_external_info
+ */
+typedef struct _WIM_PROVIDER_EXTERNAL_INFO {
+#define WIM_PROVIDER_CURRENT_VERSION 1
+       ULONG         Version;
+       ULONG         Flags;
+       LARGE_INTEGER DataSourceId;
+#define WIM_PROVIDER_HASH_SIZE 20
+       UCHAR         ResourceHash[WIM_PROVIDER_HASH_SIZE];
+} WIM_PROVIDER_EXTERNAL_INFO, *PWIM_PROVIDER_EXTERNAL_INFO;
+#endif /* WOF_PROVIDER_WIM */
+
+/* File provider ("system compression") */
+#ifndef WOF_PROVIDER_FILE
+#define WOF_PROVIDER_FILE 2
+/* Defines metadata specific to files provided by WOF_PROVIDER_FILE.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_provider_external_info_v1 */
+typedef struct _FILE_PROVIDER_EXTERNAL_INFO_V1 {
+#define FILE_PROVIDER_CURRENT_VERSION 1
+       DWORD Version;
+#define FILE_PROVIDER_COMPRESSION_XPRESS4K     0
+#define FILE_PROVIDER_COMPRESSION_LZX          1
+#define FILE_PROVIDER_COMPRESSION_XPRESS8K     2
+#define FILE_PROVIDER_COMPRESSION_XPRESS16K    3
+       DWORD Algorithm;
+       DWORD Flags;
+} FILE_PROVIDER_EXTERNAL_INFO_V1, *PFILE_PROVIDER_EXTERNAL_INFO_V1;
+#endif /* WOF_PROVIDER_FILE */
+
+/* Sets the backing source for a file.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-set-external-backing */
+#ifndef FSCTL_SET_EXTERNAL_BACKING
+#define FSCTL_SET_EXTERNAL_BACKING \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 195, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
+#endif
+
+/* Gets the backing information for a file from an external backing provider.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-get-external-backing */
+#ifndef FSCTL_GET_EXTERNAL_BACKING
+#define FSCTL_GET_EXTERNAL_BACKING \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 196, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+#ifndef STATUS_OBJECT_NOT_EXTERNALLY_BACKED
+#define STATUS_OBJECT_NOT_EXTERNALLY_BACKED    0xC000046D
+#endif
+
+/* Removes the association of a file with an external backing provider.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-delete-external-backing */
+#ifndef FSCTL_DELETE_EXTERNAL_BACKING
+#define FSCTL_DELETE_EXTERNAL_BACKING \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 197, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
+#endif
+
+/* Begins or continues an enumeration of files on a volume that have a backing source.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-enum-external-backing */
+#ifndef FSCTL_ENUM_EXTERNAL_BACKING
+#define FSCTL_ENUM_EXTERNAL_BACKING \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 198, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+/* Enumerates all the data sources from a backing provider for a specified volume.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-enum-overlay */
+#ifndef FSCTL_ENUM_OVERLAY
+#define FSCTL_ENUM_OVERLAY \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 199, METHOD_NEITHER, FILE_ANY_ACCESS)
+#endif
+typedef struct _WIM_PROVIDER_OVERLAY_ENTRY {
+       ULONG NextEntryOffset;
+       LARGE_INTEGER DataSourceId;
+       GUID WimGuid;
+       ULONG WimFileNameOffset;
+       ULONG WimType;
+       ULONG WimIndex;
+       ULONG Flags;
+} WIM_PROVIDER_OVERLAY_ENTRY, *PWIM_PROVIDER_OVERLAY_ENTRY;
+
+/* Add a new external backing source to a volume's namespace.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-add-overlay */
+#ifndef FSCTL_ADD_OVERLAY
+#define FSCTL_ADD_OVERLAY \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 204, METHOD_BUFFERED, FILE_WRITE_DATA)
+#endif
+typedef struct _WIM_PROVIDER_ADD_OVERLAY_INPUT {
+#define WIM_BOOT_NOT_OS_WIM    0
+#define WIM_BOOT_OS_WIM                1
+       ULONG WimType;
+       ULONG WimIndex;
+       ULONG WimFileNameOffset;
+       ULONG WimFileNameLength;
+} WIM_PROVIDER_ADD_OVERLAY_INPUT, *PWIM_PROVIDER_ADD_OVERLAY_INPUT;
+
+/* Removes a backing source from a volume.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-remove-overlay */
+#ifndef FSCTL_REMOVE_OVERLAY
+#define FSCTL_REMOVE_OVERLAY \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 205, METHOD_BUFFERED, FILE_WRITE_DATA)
+#endif
+typedef struct _WIM_PROVIDER_REMOVE_OVERLAY_INPUT {
+       LARGE_INTEGER DataSourceId;
+} WIM_PROVIDER_REMOVE_OVERLAY_INPUT, *PWIM_PROVIDER_REMOVE_OVERLAY_INPUT;
+
+/* Updates a new data source identifier for a backing source attached to a volume.
+ * Ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-update-overlay */
+#ifndef FSCTL_UPDATE_OVERLAY
+#define FSCTL_UPDATE_OVERLAY \
+       CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 206, METHOD_BUFFERED, FILE_WRITE_DATA)
+#endif
+typedef struct _WIM_PROVIDER_UPDATE_OVERLAY_INPUT {
+       LARGE_INTEGER DataSourceId;
+       ULONG         WimFileNameOffset;
+       ULONG         WimFileNameLength;
+} WIM_PROVIDER_UPDATE_OVERLAY_INPUT, *PWIM_PROVIDER_UPDATE_OVERLAY_INPUT;
+
+/*----------------------------------------------------------------------------*
+ *        WOF reparse point and WimOverlay.dat structs (undocumented)         *
+ *----------------------------------------------------------------------------*/
 
 /*
  * Format of the WIM provider reparse data.  This is the data which follows the
  * portion of the reparse point common to WOF.  (The common portion consists of
- * a reparse point header where the reparse tag is 0x80000017, then a 'struct
- * wof_external_info' which specifies the provider.)
+ * a reparse point header where the reparse tag is 0x80000017, then a
+ * WOF_EXTERNAL_INFO struct which specifies the provider.)
  *
  * Note that Microsoft does not document any of the reparse point formats for
  * WOF, although they document the structures which must be passed into the
@@ -131,7 +248,7 @@ struct wim_provider_rpdata {
 
        /* Byte offset of the file's unnamed data stream in the WIM.  */
        le64 unnamed_data_stream_offset_in_wim;
-} _packed_attribute;
+} __attribute__((packed));
 
 /* WIM-specific information about a WIM data source  */
 struct WimOverlay_dat_entry_1 {
@@ -160,7 +277,7 @@ struct WimOverlay_dat_entry_1 {
 
        /* GUID of the WIM file (copied from the WIM header, offset +0x18).  */
        u8 guid[16];
-} _packed_attribute;
+} __attribute__((packed));
 
 /*
  * Format of file: "\System Volume Information\WimOverlay.dat"
@@ -191,7 +308,7 @@ struct WimOverlay_dat_header {
        le64 next_data_source_id;
 
        struct WimOverlay_dat_entry_1 entry_1s[];
-} _packed_attribute;
+} __attribute__((packed));
 
 /* Location information about a WIM data source  */
 struct WimOverlay_dat_entry_2 {
@@ -297,10 +414,10 @@ struct WimOverlay_dat_entry_2 {
                /* Null-terminated path to WIM file.  Begins with \ but does
                 * *not* include drive letter!  */
                utf16lechar wim_file_name[];
-       } _packed_attribute;
-} _packed_attribute;
+       } __attribute__((packed));
+} __attribute__((packed));
 
-static _unused_attribute void
+static void __attribute__((unused))
 wof_check_structs(void)
 {
        STATIC_ASSERT(sizeof(struct WimOverlay_dat_header) == 24);
@@ -308,279 +425,6 @@ wof_check_structs(void)
        STATIC_ASSERT(sizeof(struct WimOverlay_dat_entry_2) == 104);
 }
 
-/*****************************************************************************
- *
- * --- FSCTL_SET_EXTERNAL_BACKING ---
- *
- * Sets the backing source of a file.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     0 (FILE_ANY_ACCESS)
- * Function:   195
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer:  'struct wof_external_info' followed by provider-specific data
- * ('struct wim_provider_external_info' in the case of WIM).
- *
- * Output buffer: None
- */
-#define FSCTL_SET_EXTERNAL_BACKING 0x9030C
-
-struct wim_provider_external_info {
-
-       /* Set to WIM_PROVIDER_CURRENT_VERSION.  */
-       u32 version;
-
-       /* 0 when WIM provider active, otherwise
-        * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or
-        * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED.  */
-       u32 flags;
-
-       /* Integer ID that identifies the WIM.  Get this with the
-        * FSCTL_ADD_OVERLAY ioctl.  */
-       u64 data_source_id;
-
-       /* SHA-1 message digest of the file's unnamed data stream.  */
-       u8 unnamed_data_stream_hash[20];
-};
-
-struct file_provider_external_info {
-
-       /* Set to FILE_PROVIDER_CURRENT_VERSION.  */
-       u32 version;
-
-       u32 compression_format;
-#define FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K      0
-#define FILE_PROVIDER_COMPRESSION_FORMAT_LZX           1
-#define FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K      2
-#define FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K     3
-};
-
-/*****************************************************************************
- *
- * --- FSCTL_GET_EXTERNAL_BACKING ---
- *
- * Get external backing information for the specified file.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     0 (FILE_ANY_ACCESS)
- * Function:   196
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer: None
- * Output buffer:  'struct wof_external_info' followed by provider-specific data
- * ('struct wim_provider_external_info' in the case of WIM).
- */
-#define FSCTL_GET_EXTERNAL_BACKING 0x90310
-
-#define STATUS_OBJECT_NOT_EXTERNALLY_BACKED    0xC000046D
-
-/*****************************************************************************
- *
- * --- FSCTL_DELETE_EXTERNAL_BACKING ---
- *
- * Copy a file from its backing source to its volume, then disassociate it from
- * its backing provider.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     0 (FILE_ANY_ACCESS)
- * Function:   197
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer: None
- * Output buffer: None
- */
-#define FSCTL_DELETE_EXTERNAL_BACKING 0x90314
-
-/*****************************************************************************
- *
- * --- FSCTL_ENUM_EXTERNAL_BACKING ---
- *
- * Enumerate externally backed files on a volume.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     0 (FILE_ANY_ACCESS)
- * Function:   198
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer: None
- * Output buffer: A 16-byte buffer that receives the 128-bit file ID for the
- * next externally backed file.
- *
- * The handle used may be either the volume handle or the handle for any file or
- * directory on the volume.
- *
- * When all externally backed files on the volume have been enumerated, the
- * function fails with ERROR_NO_MORE_FILES.
- */
-#define FSCTL_ENUM_EXTERNAL_BACKING 0x90318
-
-/*****************************************************************************
- *
- * --- FSCTL_ENUM_OVERLAY ---
- *
- * Enumerates the volume's overlay sources from the specified provider.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     0 (FILE_ANY_ACCESS)
- * Function:   199
- * Method:     3 (METHOD_NEITHER)
- *
- * Input buffer:  'struct wof_external_info' to specify the provider for which
- * to enumerate the overlay sources.
- *
- * Output buffer:  Provider-specific data.  For the WIM provider, an array of
- * 'struct wim_provider_overlay_entry'.
- *
- * This ioctl must be performed on the volume handle, such as \\.\C:
- */
-#define FSCTL_ENUM_OVERLAY 0x9031F
-
-struct wim_provider_overlay_entry {
-       /* Byte offset of the next entry from the beginning of this structure,
-        * or 0 if there are no more entries.  */
-       u32 next_entry_offset;
-
-       u32 padding;
-
-       /* Identifier for the WIM file.  */
-       u64 data_source_id;
-
-       /* GUID of the WIM file.  */
-       u8 guid[16];
-
-       /* Byte offset of the WIM's file name from the beginning of this
-        * structure.  */
-       u32 wim_file_name_offset;
-
-       /* Type of WIM file: WIM_BOOT_OS_WIM or WIM_BOOT_NOT_OS_WIM.  */
-       u32 wim_type;
-
-       /* Index of the image in the WIM to use??? (This doesn't really make
-        * sense, since WIM files combine file data "blobs" for all images into
-        * a single table.  Set to 1 if unsure...)  */
-       u32 wim_index;
-
-       /* 0 when WIM provider active, otherwise
-        * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or
-        * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED.  */
-       u32 flags;
-
-       /* Full path to the WIM in the NT device namespace, e.g.
-        * "\Device\HardDiskVolume2\test.wim".  Seems to be null-terminated,
-        * although you probably shouldn't assume so.  */
-       wchar_t wim_file_name[];
-};
-
-
-/*****************************************************************************
- *
- * --- FSCTL_ADD_OVERLAY ---
- *
- * Adds a new external backing source to a volume.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     2 (FILE_WRITE_ACCESS)
- * Function:   204
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer:  'struct wof_external_info' followed by provider-specific data
- * ('struct wim_provider_add_overlay_input' in the case of WIM).
- *
- * Output buffer:  Buffer large enough to receive any information resulting from
- * the add operation.  For the WIM provider, this must be an 8 byte buffer that
- * receives the 64-bit WIM file ID.
- *
- * This ioctl must be performed on the volume handle, such as \\.\C:
- */
-#define FSCTL_ADD_OVERLAY 0x98330
-
-struct wim_provider_add_overlay_input {
-
-       /* Type of WIM file.  */
-       u32 wim_type;
-#define WIM_BOOT_OS_WIM                0
-#define WIM_BOOT_NOT_OS_WIM    1
-
-       /* Index of the image in the WIM to use??? (This doesn't really make
-        * sense, since WIM files combine file data "blobs" for all images into
-        * a single table.  Set to 1 if unsure...)  */
-       u32 wim_index;
-
-       /* Byte offset of wim_file_name in this buffer, not including the
-        * preceding 'struct wof_external_info' (should be 16).  */
-       u32 wim_file_name_offset;
-
-       /* Number of bytes in wim_file_name.  */
-       u32 wim_file_name_length;
-
-       /* Full path to the WIM, e.g. "\??\C:\test-wimboot.wim".
-        * Does NOT need to be null terminated (MS docs claim otherwise).  */
-       wchar_t wim_file_name[];
-};
-
-/*****************************************************************************
- *
- * --- FSCTL_REMOVE_OVERLAY ---
- *
- * Removes an external backing source from a volume.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     2 (FILE_WRITE_ACCESS)
- * Function:   205
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer:  'struct wof_external_info' followed by provider-specific data
- * ('struct wim_provider_remove_overlay_input' in the case of WIM).
- *
- * Output buffer:  None
- *
- * This ioctl must be performed on the volume handle, such as \\.\C:
- */
-#define FSCTL_REMOVE_OVERLAY 0x98334
-
-struct wim_provider_remove_overlay_input {
-       /* Integer ID that identifies the WIM.  */
-       u64 data_source_id;
-};
-
-
-/*****************************************************************************
- *
- * --- FSCTL_UPDATE_OVERLAY ---
- *
- * Updates an overlay source for a volume.
- *
- * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM)
- * Access:     2 (FILE_WRITE_ACCESS)
- * Function:   206
- * Method:     0 (METHOD_BUFFERED)
- *
- * Input buffer:  'struct wof_external_info' followed by provider-specific data
- * ('struct wim_provider_update_overlay_input' in the case of WIM).
- *
- * Output buffer:  None
- *
- * This ioctl must be performed on the volume handle, such as \\.\C:
- */
-#define FSCTL_UPDATE_OVERLAY 0x98338
-
-struct wim_provider_update_overlay_input {
-       /* Integer ID that identifies the WIM data source.  */
-       u64 data_source_id;
-
-       /* Byte offset of wim_file_name in this buffer, not including the
-        * preceding 'struct wof_external_info' (should be 16).  */
-       u32 wim_file_name_offset;
-
-       /* Number of bytes in wim_file_name.  */
-       u32 wim_file_name_length;
-
-       /* Full path to the WIM, e.g. "\??\C:\test-wimboot.wim".
-        * Does NOT need to be null terminated (MS docs claim otherwise).
-        * This WIM must be renamed from the original WIM, or at least be an
-        * identical copy of it!  (Maybe the WIM's GUID field is checked.)  */
-       wchar_t wim_file_name[];
-};
+#endif /* _WIN32 */
 
 #endif /* _WOF_H_ */
index 3f7a7e5463d6d0fe33b5c6fbbf12e5ba5a7823c9..605b10de8acb48d43eb456ab4c42efc9ecc3ef3c 100644 (file)
@@ -31,9 +31,9 @@
        WIMLIB_WRITE_FLAG_UNSAFE_COMPACT)
 
 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
-extern int
+int
 lock_wim_for_append(WIMStruct *wim);
-extern void
+void
 unlock_wim_for_append(WIMStruct *wim);
 #else
 static inline int
diff --git a/include/wimlib/x86_cpu_features.h b/include/wimlib/x86_cpu_features.h
deleted file mode 100644 (file)
index e57742b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _WIMLIB_X86_CPU_FEATURES_H
-#define _WIMLIB_X86_CPU_FEATURES_H
-
-#include "wimlib/types.h"
-
-#if defined(__i386__) || defined(__x86_64__)
-
-#define X86_CPU_FEATURE_SSE            0x00000001
-#define X86_CPU_FEATURE_SSE2           0x00000002
-#define X86_CPU_FEATURE_SSE3           0x00000004
-#define X86_CPU_FEATURE_SSSE3          0x00000008
-#define X86_CPU_FEATURE_SSE4_1         0x00000010
-#define X86_CPU_FEATURE_SSE4_2         0x00000020
-#define X86_CPU_FEATURE_AVX            0x00000040
-#define X86_CPU_FEATURE_BMI            0x00000080
-#define X86_CPU_FEATURE_AVX2           0x00000100
-#define X86_CPU_FEATURE_BMI2           0x00000200
-
-#define X86_CPU_FEATURES_KNOWN         0x80000000
-
-extern u32 _x86_cpu_features;
-
-extern void
-x86_setup_cpu_features(void);
-
-/* Does the processor has the specified feature?  */
-static inline bool
-x86_have_cpu_feature(u32 feature)
-{
-       if (!(_x86_cpu_features & X86_CPU_FEATURES_KNOWN))
-               x86_setup_cpu_features();
-       return _x86_cpu_features & feature;
-}
-
-#else
-
-static inline bool
-x86_have_cpu_feature(u32 feature)
-{
-       return false;
-}
-
-#endif /* __i386__ || __x86_64__ */
-
-#endif /* _WIMLIB_X86_CPU_FEATURES_H */
index 2d297245aaf3a808115989d05d21d885fd876b3c..074fed9d1fe0df04e7d368f09a477dffa501cdea 100644 (file)
@@ -40,7 +40,7 @@ struct wim_xattr_entry {
        /* u8 value[0]; */
 
        /* no padding at end! */
-} _packed_attribute;
+} __attribute__((packed));
 
 static inline size_t
 xattr_entry_size(const struct wim_xattr_entry *entry)
@@ -98,7 +98,7 @@ struct wimlib_xattr_entry_old {
        /* u8 value[0]; */
 
        /* then zero-padded to a 4-byte boundary */
-} _aligned_attribute(4);
+} __attribute__((aligned(4)));
 
 static inline size_t
 old_xattr_entry_size(const struct wimlib_xattr_entry_old *entry)
index 9ce52104093ea646686775448733672f43794613..c11138c927634bbde25e8ea044e95ba41cf1b884 100644 (file)
@@ -7,53 +7,53 @@
 
 struct wim_xml_info;
 
-extern struct wim_xml_info *
+struct wim_xml_info *
 xml_new_info_struct(void);
 
-extern void
+void
 xml_free_info_struct(struct wim_xml_info *info);
 
 /*****************************************************************************/
 
-extern int
+int
 xml_get_image_count(const struct wim_xml_info *info);
 
-extern u64
+u64
 xml_get_total_bytes(const struct wim_xml_info *info);
 
-extern u64
+u64
 xml_get_image_total_bytes(const struct wim_xml_info *info, int image);
 
-extern u64
+u64
 xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image);
 
-extern bool
+bool
 xml_get_wimboot(const struct wim_xml_info *info, int image);
 
-extern u64
+u64
 xml_get_windows_build_number(const struct wim_xml_info *info, int image);
 
-extern int
+int
 xml_set_wimboot(struct wim_xml_info *info, int image);
 
 /*****************************************************************************/
 
-extern int
+int
 xml_update_image_info(WIMStruct *wim, int image);
 
-extern int
+int
 xml_add_image(struct wim_xml_info *info, const tchar *name);
 
-extern int
+int
 xml_export_image(const struct wim_xml_info *src_info, int src_image,
                 struct wim_xml_info *dest_info, const tchar *dest_image_name,
                 const tchar *dest_image_description, bool wimboot);
 
-extern void
+void
 xml_delete_image(struct wim_xml_info *info, int image);
 
 
-extern void
+void
 xml_print_image_info(struct wim_xml_info *info, int image);
 
 /*****************************************************************************/
@@ -63,25 +63,12 @@ struct wim_reshdr;
 #define WIM_TOTALBYTES_USE_EXISTING  ((u64)(-1))
 #define WIM_TOTALBYTES_OMIT          ((u64)(-2))
 
-extern int
+int
 read_wim_xml_data(WIMStruct *wim);
 
-extern int
+int
 write_wim_xml_data(WIMStruct *wim, int image,
                   u64 total_bytes, struct wim_reshdr *out_reshdr,
                   int write_resource_flags);
 
-/*****************************************************************************/
-
-extern void
-xml_global_init(void);
-
-extern void
-xml_global_cleanup(void);
-
-extern void
-xml_set_memory_allocator(void *(*malloc_func)(size_t),
-                        void (*free_func)(void *),
-                        void *(*realloc_func)(void *, size_t));
-
 #endif /* _WIMLIB_XML_H */
index c374bc8afd4914017e6b0505bc101fe4d691d54c..203e79d04db89fbacf71f65dd4fb98b9123d2250 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "wimlib/types.h"
 
-extern int
+int
 set_windows_specific_info(WIMStruct *wim);
 
 #endif /* _WIMLIB_XML_WINDOWS_H */
diff --git a/include/wimlib/xmlproc.h b/include/wimlib/xmlproc.h
new file mode 100644 (file)
index 0000000..c202d62
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef _WIMLIB_XMLPROC_H
+#define _WIMLIB_XMLPROC_H
+
+#include "wimlib/list.h"
+#include "wimlib/types.h"
+
+/*****************************************************************************/
+
+enum xml_node_type {
+       XML_ELEMENT_NODE,
+       XML_TEXT_NODE,
+       XML_ATTRIBUTE_NODE,
+};
+
+struct xml_node {
+       enum xml_node_type type;        /* type of node */
+       tchar *name;                    /* name of ELEMENT or ATTRIBUTE */
+       tchar *value;                   /* value of TEXT or ATTRIBUTE */
+       struct xml_node *parent;        /* parent, or NULL if none */
+       struct list_head children;      /* children; only used for ELEMENT */
+       struct list_head sibling_link;
+};
+
+/* Iterate through the children of an xml_node.  Does nothing if passed NULL. */
+#define xml_node_for_each_child(parent, child) \
+       if (parent) list_for_each_entry(child, &(parent)->children, sibling_link)
+
+static inline bool
+xml_node_is_element(const struct xml_node *node, const tchar *name)
+{
+       return node->type == XML_ELEMENT_NODE && !tstrcmp(node->name, name);
+}
+
+struct xml_node *
+xml_new_element(struct xml_node *parent, const tchar *name);
+
+struct xml_node *
+xml_new_element_with_text(struct xml_node *parent, const tchar *name,
+                         const tchar *text);
+
+void
+xml_add_child(struct xml_node *parent, struct xml_node *child);
+
+void
+xml_unlink_node(struct xml_node *node);
+
+void
+xml_free_node(struct xml_node *node);
+
+const tchar *
+xml_element_get_text(const struct xml_node *element);
+
+int
+xml_element_set_text(struct xml_node *element, const tchar *text);
+
+struct xml_node *
+xml_get_attrib(const struct xml_node *element, const tchar *name);
+
+int
+xml_set_attrib(struct xml_node *element, const tchar *name, const tchar *value);
+
+void
+xml_replace_child(struct xml_node *parent, struct xml_node *replacement);
+
+struct xml_node *
+xml_clone_tree(struct xml_node *orig);
+
+bool
+xml_legal_path(const tchar *name);
+
+bool
+xml_legal_value(const tchar *value);
+
+/*****************************************************************************/
+
+int
+xml_parse_document(const tchar *raw_doc, struct xml_node **doc_ret);
+
+/*****************************************************************************/
+
+struct xml_out_buf {
+       tchar *buf;
+       size_t count;
+       size_t capacity;
+       bool oom;
+};
+
+int
+xml_write_document(struct xml_node *doc, struct xml_out_buf *buf);
+
+#endif /* _WIMLIB_XMLPROC_H */
index 2585fa660b8183fe246370cd75f90a8d2db79358..781f370c7d42760ebb3391ee6adc7b6daea2e702 100644 (file)
@@ -4,7 +4,7 @@
 /* Functions to act on "tchar" strings, which have a platform-dependent encoding
  * and character size. */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #include <wchar.h>
 /*
  * For Windows builds, the "tchar" type will be 2 bytes and will be equivalent
@@ -39,6 +39,7 @@ typedef wchar_t tchar;
 #  define tstrchr      wcschr
 #  define tstrpbrk     wcspbrk
 #  define tstrrchr     wcsrchr
+#  define tstrstr      wcsstr
 #  define tstrlen      wcslen
 #  define tmemcmp      wmemcmp
 #  define tstrcasecmp   _wcsicmp
@@ -64,10 +65,11 @@ typedef wchar_t tchar;
  * function defined ourselves. */
 #  define TSTRDUP      WCSDUP
 #  define tmkdir(path, mode) _wmkdir(path)
-#  define tstrerror_r   win32_strerror_r_replacement
+#  define tstrerror_r(errnum, buf, bufsize) \
+                       _wcserror_s((buf), (bufsize), (errnum))
 #  define trename      win32_rename_replacement
 #  define tglob                win32_wglob
-#else /* __WIN32__ */
+#else /* _WIN32 */
 /*
  * For non-Windows builds, the "tchar" type will be one byte and will specify a
  * string encoded in UTF-8 with the additional possibility of surrogate
@@ -101,6 +103,7 @@ typedef char tchar;
 #  define tstrchr      strchr
 #  define tstrpbrk     strpbrk
 #  define tstrrchr     strrchr
+#  define tstrstr      strstr
 #  define tstrlen      strlen
 #  define tmemcmp      memcmp
 #  define tstrcasecmp   strcasecmp
@@ -126,6 +129,6 @@ typedef char tchar;
 #  define trename      rename
 #  define taccess      access
 #  define tglob                glob
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
 #endif /* _WIMLIB_TCHAR_H */
index d383ad5c6d6a5061370800bb1dc89b7a334c0638..9f35d139149f8d9bda17cddb730cd13bcf775465 100644 (file)
@@ -1,5 +1,5 @@
 # ===========================================================================
-#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+#        https://www.gnu.org/software/autoconf-archive/ax_pthread.html
 # ===========================================================================
 #
 # SYNOPSIS
 #   flags that are needed. (The user can also force certain compiler
 #   flags/libs to be tested by setting these environment variables.)
 #
-#   Also sets PTHREAD_CC to any special C compiler that is needed for
-#   multi-threaded programs (defaults to the value of CC otherwise). (This
-#   is necessary on AIX to use the special cc_r compiler alias.)
+#   Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
+#   needed for multi-threaded programs (defaults to the value of CC
+#   respectively CXX otherwise). (This is necessary on e.g. AIX to use the
+#   special cc_r/CC_r compiler alias.)
 #
 #   NOTE: You are assumed to not only compile your program with these flags,
-#   but also link it with them as well. e.g. you should link with
+#   but also to link with them as well. For example, you might link with
 #   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#   $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
 #
-#   If you are only building threads programs, you may wish to use these
+#   If you are only building threaded programs, you may wish to use these
 #   variables in your default LIBS, CFLAGS, and CC:
 #
 #     LIBS="$PTHREAD_LIBS $LIBS"
 #     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
 #     CC="$PTHREAD_CC"
+#     CXX="$PTHREAD_CXX"
 #
 #   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
-#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
-#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#   has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
+#   that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
 #
 #   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
 #   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
@@ -55,6 +59,7 @@
 #
 #   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
 #   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#   Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
 #
 #   This program is free software: you can redistribute it and/or modify it
 #   under the terms of the GNU General Public License as published by the
@@ -67,7 +72,7 @@
 #   Public License for more details.
 #
 #   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#   with this program. If not, see <https://www.gnu.org/licenses/>.
 #
 #   As a special exception, the respective Autoconf Macro's copyright owner
 #   gives unlimited permission to copy, distribute and modify the configure
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 21
+#serial 31
 
 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
 AC_DEFUN([AX_PTHREAD], [
 AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_SED])
 AC_LANG_PUSH([C])
 ax_pthread_ok=no
 
 # We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
+# requires special compiler flags (e.g. on Tru64 or Sequent).
 # It gets checked for in the link test anyway.
 
 # First of all, check if the user has set any of the PTHREAD_LIBS,
 # etcetera environment variables, and if threads linking works using
 # them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
-        save_CFLAGS="$CFLAGS"
+if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
+        ax_pthread_save_CC="$CC"
+        ax_pthread_save_CFLAGS="$CFLAGS"
+        ax_pthread_save_LIBS="$LIBS"
+        AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
+        AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        save_LIBS="$LIBS"
         LIBS="$PTHREAD_LIBS $LIBS"
-        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
-        AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
+        AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
+        AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
         AC_MSG_RESULT([$ax_pthread_ok])
-        if test x"$ax_pthread_ok" = xno; then
+        if test "x$ax_pthread_ok" = "xno"; then
                 PTHREAD_LIBS=""
                 PTHREAD_CFLAGS=""
         fi
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
+        CC="$ax_pthread_save_CC"
+        CFLAGS="$ax_pthread_save_CFLAGS"
+        LIBS="$ax_pthread_save_LIBS"
 fi
 
 # We must check for the threads library under a number of different
@@ -118,12 +129,14 @@ fi
 # (e.g. DEC) have both -lpthread and -lpthreads, where one of the
 # libraries is broken (non-POSIX).
 
-# Create a list of thread flags to try.  Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
+# Create a list of thread flags to try. Items with a "," contain both
+# C compiler flags (before ",") and linker flags (after ","). Other items
+# starting with a "-" are C compiler flags, and remaining items are
+# library names, except for "none" which indicates that we try without
+# any flags at all, and "pthread-config" which is a program returning
+# the flags for the Pth emulation library.
 
-ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
 
 # The ordering *is* (sometimes) important.  Some notes on the
 # individual items follow:
@@ -132,82 +145,163 @@ ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mt
 # none: in case threads are in libc; should be tried before -Kthread and
 #       other compiler flags to prevent continual compiler warnings
 # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
+#           (Note: HP C rejects this with "bad form for `-t' option")
+# -pthreads: Solaris/gcc (Note: HP C also rejects)
 # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-#      doesn't hurt to check since this sometimes defines pthreads too;
-#      also defines -D_REENTRANT)
-#      ... -mt is also the pthreads flag for HP/aCC
+#      doesn't hurt to check since this sometimes defines pthreads and
+#      -D_REENTRANT too), HP C (must be checked before -lpthread, which
+#      is present but should not be used directly; and before -mthreads,
+#      because the compiler interprets this as "-mt" + "-hreads")
+# -mthreads: Mingw32/gcc, Lynx/gcc
 # pthread: Linux, etcetera
 # --thread-safe: KAI C++
 # pthread-config: use pthread-config program (for GNU Pth library)
 
-case ${host_os} in
+case $host_os in
+
+        freebsd*)
+
+        # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+        # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+
+        ax_pthread_flags="-kthread lthread $ax_pthread_flags"
+        ;;
+
+        hpux*)
+
+        # From the cc(1) man page: "[-mt] Sets various -D flags to enable
+        # multi-threading and also sets -lpthread."
+
+        ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
+        ;;
+
+        openedition*)
+
+        # IBM z/OS requires a feature-test macro to be defined in order to
+        # enable POSIX threads at all, so give the user a hint if this is
+        # not set. (We don't define these ourselves, as they can affect
+        # other portions of the system API in unpredictable ways.)
+
+        AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
+            [
+#            if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
+             AX_PTHREAD_ZOS_MISSING
+#            endif
+            ],
+            [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
+        ;;
+
         solaris*)
 
         # On Solaris (at least, for some versions), libc contains stubbed
         # (non-functional) versions of the pthreads routines, so link-based
-        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
-        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
-        # a function called by this macro, so we could check for that, but
-        # who knows whether they'll stub that too in a future libc.)  So,
-        # we'll just look for -pthreads and -lpthread first:
+        # tests will erroneously succeed. (N.B.: The stubs are missing
+        # pthread_cleanup_push, or rather a function called by this macro,
+        # so we could check for that, but who knows whether they'll stub
+        # that too in a future libc.)  So we'll check first for the
+        # standard Solaris way of linking pthreads (-mt -lpthread).
+
+        ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
+        ;;
+esac
+
+# Are we compiling with Clang?
+
+AC_CACHE_CHECK([whether $CC is Clang],
+    [ax_cv_PTHREAD_CLANG],
+    [ax_cv_PTHREAD_CLANG=no
+     # Note that Autoconf sets GCC=yes for Clang as well as GCC
+     if test "x$GCC" = "xyes"; then
+        AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
+            [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+#            if defined(__clang__) && defined(__llvm__)
+             AX_PTHREAD_CC_IS_CLANG
+#            endif
+            ],
+            [ax_cv_PTHREAD_CLANG=yes])
+     fi
+    ])
+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+
+
+# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
+
+# Note that for GCC and Clang -pthread generally implies -lpthread,
+# except when -nostdlib is passed.
+# This is problematic using libtool to build C++ shared libraries with pthread:
+# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
+# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
+# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
+# To solve this, first try -pthread together with -lpthread for GCC
+
+AS_IF([test "x$GCC" = "xyes"],
+      [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
+
+# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
+
+AS_IF([test "x$ax_pthread_clang" = "xyes"],
+      [ax_pthread_flags="-pthread,-lpthread -pthread"])
 
-        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+
+# The presence of a feature test macro requesting re-entrant function
+# definitions is, on some systems, a strong hint that pthreads support is
+# correctly enabled
+
+case $host_os in
+        darwin* | hpux* | linux* | osf* | solaris*)
+        ax_pthread_check_macro="_REENTRANT"
         ;;
 
-        darwin*)
-        ax_pthread_flags="-pthread $ax_pthread_flags"
+        aix*)
+        ax_pthread_check_macro="_THREAD_SAFE"
         ;;
-esac
 
-# Clang doesn't consider unrecognized options an error unless we specify
-# -Werror. We throw in some extra Clang-specific options to ensure that
-# this doesn't happen for GCC, which also accepts -Werror.
+        *)
+        ax_pthread_check_macro="--"
+        ;;
+esac
+AS_IF([test "x$ax_pthread_check_macro" = "x--"],
+      [ax_pthread_check_cond=0],
+      [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
 
-AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
-save_CFLAGS="$CFLAGS"
-ax_pthread_extra_flags="-Werror"
-CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
-                  [AC_MSG_RESULT([yes])],
-                  [ax_pthread_extra_flags=
-                   AC_MSG_RESULT([no])])
-CFLAGS="$save_CFLAGS"
 
-if test x"$ax_pthread_ok" = xno; then
-for flag in $ax_pthread_flags; do
+if test "x$ax_pthread_ok" = "xno"; then
+for ax_pthread_try_flag in $ax_pthread_flags; do
 
-        case $flag in
+        case $ax_pthread_try_flag in
                 none)
                 AC_MSG_CHECKING([whether pthreads work without any flags])
                 ;;
 
+                *,*)
+                PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
+                PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
+                AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
+                ;;
+
                 -*)
-                AC_MSG_CHECKING([whether pthreads work with $flag])
-                PTHREAD_CFLAGS="$flag"
+                AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
+                PTHREAD_CFLAGS="$ax_pthread_try_flag"
                 ;;
 
                 pthread-config)
                 AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
-                if test x"$ax_pthread_config" = xno; then continue; fi
+                AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
                 PTHREAD_CFLAGS="`pthread-config --cflags`"
                 PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
                 ;;
 
                 *)
-                AC_MSG_CHECKING([for the pthreads library -l$flag])
-                PTHREAD_LIBS="-l$flag"
+                AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
+                PTHREAD_LIBS="-l$ax_pthread_try_flag"
                 ;;
         esac
 
-        save_LIBS="$LIBS"
-        save_CFLAGS="$CFLAGS"
+        ax_pthread_save_CFLAGS="$CFLAGS"
+        ax_pthread_save_LIBS="$LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
         LIBS="$PTHREAD_LIBS $LIBS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
 
         # Check for various functions.  We must include pthread.h,
         # since some functions may be macros.  (On the Sequent, we
@@ -218,8 +312,18 @@ for flag in $ax_pthread_flags; do
         # pthread_cleanup_push because it is one of the few pthread
         # functions on Solaris that doesn't have a non-functional libc stub.
         # We try pthread_create on general principles.
+
         AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
-                        static void routine(void *a) { a = 0; }
+#                       if $ax_pthread_check_cond
+#                        error "$ax_pthread_check_macro must be defined"
+#                       endif
+                        static void *some_global = NULL;
+                        static void routine(void *a)
+                          {
+                             /* To avoid any unused-parameter or
+                                unused-but-set-parameter warning.  */
+                             some_global = a;
+                          }
                         static void *start_routine(void *a) { return a; }],
                        [pthread_t th; pthread_attr_t attr;
                         pthread_create(&th, 0, start_routine, 0);
@@ -227,101 +331,187 @@ for flag in $ax_pthread_flags; do
                         pthread_attr_init(&attr);
                         pthread_cleanup_push(routine, 0);
                         pthread_cleanup_pop(0) /* ; */])],
-                [ax_pthread_ok=yes],
-                [])
+            [ax_pthread_ok=yes],
+            [])
 
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
+        CFLAGS="$ax_pthread_save_CFLAGS"
+        LIBS="$ax_pthread_save_LIBS"
 
         AC_MSG_RESULT([$ax_pthread_ok])
-        if test "x$ax_pthread_ok" = xyes; then
-                break;
-        fi
+        AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
 
         PTHREAD_LIBS=""
         PTHREAD_CFLAGS=""
 done
 fi
 
+
+# Clang needs special handling, because older versions handle the -pthread
+# option in a rather... idiosyncratic way
+
+if test "x$ax_pthread_clang" = "xyes"; then
+
+        # Clang takes -pthread; it has never supported any other flag
+
+        # (Note 1: This will need to be revisited if a system that Clang
+        # supports has POSIX threads in a separate library.  This tends not
+        # to be the way of modern systems, but it's conceivable.)
+
+        # (Note 2: On some systems, notably Darwin, -pthread is not needed
+        # to get POSIX threads support; the API is always present and
+        # active.  We could reasonably leave PTHREAD_CFLAGS empty.  But
+        # -pthread does define _REENTRANT, and while the Darwin headers
+        # ignore this macro, third-party headers might not.)
+
+        # However, older versions of Clang make a point of warning the user
+        # that, in an invocation where only linking and no compilation is
+        # taking place, the -pthread option has no effect ("argument unused
+        # during compilation").  They expect -pthread to be passed in only
+        # when source code is being compiled.
+        #
+        # Problem is, this is at odds with the way Automake and most other
+        # C build frameworks function, which is that the same flags used in
+        # compilation (CFLAGS) are also used in linking.  Many systems
+        # supported by AX_PTHREAD require exactly this for POSIX threads
+        # support, and in fact it is often not straightforward to specify a
+        # flag that is used only in the compilation phase and not in
+        # linking.  Such a scenario is extremely rare in practice.
+        #
+        # Even though use of the -pthread flag in linking would only print
+        # a warning, this can be a nuisance for well-run software projects
+        # that build with -Werror.  So if the active version of Clang has
+        # this misfeature, we search for an option to squash it.
+
+        AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
+            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
+            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
+             # Create an alternate version of $ac_link that compiles and
+             # links in two steps (.c -> .o, .o -> exe) instead of one
+             # (.c -> exe), because the warning occurs only in the second
+             # step
+             ax_pthread_save_ac_link="$ac_link"
+             ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
+             ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
+             ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
+             ax_pthread_save_CFLAGS="$CFLAGS"
+             for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
+                AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
+                CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
+                ac_link="$ax_pthread_save_ac_link"
+                AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+                    [ac_link="$ax_pthread_2step_ac_link"
+                     AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+                         [break])
+                    ])
+             done
+             ac_link="$ax_pthread_save_ac_link"
+             CFLAGS="$ax_pthread_save_CFLAGS"
+             AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
+             ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
+            ])
+
+        case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
+                no | unknown) ;;
+                *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
+        esac
+
+fi # $ax_pthread_clang = yes
+
+
+
 # Various other checks:
-if test "x$ax_pthread_ok" = xyes; then
-        save_LIBS="$LIBS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        save_CFLAGS="$CFLAGS"
+if test "x$ax_pthread_ok" = "xyes"; then
+        ax_pthread_save_CFLAGS="$CFLAGS"
+        ax_pthread_save_LIBS="$LIBS"
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
 
         # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
-        AC_MSG_CHECKING([for joinable pthread attribute])
-        attr_name=unknown
-        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
-            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
-                           [int attr = $attr; return attr /* ; */])],
-                [attr_name=$attr; break],
-                [])
-        done
-        AC_MSG_RESULT([$attr_name])
-        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
-            AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
-                               [Define to necessary symbol if this constant
-                                uses a non-standard name on your system.])
-        fi
-
-        AC_MSG_CHECKING([if more special flags are required for pthreads])
-        flag=no
-        case ${host_os} in
-            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
-            osf* | hpux*) flag="-D_REENTRANT";;
-            solaris*)
-            if test "$GCC" = "yes"; then
-                flag="-D_REENTRANT"
-            else
-                # TODO: What about Clang on Solaris?
-                flag="-mt -D_REENTRANT"
-            fi
-            ;;
-        esac
-        AC_MSG_RESULT([$flag])
-        if test "x$flag" != xno; then
-            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
-        fi
+        AC_CACHE_CHECK([for joinable pthread attribute],
+            [ax_cv_PTHREAD_JOINABLE_ATTR],
+            [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
+             for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+                 AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                                                 [int attr = $ax_pthread_attr; return attr /* ; */])],
+                                [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
+                                [])
+             done
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
+               test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
+               test "x$ax_pthread_joinable_attr_defined" != "xyes"],
+              [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
+                                  [$ax_cv_PTHREAD_JOINABLE_ATTR],
+                                  [Define to necessary symbol if this constant
+                                   uses a non-standard name on your system.])
+               ax_pthread_joinable_attr_defined=yes
+              ])
+
+        AC_CACHE_CHECK([whether more special flags are required for pthreads],
+            [ax_cv_PTHREAD_SPECIAL_FLAGS],
+            [ax_cv_PTHREAD_SPECIAL_FLAGS=no
+             case $host_os in
+             solaris*)
+             ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
+             ;;
+             esac
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
+               test "x$ax_pthread_special_flags_added" != "xyes"],
+              [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
+               ax_pthread_special_flags_added=yes])
 
         AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
-            [ax_cv_PTHREAD_PRIO_INHERIT], [
-                AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
-                                                [[int i = PTHREAD_PRIO_INHERIT;]])],
-                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
-                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            [ax_cv_PTHREAD_PRIO_INHERIT],
+            [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+                                             [[int i = PTHREAD_PRIO_INHERIT;
+                                               return i;]])],
+                            [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                            [ax_cv_PTHREAD_PRIO_INHERIT=no])
             ])
-        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
-            [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
+               test "x$ax_pthread_prio_inherit_defined" != "xyes"],
+              [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
+               ax_pthread_prio_inherit_defined=yes
+              ])
 
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
+        CFLAGS="$ax_pthread_save_CFLAGS"
+        LIBS="$ax_pthread_save_LIBS"
 
         # More AIX lossage: compile with *_r variant
-        if test "x$GCC" != xyes; then
+        if test "x$GCC" != "xyes"; then
             case $host_os in
                 aix*)
                 AS_CASE(["x/$CC"],
-                  [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
-                  [#handle absolute path differently from PATH based program lookup
-                   AS_CASE(["x$CC"],
-                     [x/*],
-                     [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
-                     [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                    [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+                    [#handle absolute path differently from PATH based program lookup
+                     AS_CASE(["x$CC"],
+                         [x/*],
+                         [
+                          AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
+                          AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
+                        ],
+                         [
+                          AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
+                          AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
+                        ]
+                     )
+                    ])
                 ;;
             esac
         fi
 fi
 
 test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
 
 AC_SUBST([PTHREAD_LIBS])
 AC_SUBST([PTHREAD_CFLAGS])
 AC_SUBST([PTHREAD_CC])
+AC_SUBST([PTHREAD_CXX])
 
 # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$ax_pthread_ok" = xyes; then
+if test "x$ax_pthread_ok" = "xyes"; then
         ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
         :
 else
diff --git a/m4/nasm.m4 b/m4/nasm.m4
deleted file mode 100644 (file)
index 978a469..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-# AC_PROG_NASM
-# --------------------------
-# Check that NASM exists and determine flags
-AC_DEFUN([AC_PROG_NASM],[
-
-AC_CHECK_PROGS(NASM, [nasm nasmw yasm])
-test -z "$NASM" && AC_MSG_ERROR([no nasm (Netwide Assembler) found])
-
-AC_MSG_CHECKING([for object file format of host system])
-case "$host_os" in
-  cygwin* | mingw* | pw32* | interix*)
-    case "$host_cpu" in
-      x86_64)
-        objfmt='Win64-COFF'
-        ;;
-      *)
-        objfmt='Win32-COFF'
-        ;;
-    esac
-  ;;
-  msdosdjgpp* | go32*)
-    objfmt='COFF'
-  ;;
-  os2-emx*)                    # not tested
-    objfmt='MSOMF'             # obj
-  ;;
-  linux*coff* | linux*oldld*)
-    objfmt='COFF'              # ???
-  ;;
-  linux*aout*)
-    objfmt='a.out'
-  ;;
-  linux*)
-    case "$host_cpu" in
-      x86_64)
-        objfmt='ELF64'
-        ;;
-      *)
-        objfmt='ELF'
-        ;;
-    esac
-  ;;
-  kfreebsd* | freebsd* | netbsd* | openbsd*)
-    if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
-      objfmt='BSD-a.out'
-    else
-      case "$host_cpu" in
-        x86_64 | amd64)
-          objfmt='ELF64'
-          ;;
-        *)
-          objfmt='ELF'
-          ;;
-      esac
-    fi
-  ;;
-  solaris* | sunos* | sysv* | sco*)
-    case "$host_cpu" in
-      x86_64)
-        objfmt='ELF64'
-        ;;
-      *)
-        objfmt='ELF'
-        ;;
-    esac
-  ;;
-  darwin* | rhapsody* | nextstep* | openstep* | macos*)
-    case "$host_cpu" in
-      x86_64)
-        objfmt='Mach-O64'
-        ;;
-      *)
-        objfmt='Mach-O'
-        ;;
-    esac
-  ;;
-  *)
-    objfmt='ELF ?'
-  ;;
-esac
-
-AC_MSG_RESULT([$objfmt])
-if test "$objfmt" = 'ELF ?'; then
-  objfmt='ELF'
-  AC_MSG_WARN([unexpected host system. assumed that the format is $objfmt.])
-fi
-
-AC_MSG_CHECKING([for object file format specifier (NAFLAGS) ])
-case "$objfmt" in
-  MSOMF)      NAFLAGS='-fobj -DOBJ32';;
-  Win32-COFF) NAFLAGS='-fwin32 -DWIN32';;
-  Win64-COFF) NAFLAGS='-fwin64 -DWIN64 -D__x86_64__';;
-  COFF)       NAFLAGS='-fcoff -DCOFF';;
-  a.out)      NAFLAGS='-faout -DAOUT';;
-  BSD-a.out)  NAFLAGS='-faoutb -DAOUT';;
-  ELF)        NAFLAGS='-felf -DELF';;
-  ELF64)      NAFLAGS='-felf64 -DELF -D__x86_64__';;
-  RDF)        NAFLAGS='-frdf -DRDF';;
-  Mach-O)     NAFLAGS='-fmacho -DMACHO';;
-  Mach-O64)   NAFLAGS='-fmacho64 -DMACHO -D__x86_64__';;
-esac
-AC_MSG_RESULT([$NAFLAGS])
-AC_SUBST([NAFLAGS])
-
-AC_MSG_CHECKING([whether the assembler ($NASM $NAFLAGS) works])
-cat > conftest.asm <<EOF
-[%line __oline__ "configure"
-        section .text
-        global  _main,main
-_main:
-main:   xor     eax,eax
-        ret
-]EOF
-try_nasm='$NASM $NAFLAGS -o conftest.o conftest.asm'
-if AC_TRY_EVAL(try_nasm) && test -s conftest.o; then
-  AC_MSG_RESULT(yes)
-else
-  echo "configure: failed program was:" >&AC_FD_CC
-  cat conftest.asm >&AC_FD_CC
-  rm -rf conftest*
-  AC_MSG_RESULT(no)
-  AC_MSG_ERROR([installation or configuration problem: assembler cannot create object files.])
-fi
-
-AC_MSG_CHECKING([whether the linker accepts assembler output])
-try_nasm='${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.o $LIBS 1>&AC_FD_CC'
-if AC_TRY_EVAL(try_nasm) && test -s conftest${ac_exeext}; then
-  rm -rf conftest*
-  AC_MSG_RESULT(yes)
-else
-  rm -rf conftest*
-  AC_MSG_RESULT(no)
-  AC_MSG_ERROR([configuration problem: maybe object file format mismatch.])
-fi
-
-])
-
-# AC_CHECK_COMPATIBLE_ARM_ASSEMBLER_IFELSE
-# --------------------------
-# Test whether the assembler is suitable and supports NEON instructions
-AC_DEFUN([AC_CHECK_COMPATIBLE_ARM_ASSEMBLER_IFELSE],[
-  ac_good_gnu_arm_assembler=no
-  ac_save_CC="$CC"
-  ac_save_CFLAGS="$CFLAGS"
-  CFLAGS="$CCASFLAGS -x assembler-with-cpp"
-  CC="$CCAS"
-  AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
-    .text
-    .fpu neon
-    .arch armv7a
-    .object_arch armv4
-    .arm
-    pld [r0]
-    vmovn.u16 d0, q0]])], ac_good_gnu_arm_assembler=yes)
-
-  ac_use_gas_preprocessor=no
-  if test "x$ac_good_gnu_arm_assembler" = "xno" ; then
-    CC="gas-preprocessor.pl $CCAS"
-    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
-      .text
-      .fpu neon
-      .arch armv7a
-      .object_arch armv4
-      .arm
-      pld [r0]
-      vmovn.u16 d0, q0]])], ac_use_gas_preprocessor=yes)
-  fi
-  CFLAGS="$ac_save_CFLAGS"
-  CC="$ac_save_CC"
-
-  if test "x$ac_use_gas_preprocessor" = "xyes" ; then
-    CCAS="gas-preprocessor.pl $CCAS"
-    AC_SUBST([CCAS])
-    ac_good_gnu_arm_assembler=yes
-  fi
-
-  if test "x$ac_good_gnu_arm_assembler" = "xyes" ; then
-    $1
-  else
-    $2
-  fi
-])
-
-# AC_CHECK_COMPATIBLE_MIPSEL_ASSEMBLER_IFELSE
-# --------------------------
-# Test whether the assembler is suitable and supports MIPS instructions
-AC_DEFUN([AC_CHECK_COMPATIBLE_MIPSEL_ASSEMBLER_IFELSE],[
-  have_mips_dspr2=no
-  ac_save_CFLAGS="$CFLAGS"
-  CFLAGS="$CCASFLAGS -mdspr2"
-
-  AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
-
-  int main ()
-  {
-    int c = 0, a = 0, b = 0;
-    __asm__ __volatile__ (
-        "precr.qb.ph %[c], %[a], %[b]          \n\t"
-        : [c] "=r" (c)
-        : [a] "r" (a), [b] "r" (b)
-    );
-    return c;
-  }
-  ]])], have_mips_dspr2=yes)
-  CFLAGS=$ac_save_CFLAGS
-
-  if test "x$have_mips_dspr2" = "xyes" ; then
-    $1
-  else
-    $2
-  fi
-])
index 6ff838ca63e845a7053dbc7a2a984b9fafb9a491..01507f0ea82099fb2555927459b38aa89f3fb1d8 100644 (file)
@@ -1,6 +1,6 @@
 /* Windows-specific code for wimlib-imagex.  */
 
-#ifndef __WIN32__
+#ifndef _WIN32
 #  error "This file contains Windows code"
 #endif
 
 #include <stdio.h>
 #include <windows.h>
 
-/* Convert a string from the "current Windows codepage" to UTF-16LE.  */
-wchar_t *
-win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret)
-{
-       if (mbs_nbytes > INT_MAX) {
-               fwprintf(stderr, L"ERROR: too much data (%zu bytes)!\n",
-                        mbs_nbytes);
-               return NULL;
-       }
-       if (mbs_nbytes == 0) {
-               *num_wchars_ret = 0;
-               return (wchar_t*)mbs;
-       }
-       int len = MultiByteToWideChar(CP_ACP,
-                                     MB_ERR_INVALID_CHARS,
-                                     mbs,
-                                     mbs_nbytes,
-                                     NULL,
-                                     0);
-       if (len <= 0)
-               goto out_invalid;
-       wchar_t *wcs = malloc(len * sizeof(wchar_t));
-       if (!wcs) {
-               fwprintf(stderr, L"ERROR: out of memory!\n");
-               return NULL;
-       }
-       int len2 = MultiByteToWideChar(CP_ACP,
-                                      MB_ERR_INVALID_CHARS,
-                                      mbs,
-                                      mbs_nbytes,
-                                      wcs,
-                                      len);
-       if (len2 != len) {
-               free(wcs);
-               goto out_invalid;
-       }
-       *num_wchars_ret = len;
-       return wcs;
-out_invalid:
-       fwprintf(stderr,
-L"ERROR: Invalid multi-byte string in the text file you provided as input!\n"
-L"       Maybe try converting your text file to UTF-16LE?\n"
-       );
-       return NULL;
-}
-
 /* Set a file descriptor to binary mode.  */
 void set_fd_to_binary_mode(int fd)
 {
index 11143f7b5429c6151d396955cdd68d4ec1d5e552..f7a751bd0256460754484ff68febc89927cb0358 100644 (file)
@@ -2,17 +2,12 @@
 #define _IMAGEX_WIN32_H
 
 #include <stddef.h>
-#include <stdbool.h>
 #include <inttypes.h>
-#include <wchar.h>
 
-extern wchar_t *
-win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret);
-
-extern void
+void
 win32_print_security_descriptor(const uint8_t *sd, size_t size);
 
-extern void
+void
 set_fd_to_binary_mode(int fd);
 
 #include "wgetopt.h"
index 2d407fac422b885dd6a4882b51abe25f93aec54c..54e50bfcdd06fe68015b8a23c2ac81c36be80097 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2012-2018 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  include "imagex-win32.h"
 #  define print_security_descriptor     win32_print_security_descriptor
-#else /* __WIN32__ */
+#else /* _WIN32 */
 #  include <getopt.h>
 #  include <langinfo.h>
 #  define print_security_descriptor    default_print_security_descriptor
@@ -62,13 +62,13 @@ static inline void set_fd_to_binary_mode(int fd)
 #ifndef HAVE_GETOPT_LONG_ONLY
 #  define getopt_long_only getopt_long
 #endif
-#endif /* !__WIN32 */
+#endif /* !_WIN32 */
 
 /* Don't confuse the user by presenting the mounting commands on Windows when
  * they will never work.  However on UNIX-like systems we always present them,
  * even if WITH_FUSE is not defined at this point, as to not tie the build of
  * wimlib-imagex to a specific build of wimlib.  */
-#ifdef __WIN32__
+#ifdef _WIN32
 #  define WIM_MOUNTING_SUPPORTED 0
 #else
 #  define WIM_MOUNTING_SUPPORTED 1
@@ -202,6 +202,7 @@ enum {
        IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
        IMAGEX_REBUILD_OPTION,
        IMAGEX_RECOMPRESS_OPTION,
+       IMAGEX_RECOVER_DATA_OPTION,
        IMAGEX_RECURSIVE_OPTION,
        IMAGEX_REF_OPTION,
        IMAGEX_RPFIX_OPTION,
@@ -239,6 +240,7 @@ static const struct option apply_options[] = {
        {T("include-invalid-names"), no_argument,       NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
        {T("wimboot"),     no_argument,       NULL, IMAGEX_WIMBOOT_OPTION},
        {T("compact"),     required_argument, NULL, IMAGEX_COMPACT_OPTION},
+       {T("recover-data"), no_argument,      NULL, IMAGEX_RECOVER_DATA_OPTION},
        {NULL, 0, NULL, 0},
 };
 
@@ -336,6 +338,7 @@ static const struct option extract_options[] = {
        {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
        {T("wimboot"),     no_argument,       NULL, IMAGEX_WIMBOOT_OPTION},
        {T("compact"),     required_argument, NULL, IMAGEX_COMPACT_OPTION},
+       {T("recover-data"), no_argument,      NULL, IMAGEX_RECOVER_DATA_OPTION},
        {NULL, 0, NULL, 0},
 };
 
@@ -413,14 +416,14 @@ static const struct option unmount_options[] = {
 static const struct option update_options[] = {
        /* Careful: some of the options here set the defaults for update
         * commands, but the flags given to an actual update command (and not to
-        * `imagex update' itself are also handled in
-        * update_command_add_option().  */
+        * wimupdate itself) are also handled in update_command_add_option(). */
        {T("threads"),     required_argument, NULL, IMAGEX_THREADS_OPTION},
        {T("check"),       no_argument,       NULL, IMAGEX_CHECK_OPTION},
        {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
        {T("rebuild"),     no_argument,       NULL, IMAGEX_REBUILD_OPTION},
        {T("command"),     required_argument, NULL, IMAGEX_COMMAND_OPTION},
        {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
+       {T("ref"),         required_argument, NULL, IMAGEX_REF_OPTION},
 
        /* Default delete options */
        {T("force"),       no_argument,       NULL, IMAGEX_FORCE_OPTION},
@@ -965,152 +968,6 @@ parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
        return sources;
 }
 
-/* Reads the contents of a file into memory. */
-static char *
-file_get_contents(const tchar *filename, size_t *len_ret)
-{
-       struct stat stbuf;
-       void *buf = NULL;
-       size_t len;
-       FILE *fp;
-
-       if (tstat(filename, &stbuf) != 0) {
-               imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
-               goto out;
-       }
-       len = stbuf.st_size;
-
-       fp = tfopen(filename, T("rb"));
-       if (!fp) {
-               imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
-               goto out;
-       }
-
-       buf = malloc(len ? len : 1);
-       if (!buf) {
-               imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
-                              "contents of file \"%"TS"\""), len, filename);
-               goto out_fclose;
-       }
-       if (fread(buf, 1, len, fp) != len) {
-               imagex_error_with_errno(T("Failed to read %zu bytes from the "
-                                         "file \"%"TS"\""), len, filename);
-               goto out_free_buf;
-       }
-       *len_ret = len;
-       goto out_fclose;
-out_free_buf:
-       free(buf);
-       buf = NULL;
-out_fclose:
-       fclose(fp);
-out:
-       return buf;
-}
-
-/* Read standard input until EOF and return the full contents in a malloc()ed
- * buffer and the number of bytes of data in @len_ret.  Returns NULL on read
- * error. */
-static char *
-stdin_get_contents(size_t *len_ret)
-{
-       /* stdin can, of course, be a pipe or other non-seekable file, so the
-        * total length of the data cannot be pre-determined */
-       char *buf = NULL;
-       size_t newlen = 1024;
-       size_t pos = 0;
-       size_t inc = 1024;
-       for (;;) {
-               char *p = realloc(buf, newlen);
-               size_t bytes_read, bytes_to_read;
-               if (!p) {
-                       imagex_error(T("out of memory while reading stdin"));
-                       break;
-               }
-               buf = p;
-               bytes_to_read = newlen - pos;
-               bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
-               pos += bytes_read;
-               if (bytes_read != bytes_to_read) {
-                       if (feof(stdin)) {
-                               *len_ret = pos;
-                               return buf;
-                       } else {
-                               imagex_error_with_errno(T("error reading stdin"));
-                               break;
-                       }
-               }
-               newlen += inc;
-               inc *= 3;
-               inc /= 2;
-       }
-       free(buf);
-       return NULL;
-}
-
-
-static tchar *
-translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
-{
-#ifndef __WIN32__
-       /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
-        * */
-       *num_tchars_ret = num_bytes;
-       return text;
-#else /* !__WIN32__ */
-       /* On Windows, translate the text to UTF-16LE */
-       wchar_t *text_wstr;
-       size_t num_wchars;
-
-       if (num_bytes >= 2 &&
-           (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
-            ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
-       {
-               /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
-                * with something that looks like an ASCII character encoded as
-                * a UTF-16LE code unit.  Assume the file is encoded as
-                * UTF-16LE.  This is not a 100% reliable check. */
-               num_wchars = num_bytes / 2;
-               text_wstr = (wchar_t*)text;
-       } else {
-               /* File does not look like UTF-16LE.  Assume it is encoded in
-                * the current Windows code page.  I think these are always
-                * ASCII-compatible, so any so-called "plain-text" (ASCII) files
-                * should work as expected. */
-               text_wstr = win32_mbs_to_wcs(text,
-                                            num_bytes,
-                                            &num_wchars);
-               free(text);
-       }
-       *num_tchars_ret = num_wchars;
-       return text_wstr;
-#endif /* __WIN32__ */
-}
-
-static tchar *
-file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
-{
-       char *contents;
-       size_t num_bytes;
-
-       contents = file_get_contents(filename, &num_bytes);
-       if (!contents)
-               return NULL;
-       return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
-}
-
-static tchar *
-stdin_get_text_contents(size_t *num_tchars_ret)
-{
-       char *contents;
-       size_t num_bytes;
-
-       contents = stdin_get_contents(&num_bytes);
-       if (!contents)
-               return NULL;
-       return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
-}
-
 #define TO_PERCENT(numerator, denominator) \
        (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
 
@@ -1163,6 +1020,31 @@ report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
                last_scan_progress = *scan;
        }
 }
+
+static struct wimlib_progress_info_split last_split_progress;
+
+static void
+report_split_progress(uint64_t bytes_completed_in_part)
+{
+       uint64_t completed_bytes = last_split_progress.completed_bytes +
+                                  bytes_completed_in_part;
+       unsigned percent_done = TO_PERCENT(completed_bytes,
+                                          last_split_progress.total_bytes);
+       unsigned unit_shift;
+       const tchar *unit_name;
+
+       unit_shift = get_unit(last_split_progress.total_bytes, &unit_name);
+       imagex_printf(T("\rSplitting WIM: %"PRIu64" %"TS" of "
+                       "%"PRIu64" %"TS" (%u%%) written, part %u of %u"),
+                     completed_bytes >> unit_shift,
+                     unit_name,
+                     last_split_progress.total_bytes >> unit_shift,
+                     unit_name,
+                     percent_done,
+                     last_split_progress.cur_part_number,
+                     last_split_progress.total_parts);
+}
+
 /* Progress callback function passed to various wimlib functions. */
 static enum wimlib_progress_status
 imagex_progress_func(enum wimlib_progress_msg msg,
@@ -1175,6 +1057,12 @@ imagex_progress_func(enum wimlib_progress_msg msg,
 
        switch (msg) {
        case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
+               if (last_split_progress.total_bytes != 0) {
+                       /* wimlib_split() in progress; use the split-specific
+                        * progress message.  */
+                       report_split_progress(info->write_streams.completed_compressed_bytes);
+                       break;
+               }
                {
                        static bool started;
                        if (!started) {
@@ -1231,7 +1119,7 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                         * default installation.  On UNIX-like systems, warn the
                         * user when fixing the target of an absolute symbolic
                         * link, so they know to disable this if they want.  */
-               #ifndef __WIN32__
+               #ifndef _WIN32
                        imagex_printf(T("\nWARNING: Adjusted target of "
                                        "absolute symbolic link \"%"TS"\"\n"
                                        "           (Use --norpfix to capture "
@@ -1330,26 +1218,9 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                }
                break;
        case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
-               percent_done = TO_PERCENT(info->split.completed_bytes,
-                                         info->split.total_bytes);
-               unit_shift = get_unit(info->split.total_bytes, &unit_name);
-               imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
-                         "%"PRIu64" %"TS" (%u%%) written\n"),
-                       info->split.part_name,
-                       info->split.cur_part_number,
-                       info->split.total_parts,
-                       info->split.completed_bytes >> unit_shift,
-                       unit_name,
-                       info->split.total_bytes >> unit_shift,
-                       unit_name,
-                       percent_done);
-               break;
        case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
-               if (info->split.completed_bytes == info->split.total_bytes) {
-                       imagex_printf(T("Finished writing split WIM part %u of %u\n"),
-                               info->split.cur_part_number,
-                               info->split.total_parts);
-               }
+               last_split_progress = info->split;
+               report_split_progress(0);
                break;
        case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
                switch (info->update.command->op) {
@@ -1512,7 +1383,7 @@ update_command_add_option(int op, const tchar *option,
        return recognized;
 }
 
-/* How many nonoption arguments each `imagex update' command expects */
+/* How many nonoption arguments each wimupdate command expects */
 static const unsigned update_command_num_nonoptions[] = {
        [WIMLIB_UPDATE_OP_ADD] = 2,
        [WIMLIB_UPDATE_OP_DELETE] = 1,
@@ -1544,7 +1415,7 @@ update_command_add_nonoption(int op, const tchar *nonoption,
 }
 
 /*
- * Parse a command passed on stdin to `imagex update'.
+ * Parse a command passed on stdin to wimupdate.
  *
  * @line:      Text of the command.
  * @len:       Length of the line, including a null terminator
@@ -1733,6 +1604,9 @@ imagex_apply(int argc, tchar **argv, int cmd)
                        if (ret)
                                goto out_free_refglobs;
                        break;
+               case IMAGEX_RECOVER_DATA_OPTION:
+                       extract_flags |= WIMLIB_EXTRACT_FLAG_RECOVER_DATA;
+                       break;
                default:
                        goto out_usage;
                }
@@ -1797,7 +1671,7 @@ imagex_apply(int argc, tchar **argv, int cmd)
                        goto out_wimlib_free;
        }
 
-#ifndef __WIN32__
+#ifndef _WIN32
        {
                /* Interpret a regular file or block device target as an NTFS
                 * volume.  */
@@ -2020,7 +1894,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                                        template_image_name_or_num = optarg;
                                }
                        }
-               #ifdef __WIN32__
+               #ifdef _WIN32
                        imagex_printf(T("[WARNING] '--update-of' is unreliable on Windows!\n"));
                #endif
                        break;
@@ -2182,13 +2056,8 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
 
        if (source_list) {
                /* Set up capture sources in source list mode */
-               if (source[0] == T('-') && source[1] == T('\0')) {
-                       source_list_contents = stdin_get_text_contents(&source_list_nchars);
-               } else {
-                       source_list_contents = file_get_text_contents(source,
-                                                                     &source_list_nchars);
-               }
-               if (!source_list_contents)
+               if (wimlib_load_text_file(source, &source_list_contents,
+                                         &source_list_nchars) != 0)
                        goto out_err;
 
                capture_sources = parse_source_list(&source_list_contents,
@@ -2258,7 +2127,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                        goto out_free_wim;
        }
 
-#ifndef __WIN32__
+#ifndef _WIN32
        /* Detect if source is regular file or block device and set NTFS volume
         * capture mode.  */
        if (!source_list) {
@@ -2701,7 +2570,7 @@ print_blobs(WIMStruct *wim)
        wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
 }
 
-#ifndef __WIN32__
+#ifndef _WIN32
 static void
 default_print_security_descriptor(const uint8_t *sd, size_t size)
 {
@@ -3280,6 +3149,9 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        if (ret)
                                goto out_free_refglobs;
                        break;
+               case IMAGEX_RECOVER_DATA_OPTION:
+                       extract_flags |= WIMLIB_EXTRACT_FLAG_RECOVER_DATA;
+                       break;
                default:
                        goto out_usage;
                }
@@ -3834,6 +3706,7 @@ imagex_optimize(int argc, tchar **argv, int cmd)
        int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
        int ret;
        WIMStruct *wim;
+       struct wimlib_wim_info info;
        const tchar *wimfile;
        off_t old_size;
        off_t new_size;
@@ -3877,6 +3750,9 @@ imagex_optimize(int argc, tchar **argv, int cmd)
                case IMAGEX_SOLID_OPTION:
                        write_flags |= WIMLIB_WRITE_FLAG_SOLID;
                        write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
+                       /* Reset the non-solid compression type to LZMS. */
+                       if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
+                               compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
                        break;
                case IMAGEX_NO_SOLID_SORT_OPTION:
                        write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
@@ -3912,11 +3788,18 @@ imagex_optimize(int argc, tchar **argv, int cmd)
        if (ret)
                goto out;
 
-       if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
+       wimlib_get_wim_info(wim, &info);
+
+       if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID &&
+           compression_type != info.compression_type) {
                /* Change compression type.  */
                ret = wimlib_set_output_compression_type(wim, compression_type);
                if (ret)
                        goto out_wimlib_free;
+
+               /* Reset the chunk size. */
+               if (chunk_size == UINT32_MAX)
+                       chunk_size = 0;
        }
 
        if (chunk_size != UINT32_MAX) {
@@ -3983,7 +3866,7 @@ imagex_split(int argc, tchar **argv, int cmd)
        int c;
        int open_flags = 0;
        int write_flags = 0;
-       unsigned long part_size;
+       uint64_t part_size;
        tchar *tmp;
        int ret;
        WIMStruct *wim;
@@ -4019,6 +3902,8 @@ imagex_split(int argc, tchar **argv, int cmd)
                goto out;
 
        ret = wimlib_split(wim, argv[1], part_size, write_flags);
+       if (ret == 0)
+               tprintf(T("\nFinished splitting \"%"TS"\"\n"), argv[0]);
        wimlib_free(wim);
 out:
        return ret;
@@ -4120,6 +4005,7 @@ imagex_update(int argc, tchar **argv, int cmd)
                                WIMLIB_ADD_FLAG_WINCONFIG;
        int default_delete_flags = 0;
        unsigned num_threads = 0;
+       STRING_LIST(refglobs);
        int c;
        tchar *cmd_file_contents;
        size_t cmd_file_nchars;
@@ -4163,6 +4049,13 @@ imagex_update(int argc, tchar **argv, int cmd)
                case IMAGEX_WIMBOOT_CONFIG_OPTION:
                        wimboot_config = optarg;
                        break;
+               case IMAGEX_REF_OPTION:
+                       ret = string_list_append(&refglobs, optarg);
+                       if (ret)
+                               goto out;
+                       /* assume delta WIM */
+                       write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
+                       break;
                /* Default delete options */
                case IMAGEX_FORCE_OPTION:
                        default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
@@ -4213,7 +4106,7 @@ imagex_update(int argc, tchar **argv, int cmd)
        ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
                                            imagex_progress_func, NULL);
        if (ret)
-               goto out_free_command_str;
+               goto out;
 
        if (argc >= 2) {
                /* Image explicitly specified.  */
@@ -4237,6 +4130,10 @@ imagex_update(int argc, tchar **argv, int cmd)
                image = 1;
        }
 
+       ret = wim_reference_globs(wim, &refglobs, open_flags);
+       if (ret)
+               goto out_wimlib_free;
+
        /* Read update commands from standard input, or the command string if
         * specified.  */
        if (command_str) {
@@ -4252,8 +4149,8 @@ imagex_update(int argc, tchar **argv, int cmd)
                        tputs(T("Reading update commands from standard input..."));
                        recommend_man_page(CMD_UPDATE, stdout);
                }
-               cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
-               if (!cmd_file_contents) {
+               if (wimlib_load_text_file(NULL, &cmd_file_contents,
+                                         &cmd_file_nchars) != 0) {
                        ret = -1;
                        goto out_wimlib_free;
                }
@@ -4316,15 +4213,16 @@ out_free_cmd_file_contents:
        free(cmd_file_contents);
 out_wimlib_free:
        wimlib_free(wim);
-out_free_command_str:
+out:
        free(command_str);
+       string_list_destroy(&refglobs);
        return ret;
 
 out_usage:
        usage(CMD_UPDATE, stderr);
 out_err:
        ret = -1;
-       goto out_free_command_str;
+       goto out;
 }
 
 /* Verify a WIM file.  */
@@ -4435,7 +4333,7 @@ static const struct imagex_command imagex_commands[] = {
        [CMD_VERIFY]   = {T("verify"),   imagex_verify},
 };
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
    /* Can be a directory or source list file.  But source list file is probably
     * a rare use case, so just say directory.  */
@@ -4469,7 +4367,7 @@ T(
 "                    [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
 "                    [--no-attributes] [--rpfix] [--norpfix]\n"
 "                    [--include-invalid-names] [--wimboot] [--unix-data]\n"
-"                    [--compact=FORMAT]\n"
+"                    [--compact=FORMAT] [--recover-data]\n"
 ),
 [CMD_CAPTURE] =
 T(
@@ -4502,8 +4400,8 @@ T(
 "    %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
 "                    [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
 "                    [--to-stdout] [--no-acls] [--strict-acls]\n"
-"                    [--no-attributes] [--include-invalid-names]\n"
-"                    [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
+"                    [--no-attributes] [--include-invalid-names] [--no-globs]\n"
+"                    [--nullglob] [--preserve-dir-structure] [--recover-data]\n"
 ),
 [CMD_INFO] =
 T(
@@ -4587,8 +4485,8 @@ version(void)
        static const tchar * const fmt =
        T(
 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
-"Copyright (C) 2012-2018 Eric Biggers\n"
-"License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
+"Copyright 2012-2023 Eric Biggers\n"
+"License GPLv3+; GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n"
 "This is free software: you are free to change and redistribute it.\n"
 "There is NO WARRANTY, to the extent permitted by law.\n"
 "\n"
@@ -4641,7 +4539,7 @@ static void
 recommend_man_page(int cmd, FILE *fp)
 {
        const tchar *format_str;
-#ifdef __WIN32__
+#ifdef _WIN32
        format_str = T("Some uncommon options are not listed;\n"
                       "See %"TS".pdf in the doc directory for more details.\n");
 #else
@@ -4682,8 +4580,8 @@ usage_all(FILE *fp)
        recommend_man_page(CMD_NONE, fp);
 }
 
-#ifdef __WIN32__
-extern int wmain(int argc, wchar_t **argv);
+#ifdef _WIN32
+int wmain(int argc, wchar_t **argv);
 #define main wmain
 #endif
 
index 85dadc77b5f1c3f2ddaabcf9b09048947d9cbef8..8e9cfeb0b141069b16918ef18823fa3a7dc2a0f5 100755 (executable)
@@ -16,7 +16,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 script_name="$(basename "$0")"
 PREFIX_REG="::"
index c4aeb9f4b4db92755497d1565d5437631d4ec262..47f34a5b1b85b056731828a70955cda2ba6bd036 100644 (file)
@@ -17,7 +17,7 @@
  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License along with
- * this file; if not, see http://www.gnu.org/licenses/.
+ * this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #include "wgetopt.h"
index 9b2f3a3b9ac426d52585651d1f2a02a93a2e634f..708870620df05a9934616c941ac923330fecb5c3 100644 (file)
@@ -17,14 +17,14 @@ struct woption {
 #define required_argument 1
 #define optional_argument 2
 
-extern int
+int
 wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring);
 
-extern int
+int
 wgetopt_long(int argc, wchar_t * const *argv, const wchar_t *options,
             const struct woption *long_options, int *opt_index);
 
-extern int
+int
 wgetopt_long_only(int argc, wchar_t *const *argv, const wchar_t *options,
                  const struct woption *long_options, int *opt_index);
 
diff --git a/rpm/wimtools.spec b/rpm/wimtools.spec
deleted file mode 100644 (file)
index 4c4a259..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-Name:      wimtools
-Summary:   Tools to create, extract, modify, and mount WIM files
-Version:   1.13.1
-Release:   1
-License:   GPLv3+
-URL:       https://wimlib.net
-Packager:  Eric Biggers <ebiggers3@gmail.com>
-Source:    https://wimlib.net/downloads/wimlib-%{version}.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
-
-
-Group:  Applications/System
-Requires: libwim15
-%description
-Tools to extract, create, modify, and mount WIM (Windows Imaging) files.  WIM is
-an archive format designed primarily for archiving Windows filesystems.  It
-features single-instancing and LZ77-based compression and is used by Microsoft
-to distribute and deploy Windows Vista and later.  WIM files are normally
-created by using the `imagex.exe' utility on Windows, but this package contains
-a free implementation of ImageX called "wimlib-imagex" that is designed to work
-on both UNIX-like systems and Windows.
-
-In addition to the usual extract/create/update support, wimlib-imagex allows you
-to mount WIM images readonly or read-write, and it even allows you to extract or
-create a WIM image directly to/from an unmounted NTFS volume.  This makes it
-possible to, from Linux, back up or deploy a Windows OS directly to or from a
-WIM file, such as the install.wim distributed on the Windows installation media.
-
-This package also contains a script to make a customized Windows PE image based
-on the capabilities provided by wimlib-imagex.
-
-%package -n libwim15-devel
-Summary:  Development files for wimlib
-Group:  Development/Libraries
-%description -n libwim15-devel
-Development files for wimlib
-
-%package -n libwim15
-Summary:  Library to extract, create, modify, and mount WIM files
-Group:  System Environment/Libraries
-Requires:  fuse
-BuildRequires: libxml2-devel, fuse, fuse-devel, openssl-devel, attr
-BuildRequires: ntfs-3g-devel, ntfsprogs, libtool, pkgconfig
-%description -n libwim15
-wimlib is a C library for extracting, creating, modifying, and mounting WIM
-(Windows Imaging) files.  WIM is an archive format designed primarily for
-archiving Windows filesystems.  It features single-instancing and LZ77-based
-compression, and is used by Microsoft to distribute and deploy Windows Vista and
-later.  wimlib is an independent implementation of an API for handling WIM
-files, available on both UNIX-like systems and Windows, that provides features
-similar to Microsoft's WIMGAPI, as well as additional features such as support
-for pipable WIM files and programatically making changes to WIM images without
-mounting them.
-%post -n libwim15 -p /sbin/ldconfig
-%postun -n libwim15 -p /sbin/ldconfig
-
-%prep
-%setup -q -n wimlib-%{version}
-
-%build
-%configure --prefix=/usr               \
-           --disable-rpath             \
-          --with-libcrypto             \
-          --with-ntfs-3g               \
-          --with-fuse
-make %{?_smp_mflags}
-
-%install
-rm -rf %{buildroot}
-make DESTDIR=%{buildroot} install
-
-%clean
-rm -rf %{buildroot}
-
-%files
-%defattr(-, root, root)
-%{_bindir}/*
-%doc %{_mandir}/man1/*.1.gz
-%doc README COPYING COPYING.GPLv3
-
-%files -n libwim15-devel
-%defattr(-, root, root)
-%{_libdir}/libwim.a
-%{_libdir}/libwim.so
-%exclude %{_libdir}/libwim.la
-%{_includedir}/wimlib.h
-%{_libdir}/pkgconfig/wimlib.pc
-
-%files -n libwim15
-%defattr(-, root, root)
-%{_libdir}/libwim.so.*
-%doc COPYING COPYING.GPLv3 COPYING.LGPLv3 COPYING.CC0
index 740f11d6aea27f29738651e1da2e2fb7dadf8c3c..abe96a7fff1f41593277698bbe018bd395f01c9a 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 50ed1777cf9f52e4e78ae1dfa35d5eb51f1ba57c..136e95722684186d0080c94d327779e72ff2619c 100644 (file)
@@ -2,21 +2,28 @@
  * avl_tree.c - intrusive, nonrecursive AVL tree data structure (self-balancing
  *             binary search tree), implementation file
  *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * Copyright 2022 Eric Biggers
+ *
+ * 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.
  */
 
 #ifdef HAVE_CONFIG_H
index ab8d08467e717a9fd093488094453cecc394e586..386155031c264ab87f799228966002db836d85cd 100644 (file)
@@ -22,7 +22,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -36,6 +36,7 @@
 #include "wimlib/assert.h"
 #include "wimlib/bitops.h"
 #include "wimlib/blob_table.h"
+#include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
 #include "wimlib/endianness.h"
 #include "wimlib/error.h"
@@ -131,7 +132,7 @@ clone_blob_descriptor(const struct blob_descriptor *old)
                if (new->file_on_disk == NULL)
                        goto out_free;
                break;
-#ifdef __WIN32__
+#ifdef _WIN32
        case BLOB_IN_WINDOWS_FILE:
                new->windows_file = clone_windows_file(old->windows_file);
                break;
@@ -183,7 +184,7 @@ blob_release_location(struct blob_descriptor *blob)
                              (void*)&blob->attached_buffer);
                FREE(blob->file_on_disk);
                break;
-#ifdef __WIN32__
+#ifdef _WIN32
        case BLOB_IN_WINDOWS_FILE:
                free_windows_file(blob->windows_file);
                break;
@@ -461,7 +462,7 @@ cmp_blobs_by_sequential_order(const void *p1, const void *p2)
                /* Compare files by path: just a heuristic that will place files
                 * in the same directory next to each other.  */
                return tstrcmp(blob1->file_on_disk, blob2->file_on_disk);
-#ifdef __WIN32__
+#ifdef _WIN32
        case BLOB_IN_WINDOWS_FILE:
                return cmp_windows_files(blob1->windows_file, blob2->windows_file);
 #endif
@@ -584,7 +585,7 @@ struct blob_descriptor_disk {
        /* SHA-1 message digest of the uncompressed data of this blob, or all
         * zeroes if this blob is of zero length.  */
        u8 hash[SHA1_HASH_SIZE];
-} _packed_attribute;
+} __attribute__((packed));
 
 /* Given a nonempty run of consecutive blob descriptors with the SOLID flag set,
  * count how many specify resources (as opposed to blobs within those
@@ -900,7 +901,7 @@ read_blob_table(WIMStruct *wim)
        if (!table)
                goto oom;
 
-       /* Allocate and initalize blob descriptors from the raw blob table
+       /* Allocate and initialize blob descriptors from the raw blob table
         * buffer.  */
        for (size_t i = 0; i < num_entries; i++) {
                const struct blob_descriptor_disk *disk_entry =
@@ -1235,7 +1236,7 @@ new_blob_from_data_buffer(const void *buffer, size_t size,
        struct blob_descriptor *blob;
        void *buffer_copy;
 
-       sha1_buffer(buffer, size, hash);
+       sha1(buffer, size, hash);
 
        blob = lookup_blob(blob_table, hash);
        if (blob)
@@ -1259,7 +1260,7 @@ new_blob_from_data_buffer(const void *buffer, size_t size,
 struct blob_descriptor *
 after_blob_hashed(struct blob_descriptor *blob,
                  struct blob_descriptor **back_ptr,
-                 struct blob_table *blob_table)
+                 struct blob_table *blob_table, struct wim_inode *inode)
 {
        struct blob_descriptor *duplicate_blob;
 
@@ -1273,7 +1274,16 @@ after_blob_hashed(struct blob_descriptor *blob,
                 * this blob to the duplicate and update the reference to this
                 * blob (from a stream) to point to the duplicate.  The caller
                 * is responsible for freeing @blob if needed.  */
-               wimlib_assert(duplicate_blob->size == blob->size);
+               if (duplicate_blob->size != blob->size) {
+                       tchar hash_str[SHA1_HASH_STRING_LEN];
+
+                       sprint_hash(blob->hash, hash_str);
+                       WARNING("SHA-1 collision at \"%"TS"\"\n"
+                               "          (hash=%"TS", size=%"PRIu64", other_size=%"PRIu64").\n"
+                               "          File will be corrupted!",
+                               inode_any_full_path(inode), hash_str,
+                               blob->size, duplicate_blob->size);
+               }
                duplicate_blob->refcnt += blob->refcnt;
                blob->refcnt = 0;
                *back_ptr = duplicate_blob;
@@ -1307,15 +1317,17 @@ hash_unhashed_blob(struct blob_descriptor *blob, struct blob_table *blob_table,
                   struct blob_descriptor **blob_ret)
 {
        struct blob_descriptor **back_ptr;
+       struct wim_inode *inode;
        int ret;
 
        back_ptr = retrieve_pointer_to_unhashed_blob(blob);
+       inode = blob->back_inode;
 
        ret = sha1_blob(blob);
        if (ret)
                return ret;
 
-       *blob_ret = after_blob_hashed(blob, back_ptr, blob_table);
+       *blob_ret = after_blob_hashed(blob, back_ptr, blob_table, inode);
        return 0;
 }
 
index 1b0d2c9f9aff5d0f04b1537bda5f264b3c9efd89..8396316dca06493a35560977c6259501dc0fbfa5 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -122,6 +122,11 @@ wimlib_create_compressor(enum wimlib_compression_type ctype,
 {
        bool destructive;
        struct wimlib_compressor *c;
+       int ret;
+
+       ret = wimlib_global_init(0);
+       if (ret)
+               return ret;
 
        destructive = (compression_level & WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE);
        compression_level &= ~WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE;
@@ -146,8 +151,6 @@ wimlib_create_compressor(enum wimlib_compression_type ctype,
        c->ctype = ctype;
        c->max_block_size = max_block_size;
        if (c->ops->create_compressor) {
-               int ret;
-
                if (compression_level == 0)
                        compression_level = default_compression_levels[ctype];
                if (compression_level == 0)
index c6d1133f901c909181a170f9bab0b1870358b7ab..0e2ba05a3a067d70d0d58470c50325b1e74c4d11 100644 (file)
@@ -3,21 +3,28 @@
  *
  * Code for compression shared among multiple compression formats.
  *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2012-2014 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * Copyright 2022 Eric Biggers
+ *
+ * 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.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include <string.h>
 
+#include "wimlib/assert.h"
 #include "wimlib/compress_common.h"
 #include "wimlib/util.h"
 
-/* Given the binary tree node A[subtree_idx] whose children already
- * satisfy the maxheap property, swap the node with its greater child
- * until it is greater than both its children, so that the maxheap
- * property is satisfied in the subtree rooted at A[subtree_idx].  */
+/*
+ * Given the binary tree node A[subtree_idx] whose children already satisfy the
+ * maxheap property, swap the node with its greater child until it is greater
+ * than or equal to both of its children, so that the maxheap property is
+ * satisfied in the subtree rooted at A[subtree_idx].  'A' uses 1-based indices.
+ */
 static void
 heapify_subtree(u32 A[], unsigned length, unsigned subtree_idx)
 {
@@ -53,297 +63,306 @@ heapify_subtree(u32 A[], unsigned length, unsigned subtree_idx)
        A[parent_idx] = v;
 }
 
-/* Rearrange the array 'A' so that it satisfies the maxheap property.
+/*
+ * Rearrange the array 'A' so that it satisfies the maxheap property.
  * 'A' uses 1-based indices, so the children of A[i] are A[i*2] and A[i*2 + 1].
  */
 static void
 heapify_array(u32 A[], unsigned length)
 {
-       for (unsigned subtree_idx = length / 2; subtree_idx >= 1; subtree_idx--)
+       unsigned subtree_idx;
+
+       for (subtree_idx = length / 2; subtree_idx >= 1; subtree_idx--)
                heapify_subtree(A, length, subtree_idx);
 }
 
-/* Sort the array 'A', which contains 'length' unsigned 32-bit integers.  */
+/*
+ * Sort the array 'A', which contains 'length' unsigned 32-bit integers.
+ *
+ * Note: name this function heap_sort() instead of heapsort() to avoid colliding
+ * with heapsort() from stdlib.h on BSD-derived systems --- though this isn't
+ * necessary when compiling with -D_ANSI_SOURCE, which is the better solution.
+ */
 static void
-heapsort(u32 A[], unsigned length)
+heap_sort(u32 A[], unsigned length)
 {
        A--; /* Use 1-based indices  */
 
        heapify_array(A, length);
 
        while (length >= 2) {
-               swap(A[1], A[length]);
+               u32 tmp = A[length];
+
+               A[length] = A[1];
+               A[1] = tmp;
                length--;
                heapify_subtree(A, length, 1);
        }
 }
 
 #define NUM_SYMBOL_BITS 10
-#define SYMBOL_MASK ((1 << NUM_SYMBOL_BITS) - 1)
+#define NUM_FREQ_BITS  (32 - NUM_SYMBOL_BITS)
+#define SYMBOL_MASK    ((1 << NUM_SYMBOL_BITS) - 1)
+#define FREQ_MASK      (~SYMBOL_MASK)
+
+#define GET_NUM_COUNTERS(num_syms)     (num_syms)
 
 /*
- * Sort the symbols primarily by frequency and secondarily by symbol
- * value.  Discard symbols with zero frequency and fill in an array with
- * the remaining symbols, along with their frequencies.  The low
- * NUM_SYMBOL_BITS bits of each array entry will contain the symbol
- * value, and the remaining bits will contain the frequency.
+ * Sort the symbols primarily by frequency and secondarily by symbol value.
+ * Discard symbols with zero frequency and fill in an array with the remaining
+ * symbols, along with their frequencies.  The low NUM_SYMBOL_BITS bits of each
+ * array entry will contain the symbol value, and the remaining bits will
+ * contain the frequency.
  *
  * @num_syms
- *     Number of symbols in the alphabet.
- *     Can't be greater than (1 << NUM_SYMBOL_BITS).
+ *     Number of symbols in the alphabet, at most 1 << NUM_SYMBOL_BITS.
  *
  * @freqs[num_syms]
- *     The frequency of each symbol.
+ *     Frequency of each symbol, summing to at most (1 << NUM_FREQ_BITS) - 1.
  *
  * @lens[num_syms]
- *     An array that eventually will hold the length of each codeword.
- *     This function only fills in the codeword lengths for symbols that
- *     have zero frequency, which are not well defined per se but will
- *     be set to 0.
+ *     An array that eventually will hold the length of each codeword.  This
+ *     function only fills in the codeword lengths for symbols that have zero
+ *     frequency, which are not well defined per se but will be set to 0.
  *
  * @symout[num_syms]
  *     The output array, described above.
  *
- * Returns the number of entries in 'symout' that were filled.  This is
- * the number of symbols that have nonzero frequency.
+ * Returns the number of entries in 'symout' that were filled.  This is the
+ * number of symbols that have nonzero frequency.
  */
 static unsigned
-sort_symbols(unsigned num_syms, const u32 freqs[restrict],
-            u8 lens[restrict], u32 symout[restrict])
+sort_symbols(unsigned num_syms, const u32 freqs[], u8 lens[], u32 symout[])
 {
+       unsigned sym;
+       unsigned i;
        unsigned num_used_syms;
        unsigned num_counters;
+       unsigned counters[GET_NUM_COUNTERS(MAX_NUM_SYMS)];
 
-       /* We rely on heapsort, but with an added optimization.  Since
-        * it's common for most symbol frequencies to be low, we first do
-        * a count sort using a limited number of counters.  High
-        * frequencies will be counted in the last counter, and only they
-        * will be sorted with heapsort.
+       /*
+        * We use heapsort, but with an added optimization.  Since often most
+        * symbol frequencies are low, we first do a count sort using a limited
+        * number of counters.  High frequencies are counted in the last
+        * counter, and only they will be sorted with heapsort.
         *
         * Note: with more symbols, it is generally beneficial to have more
-        * counters.  About 1 counter per 4 symbols seems fast.
-        *
-        * Note: I also tested radix sort, but even for large symbol
-        * counts (> 255) and frequencies bounded at 16 bits (enabling
-        * radix sort by just two base-256 digits), it didn't seem any
-        * faster than the method implemented here.
-        *
-        * Note: I tested the optimized quicksort implementation from
-        * glibc (with indirection overhead removed), but it was only
-        * marginally faster than the simple heapsort implemented here.
-        *
-        * Tests were done with building the codes for LZX.  Results may
-        * vary for different compression algorithms...!  */
-
-       num_counters = ALIGN(DIV_ROUND_UP(num_syms, 4), 4);
+        * counters.  About 1 counter per symbol seems fastest.
+        */
 
-       unsigned counters[num_counters];
+       num_counters = GET_NUM_COUNTERS(num_syms);
 
-       memset(counters, 0, sizeof(counters));
+       memset(counters, 0, num_counters * sizeof(counters[0]));
 
-       /* Count the frequencies.  */
-       for (unsigned sym = 0; sym < num_syms; sym++)
-               counters[min(freqs[sym], num_counters - 1)]++;
+       /* Count the frequencies. */
+       for (sym = 0; sym < num_syms; sym++)
+               counters[MIN(freqs[sym], num_counters - 1)]++;
 
-       /* Make the counters cumulative, ignoring the zero-th, which
-        * counted symbols with zero frequency.  As a side effect, this
-        * calculates the number of symbols with nonzero frequency.  */
+       /*
+        * Make the counters cumulative, ignoring the zero-th, which counted
+        * symbols with zero frequency.  As a side effect, this calculates the
+        * number of symbols with nonzero frequency.
+        */
        num_used_syms = 0;
-       for (unsigned i = 1; i < num_counters; i++) {
+       for (i = 1; i < num_counters; i++) {
                unsigned count = counters[i];
+
                counters[i] = num_used_syms;
                num_used_syms += count;
        }
 
-       /* Sort nonzero-frequency symbols using the counters.  At the
-        * same time, set the codeword lengths of zero-frequency symbols
-        * to 0.  */
-       for (unsigned sym = 0; sym < num_syms; sym++) {
+       /*
+        * Sort nonzero-frequency symbols using the counters.  At the same time,
+        * set the codeword lengths of zero-frequency symbols to 0.
+        */
+       for (sym = 0; sym < num_syms; sym++) {
                u32 freq = freqs[sym];
+
                if (freq != 0) {
-                       symout[counters[min(freq, num_counters - 1)]++] =
+                       symout[counters[MIN(freq, num_counters - 1)]++] =
                                sym | (freq << NUM_SYMBOL_BITS);
                } else {
                        lens[sym] = 0;
                }
        }
 
-       /* Sort the symbols counted in the last counter.  */
-       heapsort(symout + counters[num_counters - 2],
-                counters[num_counters - 1] - counters[num_counters - 2]);
+       /* Sort the symbols counted in the last counter. */
+       heap_sort(symout + counters[num_counters - 2],
+                 counters[num_counters - 1] - counters[num_counters - 2]);
 
        return num_used_syms;
 }
 
 /*
- * Build the Huffman tree.
+ * Build a Huffman tree.
  *
  * This is an optimized implementation that
  *     (a) takes advantage of the frequencies being already sorted;
- *     (b) only generates non-leaf nodes, since the non-leaf nodes of a
- *         Huffman tree are sufficient to generate a canonical code;
+ *     (b) only generates non-leaf nodes, since the non-leaf nodes of a Huffman
+ *         tree are sufficient to generate a canonical code;
  *     (c) Only stores parent pointers, not child pointers;
- *     (d) Produces the nodes in the same memory used for input
- *         frequency information.
- *
- * Array 'A', which contains 'sym_count' entries, is used for both input
- * and output.  For this function, 'sym_count' must be at least 2.
- *
- * For input, the array must contain the frequencies of the symbols,
- * sorted in increasing order.  Specifically, each entry must contain a
- * frequency left shifted by NUM_SYMBOL_BITS bits.  Any data in the low
- * NUM_SYMBOL_BITS bits of the entries will be ignored by this function.
- * Although these bits will, in fact, contain the symbols that correspond
- * to the frequencies, this function is concerned with frequencies only
- * and keeps the symbols as-is.
- *
- * For output, this function will produce the non-leaf nodes of the
- * Huffman tree.  These nodes will be stored in the first (sym_count - 1)
- * entries of the array.  Entry A[sym_count - 2] will represent the root
- * node.  Each other node will contain the zero-based index of its parent
- * node in 'A', left shifted by NUM_SYMBOL_BITS bits.  The low
- * NUM_SYMBOL_BITS bits of each entry in A will be kept as-is.  Again,
- * note that although these low bits will, in fact, contain a symbol
- * value, this symbol will have *no relationship* with the Huffman tree
- * node that happens to occupy the same slot.  This is because this
+ *     (d) Produces the nodes in the same memory used for input frequency
+ *         information.
+ *
+ * Array 'A', which contains 'sym_count' entries, is used for both input and
+ * output.  For this function, 'sym_count' must be at least 2.
+ *
+ * For input, the array must contain the frequencies of the symbols, sorted in
+ * increasing order.  Specifically, each entry must contain a frequency left
+ * shifted by NUM_SYMBOL_BITS bits.  Any data in the low NUM_SYMBOL_BITS bits of
+ * the entries will be ignored by this function.  Although these bits will, in
+ * fact, contain the symbols that correspond to the frequencies, this function
+ * is concerned with frequencies only and keeps the symbols as-is.
+ *
+ * For output, this function will produce the non-leaf nodes of the Huffman
+ * tree.  These nodes will be stored in the first (sym_count - 1) entries of the
+ * array.  Entry A[sym_count - 2] will represent the root node.  Each other node
+ * will contain the zero-based index of its parent node in 'A', left shifted by
+ * NUM_SYMBOL_BITS bits.  The low NUM_SYMBOL_BITS bits of each entry in A will
+ * be kept as-is.  Again, note that although these low bits will, in fact,
+ * contain a symbol value, this symbol will have *no relationship* with the
+ * Huffman tree node that happens to occupy the same slot.  This is because this
  * implementation only generates the non-leaf nodes of the tree.
  */
 static void
 build_tree(u32 A[], unsigned sym_count)
 {
-       /* Index, in 'A', of next lowest frequency symbol that has not
-        * yet been processed.  */
+       const unsigned last_idx = sym_count - 1;
+
+       /* Index of the next lowest frequency leaf that still needs a parent */
        unsigned i = 0;
 
-       /* Index, in 'A', of next lowest frequency parentless non-leaf
-        * node; or, if equal to 'e', then no such node exists yet.  */
+       /*
+        * Index of the next lowest frequency non-leaf that still needs a
+        * parent, or 'e' if there is currently no such node
+        */
        unsigned b = 0;
 
-       /* Index, in 'A', of next node to allocate as a non-leaf.  */
+       /* Index of the next spot for a non-leaf (will overwrite a leaf) */
        unsigned e = 0;
 
        do {
-               unsigned m, n;
-               u32 freq_shifted;
-
-               /* Choose the two next lowest frequency entries.  */
+               u32 new_freq;
 
-               if (i != sym_count &&
-                   (b == e || (A[i] >> NUM_SYMBOL_BITS) <= (A[b] >> NUM_SYMBOL_BITS)))
-                       m = i++;
-               else
-                       m = b++;
-
-               if (i != sym_count &&
-                   (b == e || (A[i] >> NUM_SYMBOL_BITS) <= (A[b] >> NUM_SYMBOL_BITS)))
-                       n = i++;
-               else
-                       n = b++;
-
-               /* Allocate a non-leaf node and link the entries to it.
-                *
-                * If we link an entry that we're visiting for the first
-                * time (via index 'i'), then we're actually linking a
-                * leaf node and it will have no effect, since the leaf
-                * will be overwritten with a non-leaf when index 'e'
-                * catches up to it.  But it's not any slower to
-                * unconditionally set the parent index.
+               /*
+                * Select the next two lowest frequency nodes among the leaves
+                * A[i] and non-leaves A[b], and create a new node A[e] to be
+                * their parent.  Set the new node's frequency to the sum of the
+                * frequencies of its two children.
                 *
-                * We also compute the frequency of the non-leaf node as
-                * the sum of its two children's frequencies.  */
-
-               freq_shifted = (A[m] & ~SYMBOL_MASK) + (A[n] & ~SYMBOL_MASK);
-
-               A[m] = (A[m] & SYMBOL_MASK) | (e << NUM_SYMBOL_BITS);
-               A[n] = (A[n] & SYMBOL_MASK) | (e << NUM_SYMBOL_BITS);
-               A[e] = (A[e] & SYMBOL_MASK) | freq_shifted;
-               e++;
-       } while (sym_count - e > 1);
-               /* When just one entry remains, it is a "leaf" that was
-                * linked to some other node.  We ignore it, since the
-                * rest of the array contains the non-leaves which we
-                * need.  (Note that we're assuming the cases with 0 or 1
-                * symbols were handled separately.) */
+                * Usually the next two lowest frequency nodes are of the same
+                * type (leaf or non-leaf), so check those cases first.
+                */
+               if (i + 1 <= last_idx &&
+                   (b == e || (A[i + 1] & FREQ_MASK) <= (A[b] & FREQ_MASK))) {
+                       /* Two leaves */
+                       new_freq = (A[i] & FREQ_MASK) + (A[i + 1] & FREQ_MASK);
+                       i += 2;
+               } else if (b + 2 <= e &&
+                          (i > last_idx ||
+                           (A[b + 1] & FREQ_MASK) < (A[i] & FREQ_MASK))) {
+                       /* Two non-leaves */
+                       new_freq = (A[b] & FREQ_MASK) + (A[b + 1] & FREQ_MASK);
+                       A[b] = (e << NUM_SYMBOL_BITS) | (A[b] & SYMBOL_MASK);
+                       A[b + 1] = (e << NUM_SYMBOL_BITS) |
+                                  (A[b + 1] & SYMBOL_MASK);
+                       b += 2;
+               } else {
+                       /* One leaf and one non-leaf */
+                       new_freq = (A[i] & FREQ_MASK) + (A[b] & FREQ_MASK);
+                       A[b] = (e << NUM_SYMBOL_BITS) | (A[b] & SYMBOL_MASK);
+                       i++;
+                       b++;
+               }
+               A[e] = new_freq | (A[e] & SYMBOL_MASK);
+               /*
+                * A binary tree with 'n' leaves has 'n - 1' non-leaves, so the
+                * tree is complete once we've created 'n - 1' non-leaves.
+                */
+       } while (++e < last_idx);
 }
 
 /*
- * Given the stripped-down Huffman tree constructed by build_tree(),
- * determine the number of codewords that should be assigned each
- * possible length, taking into account the length-limited constraint.
+ * Given the stripped-down Huffman tree constructed by build_tree(), determine
+ * the number of codewords that should be assigned each possible length, taking
+ * into account the length-limited constraint.
  *
  * @A
- *     The array produced by build_tree(), containing parent index
- *     information for the non-leaf nodes of the Huffman tree.  Each
- *     entry in this array is a node; a node's parent always has a
- *     greater index than that node itself.  This function will
- *     overwrite the parent index information in this array, so
- *     essentially it will destroy the tree.  However, the data in the
- *     low NUM_SYMBOL_BITS of each entry will be preserved.
+ *     The array produced by build_tree(), containing parent index information
+ *     for the non-leaf nodes of the Huffman tree.  Each entry in this array is
+ *     a node; a node's parent always has a greater index than that node
+ *     itself.  This function will overwrite the parent index information in
+ *     this array, so essentially it will destroy the tree.  However, the data
+ *     in the low NUM_SYMBOL_BITS of each entry will be preserved.
  *
  * @root_idx
- *     The 0-based index of the root node in 'A', and consequently one
- *     less than the number of tree node entries in 'A'.  (Or, really 2
- *     less than the actual length of 'A'.)
+ *     The 0-based index of the root node in 'A', and consequently one less
+ *     than the number of tree node entries in 'A'.  (Or, really 2 less than
+ *     the actual length of 'A'.)
  *
  * @len_counts
  *     An array of length ('max_codeword_len' + 1) in which the number of
- *     codewords having each length <= max_codeword_len will be
- *     returned.
+ *     codewords having each length <= max_codeword_len will be returned.
  *
  * @max_codeword_len
  *     The maximum permissible codeword length.
  */
 static void
-compute_length_counts(u32 A[restrict], unsigned root_idx,
-                     unsigned len_counts[restrict], unsigned max_codeword_len)
+compute_length_counts(u32 A[], unsigned root_idx, unsigned len_counts[],
+                     unsigned max_codeword_len)
 {
-       /* The key observations are:
+       unsigned len;
+       int node;
+
+       /*
+        * The key observations are:
         *
-        * (1) We can traverse the non-leaf nodes of the tree, always
-        * visiting a parent before its children, by simply iterating
-        * through the array in reverse order.  Consequently, we can
-        * compute the depth of each node in one pass, overwriting the
-        * parent indices with depths.
+        * (1) We can traverse the non-leaf nodes of the tree, always visiting a
+        *     parent before its children, by simply iterating through the array
+        *     in reverse order.  Consequently, we can compute the depth of each
+        *     node in one pass, overwriting the parent indices with depths.
         *
-        * (2) We can initially assume that in the real Huffman tree,
-        * both children of the root are leaves.  This corresponds to two
-        * codewords of length 1.  Then, whenever we visit a (non-leaf)
-        * node during the traversal, we modify this assumption to
-        * account for the current node *not* being a leaf, but rather
-        * its two children being leaves.  This causes the loss of one
-        * codeword for the current depth and the addition of two
-        * codewords for the current depth plus one.
+        * (2) We can initially assume that in the real Huffman tree, both
+        *     children of the root are leaves.  This corresponds to two
+        *     codewords of length 1.  Then, whenever we visit a (non-leaf) node
+        *     during the traversal, we modify this assumption to account for
+        *     the current node *not* being a leaf, but rather its two children
+        *     being leaves.  This causes the loss of one codeword for the
+        *     current depth and the addition of two codewords for the current
+        *     depth plus one.
         *
-        * (3) We can handle the length-limited constraint fairly easily
-        * by simply using the largest length available when a depth
-        * exceeds max_codeword_len.
+        * (3) We can handle the length-limited constraint fairly easily by
+        *     simply using the largest length available when a depth exceeds
+        *     max_codeword_len.
         */
 
-       for (unsigned len = 0; len <= max_codeword_len; len++)
+       for (len = 0; len <= max_codeword_len; len++)
                len_counts[len] = 0;
        len_counts[1] = 2;
 
-       /* Set the root node's depth to 0.  */
+       /* Set the root node's depth to 0. */
        A[root_idx] &= SYMBOL_MASK;
 
-       for (int node = root_idx - 1; node >= 0; node--) {
+       for (node = root_idx - 1; node >= 0; node--) {
 
-               /* Calculate the depth of this node.  */
+               /* Calculate the depth of this node. */
 
                unsigned parent = A[node] >> NUM_SYMBOL_BITS;
                unsigned parent_depth = A[parent] >> NUM_SYMBOL_BITS;
                unsigned depth = parent_depth + 1;
                unsigned len = depth;
 
-               /* Set the depth of this node so that it is available
-                * when its children (if any) are processed.  */
-
+               /*
+                * Set the depth of this node so that it is available when its
+                * children (if any) are processed.
+                */
                A[node] = (A[node] & SYMBOL_MASK) | (depth << NUM_SYMBOL_BITS);
 
-               /* If needed, decrease the length to meet the
-                * length-limited constraint.  This is not the optimal
-                * method for generating length-limited Huffman codes!
-                * But it should be good enough.  */
+               /*
+                * If needed, decrease the length to meet the length-limited
+                * constraint.  This is not the optimal method for generating
+                * length-limited Huffman codes!  But it should be good enough.
+                */
                if (len >= max_codeword_len) {
                        len = max_codeword_len;
                        do {
@@ -351,8 +370,10 @@ compute_length_counts(u32 A[restrict], unsigned root_idx,
                        } while (len_counts[len] == 0);
                }
 
-               /* Account for the fact that we have a non-leaf node at
-                * the current depth.  */
+               /*
+                * Account for the fact that we have a non-leaf node at the
+                * current depth.
+                */
                len_counts[len]--;
                len_counts[len + 1] += 2;
        }
@@ -382,34 +403,40 @@ compute_length_counts(u32 A[restrict], unsigned root_idx,
  *     frequency.  This is the length of the 'A' and 'len' arrays.
  */
 static void
-gen_codewords(u32 A[restrict], u8 lens[restrict],
-             const unsigned len_counts[restrict],
+gen_codewords(u32 A[], u8 lens[], const unsigned len_counts[],
              unsigned max_codeword_len, unsigned num_syms)
 {
-       u32 next_codewords[max_codeword_len + 1];
-
-       /* Given the number of codewords that will have each length,
-        * assign codeword lengths to symbols.  We do this by assigning
-        * the lengths in decreasing order to the symbols sorted
-        * primarily by increasing frequency and secondarily by
-        * increasing symbol value.  */
-       for (unsigned i = 0, len = max_codeword_len; len >= 1; len--) {
+       u32 next_codewords[MAX_CODEWORD_LEN + 1];
+       unsigned i;
+       unsigned len;
+       unsigned sym;
+
+       /*
+        * Given the number of codewords that will have each length, assign
+        * codeword lengths to symbols.  We do this by assigning the lengths in
+        * decreasing order to the symbols sorted primarily by increasing
+        * frequency and secondarily by increasing symbol value.
+        */
+       for (i = 0, len = max_codeword_len; len >= 1; len--) {
                unsigned count = len_counts[len];
+
                while (count--)
                        lens[A[i++] & SYMBOL_MASK] = len;
        }
 
-       /* Generate the codewords themselves.  We initialize the
+       /*
+        * Generate the codewords themselves.  We initialize the
         * 'next_codewords' array to provide the lexicographically first
-        * codeword of each length, then assign codewords in symbol
-        * order.  This produces a canonical code.  */
+        * codeword of each length, then assign codewords in symbol order.  This
+        * produces a canonical code.
+        */
        next_codewords[0] = 0;
        next_codewords[1] = 0;
-       for (unsigned len = 2; len <= max_codeword_len; len++)
+       for (len = 2; len <= max_codeword_len; len++)
                next_codewords[len] =
                        (next_codewords[len - 1] + len_counts[len - 1]) << 1;
 
-       for (unsigned sym = 0; sym < num_syms; sym++)
+       for (sym = 0; sym < num_syms; sym++)
                A[sym] = next_codewords[lens[sym]]++;
 }
 
@@ -422,88 +449,81 @@ gen_codewords(u32 A[restrict], u8 lens[restrict],
  * length-limited canonical Huffman code.
  *
  * @num_syms
- *     The number of symbols in the alphabet.  The symbols are the
- *     integers in the range [0, num_syms - 1].  This parameter must be
- *     at least 2 and can't be greater than (1 << NUM_SYMBOL_BITS).
+ *     The number of symbols in the alphabet.  The symbols are the integers in
+ *     the range [0, num_syms - 1].  This parameter must be at least 2 and
+ *     must not exceed (1 << NUM_SYMBOL_BITS).
  *
  * @max_codeword_len
  *     The maximum permissible codeword length.
  *
  * @freqs
- *     An array of @num_syms entries, each of which specifies the
- *     frequency of the corresponding symbol.  It is valid for some,
- *     none, or all of the frequencies to be 0.
+ *     An array of length @num_syms that gives the frequency of each symbol.
+ *     It is valid for some, none, or all of the frequencies to be 0.  The sum
+ *     of frequencies must not exceed (1 << NUM_FREQ_BITS) - 1.
  *
  * @lens
- *     An array of @num_syms entries in which this function will return
- *     the length, in bits, of the codeword assigned to each symbol.
- *     Symbols with 0 frequency will not have codewords per se, but
- *     their entries in this array will be set to 0.  No lengths greater
- *     than @max_codeword_len will be assigned.
+ *     An array of @num_syms entries in which this function will return the
+ *     length, in bits, of the codeword assigned to each symbol.  Symbols with
+ *     0 frequency will not have codewords per se, but their entries in this
+ *     array will be set to 0.  No lengths greater than @max_codeword_len will
+ *     be assigned.
  *
  * @codewords
- *     An array of @num_syms entries in which this function will return
- *     the codeword for each symbol, right-justified and padded on the
- *     left with zeroes.  Codewords for symbols with 0 frequency will be
- *     undefined.
+ *     An array of @num_syms entries in which this function will return the
+ *     codeword for each symbol, right-justified and padded on the left with
+ *     zeroes.  Codewords for symbols with 0 frequency will be undefined.
  *
  * ---------------------------------------------------------------------
  *
  * This function builds a length-limited canonical Huffman code.
  *
  * A length-limited Huffman code contains no codewords longer than some
- * specified length, and has exactly (with some algorithms) or
- * approximately (with the algorithm used here) the minimum weighted path
- * length from the root, given this constraint.
- *
- * A canonical Huffman code satisfies the properties that a longer
- * codeword never lexicographically precedes a shorter codeword, and the
- * lexicographic ordering of codewords of the same length is the same as
- * the lexicographic ordering of the corresponding symbols.  A canonical
- * Huffman code, or more generally a canonical prefix code, can be
- * reconstructed from only a list containing the codeword length of each
- * symbol.
- *
- * The classic algorithm to generate a Huffman code creates a node for
- * each symbol, then inserts these nodes into a min-heap keyed by symbol
- * frequency.  Then, repeatedly, the two lowest-frequency nodes are
- * removed from the min-heap and added as the children of a new node
- * having frequency equal to the sum of its two children, which is then
- * inserted into the min-heap.  When only a single node remains in the
- * min-heap, it is the root of the Huffman tree.  The codeword for each
- * symbol is determined by the path needed to reach the corresponding
- * node from the root.  Descending to the left child appends a 0 bit,
- * whereas descending to the right child appends a 1 bit.
- *
- * The classic algorithm is relatively easy to understand, but it is
- * subject to a number of inefficiencies.  In practice, it is fastest to
- * first sort the symbols by frequency.  (This itself can be subject to
- * an optimization based on the fact that most frequencies tend to be
- * low.)  At the same time, we sort secondarily by symbol value, which
- * aids the process of generating a canonical code.  Then, during tree
- * construction, no heap is necessary because both the leaf nodes and the
- * unparented non-leaf nodes can be easily maintained in sorted order.
- * Consequently, there can never be more than two possibilities for the
- * next-lowest-frequency node.
- *
- * In addition, because we're generating a canonical code, we actually
- * don't need the leaf nodes of the tree at all, only the non-leaf nodes.
- * This is because for canonical code generation we don't need to know
- * where the symbols are in the tree.  Rather, we only need to know how
- * many leaf nodes have each depth (codeword length).  And this
- * information can, in fact, be quickly generated from the tree of
- * non-leaves only.
- *
- * Furthermore, we can build this stripped-down Huffman tree directly in
- * the array in which the codewords are to be generated, provided that
- * these array slots are large enough to hold a symbol and frequency
- * value.
- *
- * Still furthermore, we don't even need to maintain explicit child
- * pointers.  We only need the parent pointers, and even those can be
- * overwritten in-place with depth information as part of the process of
- * extracting codeword lengths from the tree.  So in summary, we do NOT
- * need a big structure like:
+ * specified length, and has exactly (with some algorithms) or approximately
+ * (with the algorithm used here) the minimum weighted path length from the
+ * root, given this constraint.
+ *
+ * A canonical Huffman code satisfies the properties that a longer codeword
+ * never lexicographically precedes a shorter codeword, and the lexicographic
+ * ordering of codewords of the same length is the same as the lexicographic
+ * ordering of the corresponding symbols.  A canonical Huffman code, or more
+ * generally a canonical prefix code, can be reconstructed from only a list
+ * containing the codeword length of each symbol.
+ *
+ * The classic algorithm to generate a Huffman code creates a node for each
+ * symbol, then inserts these nodes into a min-heap keyed by symbol frequency.
+ * Then, repeatedly, the two lowest-frequency nodes are removed from the
+ * min-heap and added as the children of a new node having frequency equal to
+ * the sum of its two children, which is then inserted into the min-heap.  When
+ * only a single node remains in the min-heap, it is the root of the Huffman
+ * tree.  The codeword for each symbol is determined by the path needed to reach
+ * the corresponding node from the root.  Descending to the left child appends a
+ * 0 bit, whereas descending to the right child appends a 1 bit.
+ *
+ * The classic algorithm is relatively easy to understand, but it is subject to
+ * a number of inefficiencies.  In practice, it is fastest to first sort the
+ * symbols by frequency.  (This itself can be subject to an optimization based
+ * on the fact that most frequencies tend to be low.)  At the same time, we sort
+ * secondarily by symbol value, which aids the process of generating a canonical
+ * code.  Then, during tree construction, no heap is necessary because both the
+ * leaf nodes and the unparented non-leaf nodes can be easily maintained in
+ * sorted order.  Consequently, there can never be more than two possibilities
+ * for the next-lowest-frequency node.
+ *
+ * In addition, because we're generating a canonical code, we actually don't
+ * need the leaf nodes of the tree at all, only the non-leaf nodes.  This is
+ * because for canonical code generation we don't need to know where the symbols
+ * are in the tree.  Rather, we only need to know how many leaf nodes have each
+ * depth (codeword length).  And this information can, in fact, be quickly
+ * generated from the tree of non-leaves only.
+ *
+ * Furthermore, we can build this stripped-down Huffman tree directly in the
+ * array in which the codewords are to be generated, provided that these array
+ * slots are large enough to hold a symbol and frequency value.
+ *
+ * Still furthermore, we don't even need to maintain explicit child pointers.
+ * We only need the parent pointers, and even those can be overwritten in-place
+ * with depth information as part of the process of extracting codeword lengths
+ * from the tree.  So in summary, we do NOT need a big structure like:
  *
  *     struct huffman_tree_node {
  *             unsigned int symbol;
@@ -514,48 +534,40 @@ gen_codewords(u32 A[restrict], u8 lens[restrict],
  *     };
  *
  *
- *   ... which often gets used in "naive" implementations of Huffman code
- *   generation.
- *
- * Most of these optimizations are based on the implementation in 7-Zip
- * (source file: C/HuffEnc.c), which has been placed in the public domain
- * by Igor Pavlov.  But I've rewritten the code with extensive comments,
- * as it took me a while to figure out what it was doing...!
- *
- * ---------------------------------------------------------------------
- *
- * NOTE: in general, the same frequencies can be used to generate
- * different length-limited canonical Huffman codes.  One choice we have
- * is during tree construction, when we must decide whether to prefer a
- * leaf or non-leaf when there is a tie in frequency.  Another choice we
- * have is how to deal with codewords that would exceed @max_codeword_len
- * bits in length.  Both of these choices affect the resulting codeword
- * lengths, which otherwise can be mapped uniquely onto the resulting
- * canonical Huffman code.
- *
- * Normally, there is no problem with choosing one valid code over
- * another, provided that they produce similar compression ratios.
- * However, the LZMS compression format uses adaptive Huffman coding.  It
- * requires that both the decompressor and compressor build a canonical
- * code equivalent to that which can be generated by using the classic
- * Huffman tree construction algorithm and always processing leaves
- * before non-leaves when there is a frequency tie.  Therefore, we make
- * sure to do this.  This method also has the advantage of sometimes
- * shortening the longest codeword that is generated.
- *
- * There also is the issue of how codewords longer than @max_codeword_len
- * are dealt with.  Fortunately, for LZMS this is irrelevant because
- * because for the LZMS alphabets no codeword can ever exceed
- * LZMS_MAX_CODEWORD_LEN (= 15).  Since the LZMS algorithm regularly
- * halves all frequencies, the frequencies cannot become high enough for
- * a length 16 codeword to be generated.  Specifically, I think that if
- * ties are broken in favor of non-leaves (as we do), the lowest total
- * frequency that would give a length-16 codeword would be the sum of the
- * frequencies 1 1 1 3 4 7 11 18 29 47 76 123 199 322 521 843 1364, which
- * is 3570.  And in LZMS we can't get a frequency that high based on the
- * alphabet sizes, rebuild frequencies, and scaling factors.  This
- * worst-case scenario is based on the following degenerate case (only
- * the bottom of the tree shown):
+ * ... which often gets used in "naive" implementations of Huffman code
+ * generation.
+ *
+ * Many of these optimizations are based on the implementation in 7-Zip (source
+ * file: C/HuffEnc.c), which was placed in the public domain by Igor Pavlov.
+ *
+ * NOTE: in general, the same frequencies can be used to generate different
+ * length-limited canonical Huffman codes.  One choice we have is during tree
+ * construction, when we must decide whether to prefer a leaf or non-leaf when
+ * there is a tie in frequency.  Another choice we have is how to deal with
+ * codewords that would exceed @max_codeword_len bits in length.  Both of these
+ * choices affect the resulting codeword lengths, which otherwise can be mapped
+ * uniquely onto the resulting canonical Huffman code.
+ *
+ * Normally, there is no problem with choosing one valid code over another,
+ * provided that they produce similar compression ratios.  However, the LZMS
+ * compression format uses adaptive Huffman coding.  It requires that both the
+ * decompressor and compressor build a canonical code equivalent to that which
+ * can be generated by using the classic Huffman tree construction algorithm and
+ * always processing leaves before non-leaves when there is a frequency tie.
+ * Therefore, we make sure to do this.  This method also has the advantage of
+ * sometimes shortening the longest codeword that is generated.
+ *
+ * There also is the issue of how codewords longer than @max_codeword_len are
+ * dealt with.  Fortunately, for LZMS this is irrelevant because for the LZMS
+ * alphabets no codeword can ever exceed LZMS_MAX_CODEWORD_LEN (= 15).  Since
+ * the LZMS algorithm regularly halves all frequencies, the frequencies cannot
+ * become high enough for a length 16 codeword to be generated.  Specifically, I
+ * think that if ties are broken in favor of non-leaves (as we do), the lowest
+ * total frequency that would give a length-16 codeword would be the sum of the
+ * frequencies 1 1 1 3 4 7 11 18 29 47 76 123 199 322 521 843 1364, which is
+ * 3570.  And in LZMS we can't get a frequency that high based on the alphabet
+ * sizes, rebuild frequencies, and scaling factors.  This worst-case scenario is
+ * based on the following degenerate case (only the bottom of the tree shown):
  *
  *                          ...
  *                        17
@@ -570,57 +582,65 @@ gen_codewords(u32 A[restrict], u8 lens[restrict],
  *               / \
  *              1   1
  *
- * Excluding the first leaves (those with value 1), each leaf value must
- * be greater than the non-leaf up 1 and down 2 from it; otherwise that
- * leaf would have taken precedence over that non-leaf and been combined
- * with the leaf below, thereby decreasing the height compared to that
- * shown.
- *
- * Interesting fact: if we were to instead prioritize non-leaves over
- * leaves, then the worst case frequencies would be the Fibonacci
- * sequence, plus an extra frequency of 1.  In this hypothetical
- * scenario, it would be slightly easier for longer codewords to be
- * generated.
+ * Excluding the first leaves (those with value 1), each leaf value must be
+ * greater than the non-leaf up 1 and down 2 from it; otherwise that leaf would
+ * have taken precedence over that non-leaf and been combined with the leaf
+ * below, thereby decreasing the height compared to that shown.
+ *
+ * Interesting fact: if we were to instead prioritize non-leaves over leaves,
+ * then the worst case frequencies would be the Fibonacci sequence, plus an
+ * extra frequency of 1.  In this hypothetical scenario, it would be slightly
+ * easier for longer codewords to be generated.
  */
 void
 make_canonical_huffman_code(unsigned num_syms, unsigned max_codeword_len,
-                           const u32 freqs[restrict],
-                           u8 lens[restrict], u32 codewords[restrict])
+                           const u32 freqs[], u8 lens[], u32 codewords[])
 {
        u32 *A = codewords;
        unsigned num_used_syms;
 
-       /* We begin by sorting the symbols primarily by frequency and
-        * secondarily by symbol value.  As an optimization, the array
-        * used for this purpose ('A') shares storage with the space in
-        * which we will eventually return the codewords.  */
+       wimlib_assert(num_syms <= MAX_NUM_SYMS);
+       STATIC_ASSERT(MAX_NUM_SYMS <= 1 << NUM_SYMBOL_BITS);
+       wimlib_assert(max_codeword_len <= MAX_CODEWORD_LEN);
 
+       /*
+        * We begin by sorting the symbols primarily by frequency and
+        * secondarily by symbol value.  As an optimization, the array used for
+        * this purpose ('A') shares storage with the space in which we will
+        * eventually return the codewords.
+        */
        num_used_syms = sort_symbols(num_syms, freqs, lens, A);
 
-       /* 'num_used_syms' is the number of symbols with nonzero
-        * frequency.  This may be less than @num_syms.  'num_used_syms'
-        * is also the number of entries in 'A' that are valid.  Each
-        * entry consists of a distinct symbol and a nonzero frequency
-        * packed into a 32-bit integer.  */
+       /*
+        * 'num_used_syms' is the number of symbols with nonzero frequency.
+        * This may be less than @num_syms.  'num_used_syms' is also the number
+        * of entries in 'A' that are valid.  Each entry consists of a distinct
+        * symbol and a nonzero frequency packed into a 32-bit integer.
+        */
 
-       /* Handle special cases where only 0 or 1 symbols were used (had
-        * nonzero frequency).  */
+       /*
+        * Handle special cases where only 0 or 1 symbols were used (had nonzero
+        * frequency).
+        */
 
        if (unlikely(num_used_syms == 0)) {
-               /* Code is empty.  sort_symbols() already set all lengths
-                * to 0, so there is nothing more to do.  */
+               /*
+                * Code is empty.  sort_symbols() already set all lengths to 0,
+                * so there is nothing more to do.
+                */
                return;
        }
 
        if (unlikely(num_used_syms == 1)) {
-               /* Only one symbol was used, so we only need one
-                * codeword.  But two codewords are needed to form the
-                * smallest complete Huffman code, which uses codewords 0
-                * and 1.  Therefore, we choose another symbol to which
-                * to assign a codeword.  We use 0 (if the used symbol is
-                * not 0) or 1 (if the used symbol is 0).  In either
-                * case, the lesser-valued symbol must be assigned
-                * codeword 0 so that the resulting code is canonical.  */
+               /*
+                * Only one symbol was used, so we only need one codeword.  But
+                * two codewords are needed to form the smallest complete
+                * Huffman code, which uses codewords 0 and 1.  Therefore, we
+                * choose another symbol to which to assign a codeword.  We use
+                * 0 (if the used symbol is not 0) or 1 (if the used symbol is
+                * 0).  In either case, the lesser-valued symbol must be
+                * assigned codeword 0 so that the resulting code is canonical.
+                */
 
                unsigned sym = A[0] & SYMBOL_MASK;
                unsigned nonzero_idx = sym ? sym : 1;
@@ -632,14 +652,16 @@ make_canonical_huffman_code(unsigned num_syms, unsigned max_codeword_len,
                return;
        }
 
-       /* Build a stripped-down version of the Huffman tree, sharing the
-        * array 'A' with the symbol values.  Then extract length counts
-        * from the tree and use them to generate the final codewords.  */
+       /*
+        * Build a stripped-down version of the Huffman tree, sharing the array
+        * 'A' with the symbol values.  Then extract length counts from the tree
+        * and use them to generate the final codewords.
+        */
 
        build_tree(A, num_used_syms);
 
        {
-               unsigned len_counts[max_codeword_len + 1];
+               unsigned len_counts[MAX_CODEWORD_LEN + 1];
 
                compute_length_counts(A, num_used_syms - 2,
                                      len_counts, max_codeword_len);
index 6aa635bf669803e1ec2500336a0ab163d0d44977..e8fb7bf137ef627adce26596778b8860008e9696 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) 2013 Eric Biggers
+ * Copyright (C) 2013-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
-#ifdef ENABLE_MULTITHREADED_COMPRESSION
-
 #include <errno.h>
-#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "wimlib/chunk_compressor.h"
 #include "wimlib/error.h"
 #include "wimlib/list.h"
+#include "wimlib/threads.h"
 #include "wimlib/util.h"
 
 struct message_queue {
        struct list_head list;
-       pthread_mutex_t lock;
-       pthread_cond_t msg_avail_cond;
-       pthread_cond_t space_avail_cond;
+       struct mutex lock;
+       struct condvar msg_avail_cond;
+       struct condvar space_avail_cond;
        bool terminating;
 };
 
 struct compressor_thread_data {
-       pthread_t thread;
+       struct thread thread;
        struct message_queue *chunks_to_compress_queue;
        struct message_queue *compressed_chunks_queue;
        struct wimlib_compressor *compressor;
@@ -91,25 +89,19 @@ struct parallel_chunk_compressor {
 static int
 message_queue_init(struct message_queue *q)
 {
-       if (pthread_mutex_init(&q->lock, NULL)) {
-               ERROR_WITH_ERRNO("Failed to initialize mutex");
+       if (!mutex_init(&q->lock))
                goto err;
-       }
-       if (pthread_cond_init(&q->msg_avail_cond, NULL)) {
-               ERROR_WITH_ERRNO("Failed to initialize condition variable");
+       if (!condvar_init(&q->msg_avail_cond))
                goto err_destroy_lock;
-       }
-       if (pthread_cond_init(&q->space_avail_cond, NULL)) {
-               ERROR_WITH_ERRNO("Failed to initialize condition variable");
+       if (!condvar_init(&q->space_avail_cond))
                goto err_destroy_msg_avail_cond;
-       }
        INIT_LIST_HEAD(&q->list);
        return 0;
 
 err_destroy_msg_avail_cond:
-       pthread_cond_destroy(&q->msg_avail_cond);
+       condvar_destroy(&q->msg_avail_cond);
 err_destroy_lock:
-       pthread_mutex_destroy(&q->lock);
+       mutex_destroy(&q->lock);
 err:
        return WIMLIB_ERR_NOMEM;
 }
@@ -118,19 +110,19 @@ static void
 message_queue_destroy(struct message_queue *q)
 {
        if (q->list.next != NULL) {
-               pthread_mutex_destroy(&q->lock);
-               pthread_cond_destroy(&q->msg_avail_cond);
-               pthread_cond_destroy(&q->space_avail_cond);
+               mutex_destroy(&q->lock);
+               condvar_destroy(&q->msg_avail_cond);
+               condvar_destroy(&q->space_avail_cond);
        }
 }
 
 static void
 message_queue_put(struct message_queue *q, struct message *msg)
 {
-       pthread_mutex_lock(&q->lock);
+       mutex_lock(&q->lock);
        list_add_tail(&msg->list, &q->list);
-       pthread_cond_signal(&q->msg_avail_cond);
-       pthread_mutex_unlock(&q->lock);
+       condvar_signal(&q->msg_avail_cond);
+       mutex_unlock(&q->lock);
 }
 
 static struct message *
@@ -138,25 +130,25 @@ message_queue_get(struct message_queue *q)
 {
        struct message *msg;
 
-       pthread_mutex_lock(&q->lock);
+       mutex_lock(&q->lock);
        while (list_empty(&q->list) && !q->terminating)
-               pthread_cond_wait(&q->msg_avail_cond, &q->lock);
+               condvar_wait(&q->msg_avail_cond, &q->lock);
        if (!q->terminating) {
                msg = list_entry(q->list.next, struct message, list);
                list_del(&msg->list);
        } else
                msg = NULL;
-       pthread_mutex_unlock(&q->lock);
+       mutex_unlock(&q->lock);
        return msg;
 }
 
 static void
 message_queue_terminate(struct message_queue *q)
 {
-       pthread_mutex_lock(&q->lock);
+       mutex_lock(&q->lock);
        q->terminating = true;
-       pthread_cond_broadcast(&q->msg_avail_cond);
-       pthread_mutex_unlock(&q->lock);
+       condvar_broadcast(&q->msg_avail_cond);
+       mutex_unlock(&q->lock);
 }
 
 static int
@@ -250,7 +242,7 @@ parallel_chunk_compressor_destroy(struct chunk_compressor *_ctx)
                message_queue_terminate(&ctx->chunks_to_compress_queue);
 
                for (i = 0; i < ctx->num_started_threads; i++)
-                       pthread_join(ctx->thread_data[i].thread, NULL);
+                       thread_join(&ctx->thread_data[i].thread);
        }
 
        message_queue_destroy(&ctx->chunks_to_compress_queue);
@@ -477,15 +469,10 @@ new_parallel_chunk_compressor(int out_ctype, u32 out_chunk_size,
             ctx->num_started_threads < num_threads;
             ctx->num_started_threads++)
        {
-               ret = pthread_create(&ctx->thread_data[ctx->num_started_threads].thread,
-                                    NULL,
-                                    compressor_thread_proc,
-                                    &ctx->thread_data[ctx->num_started_threads]);
-               if (ret) {
-                       errno = ret;
-                       WARNING_WITH_ERRNO("Failed to create compressor thread %u of %u",
-                                          ctx->num_started_threads + 1,
-                                          num_threads);
+               if (!thread_create(&ctx->thread_data[ctx->num_started_threads].thread,
+                                  compressor_thread_proc,
+                                  &ctx->thread_data[ctx->num_started_threads]))
+               {
                        ret = WIMLIB_ERR_NOMEM;
                        if (ctx->num_started_threads >= 2)
                                break;
@@ -515,5 +502,3 @@ err:
        parallel_chunk_compressor_destroy(&ctx->base);
        return ret;
 }
-
-#endif /* ENABLE_MULTITHREADED_COMPRESSION */
index 6bac63ec8f9b2037bf720c82d69649264eddc006..24a6795c0ad115da82643fc982d322be6044270c 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/src/cpu_features.c b/src/cpu_features.c
new file mode 100644 (file)
index 0000000..08520c9
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * cpu_features.c - runtime CPU feature detection
+ *
+ * Copyright 2022-2023 Eric Biggers
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib/cpu_features.h"
+
+#if CPU_FEATURES_ENABLED
+
+#include "wimlib/util.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/*
+ * With old GCC versions we have to manually save and restore the x86_32 PIC
+ * register (ebx).  See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602
+ */
+#if defined(__i386__) && defined(__PIC__)
+#  define EBX_CONSTRAINT "=&r"
+#else
+#  define EBX_CONSTRAINT "=b"
+#endif
+
+/* Execute the CPUID instruction. */
+static inline void
+cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d)
+{
+       asm volatile(".ifnc %%ebx, %1; mov  %%ebx, %1; .endif\n"
+                    "cpuid                                  \n"
+                    ".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n"
+                    : "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d)
+                    : "a" (leaf), "c" (subleaf));
+}
+
+/* Read an extended control register. */
+static inline u64
+read_xcr(u32 index)
+{
+       u32 d, a;
+
+       /*
+        * Execute the "xgetbv" instruction.  Old versions of binutils do not
+        * recognize this instruction, so list the raw bytes instead.
+        *
+        * This must be 'volatile' to prevent this code from being moved out
+        * from under the check for OSXSAVE.
+        */
+       asm volatile(".byte 0x0f, 0x01, 0xd0" :
+                    "=d" (d), "=a" (a) : "c" (index));
+
+       return ((u64)d << 32) | a;
+}
+
+static u32
+get_cpu_features(void)
+{
+       u32 max_leaf, a, b, c, d;
+       u64 xcr0 = 0;
+       u32 features = 0;
+
+       /* EAX=0: Highest Function Parameter and Manufacturer ID */
+       cpuid(0, 0, &max_leaf, &b, &c, &d);
+       if (max_leaf < 1)
+               return features;
+
+       /* EAX=1: Processor Info and Feature Bits */
+       cpuid(1, 0, &a, &b, &c, &d);
+       if (c & (1 << 9))
+               features |= X86_CPU_FEATURE_SSSE3;
+       if (c & (1 << 19))
+               features |= X86_CPU_FEATURE_SSE4_1;
+       if (c & (1 << 20))
+               features |= X86_CPU_FEATURE_SSE4_2;
+       if (c & (1 << 27))
+               xcr0 = read_xcr(0);
+       if ((c & (1 << 28)) && ((xcr0 & 0x6) == 0x6))
+               features |= X86_CPU_FEATURE_AVX;
+
+       if (max_leaf < 7)
+               return features;
+
+       /* EAX=7, ECX=0: Extended Features */
+       cpuid(7, 0, &a, &b, &c, &d);
+       if (b & (1 << 8))
+               features |= X86_CPU_FEATURE_BMI2;
+       if (b & (1 << 29))
+               features |= X86_CPU_FEATURE_SHA;
+
+       return features;
+}
+
+#elif defined(__aarch64__) && defined(__linux__)
+
+/*
+ * On Linux, arm32 and arm64 CPU features can be detected by reading the
+ * AT_HWCAP and AT_HWCAP2 values from /proc/self/auxv.
+ *
+ * Ideally we'd use the C library function getauxval(), but it's not guaranteed
+ * to be available: it was only added to glibc in 2.16, and in Android it was
+ * added to API level 18 for arm32 and level 21 for arm64.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#define AT_HWCAP       16
+#define AT_HWCAP2      26
+
+static void scan_auxv(unsigned long *hwcap, unsigned long *hwcap2)
+{
+       int fd;
+       unsigned long auxbuf[32];
+       int filled = 0;
+       int i;
+
+       fd = open("/proc/self/auxv", O_RDONLY);
+       if (fd < 0)
+               return;
+
+       for (;;) {
+               do {
+                       int ret = read(fd, &((char *)auxbuf)[filled],
+                                      sizeof(auxbuf) - filled);
+                       if (ret <= 0) {
+                               if (ret < 0 && errno == EINTR)
+                                       continue;
+                               goto out;
+                       }
+                       filled += ret;
+               } while (filled < 2 * sizeof(long));
+
+               i = 0;
+               do {
+                       unsigned long type = auxbuf[i];
+                       unsigned long value = auxbuf[i + 1];
+
+                       if (type == AT_HWCAP)
+                               *hwcap = value;
+                       else if (type == AT_HWCAP2)
+                               *hwcap2 = value;
+                       i += 2;
+                       filled -= 2 * sizeof(long);
+               } while (filled >= 2 * sizeof(long));
+
+               memmove(auxbuf, &auxbuf[i], filled);
+       }
+out:
+       close(fd);
+}
+
+static u32
+get_cpu_features(void)
+{
+       unsigned long hwcap = 0;
+       unsigned long hwcap2 = 0;
+       u32 features = 0;
+
+       scan_auxv(&hwcap, &hwcap2);
+
+       if (hwcap & (1 << 5))   /* HWCAP_SHA1 */
+               features |= ARM_CPU_FEATURE_SHA1;
+
+       return features;
+}
+
+#elif defined(__aarch64__) && defined(__APPLE__)
+
+/* On Apple platforms, arm64 CPU features can be detected via sysctlbyname(). */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+static const struct {
+       const char *name;
+       u32 feature;
+} feature_sysctls[] = {
+       { "hw.optional.arm.FEAT_SHA1",  ARM_CPU_FEATURE_SHA1 },
+};
+
+static u32
+get_cpu_features(void)
+{
+       u32 features = 0;
+
+       for (size_t i = 0; i < ARRAY_LEN(feature_sysctls); i++) {
+               const char *name = feature_sysctls[i].name;
+               u32 val = 0;
+               size_t valsize = sizeof(val);
+
+               if (sysctlbyname(name, &val, &valsize, NULL, 0) == 0 &&
+                   valsize == sizeof(val) && val == 1)
+                       features |= feature_sysctls[i].feature;
+       }
+       return features;
+}
+
+#elif defined(__aarch64__) && defined(_WIN32)
+
+#include <windows.h>
+
+static u32
+get_cpu_features(void)
+{
+       u32 features = 0;
+
+       if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE))
+               features |= ARM_CPU_FEATURE_SHA1;
+
+       return features;
+}
+
+#else
+#  error "CPU_FEATURES_ENABLED was set but no implementation is available!"
+#endif
+
+static const struct {
+       const char *name;
+       u32 feature;
+} feature_table[] = {
+#if defined(__i386__) || defined(__x86_64__)
+       {"ssse3",       X86_CPU_FEATURE_SSSE3},
+       {"sse4.1",      X86_CPU_FEATURE_SSE4_1},
+       {"sse4.2",      X86_CPU_FEATURE_SSE4_2},
+       {"avx",         X86_CPU_FEATURE_AVX},
+       {"bmi2",        X86_CPU_FEATURE_BMI2},
+       {"sha",         X86_CPU_FEATURE_SHA},
+       {"sha1",        X86_CPU_FEATURE_SHA},
+#elif defined(__aarch64__)
+       {"sha1",        ARM_CPU_FEATURE_SHA1},
+#else
+#  error "CPU_FEATURES_ENABLED was set but no features are defined!"
+#endif
+       {"*",           0xFFFFFFFF},
+};
+
+static u32
+find_cpu_feature(const char *name, size_t namelen)
+{
+       for (size_t i = 0; i < ARRAY_LEN(feature_table); i++) {
+               if (namelen == strlen(feature_table[i].name) &&
+                   memcmp(name, feature_table[i].name, namelen) == 0)
+                       return feature_table[i].feature;
+       }
+       return 0;
+}
+
+u32 cpu_features;
+
+void init_cpu_features(void)
+{
+       char *p, *sep;
+
+       cpu_features = get_cpu_features();
+
+       /*
+        * Allow disabling CPU features via an environmental variable for
+        * testing purposes.  Syntax is comma-separated list of feature names.
+        */
+       p = getenv("WIMLIB_DISABLE_CPU_FEATURES");
+       if (likely(p == NULL))
+               return;
+       for (; (sep = strchr(p, ',')) != NULL; p = sep + 1)
+               cpu_features &= ~find_cpu_feature(p, sep - p);
+       cpu_features &= ~find_cpu_feature(p, strlen(p));
+}
+
+#endif /* CPU_FEATURES_ENABLED */
index f255ea92fd503906cd8a6d73480cde25b8c3adeb..c2ab5b8732386e282e2cf3bf4931a12f63412e5d 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -56,6 +56,11 @@ wimlib_create_decompressor(enum wimlib_compression_type ctype,
                           struct wimlib_decompressor **dec_ret)
 {
        struct wimlib_decompressor *dec;
+       int ret;
+
+       ret = wimlib_global_init(0);
+       if (ret)
+               return ret;
 
        if (!decompressor_ctype_valid(ctype))
                return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
@@ -73,8 +78,6 @@ wimlib_create_decompressor(enum wimlib_compression_type ctype,
        dec->max_block_size = max_block_size;
        dec->private = NULL;
        if (dec->ops->create_decompressor) {
-               int ret;
-
                ret = dec->ops->create_decompressor(max_block_size,
                                                    &dec->private);
                if (ret) {
index 3b49274886fed539e3614982f546927101fd0db8..abdf4df49147516bae28de87bdbb1c7de9ab78c4 100644 (file)
@@ -3,21 +3,28 @@
  *
  * Code for decompression shared among multiple compression formats.
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022 Eric Biggers
  *
- * Written in 2012-2016 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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.
  */
 
 #ifdef HAVE_CONFIG_H
index 9cbec0a2ef90ba8c6ce288bbdd0b70c085dfd967..192dfbae9db0d08d6972bfa917b4519fe3bcf2b6 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 9e175f7ea5391841ca95e8e26be574e7894b77ac..f20d7dffdebcc76dc7a9a489c34c94d5b9c4429b 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 /*
@@ -117,18 +117,12 @@ struct wim_dentry_on_disk {
        le64 last_write_time;
 
        /*
-        * Usually this is the SHA-1 message digest of the file's "contents"
-        * (the unnamed data stream).
-        *
-        * If the file has FILE_ATTRIBUTE_REPARSE_POINT set, then this is
-        * instead usually the SHA-1 message digest of the uncompressed reparse
-        * point data.
-        *
-        * However, there are some special rules that need to be applied to
-        * interpret this field correctly when extra stream entries are present.
-        * See the code for details.
+        * Usually this is the SHA-1 message digest of the file's contents, or
+        * all zeroes if the file is a directory or is empty.  However, special
+        * rules apply if the file has FILE_ATTRIBUTE_REPARSE_POINT set or has
+        * named data streams.  See assign_stream_types_unencrypted().
         */
-       u8 default_hash[SHA1_HASH_SIZE];
+       u8 main_hash[SHA1_HASH_SIZE];
 
        /* Unknown field (maybe accidental padding)  */
        le32 unknown_0x54;
@@ -155,10 +149,10 @@ struct wim_dentry_on_disk {
                        le32 reparse_tag;
                        le16 rp_reserved;
                        le16 rp_flags;
-               } _packed_attribute reparse;
+               } __attribute__((packed)) reparse;
                struct {
                        le64 hard_link_group_id;
-               } _packed_attribute nonreparse;
+               } __attribute__((packed)) nonreparse;
        };
 
        /* Number of extra stream entries that directly follow this dentry
@@ -194,9 +188,9 @@ struct wim_dentry_on_disk {
         * field) after 8-byte alignment, then the remaining space will be a
         * variable-length list of tagged metadata items.  See tagged_items.c
         * for more information.  */
-       /* u8 tagged_items[] _aligned_attribute(8); */
+       /* u8 tagged_items[] __attribute__((aligned(8))); */
 
-} _packed_attribute;
+} __attribute__((packed));
        /* If num_extra_streams != 0, then there are that many extra stream
         * entries following the dentry, starting on the next 8-byte aligned
         * boundary.  They are not counted in the 'length' field of the dentry.
@@ -226,7 +220,7 @@ struct wim_extra_stream_entry_on_disk {
         * the null terminator.  There is a null terminator character if
         * @name_nbytes != 0; i.e., if this stream is named.  */
        utf16lechar name[];
-} _packed_attribute;
+} __attribute__((packed));
 
 static void
 do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *name,
@@ -354,6 +348,8 @@ dentry_out_total_length(const struct wim_dentry *dentry)
 {
        const struct wim_inode *inode = dentry->d_inode;
        size_t len;
+       unsigned num_unnamed_streams = 0;
+       bool have_named_data_stream = false;
 
        len = dentry_min_len_with_names(dentry->d_name_nbytes,
                                        dentry->d_short_name_nbytes);
@@ -362,36 +358,31 @@ dentry_out_total_length(const struct wim_dentry *dentry)
        if (inode->i_extra)
                len += ALIGN(inode->i_extra->size, 8);
 
-       if (!(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
-               /*
-                * Extra stream entries:
-                *
-                * - Use one extra stream entry for each named data stream
-                * - Use one extra stream entry for the unnamed data stream when there is either:
-                *      - a reparse point stream
-                *      - at least one named data stream (for Windows PE bug workaround)
-                * - Use one extra stream entry for the reparse point stream if there is one
-                */
-               bool have_named_data_stream = false;
-               bool have_reparse_point_stream = false;
+       /*
+        * Calculate the total length of the extra stream entries that will be
+        * written.  To match DISM, some odd rules need to be followed here.
+        * See write_dentry_streams() for explanation.  Keep this in sync with
+        * write_dentry_streams()!
+        */
+       if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+               num_unnamed_streams++;
+       } else {
                for (unsigned i = 0; i < inode->i_num_streams; i++) {
                        const struct wim_inode_stream *strm = &inode->i_streams[i];
+
                        if (stream_is_named_data_stream(strm)) {
                                len += stream_out_total_length(strm);
                                have_named_data_stream = true;
-                       } else if (strm->stream_type == STREAM_TYPE_REPARSE_POINT) {
-                               wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
-                               have_reparse_point_stream = true;
                        }
                }
-
-               if (have_named_data_stream || have_reparse_point_stream) {
-                       if (have_reparse_point_stream)
-                               len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8);
-                       len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8);
-               }
+               if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+                       num_unnamed_streams++;
+               if (!(inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
+                       num_unnamed_streams++;
        }
-
+       if (num_unnamed_streams > 1 || have_named_data_stream)
+               len += num_unnamed_streams *
+                      ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8);
        return len;
 }
 
@@ -607,7 +598,7 @@ collate_dentry_names(const struct avl_tree_node *n1,
  * WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE or
  * WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE to wimlib_global_init().  */
 bool default_ignore_case =
-#ifdef __WIN32__
+#ifdef _WIN32
        true
 #else
        false
@@ -1162,55 +1153,65 @@ assign_stream_types_encrypted(struct wim_inode *inode)
 /*
  * Set the type of each stream for an unencrypted file.
  *
- * There will be an unnamed data stream, a reparse point stream, or both an
- * unnamed data stream and a reparse point stream.  In addition, there may be
- * named data streams.
- *
- * NOTE: if the file has a reparse point stream or at least one named data
- * stream, then WIMGAPI puts *all* streams in the extra stream entries and
- * leaves the default stream hash zeroed.  wimlib now does the same.  However,
- * for input we still support the default hash field being used, since wimlib
- * used to use it and MS software is somewhat accepting of it as well.
+ * To specify the streams of each file, the WIM provides a main_hash and an
+ * optional list of "extra stream entries".  Each extra stream entry is a
+ * (name, hash) pair where the name is optional.  Hashes can be the special
+ * value of zero_hash, which means the stream is empty (zero-length).
+ *
+ * While extra stream entries with names always refer to "named data streams",
+ * the main hash and any extra unnamed hashes can be hard to interpret.  This is
+ * because the WIM file format unfortunately doesn't make it very clear which is
+ * the unnamed data stream (i.e. standard file contents) and which is the
+ * reparse stream.  The way this ambiguity is resolved (based on what MS
+ * software seems to do) is by (1) a file can have at most one unnamed data
+ * stream and at most one reparse stream, (2) a reparse stream is present if and
+ * only if the file has FILE_ATTRIBUTE_REPARSE_POINT, and (3) the reparse
+ * stream, if present, is stored before the unnamed data stream if present
+ * (considering main_hash to come before any extra hashes).  Note: directories
+ * need not have an unnamed data stream stored, even with a zero hash, as
+ * "unnamed data stream" isn't meaningful for a directory in the first place.
+ *
+ * With those rules in mind, one would expect that the first unnamed stream
+ * would use main_hash, and the second (if present) would use an extra stream
+ * entry.  However, there is another quirk that we must be compatible with:
+ * sometimes main_hash isn't used and only extra stream entries are used.  To
+ * handle this, we ignore main_hash if it is zero and there is at least one
+ * unnamed extra stream entry.  This works correctly as long as a zero main_hash
+ * and an unnamed extra stream entry is never used to represent an empty reparse
+ * stream and an unnamed data stream.  (It's not, as the reparse stream always
+ * goes in the extra stream entries in this case.  See write_dentry_streams().)
  */
 static void
 assign_stream_types_unencrypted(struct wim_inode *inode)
 {
-       bool found_reparse_point_stream = false;
+       bool found_reparse_stream = false;
        bool found_unnamed_data_stream = false;
-       struct wim_inode_stream *unnamed_stream_with_zero_hash = NULL;
 
        for (unsigned i = 0; i < inode->i_num_streams; i++) {
                struct wim_inode_stream *strm = &inode->i_streams[i];
 
                if (stream_is_named(strm)) {
-                       /* Named data stream  */
+                       /* Named extra stream entry */
                        strm->stream_type = STREAM_TYPE_DATA;
                } else if (i != 0 || !is_zero_hash(strm->_stream_hash)) {
-                       /* Unnamed stream in the extra stream entries, OR the
-                        * default stream in the dentry provided that it has a
-                        * nonzero hash.  */
+                       /* Unnamed extra stream entry or a nonzero main_hash */
                        if ((inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
-                           !found_reparse_point_stream) {
-                               found_reparse_point_stream = true;
+                           !found_reparse_stream) {
+                               found_reparse_stream = true;
                                strm->stream_type = STREAM_TYPE_REPARSE_POINT;
                        } else if (!found_unnamed_data_stream) {
                                found_unnamed_data_stream = true;
                                strm->stream_type = STREAM_TYPE_DATA;
-                       }
-               } else if (!unnamed_stream_with_zero_hash) {
-                       unnamed_stream_with_zero_hash = strm;
-               }
+                       } /* Else, too many unnamed streams were found. */
+
+               } /* Else, it's a zero main_hash. */
        }
 
-       if (unnamed_stream_with_zero_hash) {
-               int type = STREAM_TYPE_UNKNOWN;
-               if ((inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
-                   !found_reparse_point_stream) {
-                       type = STREAM_TYPE_REPARSE_POINT;
-               } else if (!found_unnamed_data_stream) {
-                       type = STREAM_TYPE_DATA;
-               }
-               unnamed_stream_with_zero_hash->stream_type = type;
+       /* If needed, use the zero main_hash. */
+       if (!found_reparse_stream && !found_unnamed_data_stream) {
+               inode->i_streams[0].stream_type =
+                       (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) ?
+                       STREAM_TYPE_REPARSE_POINT : STREAM_TYPE_DATA;
        }
 }
 
@@ -1219,7 +1220,7 @@ assign_stream_types_unencrypted(struct wim_inode *inode)
  */
 static int
 setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
-                   unsigned num_extra_streams, const u8 *default_hash,
+                   unsigned num_extra_streams, const u8 *main_hash,
                    u64 *offset_p)
 {
        const u8 *orig_p = p;
@@ -1233,13 +1234,13 @@ setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode,
                        return WIMLIB_ERR_NOMEM;
        }
 
-       /* Use the default hash field for the first stream  */
+       /* Use main_hash for the first stream. */
        inode->i_streams[0].stream_name = (utf16lechar *)NO_STREAM_NAME;
-       copy_hash(inode->i_streams[0]._stream_hash, default_hash);
+       copy_hash(inode->i_streams[0]._stream_hash, main_hash);
        inode->i_streams[0].stream_type = STREAM_TYPE_UNKNOWN;
        inode->i_streams[0].stream_id = 0;
 
-       /* Read the extra stream entries  */
+       /* Read the extra stream entries. */
        for (unsigned i = 1; i < inode->i_num_streams; i++) {
                struct wim_inode_stream *strm;
                const struct wim_extra_stream_entry_on_disk *disk_strm;
@@ -1452,7 +1453,7 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
                                  &buf[buf_len],
                                  inode,
                                  le16_to_cpu(disk_dentry->num_extra_streams),
-                                 disk_dentry->default_hash,
+                                 disk_dentry->main_hash,
                                  &offset);
        if (ret)
                goto err_free_dentry;
@@ -1679,6 +1680,105 @@ write_extra_stream_entry(u8 * restrict p, const utf16lechar * restrict name,
        return p;
 }
 
+/*
+ * Write the stream references for a WIM dentry.  To be compatible with DISM, we
+ * follow the below rules:
+ *
+ * 1. If the file has FILE_ATTRIBUTE_ENCRYPTED, then only the EFSRPC_RAW_DATA
+ *    stream is stored.  Otherwise, the streams that are stored are:
+ *    - Reparse stream if the file has FILE_ATTRIBUTE_REPARSE_POINT
+ *    - Unnamed data stream if the file doesn't have FILE_ATTRIBUTE_DIRECTORY
+ *    - Named data streams
+ *
+ * 2. If only one stream is being stored and it is the EFSRPC_RAW_DATA, unnamed
+ *    data, or reparse stream, then its hash goes in main_hash, and no extra
+ *    stream entries are stored.  Otherwise, *all* streams go in the extra
+ *    stream entries, and main_hash is left zeroed!
+ *
+ * 3. If both the reparse stream and unnamed data stream are being stored, then
+ *    the reparse stream comes first.
+ *
+ * 4. The unnamed stream(s) come before the named stream(s).  (Actually, DISM
+ *    puts the named streams between the first and second unnamed streams, but
+ *    this is incompatible with itself...  Tested with DISM 10.0.20348.681.)
+ *
+ * wimlib v1.14.1 and earlier behaved slightly differently for directories.
+ * First, wimlib always put the hash of the reparse stream in an extra stream
+ * entry, never in main_hash.  This difference vs. DISM went unnoticed for a
+ * long time, but eventually it was found that it broke the Windows 8 setup
+ * wizard.  Second, when a directory had any extra streams, wimlib created an
+ * extra stream entry to represent the (empty) unnamed data stream.  However,
+ * DISM now rejects that (though I think it used to accept it).  There isn't
+ * really any such thing as "unnamed data stream" for a directory.
+ *
+ * Keep this in sync with dentry_out_total_length()!
+ */
+static u8 *
+write_dentry_streams(const struct wim_inode *inode,
+                    struct wim_dentry_on_disk *disk_dentry, u8 *p)
+{
+       const u8 *unnamed_data_stream_hash = zero_hash;
+       const u8 *reparse_stream_hash = zero_hash;
+       const u8 *efsrpc_stream_hash = zero_hash;
+       const u8 *unnamed_stream_hashes[2] = { zero_hash };
+       unsigned num_unnamed_streams = 0;
+       unsigned num_named_streams = 0;
+
+       for (unsigned i = 0; i < inode->i_num_streams; i++) {
+               const struct wim_inode_stream *strm = &inode->i_streams[i];
+
+               switch (strm->stream_type) {
+               case STREAM_TYPE_DATA:
+                       if (stream_is_named(strm))
+                               num_named_streams++;
+                       else
+                               unnamed_data_stream_hash = stream_hash(strm);
+                       break;
+               case STREAM_TYPE_REPARSE_POINT:
+                       reparse_stream_hash = stream_hash(strm);
+                       break;
+               case STREAM_TYPE_EFSRPC_RAW_DATA:
+                       efsrpc_stream_hash = stream_hash(strm);
+                       break;
+               }
+       }
+
+       if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+               unnamed_stream_hashes[num_unnamed_streams++] = efsrpc_stream_hash;
+               num_named_streams = 0;
+       } else {
+               if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+                       unnamed_stream_hashes[num_unnamed_streams++] = reparse_stream_hash;
+               if (!(inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
+                       unnamed_stream_hashes[num_unnamed_streams++] = unnamed_data_stream_hash;
+       }
+
+       if (num_unnamed_streams <= 1 && num_named_streams == 0) {
+               /* No extra stream entries are needed. */
+               copy_hash(disk_dentry->main_hash, unnamed_stream_hashes[0]);
+               disk_dentry->num_extra_streams = 0;
+               return p;
+       }
+
+       /* Else, all streams go in extra stream entries. */
+       copy_hash(disk_dentry->main_hash, zero_hash);
+       wimlib_assert(num_unnamed_streams + num_named_streams <= 0xFFFF);
+       disk_dentry->num_extra_streams = cpu_to_le16(num_unnamed_streams +
+                                                    num_named_streams);
+       for (unsigned i = 0; i < num_unnamed_streams; i++)
+               p = write_extra_stream_entry(p, NO_STREAM_NAME,
+                                            unnamed_stream_hashes[i]);
+       for (unsigned i = 0; i < inode->i_num_streams; i++) {
+               const struct wim_inode_stream *strm = &inode->i_streams[i];
+
+               if (stream_is_named_data_stream(strm)) {
+                       p = write_extra_stream_entry(p, strm->stream_name,
+                                                    stream_hash(strm));
+               }
+       }
+       return p;
+}
+
 /*
  * Write a WIM dentry to an output buffer.
  *
@@ -1752,77 +1852,11 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
 
        disk_dentry->length = cpu_to_le64(p - orig_p);
 
-       /* Streams  */
-
-       if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
-               const struct wim_inode_stream *efs_strm;
-               const u8 *efs_hash;
-
-               efs_strm = inode_get_unnamed_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA);
-               efs_hash = efs_strm ? stream_hash(efs_strm) : zero_hash;
-               copy_hash(disk_dentry->default_hash, efs_hash);
-               disk_dentry->num_extra_streams = cpu_to_le16(0);
-       } else {
-               /*
-                * Extra stream entries:
-                *
-                * - Use one extra stream entry for each named data stream
-                * - Use one extra stream entry for the unnamed data stream when there is either:
-                *      - a reparse point stream
-                *      - at least one named data stream (for Windows PE bug workaround)
-                * - Use one extra stream entry for the reparse point stream if there is one
-                */
-               bool have_named_data_stream = false;
-               bool have_reparse_point_stream = false;
-               const u8 *unnamed_data_stream_hash = zero_hash;
-               const u8 *reparse_point_hash;
-               for (unsigned i = 0; i < inode->i_num_streams; i++) {
-                       const struct wim_inode_stream *strm = &inode->i_streams[i];
-                       if (strm->stream_type == STREAM_TYPE_DATA) {
-                               if (stream_is_named(strm))
-                                       have_named_data_stream = true;
-                               else
-                                       unnamed_data_stream_hash = stream_hash(strm);
-                       } else if (strm->stream_type == STREAM_TYPE_REPARSE_POINT) {
-                               have_reparse_point_stream = true;
-                               reparse_point_hash = stream_hash(strm);
-                       }
-               }
-
-               if (unlikely(have_reparse_point_stream || have_named_data_stream)) {
-
-                       unsigned num_extra_streams = 0;
-
-                       copy_hash(disk_dentry->default_hash, zero_hash);
-
-                       if (have_reparse_point_stream) {
-                               p = write_extra_stream_entry(p, NO_STREAM_NAME,
-                                                            reparse_point_hash);
-                               num_extra_streams++;
-                       }
-
-                       p = write_extra_stream_entry(p, NO_STREAM_NAME,
-                                                    unnamed_data_stream_hash);
-                       num_extra_streams++;
-
-                       for (unsigned i = 0; i < inode->i_num_streams; i++) {
-                               const struct wim_inode_stream *strm = &inode->i_streams[i];
-                               if (stream_is_named_data_stream(strm)) {
-                                       p = write_extra_stream_entry(p, strm->stream_name,
-                                                                    stream_hash(strm));
-                                       num_extra_streams++;
-                               }
-                       }
-                       wimlib_assert(num_extra_streams <= 0xFFFF);
-
-                       disk_dentry->num_extra_streams = cpu_to_le16(num_extra_streams);
-               } else {
-                       copy_hash(disk_dentry->default_hash, unnamed_data_stream_hash);
-                       disk_dentry->num_extra_streams = cpu_to_le16(0);
-               }
-       }
-
-       return p;
+       /*
+        * Set disk_dentry->main_hash and disk_dentry->num_extra_streams,
+        * and write any extra stream entries that are needed.
+        */
+       return write_dentry_streams(inode, disk_dentry, p);
 }
 
 static int
index c80412f5cad1f9f18478b3e4327b8fd431642d2c..81a13f976885b28ceb33b1442aa926136e2b1b1e 100644 (file)
@@ -56,9 +56,6 @@
 
 
 /*- Macros -*/
-#define SWAP swap
-#define MIN min
-#define MAX max
 
 #define STACK_PUSH(_a, _b, _c, _d)\
   do {\
index 9337c9a16e6a277b2f4880058103b4cdd1377eee..41917c9d917cb56a51d83812b175913488388145 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * encoding.c - UTF-8 and UTF-16LE codecs and utility functions
  *
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -14,7 +14,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -220,8 +220,7 @@ convert_string(const u8 * const in, const size_t in_nbytes,
               decode_codepoint_fn decode_codepoint,
               encode_codepoint_fn encode_codepoint)
 {
-       const u8 * const in_end = in + in_nbytes;
-       const u8 *p_in;
+       size_t i;
        u8 *p_out;
        size_t out_nbytes = 0;
        u8 *out;
@@ -229,8 +228,8 @@ convert_string(const u8 * const in, const size_t in_nbytes,
        u32 c;
 
        /* Validate the input string and compute the output size. */
-       for (p_in = in; p_in != in_end; ) {
-               p_in += (*decode_codepoint)(p_in, in_end - p_in, true, &c);
+       for (i = 0; i < in_nbytes; ) {
+               i += (*decode_codepoint)(&in[i], in_nbytes - i, true, &c);
                if (unlikely(c == INVALID_CODEPOINT)) {
                        errno = EILSEQ;
                        return ilseq_err;
@@ -244,8 +243,9 @@ convert_string(const u8 * const in, const size_t in_nbytes,
                return WIMLIB_ERR_NOMEM;
 
        /* Do the conversion. */
-       for (p_in = in, p_out = out; p_in != in_end; ) {
-               p_in += (*decode_codepoint)(p_in, in_end - p_in, false, &c);
+       p_out = out;
+       for (i = 0; i < in_nbytes; ) {
+               i += (*decode_codepoint)(&in[i], in_nbytes - i, false, &c);
                p_out += (*encode_codepoint)(c, p_out);
        }
 
@@ -462,3 +462,22 @@ utf16le_len_chars(const utf16lechar *s)
 {
        return utf16le_len_bytes(s) / sizeof(utf16lechar);
 }
+
+#ifdef ENABLE_TEST_SUPPORT
+
+#include "wimlib/test_support.h"
+
+WIMLIBAPI int
+wimlib_utf8_to_utf16le(const char *in, size_t in_nbytes,
+                      utf16lechar **out_ret, size_t *out_nbytes_ret)
+{
+       return utf8_to_utf16le(in, in_nbytes, out_ret, out_nbytes_ret);
+}
+
+WIMLIBAPI int
+wimlib_utf16le_to_utf8(const utf16lechar *in, size_t in_nbytes,
+                      char **out_ret, size_t *out_nbytes_ret)
+{
+       return utf16le_to_utf8(in, in_nbytes, out_ret, out_nbytes_ret);
+}
+#endif /* ENABLE_TEST_SUPPORT */
index cccb815f58373483f44c398bc7e4b851e3c4a6a5..ff3d00bbbbf4a8f73c86506406c66122d285fcec 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -46,7 +46,6 @@
 #include "wimlib/util.h"
 #include "wimlib/win32.h"
 
-#ifdef ENABLE_ERROR_MESSAGES
 bool wimlib_print_errors = false;
 FILE *wimlib_error_file = NULL; /* Set in wimlib_global_init() */
 static bool wimlib_owns_error_file = false;
@@ -69,7 +68,7 @@ wimlib_vmsg(const tchar *tag, const tchar *format, va_list va, bool perror)
                                 T("unknown error (errno=%d)"),
                                 errno_save);
                }
-       #ifdef WIN32
+       #ifdef _WIN32
                if (errno_save == EBUSY)
                        tstrcpy(buf, T("Resource busy"));
        #endif
@@ -119,7 +118,6 @@ wimlib_warning_with_errno(const tchar *format, ...)
        wimlib_vmsg(T("\r[WARNING] "), format, va, true);
        va_end(va);
 }
-#endif /* ENABLE_ERROR_MESSAGES */
 
 void
 print_byte_field(const u8 *field, size_t len, FILE *out)
@@ -131,37 +129,27 @@ print_byte_field(const u8 *field, size_t len, FILE *out)
 WIMLIBAPI int
 wimlib_set_print_errors(bool show_error_messages)
 {
-#ifdef ENABLE_ERROR_MESSAGES
        wimlib_print_errors = show_error_messages;
-#else
-       if (show_error_messages)
-               return WIMLIB_ERR_UNSUPPORTED;
-#endif
        return 0;
 }
 
 WIMLIBAPI int
 wimlib_set_error_file(FILE *fp)
 {
-#ifdef ENABLE_ERROR_MESSAGES
        if (wimlib_owns_error_file)
                fclose(wimlib_error_file);
        wimlib_error_file = fp;
        wimlib_print_errors = (fp != NULL);
        wimlib_owns_error_file = false;
        return 0;
-#else
-       return WIMLIB_ERR_UNSUPPORTED;
-#endif
 }
 
 WIMLIBAPI int
 wimlib_set_error_file_by_name(const tchar *path)
 {
-#ifdef ENABLE_ERROR_MESSAGES
        FILE *fp;
 
-#ifdef __WIN32__
+#ifdef _WIN32
        fp = win32_open_logfile(path);
 #else
        fp = fopen(path, "a");
@@ -171,9 +159,6 @@ wimlib_set_error_file_by_name(const tchar *path)
        wimlib_set_error_file(fp);
        wimlib_owns_error_file = true;
        return 0;
-#else
-       return WIMLIB_ERR_UNSUPPORTED;
-#endif
 }
 
 static const tchar * const error_strings[] = {
index b8d19dc224f37f234f5a220b695c709af0df2cee..1dff266d4228df6107803203f6664cdcb86fe01e 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -179,6 +179,26 @@ wimlib_export_image(WIMStruct *src_wim,
        /* Enable rollbacks  */
        for_blob_in_table(dest_wim->blob_table, blob_set_not_exported, NULL);
 
+       /* Forbid exports where the destination WIM already contains image(s)
+        * with the requested name(s).  However, allow multi-image exports where
+        * there is a duplication among the source names only.  */
+       if (!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES)) {
+               for (src_image = start_src_image;
+                    src_image <= end_src_image;
+                    src_image++)
+               {
+                       const tchar *name = dest_name ? dest_name :
+                               wimlib_get_image_name(src_wim, src_image);
+
+                       if (wimlib_image_name_in_use(dest_wim, name)) {
+                               ERROR("There is already an image named \"%"TS"\" "
+                                     "in the destination WIM", name);
+                               ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
+                               goto out_rollback;
+                       }
+               }
+       }
+
        /* Export each requested image.  */
        for (src_image = start_src_image;
             src_image <= end_src_image;
@@ -204,14 +224,6 @@ wimlib_export_image(WIMStruct *src_wim,
                else
                        next_dest_description = wimlib_get_image_description(src_wim, src_image);
 
-               /* Check for name conflict.  */
-               if (wimlib_image_name_in_use(dest_wim, next_dest_name)) {
-                       ERROR("There is already an image named \"%"TS"\" "
-                             "in the destination WIM", next_dest_name);
-                       ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
-                       goto out_rollback;
-               }
-
                /* Load metadata for source image into memory.  */
                ret = select_wim_image(src_wim, src_image);
                if (ret)
index 49564f4f20fcb266498a7af04b10b853ea094061..24b53cbea10a2e9253acac22fe8867cd5929501f 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 /*
@@ -72,6 +72,7 @@
 /* Keep in sync with wimlib.h  */
 #define WIMLIB_EXTRACT_MASK_PUBLIC                             \
        (WIMLIB_EXTRACT_FLAG_NTFS                       |       \
+        WIMLIB_EXTRACT_FLAG_RECOVER_DATA               |       \
         WIMLIB_EXTRACT_FLAG_UNIX_DATA                  |       \
         WIMLIB_EXTRACT_FLAG_NO_ACLS                    |       \
         WIMLIB_EXTRACT_FLAG_STRICT_ACLS                |       \
@@ -310,7 +311,9 @@ read_blobs_from_pipe(struct apply_ctx *ctx, const struct read_blob_callbacks *cb
                    && (blob->out_refcnt))
                {
                        wim_reshdr_to_desc_and_blob(&reshdr, ctx->wim, &rdesc, blob);
-                       ret = read_blob_with_sha1(blob, cbs);
+                       ret = read_blob_with_sha1(blob, cbs,
+                                                 ctx->extract_flags &
+                                                 WIMLIB_EXTRACT_FLAG_RECOVER_DATA);
                        blob_unset_is_located_in_wim_resource(blob);
                        if (ret)
                                return ret;
@@ -384,7 +387,7 @@ create_temporary_file(struct filedes *fd_ret, tchar **name_ret)
        tchar *name;
        int raw_fd;
 
-#ifdef __WIN32__
+#ifdef _WIN32
 retry:
        name = _wtempnam(NULL, L"wimlib");
        if (!name) {
@@ -397,7 +400,7 @@ retry:
                FREE(name);
                goto retry;
        }
-#else /* __WIN32__ */
+#else /* _WIN32 */
        const char *tmpdir = getenv("TMPDIR");
        if (!tmpdir)
                tmpdir = P_tmpdir;
@@ -406,7 +409,7 @@ retry:
                return WIMLIB_ERR_NOMEM;
        sprintf(name, "%s/wimlibXXXXXX", tmpdir);
        raw_fd = mkstemp(name);
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
        if (raw_fd < 0) {
                ERROR_WITH_ERRNO("Failed to create temporary file "
@@ -504,18 +507,39 @@ extract_from_tmpfile(const tchar *tmpfile_name,
 
        for (u32 i = 0; i < orig_blob->out_refcnt; i++) {
                tmpfile_blob.inline_blob_extraction_targets[0] = targets[i];
-               ret = read_blob_with_cbs(&tmpfile_blob, cbs);
+               ret = read_blob_with_cbs(&tmpfile_blob, cbs, false);
                if (ret)
                        return ret;
        }
        return 0;
 }
 
+static void
+warn_about_corrupted_file(struct wim_dentry *dentry,
+                         const struct wim_inode_stream *stream)
+{
+       WARNING("Corruption in %s\"%"TS"\"!  Extracting anyway since data recovery mode is enabled.",
+               stream_is_unnamed_data_stream(stream) ? "" : "alternate stream of ",
+               dentry_full_path(dentry));
+}
+
 static int
 end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
 {
        struct apply_ctx *ctx = _ctx;
 
+       if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RECOVER_DATA) &&
+           !status && blob->corrupted) {
+               const struct blob_extraction_target *targets =
+                       blob_extraction_targets(blob);
+               for (u32 i = 0; i < blob->out_refcnt; i++) {
+                       struct wim_dentry *dentry =
+                               inode_first_extraction_dentry(targets[i].inode);
+
+                       warn_about_corrupted_file(dentry, targets[i].stream);
+               }
+       }
+
        if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
                filedes_close(&ctx->tmpfile_fd);
                if (!status)
@@ -560,10 +584,15 @@ extract_blob_list(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs)
        if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
                return read_blobs_from_pipe(ctx, &wrapper_cbs);
        } else {
+               int flags = VERIFY_BLOB_HASHES;
+
+               if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RECOVER_DATA)
+                       flags |= RECOVER_DATA;
+
                return read_blob_list(&ctx->blob_list,
                                      offsetof(struct blob_descriptor,
                                               extraction_list),
-                                     &wrapper_cbs, VERIFY_BLOB_HASHES);
+                                     &wrapper_cbs, flags);
        }
 }
 
@@ -574,11 +603,13 @@ extract_blob_list(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs)
  * unnamed data stream only.  */
 static int
 extract_dentry_to_stdout(struct wim_dentry *dentry,
-                        const struct blob_table *blob_table)
+                        const struct blob_table *blob_table, int extract_flags)
 {
        struct wim_inode *inode = dentry->d_inode;
        struct blob_descriptor *blob;
        struct filedes _stdout;
+       bool recover = (extract_flags & WIMLIB_EXTRACT_FLAG_RECOVER_DATA);
+       int ret;
 
        if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
                                   FILE_ATTRIBUTE_DIRECTORY |
@@ -598,15 +629,23 @@ extract_dentry_to_stdout(struct wim_dentry *dentry,
        }
 
        filedes_init(&_stdout, STDOUT_FILENO);
-       return extract_blob_to_fd(blob, &_stdout);
+       ret = extract_blob_to_fd(blob, &_stdout, recover);
+       if (ret)
+               return ret;
+       if (recover && blob->corrupted)
+               warn_about_corrupted_file(dentry,
+                                         inode_get_unnamed_data_stream(inode));
+       return 0;
 }
 
 static int
 extract_dentries_to_stdout(struct wim_dentry **dentries, size_t num_dentries,
-                          const struct blob_table *blob_table)
+                          const struct blob_table *blob_table,
+                          int extract_flags)
 {
        for (size_t i = 0; i < num_dentries; i++) {
-               int ret = extract_dentry_to_stdout(dentries[i], blob_table);
+               int ret = extract_dentry_to_stdout(dentries[i], blob_table,
+                                                  extract_flags);
                if (ret)
                        return ret;
        }
@@ -765,7 +804,7 @@ destroy_blob_list(struct list_head *blob_list)
                        FREE(blob->blob_extraction_targets);
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 static const utf16lechar replacement_char = cpu_to_le16(0xfffd);
 #else
 static const utf16lechar replacement_char = cpu_to_le16('?');
@@ -780,7 +819,7 @@ file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
                return true;
        for (i = 0; i < num_chars; i++) {
                switch (le16_to_cpu(name[i])) {
-       #ifdef __WIN32__
+       #ifdef _WIN32
                case '\x01'...'\x1F':
                case '\\':
                case ':':
@@ -1428,7 +1467,7 @@ select_apply_operations(int extract_flags)
        if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
                return &ntfs_3g_apply_ops;
 #endif
-#ifdef __WIN32__
+#ifdef _WIN32
        return &win32_apply_ops;
 #else
        return &unix_apply_ops;
@@ -1446,7 +1485,8 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
 
        if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
                ret = extract_dentries_to_stdout(trees, num_trees,
-                                                wim->blob_table);
+                                                wim->blob_table,
+                                                extract_flags);
                goto out;
        }
 
@@ -1585,7 +1625,7 @@ mkdir_if_needed(const tchar *target)
        if (errno == EEXIST)
                return 0;
 
-#ifdef __WIN32__
+#ifdef _WIN32
        /* _wmkdir() fails with EACCES if called on a drive root directory.  */
        if (errno == EACCES)
                return 0;
@@ -1624,7 +1664,7 @@ check_extract_flags(const WIMStruct *wim, int *extract_flags_p)
 #endif
 
        if (extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
-#ifdef __WIN32__
+#ifdef _WIN32
                if (!wim->filename)
                        return WIMLIB_ERR_NO_FILENAME;
 #else
@@ -1638,7 +1678,7 @@ check_extract_flags(const WIMStruct *wim, int *extract_flags_p)
                             WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K |
                             WIMLIB_EXTRACT_FLAG_COMPACT_LZX))
        {
-       #ifdef __WIN32__
+       #ifdef _WIN32
                int count = 0;
                count += ((extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K) != 0);
                count += ((extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K) != 0);
@@ -1831,7 +1871,7 @@ extract_single_image(WIMStruct *wim, int image,
 }
 
 static const tchar * const filename_forbidden_chars =
-#ifdef __WIN32__
+#ifdef _WIN32
 T("<>:\"/\\|?*");
 #else
 T("/");
index 8fbd01f9669040b9b1dcf6f5abe2ad9e81478208..4cd71ec43f387864182a5e83339d66586afd067e 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -30,7 +30,7 @@
 #include "wimlib/file_io.h"
 #include "wimlib/util.h"
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  include "wimlib/win32.h"
 #  define read win32_read
 #  define write win32_write
index cf19396317ca3af6da227c6b4b60d1bd180b4b06..1a47690028959c7f32c78986ca58b03085be57b8 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2015 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -62,7 +62,7 @@
 int
 read_wim_header(WIMStruct *wim, struct wim_header *hdr)
 {
-       struct wim_header_disk disk_hdr _aligned_attribute(8);
+       struct wim_header_disk disk_hdr __attribute__((aligned(8)));
        struct filedes *in_fd = &wim->in_fd;
        const tchar *filename = wim->filename;
        int ret;
@@ -144,6 +144,18 @@ read_wim_header(WIMStruct *wim, struct wim_header *hdr)
        get_wim_reshdr(&disk_hdr.boot_metadata_reshdr, &hdr->boot_metadata_reshdr);
        hdr->boot_idx = le32_to_cpu(disk_hdr.boot_idx);
        get_wim_reshdr(&disk_hdr.integrity_table_reshdr, &hdr->integrity_table_reshdr);
+
+       /*
+        * Prevent huge memory allocations when processing fuzzed files.  The
+        * blob table, XML data, and integrity table are all uncompressed, so
+        * they should never be larger than the WIM file itself.
+        */
+       if (wim->file_size > 0 &&
+           (hdr->blob_table_reshdr.uncompressed_size > wim->file_size ||
+            hdr->xml_data_reshdr.uncompressed_size > wim->file_size ||
+            hdr->integrity_table_reshdr.uncompressed_size > wim->file_size))
+               return WIMLIB_ERR_INVALID_HEADER;
+
        return 0;
 
 read_error:
@@ -158,7 +170,7 @@ int
 write_wim_header(const struct wim_header *hdr, struct filedes *out_fd,
                 off_t offset)
 {
-       struct wim_header_disk disk_hdr _aligned_attribute(8);
+       struct wim_header_disk disk_hdr __attribute__((aligned(8)));
        int ret;
 
        disk_hdr.magic = cpu_to_le64(hdr->magic);
index 68e88c05fd6a21444c0ce716d68883d2c6cab001..40fe5679a6a3eeac8f6799ee091ca74dea8b37a5 100644 (file)
@@ -21,7 +21,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -485,7 +485,7 @@ int
 blob_not_found_error(const struct wim_inode *inode, const u8 *hash)
 {
        if (wimlib_print_errors) {
-               tchar hashstr[SHA1_HASH_SIZE * 2 + 1];
+               tchar hashstr[SHA1_HASH_STRING_LEN];
 
                sprint_hash(hash, hashstr);
 
index 8e1579db014617ac94bad6778d83ce3409453d9b..f507c9872ffe843c5375830dedacd4bb60592357 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 5955580f1cbda2526423cac140a23223944024f1..f6842f8ad81a5c1bbb5fe642ddc2312695a2ec10 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 7494c3245057ff9ef0f6a582b6add37ef62ce6f6..47d3244b53d7c11deff2f88cffc5fa2c4925dcc2 100644 (file)
@@ -20,7 +20,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -52,14 +52,14 @@ struct integrity_table {
        u32 num_entries;
        u32 chunk_size;
        u8  sha1sums[][20];
-} _packed_attribute;
+} __attribute__((packed));
 
 static int
 calculate_chunk_sha1(struct filedes *in_fd, size_t this_chunk_size,
                     off_t offset, u8 sha1_md[])
 {
        u8 buf[BUFFER_SIZE];
-       SHA_CTX ctx;
+       struct sha1_ctx ctx;
        size_t bytes_remaining;
        size_t bytes_to_read;
        int ret;
@@ -78,7 +78,7 @@ calculate_chunk_sha1(struct filedes *in_fd, size_t this_chunk_size,
                bytes_remaining -= bytes_to_read;
                offset += bytes_to_read;
        } while (bytes_remaining);
-       sha1_final(sha1_md, &ctx);
+       sha1_final(&ctx, sha1_md);
        return 0;
 }
 
index 940fc24b8e2c96d3635c6eb31e4d4b8407a1ec34..85857043150153513b1f2ccb5aec1ab1a7df304e 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -269,11 +269,6 @@ wimlib_iterate_dir_tree(WIMStruct *wim, int image, const tchar *_path,
        path = canonicalize_wim_path(_path);
        if (path == NULL)
                return WIMLIB_ERR_NOMEM;
-
-       ret = wim_checksum_unhashed_blobs(wim);
-       if (ret)
-               return ret;
-
        struct image_iterate_dir_tree_ctx ctx = {
                .path = path,
                .flags = flags,
index 27e7a02c473d877aa51999e20004b4b83183cfd7..00fd292b7117f4dafe974fe7aacf09e328fbc12b 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index b5a14e9398656be8c394b92ac525a28d7a4805bd..a1a1c02ae61e9059c7edbc13015991dd727154f1 100644 (file)
@@ -4,21 +4,28 @@
  * A match-finder for Lempel-Ziv compression based on bottom-up construction and
  * traversal of the Longest Common Prefix (LCP) interval tree.
  *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * Copyright 2022 Eric Biggers
+ *
+ * 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.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -368,8 +375,8 @@ lcpit_advance_one_byte(const u32 cur_pos,
 static void
 expand_SA(void *p, u32 n)
 {
-       typedef u32 _may_alias_attribute aliased_u32_t;
-       typedef u64 _may_alias_attribute aliased_u64_t;
+       typedef u32 __attribute__((may_alias)) aliased_u32_t;
+       typedef u64 __attribute__((may_alias)) aliased_u64_t;
 
        aliased_u32_t *SA = p;
        aliased_u64_t *SA64 = p;
index fb13a4358c5469a79d0c4c5e101c03ee2f9e59db..50176847f8b042cd20b1600342c5ee0fad0805da 100644 (file)
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#include "wimlib/cpu_features.h"
 #include "wimlib/lzms_common.h"
 #include "wimlib/unaligned.h"
-#include "wimlib/x86_cpu_features.h"
 
 #ifdef __x86_64__
 #  include <emmintrin.h>
@@ -614,7 +614,7 @@ lzms_x86_filter(u8 data[restrict], s32 size,
        tail_ptr = &data[size - 16];
 
 #ifdef __x86_64__
-       if (x86_have_cpu_feature(X86_CPU_FEATURE_SSE4_2)) {
+       if (cpu_features & X86_CPU_FEATURE_SSE4_2) {
                u8 saved_byte = *tail_ptr;
                *tail_ptr = 0xE8;
                for (;;) {
index 09999957278befa0018c86f670506fba0cdb3120..e55a32b890f3ada6354092944fef9b57cb22e6e5 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -32,9 +32,8 @@
 #include "wimlib/compressor_ops.h"
 #include "wimlib/error.h"
 #include "wimlib/lcpit_matchfinder.h"
-#include "wimlib/lz_extend.h"
-#include "wimlib/lz_hash.h"
 #include "wimlib/lzms_common.h"
+#include "wimlib/matchfinder_common.h"
 #include "wimlib/unaligned.h"
 #include "wimlib/util.h"
 
@@ -170,7 +169,7 @@ struct lzms_item {
 #define DELTA_SOURCE_POWER_SHIFT       28
 #define DELTA_SOURCE_RAW_OFFSET_MASK   (((u32)1 << DELTA_SOURCE_POWER_SHIFT) - 1)
 
-static _unused_attribute void
+static void __attribute__((unused))
 check_that_powers_fit_in_bitfield(void)
 {
        STATIC_ASSERT(LZMS_NUM_DELTA_POWER_SYMS <= (1 << (31 - DELTA_SOURCE_POWER_SHIFT)));
@@ -199,7 +198,7 @@ struct lzms_adaptive_state {
        u8 lz_rep_states[LZMS_NUM_LZ_REP_DECISIONS];
        u8 delta_state;
        u8 delta_rep_states[LZMS_NUM_DELTA_REP_DECISIONS];
-} _aligned_attribute(64);
+} __attribute__((aligned(64)));
 
 /*
  * This structure represents a byte position in the preprocessed input data and
@@ -252,7 +251,7 @@ struct lzms_optimum_node {
         * maintained per-position and are only updated occasionally.
         */
        struct lzms_adaptive_state state;
-} _aligned_attribute(64);
+} __attribute__((aligned(64)));
 
 /* The main compressor structure  */
 struct lzms_compressor {
@@ -976,7 +975,7 @@ static const u32 lzms_bit_costs[LZMS_PROBABILITY_DENOMINATOR + 1] = {
        1
 };
 
-static _unused_attribute void
+static void __attribute__((unused))
 check_cost_shift(void)
 {
        /* lzms_bit_costs is hard-coded to the current COST_SHIFT.  */
index 4dd36627b37957faae16a14f639f27bf313e9f20..c9b5c983a874737395691336f83a44e5f20640b2 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 /*
@@ -386,7 +386,7 @@ lzms_ensure_bits(struct lzms_input_bitstream *is, unsigned num_bits)
 
        avail = BITBUF_NBITS - is->bitsleft;
 
-       if (UNALIGNED_ACCESS_IS_FAST && CPU_IS_LITTLE_ENDIAN &&
+       if (UNALIGNED_ACCESS_IS_FAST && CPU_IS_LITTLE_ENDIAN() &&
            WORDBYTES == 8 && likely(is->next - is->begin >= 8))
        {
                is->next -= (avail & ~15) >> 3;
index fe201a42e5ab4ab67ca6f6f6367aa4cbdafb491b..48a34614362d505c5b555b479b585d334546ed6c 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index ac87a807bd9a3b726e891c99b1a0150ec2cba868..02498bf5f6b7b6cc51b6563a7b1fdf2892ffcaa0 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 
 #include "wimlib/compress_common.h"
 #include "wimlib/compressor_ops.h"
 #include "wimlib/error.h"
-#include "wimlib/lz_extend.h"
 #include "wimlib/lzx_common.h"
 #include "wimlib/unaligned.h"
 #include "wimlib/util.h"
@@ -288,7 +287,7 @@ struct lzx_sequence {
        u32 adjusted_offset_and_mainsym;
 #define SEQ_MAINSYM_BITS       10
 #define SEQ_MAINSYM_MASK       (((u32)1 << SEQ_MAINSYM_BITS) - 1)
-} _aligned_attribute(8);
+} __attribute__((aligned(8)));
 
 /*
  * This structure represents a byte position in the input buffer and a node in
@@ -334,7 +333,7 @@ struct lzx_optimum_node {
 #  define OPTIMUM_GAP_MATCH 0x80000000
 #endif
 
-} _aligned_attribute(8);
+} __attribute__((aligned(8)));
 
 /* The cost model for near-optimal parsing */
 struct lzx_costs {
@@ -1227,7 +1226,7 @@ lzx_flush_block(struct lzx_compressor *c, struct lzx_output_bitstream *os,
  * but rather we combine many symbols into a single "observation type".  For
  * literals we only look at the high bits and low bits, and for matches we only
  * look at whether the match is long or not.  The assumption is that for typical
- * "real" data, places that are good block boundaries will tend to be noticable
+ * "real" data, places that are good block boundaries will tend to be noticeable
  * based only on changes in these aggregate frequencies, without looking for
  * subtle differences in individual symbols.  For example, a change from ASCII
  * bytes to non-ASCII bytes, or from few matches (generally less compressible)
@@ -1316,7 +1315,7 @@ lzx_should_end_block(struct lzx_block_split_stats *stats)
  */
 struct lzx_lru_queue {
        u64 R;
-} _aligned_attribute(8);
+} __attribute__((aligned(8)));
 
 #define LZX_QUEUE_OFFSET_SHIFT 21
 #define LZX_QUEUE_OFFSET_MASK  (((u64)1 << LZX_QUEUE_OFFSET_SHIFT) - 1)
@@ -2273,7 +2272,7 @@ lzx_compress_near_optimal(struct lzx_compressor * restrict c,
                        } else {
                                /* Don't search for matches at this position. */
                                CALL_BT_MF(is_16_bit, c,
-                                          bt_matchfinder_skip_position,
+                                          bt_matchfinder_skip_byte,
                                           in_begin,
                                           in_next - in_begin,
                                           nice_len,
@@ -2569,7 +2568,7 @@ lzx_compress_lazy(struct lzx_compressor * restrict c,
                        cur_len = CALL_HC_MF(is_16_bit, c,
                                             hc_matchfinder_longest_match,
                                             in_begin,
-                                            in_next - in_begin,
+                                            in_next,
                                             2,
                                             max_len,
                                             nice_len,
@@ -2646,7 +2645,7 @@ lzx_compress_lazy(struct lzx_compressor * restrict c,
                        next_len = CALL_HC_MF(is_16_bit, c,
                                              hc_matchfinder_longest_match,
                                              in_begin,
-                                             in_next - in_begin,
+                                             in_next,
                                              cur_len - 2,
                                              max_len,
                                              nice_len,
@@ -2707,13 +2706,14 @@ lzx_compress_lazy(struct lzx_compressor * restrict c,
                        lzx_choose_match(c, cur_len, cur_adjusted_offset,
                                         recent_offsets, is_16_bit,
                                         &litrunlen, &next_seq);
-                       in_next = CALL_HC_MF(is_16_bit, c,
-                                            hc_matchfinder_skip_positions,
-                                            in_begin,
-                                            in_next - in_begin,
-                                            in_end - in_begin,
-                                            skip_len,
-                                            next_hashes);
+                       CALL_HC_MF(is_16_bit, c,
+                                  hc_matchfinder_skip_bytes,
+                                  in_begin,
+                                  in_next,
+                                  in_end,
+                                  skip_len,
+                                  next_hashes);
+                       in_next += skip_len;
 
                        /* Keep going until it's time to end the block. */
                } while (in_next < in_max_block_end &&
index 5747d327270717ae6892c257374088e4dfec789d..ba923fdd3aeb7e51b0238104f494ed07d7c1e64a 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 /*
@@ -115,7 +115,7 @@ struct lzx_decompressor {
         * bits of aligned offset blocks */
        u8 extra_offset_bits_minus_aligned[LZX_MAX_OFFSET_SLOTS];
 
-} _aligned_attribute(DECODE_TABLE_ALIGNMENT);
+} __attribute__((aligned(DECODE_TABLE_ALIGNMENT)));
 
 /* Read a Huffman-encoded symbol using the precode. */
 static forceinline unsigned
index 4cd53032cbbca435f3a0c3b824acc61fe8303e16..71d3d6d03802f49262865b2ad0916538426e079f 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -79,13 +79,25 @@ read_metadata_resource(struct wim_image_metadata *imd)
 
        metadata_blob = imd->metadata_blob;
 
+       /*
+        * Prevent huge memory allocations when processing fuzzed files.  The
+        * case of metadata resources is tough, since a metadata resource can
+        * legitimately decompress to many times the size of the WIM file
+        * itself, e.g. in the case of an image containing many empty files with
+        * similar long filenames.  Arbitrarily choose 512x as a generous limit.
+        */
+       if (metadata_blob->blob_location == BLOB_IN_WIM &&
+           metadata_blob->rdesc->wim->file_size > 0 &&
+           metadata_blob->size / 512 > metadata_blob->rdesc->wim->file_size)
+               return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
+
        /* Read the metadata resource into memory.  (It may be compressed.)  */
        ret = read_blob_into_alloc_buf(metadata_blob, &buf);
        if (ret)
                return ret;
 
        /* Checksum the metadata resource.  */
-       sha1_buffer(buf, metadata_blob->size, hash);
+       sha1(buf, metadata_blob->size, hash);
        if (!hashes_equal(metadata_blob->hash, hash)) {
                ERROR("Metadata resource is corrupted "
                      "(invalid SHA-1 message digest)!");
index 8ad4fdc8663a4cc50b76df2b37d908ac513df6aa..36bf1b97186882f6e74153cbd4fb40d9da57ae41 100644 (file)
@@ -2,13 +2,13 @@
  * mount_image.c
  *
  * This file implements mounting of WIM images using FUSE
- * (Filesystem in Userspace).  See http://fuse.sourceforge.net/.
+ * (Filesystem in Userspace).  See https://github.com/libfuse/libfuse
  *
  * Currently it is only expected to work on Linux.
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -21,7 +21,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #ifdef WITH_FUSE
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  error "FUSE mount not supported on Windows!  Please configure --without-fuse"
 #endif
 
-#define FUSE_USE_VERSION 26
+#define FUSE_USE_VERSION 30
 
 #include <sys/types.h> /* sometimes required before <sys/xattr.h> */
 #include <sys/xattr.h>
@@ -46,7 +46,6 @@
 #include <fuse.h>
 #include <limits.h>
 #include <mqueue.h>
-#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -61,6 +60,7 @@
 #include "wimlib/paths.h"
 #include "wimlib/progress.h"
 #include "wimlib/reparse.h"
+#include "wimlib/threads.h"
 #include "wimlib/timestamp.h"
 #include "wimlib/unix_data.h"
 #include "wimlib/write.h"
 #  define ENOATTR ENODATA
 #endif
 
+#ifndef RENAME_NOREPLACE
+#  define RENAME_NOREPLACE     (1 << 0)
+#endif
+#ifndef RENAME_EXCHANGE
+#  define RENAME_EXCHANGE      (1 << 1)
+#endif
+
 #define WIMFS_MQUEUE_NAME_LEN 32
 
 #define WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS 0x80000000
@@ -219,10 +226,7 @@ flags_writable(int open_flags)
 static mode_t
 fuse_mask_mode(mode_t mode, const struct fuse_context *fuse_ctx)
 {
-#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
-       mode &= ~fuse_ctx->umask;
-#endif
-       return mode;
+       return mode & ~fuse_ctx->umask;
 }
 
 /*
@@ -646,6 +650,31 @@ touch_parent(struct wim_dentry *dentry)
        touch_inode(dentry->d_parent->d_inode);
 }
 
+/*
+ * Update inode metadata after a regular file's contents have changed:
+ *
+ * - Update the timestamps
+ * - Clear the setuid and setgid bits
+ */
+static void
+file_contents_changed(struct wim_inode *inode)
+{
+       struct wimlib_unix_data unix_data;
+       bool ok;
+
+       touch_inode(inode);
+
+       if (inode_get_unix_data(inode, &unix_data)) {
+               unix_data.mode &= ~(S_ISUID | S_ISGID);
+               ok = inode_set_unix_data(inode, &unix_data, UNIX_DATA_MODE);
+               /*
+                * This cannot fail because no memory allocation should have
+                * been required, as the UNIX data already exists.
+                */
+               wimlib_assert(ok);
+       } /* Else, set[ug]id can't be set, so there's nothing to do. */
+}
+
 /*
  * Create a new file in the staging directory for a read-write mounted image.
  *
@@ -798,6 +827,8 @@ extract_blob_to_staging_dir(struct wim_inode *inode,
        prepare_unhashed_blob(new_blob, inode, strm->stream_id,
                              &wim_get_current_image_metadata(ctx->wim)->unhashed_blobs);
        inode_replace_stream_blob(inode, strm, new_blob, ctx->wim->blob_table);
+       if (size != blob_size(old_blob))
+               file_contents_changed(inode);
        return 0;
 
 out_revert_fd_changes:
@@ -1196,8 +1227,66 @@ out:
        return ret;
 }
 
+static void *
+wimfs_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
+{
+       /*
+        * Cache positive name lookups indefinitely, since names can only be
+        * added, removed, or modified through the mounted filesystem itself.
+        */
+       cfg->entry_timeout = 1000000000;
+
+       /*
+        * Cache negative name lookups indefinitely, since names can only be
+        * added, removed, or modified through the mounted filesystem itself.
+        */
+       cfg->negative_timeout = 1000000000;
+
+       /*
+        * Don't cache file/directory attributes.  This is needed as a
+        * workaround for the fact that when caching attributes, the high level
+        * interface to libfuse considers a file which has several hard-linked
+        * names as several different files.  (Otherwise, we could cache our
+        * file/directory attributes indefinitely, since they can only be
+        * changed through the mounted filesystem itself.)
+        */
+       cfg->attr_timeout = 0;
+
+       /*
+        * If an open file is unlinked, unlink it for real rather than renaming
+        * it to a hidden file.  Our code supports this; an unlinked inode is
+        * retained until all its file descriptors have been closed.
+        */
+       cfg->hard_remove = 1;
+
+       /*
+        * Make FUSE use the inode numbers we provide.  We want this, because we
+        * have inodes and will number them ourselves.
+        */
+       cfg->use_ino = 1;
+
+       /*
+        * Cache the contents of files.  This will speed up repeated access to
+        * files on a mounted WIM image, since they won't need to be
+        * decompressed repeatedly.  This option is valid because data in the
+        * WIM image should never be changed externally.  (Although, if someone
+        * really wanted to they could modify the WIM file or mess with the
+        * staging directory; but then they're asking for trouble.)
+        */
+       cfg->kernel_cache = 1;
+
+       /*
+        * We keep track of file descriptor structures (struct wimfs_fd), so
+        * there is no need to have the file path provided on operations such as
+        * read().
+        */
+       cfg->nullpath_ok = 1;
+
+       return wimfs_get_context();
+}
+
 static int
-wimfs_chmod(const char *path, mode_t mask)
+wimfs_chmod(const char *path, mode_t mask, struct fuse_file_info *fi)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_inode *inode;
@@ -1206,10 +1295,13 @@ wimfs_chmod(const char *path, mode_t mask)
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
                return -EOPNOTSUPP;
 
-       inode = wim_pathname_to_inode(ctx->wim, path);
-       if (!inode)
-               return -errno;
-
+       if (fi) {
+               inode = WIMFS_FD(fi)->f_inode;
+       } else {
+               inode = wim_pathname_to_inode(ctx->wim, path);
+               if (!inode)
+                       return -errno;
+       }
        unix_data.uid = ctx->owner_uid;
        unix_data.gid = ctx->owner_gid;
        unix_data.mode = mask;
@@ -1222,7 +1314,7 @@ wimfs_chmod(const char *path, mode_t mask)
 }
 
 static int
-wimfs_chown(const char *path, uid_t uid, gid_t gid)
+wimfs_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_inode *inode;
@@ -1232,9 +1324,13 @@ wimfs_chown(const char *path, uid_t uid, gid_t gid)
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
                return -EOPNOTSUPP;
 
-       inode = wim_pathname_to_inode(ctx->wim, path);
-       if (!inode)
-               return -errno;
+       if (fi) {
+               inode = WIMFS_FD(fi)->f_inode;
+       } else {
+               inode = wim_pathname_to_inode(ctx->wim, path);
+               if (!inode)
+                       return -errno;
+       }
 
        which = 0;
 
@@ -1260,38 +1356,32 @@ wimfs_chown(const char *path, uid_t uid, gid_t gid)
 }
 
 static int
-wimfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
-{
-       struct wimfs_fd *fd = WIMFS_FD(fi);
-       return inode_to_stbuf(fd->f_inode, fd->f_blob, stbuf);
-}
-
-static int
-wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
-{
-       struct wimfs_fd *fd = WIMFS_FD(fi);
-       if (ftruncate(fd->f_staging_fd.fd, size))
-               return -errno;
-       touch_inode(fd->f_inode);
-       fd->f_blob->size = size;
-       return 0;
-}
-
-static int
-wimfs_getattr(const char *path, struct stat *stbuf)
+wimfs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
-       struct wim_dentry *dentry;
-       struct wim_inode_stream *strm;
+       const struct wim_inode *inode;
+       const struct blob_descriptor *blob;
        int ret;
 
-       ret = wim_pathname_to_stream(ctx, path, LOOKUP_FLAG_DIRECTORY_OK,
-                                    &dentry, &strm);
-       if (ret)
-               return ret;
+       if (fi) {
+               const struct wimfs_fd *fd = WIMFS_FD(fi);
 
-       return inode_to_stbuf(dentry->d_inode,
-                             stream_blob_resolved(strm), stbuf);
+               inode = fd->f_inode;
+               blob = fd->f_blob;
+       } else {
+               struct wim_dentry *dentry;
+               struct wim_inode_stream *strm;
+
+               ret = wim_pathname_to_stream(ctx, path,
+                                            LOOKUP_FLAG_DIRECTORY_OK,
+                                            &dentry, &strm);
+               if (ret)
+                       return ret;
+               inode = dentry->d_inode;
+               blob = stream_blob_resolved(strm);
+       }
+
+       return inode_to_stbuf(inode, blob, stbuf);
 }
 
 static int
@@ -1434,7 +1524,6 @@ wimfs_listxattr(const char *path, char *list, size_t size)
        const struct wimfs_context *ctx = wimfs_get_context();
        const struct wim_inode *inode;
        char *p = list;
-       char *end = list + size;
        int total_size = 0;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
@@ -1470,7 +1559,7 @@ wimfs_listxattr(const char *path, char *list, size_t size)
 
                total_size += stream_name_mbs_nbytes + 6;
                if (size) {
-                       if (end - p < stream_name_mbs_nbytes + 6) {
+                       if (list + size - p < stream_name_mbs_nbytes + 6) {
                                FREE(stream_name_mbs);
                                return -ERANGE;
                        }
@@ -1605,12 +1694,17 @@ wimfs_open(const char *path, struct fuse_file_info *fi)
                int raw_fd;
 
                raw_fd = openat(blob->staging_dir_fd, blob->staging_file_name,
-                               (fi->flags & O_ACCMODE) | O_NOFOLLOW);
+                               (fi->flags & (O_ACCMODE | O_TRUNC)) |
+                               O_NOFOLLOW);
                if (raw_fd < 0) {
                        close_wimfs_fd(fd);
                        return -errno;
                }
                filedes_init(&fd->f_staging_fd, raw_fd);
+               if (fi->flags & O_TRUNC) {
+                       blob->size = 0;
+                       file_contents_changed(inode);
+               }
        }
        fi->fh = (uintptr_t)fd;
        return 0;
@@ -1686,7 +1780,8 @@ wimfs_read(const char *path, char *buf, size_t size,
 
 static int
 wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-             off_t offset, struct fuse_file_info *fi)
+             off_t offset, struct fuse_file_info *fi,
+             enum fuse_readdir_flags flags)
 {
        struct wimfs_fd *fd = WIMFS_FD(fi);
        const struct wim_inode *inode;
@@ -1695,10 +1790,10 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 
        inode = fd->f_inode;
 
-       ret = filler(buf, ".", NULL, 0);
+       ret = filler(buf, ".", NULL, 0, 0);
        if (ret)
                return ret;
-       ret = filler(buf, "..", NULL, 0);
+       ret = filler(buf, "..", NULL, 0, 0);
        if (ret)
                return ret;
 
@@ -1710,7 +1805,7 @@ wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                                    &name, &name_nbytes))
                        return -errno;
 
-               ret = filler(buf, name, NULL, 0);
+               ret = filler(buf, name, NULL, 0, 0);
                FREE(name);
                if (ret)
                        return ret;
@@ -1779,10 +1874,13 @@ wimfs_removexattr(const char *path, const char *name)
 }
 
 static int
-wimfs_rename(const char *from, const char *to)
+wimfs_rename(const char *from, const char *to, unsigned int flags)
 {
+       if (flags & RENAME_EXCHANGE)
+               return -EINVAL;
        return rename_wim_path(wimfs_get_WIMStruct(), from, to,
-                              WIMLIB_CASE_SENSITIVE, NULL);
+                              WIMLIB_CASE_SENSITIVE,
+                              (flags & RENAME_NOREPLACE), NULL);
 }
 
 static int
@@ -1913,14 +2011,32 @@ wimfs_symlink(const char *to, const char *from)
 }
 
 static int
-wimfs_truncate(const char *path, off_t size)
+do_truncate(int staging_fd, off_t size,
+           struct wim_inode *inode, struct blob_descriptor *blob)
+{
+       if (ftruncate(staging_fd, size))
+               return -errno;
+       file_contents_changed(inode);
+       blob->size = size;
+       return 0;
+}
+
+static int
+wimfs_truncate(const char *path, off_t size, struct fuse_file_info *fi)
 {
        const struct wimfs_context *ctx = wimfs_get_context();
        struct wim_dentry *dentry;
        struct wim_inode_stream *strm;
        struct blob_descriptor *blob;
        int ret;
-       int fd;
+       int staging_fd;
+
+       if (fi) {
+               struct wimfs_fd *fd = WIMFS_FD(fi);
+
+               return do_truncate(fd->f_staging_fd.fd, size, fd->f_inode,
+                                  fd->f_blob);
+       }
 
        ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm);
        if (ret)
@@ -1937,16 +2053,14 @@ wimfs_truncate(const char *path, off_t size)
        }
 
        /* Truncate the staging file.  */
-       fd = openat(blob->staging_dir_fd, blob->staging_file_name,
-                   O_WRONLY | O_NOFOLLOW);
-       if (fd < 0)
+       staging_fd = openat(blob->staging_dir_fd, blob->staging_file_name,
+                           O_WRONLY | O_NOFOLLOW);
+       if (staging_fd < 0)
                return -errno;
-       ret = ftruncate(fd, size);
-       if (close(fd) || ret)
-               return -errno;
-       blob->size = size;
-       touch_inode(dentry->d_inode);
-       return 0;
+       ret = do_truncate(staging_fd, size, dentry->d_inode, blob);
+       if (close(staging_fd) && !ret)
+               ret = -errno;
+       return ret;
 }
 
 static int
@@ -1971,21 +2085,24 @@ wimfs_unlink(const char *path)
        return 0;
 }
 
-#ifdef HAVE_UTIMENSAT
 /*
  * Change the timestamp on a file dentry.
  *
  * Note that alternate data streams do not have their own timestamps.
  */
 static int
-wimfs_utimens(const char *path, const struct timespec tv[2])
+wimfs_utimens(const char *path, const struct timespec tv[2],
+             struct fuse_file_info *fi)
 {
-       WIMStruct *wim = wimfs_get_WIMStruct();
        struct wim_inode *inode;
 
-       inode = wim_pathname_to_inode(wim, path);
-       if (!inode)
-               return -errno;
+       if (fi) {
+               inode = WIMFS_FD(fi)->f_inode;
+       } else {
+               inode = wim_pathname_to_inode(wimfs_get_WIMStruct(), path);
+               if (!inode)
+                       return -errno;
+       }
 
        if (tv[0].tv_nsec != UTIME_OMIT) {
                if (tv[0].tv_nsec == UTIME_NOW)
@@ -2001,22 +2118,6 @@ wimfs_utimens(const char *path, const struct timespec tv[2])
        }
        return 0;
 }
-#else /* HAVE_UTIMENSAT */
-static int
-wimfs_utime(const char *path, struct utimbuf *times)
-{
-       WIMStruct *wim = wimfs_get_WIMStruct();
-       struct wim_inode *inode;
-
-       inode = wim_pathname_to_inode(wim, path);
-       if (!inode)
-               return -errno;
-
-       inode->i_last_access_time = time_t_to_wim_timestamp(times->actime);
-       inode->i_last_write_time = time_t_to_wim_timestamp(times->modtime);
-       return 0;
-}
-#endif /* !HAVE_UTIMENSAT */
 
 static int
 wimfs_write(const char *path, const char *buf, size_t size,
@@ -2032,15 +2133,14 @@ wimfs_write(const char *path, const char *buf, size_t size,
        if (offset + size > fd->f_blob->size)
                fd->f_blob->size = offset + size;
 
-       touch_inode(fd->f_inode);
+       file_contents_changed(fd->f_inode);
        return ret;
 }
 
 static const struct fuse_operations wimfs_operations = {
+       .init        = wimfs_init,
        .chmod       = wimfs_chmod,
        .chown       = wimfs_chown,
-       .fgetattr    = wimfs_fgetattr,
-       .ftruncate   = wimfs_ftruncate,
        .getattr     = wimfs_getattr,
        .getxattr    = wimfs_getxattr,
        .link        = wimfs_link,
@@ -2061,23 +2161,9 @@ static const struct fuse_operations wimfs_operations = {
        .symlink     = wimfs_symlink,
        .truncate    = wimfs_truncate,
        .unlink      = wimfs_unlink,
-#ifdef HAVE_UTIMENSAT
        .utimens     = wimfs_utimens,
-#else
-       .utime       = wimfs_utime,
-#endif
        .write       = wimfs_write,
 
-       /* We keep track of file descriptor structures (struct wimfs_fd), so
-        * there is no need to have the file path provided on operations such as
-        * read().  */
-#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
-       .flag_nullpath_ok = 1,
-#endif
-#if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 9)
-       .flag_nopath = 1,
-       .flag_utime_omit_ok = 1,
-#endif
 };
 
 /* API function documented in wimlib.h  */
@@ -2195,61 +2281,15 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        /*
         * Build the FUSE mount options:
         *
-        * use_ino
-        *      FUSE will use the inode numbers we provide.  We want this,
-        *      because we have inodes and will number them ourselves.
-        *
         * subtype=wimfs
         *      Name for our filesystem (main type is "fuse").
         *
-        * hard_remove
-        *      If an open file is unlinked, unlink it for real rather than
-        *      renaming it to a hidden file.  Our code supports this; an
-        *      unlinked inode is retained until all its file descriptors have
-        *      been closed.
-        *
         * default_permissions
         *      FUSE will perform permission checking.  Useful when
         *      WIMLIB_MOUNT_FLAG_UNIX_DATA is provided and the WIM image
         *      contains the UNIX permissions for each file.
-        *
-        * kernel_cache
-        *      Cache the contents of files.  This will speed up repeated access
-        *      to files on a mounted WIM image, since they won't need to be
-        *      decompressed repeatedly.  This option is valid because data in
-        *      the WIM image should never be changed externally.  (Although, if
-        *      someone really wanted to they could modify the WIM file or mess
-        *      with the staging directory; but then they're asking for
-        *      trouble.)
-        *
-        * entry_timeout=1000000000
-        *      Cache positive name lookups indefinitely, since names can only
-        *      be added, removed, or modified through the mounted filesystem
-        *      itself.
-        *
-        * negative_timeout=1000000000
-        *      Cache negative name lookups indefinitely, since names can only
-        *      be added, removed, or modified through the mounted filesystem
-        *      itself.
-        *
-        * attr_timeout=0
-        *      Don't cache file/directory attributes.  This is needed as a
-        *      workaround for the fact that when caching attributes, the high
-        *      level interface to libfuse considers a file which has several
-        *      hard-linked names as several different files.  (Otherwise, we
-        *      could cache our file/directory attributes indefinitely, since
-        *      they can only be changed through the mounted filesystem itself.)
         */
-       char optstring[256] =
-               "use_ino"
-               ",subtype=wimfs"
-               ",hard_remove"
-               ",default_permissions"
-               ",kernel_cache"
-               ",entry_timeout=1000000000"
-               ",negative_timeout=1000000000"
-               ",attr_timeout=0"
-               ;
+       char optstring[128] = "subtype=wimfs,default_permissions";
        fuse_argv[fuse_argc++] = "-o";
        fuse_argv[fuse_argc++] = optstring;
        if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE))
@@ -2388,9 +2428,9 @@ do_unmount_commit(const char *dir, int unmount_flags,
                  wimlib_progress_func_t progfunc, void *progctx)
 {
        struct wimfs_unmount_info unmount_info;
-       mqd_t mq;
+       mqd_t mq = (mqd_t)-1;
        struct commit_progress_thread_args args;
-       pthread_t commit_progress_tid;
+       struct thread commit_progress_tid;
        int ret;
 
        memset(&unmount_info, 0, sizeof(unmount_info));
@@ -2409,11 +2449,8 @@ do_unmount_commit(const char *dir, int unmount_flags,
                args.mq = mq;
                args.progfunc = progfunc;
                args.progctx = progctx;
-               ret = pthread_create(&commit_progress_tid, NULL,
-                                    commit_progress_thread_proc, &args);
-               if (ret) {
-                       errno = ret;
-                       ERROR_WITH_ERRNO("Can't create thread");
+               if (!thread_create(&commit_progress_tid,
+                                  commit_progress_thread_proc, &args)) {
                        ret = WIMLIB_ERR_NOMEM;
                        goto out_delete_mq;
                }
@@ -2427,7 +2464,7 @@ do_unmount_commit(const char *dir, int unmount_flags,
                /* Terminate the progress thread.  */
                char empty[1];
                mq_send(mq, empty, 0, 1);
-               pthread_join(commit_progress_tid, NULL);
+               thread_join(&commit_progress_tid);
        }
 out_delete_mq:
        if (progfunc) {
@@ -2518,7 +2555,7 @@ wimlib_unmount_image_with_progress(const char *dir, int unmount_flags,
 static int
 mount_unsupported_error(void)
 {
-#if defined(__WIN32__)
+#ifdef _WIN32
        ERROR("Sorry-- Mounting WIM images is not supported on Windows!");
 #else
        ERROR("wimlib was compiled with --without-fuse, which disables support "
index c012c8e5b600188d76115949098763eb5b22d6b0..b0b376fac22ff743b6dc5479a0cf515b7a50f333 100644 (file)
@@ -23,7 +23,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index bc8fed710cb898b303b5cbd4e3d9ac6eafcf4e15..7aa24c160cab8f9fdfb962fe450dad87bb2b19be 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -117,7 +117,8 @@ open_ntfs_attr(ntfs_inode *ni, const struct ntfs_location *loc)
 
 int
 read_ntfs_attribute_prefix(const struct blob_descriptor *blob, u64 size,
-                          const struct consume_chunk_callback *cb)
+                          const struct consume_chunk_callback *cb,
+                          bool recover_data)
 {
        const struct ntfs_location *loc = blob->ntfs_loc;
        ntfs_volume *vol = loc->volume->vol;
index 11bd8461e8a6b0053af878af5a57b9cda7c0cd42..0b623f81e32f37376c6e53a6b8e45a2f07c8e283 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 4b9db67f87ebed18b7404c5864cf676f4fb6c5a4..34d08a5c8bd61a3928fbf63df56e6235dfe8b4a2 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index a9e3fbcd737b541418a3c221a95efe7d4ea03262..caad2f5ec708962582156ae4e9f6fc78bc444592 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 5696fbae87435284725c154c55dd2f32eb881b88..59c70662e45f3e4aeb3a0dfca948b636e804c308 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index a380d4eeff1dd89715a4993e04aa6c5d21477e34..b7248f6d3a40142262f0af193fafe75ee762bb9f 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 0f753e8c7de0b6bd58bc225f8c7eb5f7a420e88d..5672e707eeb5c84129fc6016888f062ad24a9400 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -46,7 +46,7 @@ struct regf {
        le32 total_hbin_size;           /* Total size of all hbins  */
        le32 f3[1013];
        u8 hbin_area[0];                /* Start of hbin area  */
-} _packed_attribute;
+} __attribute__((packed));
 
 
 /* Cell header  */
@@ -56,7 +56,7 @@ struct cell {
 
        /* Magic characters which identify the cell type  */
        le16 magic;
-} _packed_attribute;
+} __attribute__((packed));
 
 /* NK cell - represents a registry key  */
 struct nk {
@@ -84,7 +84,7 @@ struct nk {
        le16 name_size;
        le16 unknown_0x4E;
        char name[0];
-} _packed_attribute;
+} __attribute__((packed));
 
 /* Subkey list cell.  There are four types.  LF, LH, and LI cells reference
  * subkey NK cells directly, while RI cells reference other subkey lists.  All
@@ -99,13 +99,13 @@ struct subkey_list {
        struct cell base;
        le16 num_offsets;
        le32 elements[0];
-} _packed_attribute;
+} __attribute__((packed));
 
 /* Value list cell - contains a list of value references  */
 struct value_list {
        le32 size;
        le32 vk_offsets[0];
-} _packed_attribute;
+} __attribute__((packed));
 
 /* VK cell - contains a value's data, or a reference to it  */
 struct vk {
index da8642f8561fd74696726a11e583eedeb3c9f219..2838f01bd9789b388f814a20fc26db0920ec1dc0 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -156,7 +156,7 @@ make_link_reparse_point(const struct link_reparse_point *link,
 }
 
 /* UNIX symlink <=> Windows reparse point translation  */
-#ifndef __WIN32__
+#ifndef _WIN32
 
 /* Retrieve the inode's reparse point buffer into @rpbuf and @rpbuflen_ret.
  * This gets the reparse data from @blob if specified, otherwise from the
@@ -442,4 +442,4 @@ out_free_target:
        return ret;
 }
 
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
index 9054b55efaf46e058a145231cf79e4fd462c2e4d..a2aee42dd0bf0103cc95c4c09a5b9913124e2fd9 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -83,6 +83,34 @@ struct data_range {
        u64 size;
 };
 
+static int
+decompress_chunk(const void *cbuf, u32 chunk_csize, u8 *ubuf, u32 chunk_usize,
+                struct wimlib_decompressor *decompressor, bool recover_data)
+{
+       int res = wimlib_decompress(cbuf, chunk_csize, ubuf, chunk_usize,
+                                   decompressor);
+       if (likely(res == 0))
+               return 0;
+
+       if (recover_data) {
+               WARNING("Failed to decompress data!  Continuing anyway since data recovery mode is enabled.");
+
+               /* Continue on with *something*.  In the worst case just use a
+                * zeroed buffer.  But, try to fill as much of it with
+                * decompressed data as we can.  This works because if the
+                * corruption isn't located right at the beginning of the
+                * compressed chunk, wimlib_decompress() may write some correct
+                * output at the beginning even if it fails later.  */
+               memset(ubuf, 0, chunk_usize);
+               (void)wimlib_decompress(cbuf, chunk_csize, ubuf,
+                                       chunk_usize, decompressor);
+               return 0;
+       }
+       ERROR("Failed to decompress data!");
+       errno = EINVAL;
+       return WIMLIB_ERR_DECOMPRESSION;
+}
+
 /*
  * Read data from a compressed WIM resource.
  *
@@ -98,6 +126,9 @@ struct data_range {
  *     the data being read.  Each call provides the next chunk of the requested
  *     data, uncompressed.  Each chunk will be nonempty and will not cross
  *     range boundaries but otherwise will be of unspecified size.
+ * @recover_data
+ *     If a chunk can't be fully decompressed due to being corrupted, continue
+ *     with whatever data can be recovered rather than return an error.
  *
  * Possible return values:
  *
@@ -114,7 +145,8 @@ static int
 read_compressed_wim_resource(const struct wim_resource_descriptor * const rdesc,
                             const struct data_range * const ranges,
                             const size_t num_ranges,
-                            const struct consume_chunk_callback *cb)
+                            const struct consume_chunk_callback *cb,
+                            bool recover_data)
 {
        int ret;
        u64 *chunk_offsets = NULL;
@@ -301,8 +333,8 @@ read_compressed_wim_resource(const struct wim_resource_descriptor * const rdesc,
                /* Now fill in chunk_offsets from the entries we have read in
                 * chunk_tab_data.  We break aliasing rules here to avoid having
                 * to allocate yet another array.  */
-               typedef le64 _may_alias_attribute aliased_le64_t;
-               typedef le32 _may_alias_attribute aliased_le32_t;
+               typedef le64 __attribute__((may_alias)) aliased_le64_t;
+               typedef le32 __attribute__((may_alias)) aliased_le32_t;
                u64 * chunk_offsets_p = chunk_offsets;
 
                if (alt_chunk_table) {
@@ -446,17 +478,12 @@ read_compressed_wim_resource(const struct wim_resource_descriptor * const rdesc,
                                goto read_error;
 
                        if (read_buf == cbuf) {
-                               ret = wimlib_decompress(cbuf,
-                                                       chunk_csize,
-                                                       ubuf,
-                                                       chunk_usize,
-                                                       decompressor);
-                               if (unlikely(ret)) {
-                                       ERROR("Failed to decompress data!");
-                                       ret = WIMLIB_ERR_DECOMPRESSION;
-                                       errno = EINVAL;
+                               ret = decompress_chunk(cbuf, chunk_csize,
+                                                      ubuf, chunk_usize,
+                                                      decompressor,
+                                                      recover_data);
+                               if (unlikely(ret))
                                        goto out_cleanup;
-                               }
                        }
                        cur_read_offset += chunk_csize;
 
@@ -592,7 +619,8 @@ bufferer_cb(const void *chunk, size_t size, void *_ctx)
 static int
 read_partial_wim_resource(const struct wim_resource_descriptor *rdesc,
                          const u64 offset, const u64 size,
-                         const struct consume_chunk_callback *cb)
+                         const struct consume_chunk_callback *cb,
+                         bool recover_data)
 {
        if (rdesc->flags & (WIM_RESHDR_FLAG_COMPRESSED |
                            WIM_RESHDR_FLAG_SOLID))
@@ -604,7 +632,8 @@ read_partial_wim_resource(const struct wim_resource_descriptor *rdesc,
                        .offset = offset,
                        .size = size,
                };
-               return read_compressed_wim_resource(rdesc, &range, 1, cb);
+               return read_compressed_wim_resource(rdesc, &range, 1, cb,
+                                                   recover_data);
        }
 
        /* Uncompressed resource  */
@@ -626,7 +655,7 @@ read_partial_wim_blob_into_buf(const struct blob_descriptor *blob,
        return read_partial_wim_resource(blob->rdesc,
                                         blob->offset_in_res + offset,
                                         size,
-                                        &cb);
+                                        &cb, false);
 }
 
 static int
@@ -643,15 +672,15 @@ skip_wim_resource(const struct wim_resource_descriptor *rdesc)
                .func = noop_cb,
        };
        return read_partial_wim_resource(rdesc, 0,
-                                        rdesc->uncompressed_size, &cb);
+                                        rdesc->uncompressed_size, &cb, false);
 }
 
 static int
 read_wim_blob_prefix(const struct blob_descriptor *blob, u64 size,
-                    const struct consume_chunk_callback *cb)
+                    const struct consume_chunk_callback *cb, bool recover_data)
 {
        return read_partial_wim_resource(blob->rdesc, blob->offset_in_res,
-                                        size, cb);
+                                        size, cb, recover_data);
 }
 
 /* This function handles reading blob data that is located in an external file,
@@ -664,7 +693,8 @@ read_wim_blob_prefix(const struct blob_descriptor *blob, u64 size,
  * encrypted), so Windows uses its own code for its equivalent case.  */
 static int
 read_file_on_disk_prefix(const struct blob_descriptor *blob, u64 size,
-                        const struct consume_chunk_callback *cb)
+                        const struct consume_chunk_callback *cb,
+                        bool recover_data)
 {
        int ret;
        int raw_fd;
@@ -684,7 +714,8 @@ read_file_on_disk_prefix(const struct blob_descriptor *blob, u64 size,
 #ifdef WITH_FUSE
 static int
 read_staging_file_prefix(const struct blob_descriptor *blob, u64 size,
-                        const struct consume_chunk_callback *cb)
+                        const struct consume_chunk_callback *cb,
+                        bool recover_data)
 {
        int raw_fd;
        struct filedes fd;
@@ -708,7 +739,8 @@ read_staging_file_prefix(const struct blob_descriptor *blob, u64 size,
  * already located in an in-memory buffer.  */
 static int
 read_buffer_prefix(const struct blob_descriptor *blob,
-                  u64 size, const struct consume_chunk_callback *cb)
+                  u64 size, const struct consume_chunk_callback *cb,
+                  bool recover_data)
 {
        if (unlikely(!size))
                return 0;
@@ -717,7 +749,8 @@ read_buffer_prefix(const struct blob_descriptor *blob,
 
 typedef int (*read_blob_prefix_handler_t)(const struct blob_descriptor *blob,
                                          u64 size,
-                                         const struct consume_chunk_callback *cb);
+                                         const struct consume_chunk_callback *cb,
+                                         bool recover_data);
 
 /*
  * Read the first @size bytes from a generic "blob", which may be located in any
@@ -728,11 +761,12 @@ typedef int (*read_blob_prefix_handler_t)(const struct blob_descriptor *blob,
  * Returns 0 on success; nonzero on error.  A nonzero value will be returned if
  * the blob data cannot be successfully read (for a number of different reasons,
  * depending on the blob location), or if @cb returned nonzero in which case
- * that error code will be returned.
+ * that error code will be returned.  If @recover_data is true, then errors
+ * decompressing chunks in WIM resources will be ignored.
  */
 static int
 read_blob_prefix(const struct blob_descriptor *blob, u64 size,
-                const struct consume_chunk_callback *cb)
+                const struct consume_chunk_callback *cb, bool recover_data)
 {
        static const read_blob_prefix_handler_t handlers[] = {
                [BLOB_IN_WIM] = read_wim_blob_prefix,
@@ -744,14 +778,14 @@ read_blob_prefix(const struct blob_descriptor *blob, u64 size,
        #ifdef WITH_NTFS_3G
                [BLOB_IN_NTFS_VOLUME] = read_ntfs_attribute_prefix,
        #endif
-       #ifdef __WIN32__
+       #ifdef _WIN32
                [BLOB_IN_WINDOWS_FILE] = read_windows_file_prefix,
        #endif
        };
        wimlib_assert(blob->blob_location < ARRAY_LEN(handlers)
                      && handlers[blob->blob_location] != NULL);
        wimlib_assert(size <= blob->size);
-       return handlers[blob->blob_location](blob, size, cb);
+       return handlers[blob->blob_location](blob, size, cb, recover_data);
 }
 
 struct blob_chunk_ctx {
@@ -775,7 +809,7 @@ consume_blob_chunk(const void *chunk, size_t size, void *_ctx)
  * callbacks (all of which are optional).  */
 int
 read_blob_with_cbs(struct blob_descriptor *blob,
-                  const struct read_blob_callbacks *cbs)
+                  const struct read_blob_callbacks *cbs, bool recover_data)
 {
        int ret;
        struct blob_chunk_ctx ctx = {
@@ -792,7 +826,7 @@ read_blob_with_cbs(struct blob_descriptor *blob,
        if (unlikely(ret))
                return ret;
 
-       ret = read_blob_prefix(blob, blob->size, &cb);
+       ret = read_blob_prefix(blob, blob->size, &cb, recover_data);
 
        return call_end_blob(blob, ret, cbs);
 }
@@ -807,7 +841,7 @@ read_blob_into_buf(const struct blob_descriptor *blob, void *buf)
                .func   = bufferer_cb,
                .ctx    = &buf,
        };
-       return read_blob_prefix(blob, blob->size, &cb);
+       return read_blob_prefix(blob, blob->size, &cb, false);
 }
 
 /* Retrieve the full uncompressed data of the specified blob.  A buffer large
@@ -942,7 +976,7 @@ blobifier_cb(const void *chunk, size_t size, void *_ctx)
 }
 
 struct hasher_context {
-       SHA_CTX sha_ctx;
+       struct sha1_ctx sha_ctx;
        int flags;
        struct read_blob_callbacks cbs;
 };
@@ -955,6 +989,7 @@ hasher_begin_blob(struct blob_descriptor *blob, void *_ctx)
        struct hasher_context *ctx = _ctx;
 
        sha1_init(&ctx->sha_ctx);
+       blob->corrupted = 0;
 
        return call_begin_blob(blob, &ctx->cbs);
 }
@@ -977,11 +1012,11 @@ hasher_continue_blob(const struct blob_descriptor *blob, u64 offset,
 }
 
 static int
-report_sha1_mismatch_error(const struct blob_descriptor *blob,
-                          const u8 actual_hash[SHA1_HASH_SIZE])
+report_sha1_mismatch(struct blob_descriptor *blob,
+                    const u8 actual_hash[SHA1_HASH_SIZE], bool recover_data)
 {
-       tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1];
-       tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1];
+       tchar expected_hashstr[SHA1_HASH_STRING_LEN];
+       tchar actual_hashstr[SHA1_HASH_STRING_LEN];
 
        wimlib_assert(blob->blob_location != BLOB_NONEXISTENT);
        wimlib_assert(blob->blob_location != BLOB_IN_ATTACHED_BUFFER);
@@ -989,6 +1024,8 @@ report_sha1_mismatch_error(const struct blob_descriptor *blob,
        sprint_hash(blob->hash, expected_hashstr);
        sprint_hash(actual_hash, actual_hashstr);
 
+       blob->corrupted = 1;
+
        if (blob_is_in_file(blob)) {
                ERROR("A file was concurrently modified!\n"
                      "        Path: \"%"TS"\"\n"
@@ -998,17 +1035,19 @@ report_sha1_mismatch_error(const struct blob_descriptor *blob,
                return WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED;
        } else if (blob->blob_location == BLOB_IN_WIM) {
                const struct wim_resource_descriptor *rdesc = blob->rdesc;
-               ERROR("A WIM resource is corrupted!\n"
-                     "        WIM file: \"%"TS"\"\n"
-                     "        Blob uncompressed size: %"PRIu64"\n"
-                     "        Resource offset in WIM: %"PRIu64"\n"
-                     "        Resource uncompressed size: %"PRIu64"\n"
-                     "        Resource size in WIM: %"PRIu64"\n"
-                     "        Resource flags: 0x%x%"TS"\n"
-                     "        Resource compression type: %"TS"\n"
-                     "        Resource compression chunk size: %"PRIu32"\n"
-                     "        Expected SHA-1: %"TS"\n"
-                     "        Actual SHA-1: %"TS"\n",
+
+               (recover_data ? wimlib_warning : wimlib_error)(
+                     T("A WIM resource is corrupted!\n"
+                       "        WIM file: \"%"TS"\"\n"
+                       "        Blob uncompressed size: %"PRIu64"\n"
+                       "        Resource offset in WIM: %"PRIu64"\n"
+                       "        Resource uncompressed size: %"PRIu64"\n"
+                       "        Resource size in WIM: %"PRIu64"\n"
+                       "        Resource flags: 0x%x%"TS"\n"
+                       "        Resource compression type: %"TS"\n"
+                       "        Resource compression chunk size: %"PRIu32"\n"
+                       "        Expected SHA-1: %"TS"\n"
+                       "        Actual SHA-1: %"TS"\n"),
                      rdesc->wim->filename,
                      blob->size,
                      rdesc->offset_in_wim,
@@ -1020,6 +1059,8 @@ report_sha1_mismatch_error(const struct blob_descriptor *blob,
                                                rdesc->compression_type),
                      rdesc->chunk_size,
                      expected_hashstr, actual_hashstr);
+               if (recover_data)
+                       return 0;
                return WIMLIB_ERR_INVALID_RESOURCE_HASH;
        } else {
                ERROR("File data was concurrently modified!\n"
@@ -1048,7 +1089,7 @@ hasher_end_blob(struct blob_descriptor *blob, int status, void *_ctx)
        }
 
        /* Retrieve the final SHA-1 message digest.  */
-       sha1_final(hash, &ctx->sha_ctx);
+       sha1_final(&ctx->sha_ctx, hash);
 
        /* Set the SHA-1 message digest of the blob, or compare the calculated
         * value with stored value.  */
@@ -1058,7 +1099,8 @@ hasher_end_blob(struct blob_descriptor *blob, int status, void *_ctx)
        } else if ((ctx->flags & VERIFY_BLOB_HASHES) &&
                   unlikely(!hashes_equal(hash, blob->hash)))
        {
-               ret = report_sha1_mismatch_error(blob, hash);
+               ret = report_sha1_mismatch(blob, hash,
+                                          ctx->flags & RECOVER_DATA);
                goto out_next_cb;
        }
        ret = 0;
@@ -1071,10 +1113,11 @@ out_next_cb:
  * SHA-1 message digest of the blob.  */
 int
 read_blob_with_sha1(struct blob_descriptor *blob,
-                   const struct read_blob_callbacks *cbs)
+                   const struct read_blob_callbacks *cbs, bool recover_data)
 {
        struct hasher_context hasher_ctx = {
-               .flags = VERIFY_BLOB_HASHES | COMPUTE_MISSING_BLOB_HASHES,
+               .flags = VERIFY_BLOB_HASHES | COMPUTE_MISSING_BLOB_HASHES |
+                        (recover_data ? RECOVER_DATA : 0),
                .cbs = *cbs,
        };
        struct read_blob_callbacks hasher_cbs = {
@@ -1083,7 +1126,7 @@ read_blob_with_sha1(struct blob_descriptor *blob,
                .end_blob       = hasher_end_blob,
                .ctx            = &hasher_ctx,
        };
-       return read_blob_with_cbs(blob, &hasher_cbs);
+       return read_blob_with_cbs(blob, &hasher_cbs, recover_data);
 }
 
 static int
@@ -1091,7 +1134,8 @@ read_blobs_in_solid_resource(struct blob_descriptor *first_blob,
                             struct blob_descriptor *last_blob,
                             size_t blob_count,
                             size_t list_head_offset,
-                            const struct read_blob_callbacks *sink_cbs)
+                            const struct read_blob_callbacks *sink_cbs,
+                            bool recover_data)
 {
        struct data_range *ranges;
        bool ranges_malloced;
@@ -1141,7 +1185,7 @@ read_blobs_in_solid_resource(struct blob_descriptor *first_blob,
        };
 
        ret = read_compressed_wim_resource(first_blob->rdesc, ranges,
-                                          blob_count, &cb);
+                                          blob_count, &cb, recover_data);
 
        if (ranges_malloced)
                FREE(ranges);
@@ -1178,7 +1222,8 @@ oom:
  *             For all blobs being read that have already had SHA-1 message
  *             digests computed, calculate the SHA-1 message digest of the read
  *             data and compare it with the previously computed value.  If they
- *             do not match, return WIMLIB_ERR_INVALID_RESOURCE_HASH.
+ *             do not match, return WIMLIB_ERR_INVALID_RESOURCE_HASH (unless
+ *             RECOVER_DATA is also set, in which case just issue a warning).
  *
  *     COMPUTE_MISSING_BLOB_HASHES
  *             For all blobs being read that have not yet had their SHA-1
@@ -1188,6 +1233,9 @@ oom:
  *     BLOB_LIST_ALREADY_SORTED
  *             @blob_list is already sorted in sequential order for reading.
  *
+ *     RECOVER_DATA
+ *             Don't consider corrupted blob data to be an error.
+ *
  * The callback functions are allowed to delete the current blob from the list
  * if necessary.
  *
@@ -1273,14 +1321,15 @@ read_blob_list(struct list_head *blob_list, size_t list_head_offset,
                                ret = read_blobs_in_solid_resource(blob, blob_last,
                                                                   blob_count,
                                                                   list_head_offset,
-                                                                  sink_cbs);
+                                                                  sink_cbs,
+                                                                  flags & RECOVER_DATA);
                                if (ret)
                                        return ret;
                                continue;
                        }
                }
 
-               ret = read_blob_with_cbs(blob, sink_cbs);
+               ret = read_blob_with_cbs(blob, sink_cbs, flags & RECOVER_DATA);
                if (unlikely(ret && ret != BEGIN_BLOB_STATUS_SKIP_BLOB))
                        return ret;
        }
@@ -1314,19 +1363,20 @@ extract_blob_prefix_to_fd(struct blob_descriptor *blob, u64 size,
                .func   = extract_chunk_to_fd,
                .ctx    = fd,
        };
-       return read_blob_prefix(blob, size, &cb);
+       return read_blob_prefix(blob, size, &cb, false);
 }
 
 /* Extract the full uncompressed contents of the specified blob to the specified
  * file descriptor.  This checks the SHA-1 message digest.  */
 int
-extract_blob_to_fd(struct blob_descriptor *blob, struct filedes *fd)
+extract_blob_to_fd(struct blob_descriptor *blob, struct filedes *fd,
+                  bool recover_data)
 {
        struct read_blob_callbacks cbs = {
                .continue_blob  = extract_blob_chunk_to_fd,
                .ctx            = fd,
        };
-       return read_blob_with_sha1(blob, &cbs);
+       return read_blob_with_sha1(blob, &cbs, recover_data);
 }
 
 /* Calculate the SHA-1 message digest of a blob and store it in @blob->hash.  */
@@ -1335,7 +1385,7 @@ sha1_blob(struct blob_descriptor *blob)
 {
        static const struct read_blob_callbacks cbs = {
        };
-       return read_blob_with_sha1(blob, &cbs);
+       return read_blob_with_sha1(blob, &cbs, false);
 }
 
 /*
index da6fc09e6f369050ba1dc5c879ef007ad81fb547..483428fc1db8d546df052f082b66f3a3005cf93a 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index 85cfb5bc6ce968f85671672577bd0c3984a69e41..617abfbdd3e4dbaf2e3fd841476231c4707eb57e 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -37,7 +37,7 @@ struct wim_security_data_disk {
        le32 total_length;
        le32 num_entries;
        le64 sizes[];
-} _packed_attribute;
+} __attribute__((packed));
 
 struct wim_security_data *
 new_wim_security_data(void)
@@ -242,10 +242,10 @@ void
 rollback_new_security_descriptors(struct wim_sd_set *sd_set)
 {
        struct wim_security_data *sd = sd_set->sd;
-       u8 **descriptors = sd->descriptors + sd_set->orig_num_entries;
-       u32 num_entries  = sd->num_entries - sd_set->orig_num_entries;
-       while (num_entries--)
-               FREE(*descriptors++);
+       u32 i;
+
+       for (i = sd_set->orig_num_entries; i < sd->num_entries; i++)
+               FREE(sd->descriptors[i]);
        sd->num_entries = sd_set->orig_num_entries;
 }
 
@@ -307,7 +307,7 @@ sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
        struct wim_security_data *sd;
        bool bret;
 
-       sha1_buffer(descriptor, size, hash);
+       sha1(descriptor, size, hash);
 
        security_id = lookup_sd(sd_set, hash);
        if (security_id >= 0) /* Identical descriptor already exists */
@@ -378,7 +378,7 @@ init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
                        ret = WIMLIB_ERR_NOMEM;
                        goto out_destroy_sd_set;
                }
-               sha1_buffer(sd->descriptors[i], sd->sizes[i], new->hash);
+               sha1(sd->descriptors[i], sd->sizes[i], new->hash);
                new->security_id = i;
                if (!insert_sd_node(sd_set, new))
                        FREE(new); /* Ignore duplicate security descriptor */
diff --git a/src/sha1-ssse3.asm b/src/sha1-ssse3.asm
deleted file mode 100644 (file)
index c3b07ab..0000000
+++ /dev/null
@@ -1,571 +0,0 @@
-;---------------------
-;
-;   This code implements two interfaces of SHA-1 update function: 1) working on a single 
-;   64-byte block and 2) working on a buffer of multiple 64-bit blocks. Multiple blocks 
-;   version of code is software pipelined and faster overall, it is a default. Assemble
-;   with -DINTEL_SHA1_SINGLEBLOCK to select single 64-byte block function interface.
-;
-;   C++ prototypes of implemented functions are below:
-;
-;   #ifndef INTEL_SHA1_SINGLEBLOCK
-;      // Updates 20-byte SHA-1 record in 'hash' for 'num_blocks' consecutive 64-byte blocks
-;      extern "C" void sha1_update_intel(int *hash, const char* input, size_t num_blocks );
-;   #else
-;      // Updates 20-byte SHA-1 record in 'hash' for one 64-byte block pointed by 'input'
-;      extern "C" void sha1_update_intel(int *hash, const char* input);
-;   #endif
-;
-;   Function name 'sha1_update_intel' can be changed in the source or via macro:
-;     -DINTEL_SHA1_UPDATE_FUNCNAME=my_sha1_update_func_name
-;
-;   It implements both UNIX(default) and Windows ABIs, use -DWIN_ABI on Windows
-;
-;   Code checks CPU for SSSE3 support via CPUID feature flag (CPUID.1.ECX.SSSE3[bit 9]==1),
-;   and performs dispatch. Since in most cases the functionality on non-SSSE3 supporting CPUs 
-;   is also required, the default (e.g. one being replaced) function can be provided for 
-;   dispatch on such CPUs, the name of old function can be changed in the source or via macro:
-;      -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=default_sha1_update_function_name 
-; 
-;   Authors: Maxim Locktyukhin and Ronen Zohar at Intel.com
-;
-
-%ifndef INTEL_SHA1_UPDATE_DEFAULT_DISPATCH
-;; can be replaced with a default SHA-1 update function name
-%define INTEL_SHA1_UPDATE_DEFAULT_DISPATCH  sha1_intel_non_ssse3_cpu_stub_  
-%else 
-extern  INTEL_SHA1_UPDATE_DEFAULT_DISPATCH
-%endif
-
-;; provide alternative SHA-1 update function's name here
-%ifndef INTEL_SHA1_UPDATE_FUNCNAME
-%define INTEL_SHA1_UPDATE_FUNCNAME     sha1_update_intel
-%endif
-
-global INTEL_SHA1_UPDATE_FUNCNAME
-
-
-%ifndef INTEL_SHA1_SINGLEBLOCK
-%assign multiblock 1
-%else
-%assign multiblock 0
-%endif
-
-
-bits 64
-default rel
-
-%ifdef WIN_ABI
-%xdefine arg1 rcx
-%xdefine arg2 rdx
-%xdefine arg3 r8
-%else
-%xdefine arg1 rdi
-%xdefine arg2 rsi
-%xdefine arg3 rdx
-%endif
-
-%xdefine ctx arg1
-%xdefine buf arg2
-%xdefine cnt arg3
-
-%macro REGALLOC 0
-%xdefine A ecx
-%xdefine B esi
-%xdefine C edi
-%xdefine D ebp
-%xdefine E edx
-
-%xdefine T1 eax
-%xdefine T2 ebx
-%endmacro
-
-%xdefine K_BASE     r8
-%xdefine HASH_PTR   r9
-%xdefine BUFFER_PTR r10
-%xdefine BUFFER_END r11
-
-%xdefine W_TMP  xmm0
-%xdefine W_TMP2 xmm9
-
-%xdefine W0  xmm1
-%xdefine W4  xmm2
-%xdefine W8  xmm3
-%xdefine W12 xmm4
-%xdefine W16 xmm5
-%xdefine W20 xmm6
-%xdefine W24 xmm7
-%xdefine W28 xmm8
-
-%xdefine XMM_SHUFB_BSWAP xmm10
-
-;; we keep window of 64 w[i]+K pre-calculated values in a circular buffer
-%xdefine WK(t) (rsp + (t & 15)*4)
-
-;------------------------------------------------------------------------------
-;
-; macro implements SHA-1 function's body for single or several 64-byte blocks
-; first param: function's name
-; second param: =0 - function implements single 64-byte block hash
-;               =1 - function implements multiple64-byte blocks hash
-;                    3rd function's argument is a number, greater 0, of 64-byte blocks to calc hash for
-;
-%macro  SHA1_VECTOR_ASM  2
-align 4096
-%1:
-push rbx
-push rbp
-
-%ifdef WIN_ABI
-push rdi
-push rsi
-
-%xdefine stack_size (16*4 + 16*5 + 8)
-%else
-%xdefine stack_size (16*4 + 8)
-%endif    
-
-sub     rsp, stack_size
-
-%ifdef WIN_ABI
-%xdefine xmm_save_base (rsp + 16*4)
-
-xmm_mov [xmm_save_base + 0*16], xmm6
-xmm_mov [xmm_save_base + 1*16], xmm7
-xmm_mov [xmm_save_base + 2*16], xmm8
-xmm_mov [xmm_save_base + 3*16], xmm9
-xmm_mov [xmm_save_base + 4*16], xmm10
-%endif
-
-mov     HASH_PTR, ctx
-mov     BUFFER_PTR, buf
-
-%if (%2 == 1)
-shl     cnt, 6           ;; mul by 64
-add     cnt, buf
-mov     BUFFER_END, cnt
-%endif
-
-lea     K_BASE, [K_XMM_AR]
-xmm_mov XMM_SHUFB_BSWAP, [bswap_shufb_ctl]
-
-SHA1_PIPELINED_MAIN_BODY %2
-
-%ifdef WIN_ABI
-xmm_mov xmm6, [xmm_save_base + 0*16]
-xmm_mov xmm7, [xmm_save_base + 1*16]
-xmm_mov xmm8, [xmm_save_base + 2*16]
-xmm_mov xmm9, [xmm_save_base + 3*16]
-xmm_mov xmm10,[xmm_save_base + 4*16]
-%endif
-
-add rsp, stack_size
-
-%ifdef WIN_ABI
-pop rsi
-pop rdi
-%endif
-
-pop rbp
-pop rbx
-
-ret
-%endmacro
-
-;--------------------------------------------
-; macro implements 80 rounds of SHA-1, for one 64-byte block or multiple blocks with s/w pipelining
-; macro param: =0 - process single 64-byte block
-;              =1 - multiple blocks
-;
-%macro SHA1_PIPELINED_MAIN_BODY 1
-
-REGALLOC
-
-mov A, [HASH_PTR   ]
-mov B, [HASH_PTR+ 4]
-mov C, [HASH_PTR+ 8]
-mov D, [HASH_PTR+12]
-
-mov E, [HASH_PTR+16]
-
-%assign i 0
-%rep    W_PRECALC_AHEAD
-W_PRECALC i
-%assign i i+1
-%endrep
-
-%xdefine F F1
-
-%if (%1 == 1)                         ;; code loops through more than one block
-%%_loop:
-cmp BUFFER_PTR, K_BASE          ;; we use K_BASE value as a signal of a last block,
-jne %%_begin                    ;; it is set below by: cmovae BUFFER_PTR, K_BASE
-jmp %%_end
-
-align 32
-%%_begin:
-%endif
-RR A,B,C,D,E,0
-RR D,E,A,B,C,2
-RR B,C,D,E,A,4
-RR E,A,B,C,D,6
-RR C,D,E,A,B,8
-
-RR A,B,C,D,E,10
-RR D,E,A,B,C,12
-RR B,C,D,E,A,14
-RR E,A,B,C,D,16
-RR C,D,E,A,B,18
-
-%xdefine F F2
-
-RR A,B,C,D,E,20 
-RR D,E,A,B,C,22 
-RR B,C,D,E,A,24 
-RR E,A,B,C,D,26 
-RR C,D,E,A,B,28 
-
-RR A,B,C,D,E,30 
-RR D,E,A,B,C,32 
-RR B,C,D,E,A,34 
-RR E,A,B,C,D,36 
-RR C,D,E,A,B,38 
-
-%xdefine F F3
-
-RR A,B,C,D,E,40 
-RR D,E,A,B,C,42 
-RR B,C,D,E,A,44 
-RR E,A,B,C,D,46 
-RR C,D,E,A,B,48 
-
-RR A,B,C,D,E,50 
-RR D,E,A,B,C,52 
-RR B,C,D,E,A,54 
-RR E,A,B,C,D,56 
-RR C,D,E,A,B,58 
-
-%xdefine F F4
-
-%if (%1 == 1)                         ;; if code loops through more than one block
-add   BUFFER_PTR, 64            ;; move to next 64-byte block
-cmp   BUFFER_PTR, BUFFER_END    ;; check if current block is the last one
-cmovae BUFFER_PTR, K_BASE       ;; smart way to signal the last iteration
-%else
-%xdefine W_NO_TAIL_PRECALC 1    ;; no software pipelining for single block interface
-%endif
-
-RR A,B,C,D,E,60
-RR D,E,A,B,C,62
-RR B,C,D,E,A,64
-RR E,A,B,C,D,66
-RR C,D,E,A,B,68
-
-RR A,B,C,D,E,70
-RR D,E,A,B,C,72
-RR B,C,D,E,A,74
-RR E,A,B,C,D,76
-RR C,D,E,A,B,78
-
-UPDATE_HASH [HASH_PTR   ],A
-UPDATE_HASH [HASH_PTR+ 4],B
-UPDATE_HASH [HASH_PTR+ 8],C
-UPDATE_HASH [HASH_PTR+12],D
-UPDATE_HASH [HASH_PTR+16],E
-
-%if (%1 == 1)
-jmp %%_loop
-
-align 32  
-%%_end:
-%endif
-
-
-%xdefine W_NO_TAIL_PRECALC 0
-%xdefine F %error
-
-%endmacro
-
-
-%macro F1 3
-mov T1,%2
-xor T1,%3
-and T1,%1
-xor T1,%3
-%endmacro
-
-%macro F2 3
-mov T1,%3
-xor T1,%2
-xor T1,%1
-%endmacro
-
-%macro F3 3
-mov T1,%2
-mov T2,%1
-or  T1,%1
-and T2,%2
-and T1,%3
-or  T1,T2
-%endmacro
-
-%define F4 F2
-
-%macro UPDATE_HASH 2
-add %2, %1
-mov %1, %2
-%endmacro 
-
-
-%macro W_PRECALC 1
-%xdefine i (%1)
-
-%if (i < 20)
-%xdefine K_XMM  0
-%elif (i < 40)
-%xdefine K_XMM  16
-%elif (i < 60)
-%xdefine K_XMM  32
-%else
-%xdefine K_XMM  48
-%endif
-
-%if (i<16 || (i>=80 && i<(80 + W_PRECALC_AHEAD)))
-
-%if (W_NO_TAIL_PRECALC == 0)
-
-%xdefine i ((%1) % 80)        ;; pre-compute for the next iteration
-
-%if (i == 0)
-W_PRECALC_RESET
-%endif
-
-
-W_PRECALC_00_15
-%endif
-
-%elif (i < 32)
-W_PRECALC_16_31
-%elif (i < 80)   ;; rounds 32-79
-W_PRECALC_32_79
-%endif
-%endmacro
-
-%macro W_PRECALC_RESET 0
-%xdefine    W             W0
-%xdefine    W_minus_04    W4
-%xdefine    W_minus_08    W8
-%xdefine    W_minus_12    W12
-%xdefine    W_minus_16    W16
-%xdefine    W_minus_20    W20
-%xdefine    W_minus_24    W24
-%xdefine    W_minus_28    W28
-%xdefine    W_minus_32    W
-%endmacro
-
-%macro W_PRECALC_ROTATE 0
-%xdefine    W_minus_32    W_minus_28
-%xdefine    W_minus_28    W_minus_24
-%xdefine    W_minus_24    W_minus_20
-%xdefine    W_minus_20    W_minus_16
-%xdefine    W_minus_16    W_minus_12
-%xdefine    W_minus_12    W_minus_08
-%xdefine    W_minus_08    W_minus_04
-%xdefine    W_minus_04    W
-%xdefine    W             W_minus_32
-%endmacro
-
-%xdefine W_PRECALC_AHEAD   16
-%xdefine W_NO_TAIL_PRECALC 0
-
-
-%xdefine xmm_mov            movdqa
-
-%macro W_PRECALC_00_15 0
-;; message scheduling pre-compute for rounds 0-15
-%if ((i & 3) == 0)       ;; blended SSE and ALU instruction scheduling, 1 vector iteration per 4 rounds
-movdqu W_TMP, [BUFFER_PTR + (i * 4)]
-%elif ((i & 3) == 1)
-pshufb W_TMP, XMM_SHUFB_BSWAP
-movdqa W, W_TMP
-%elif ((i & 3) == 2)
-paddd  W_TMP, [K_BASE]
-%elif ((i & 3) == 3)
-movdqa  [WK(i&~3)], W_TMP
-
-W_PRECALC_ROTATE
-%endif
-%endmacro
-
-%macro W_PRECALC_16_31 0
-;; message scheduling pre-compute for rounds 16-31
-;; calculating last 32 w[i] values in 8 XMM registers
-;; pre-calculate K+w[i] values and store to mem, for later load by ALU add instruction
-;;
-;; "brute force" vectorization for rounds 16-31 only due to w[i]->w[i-3] dependency
-;;
-%if ((i & 3) == 0)    ;; blended SSE and ALU instruction scheduling, 1 vector iteration per 4 rounds
-movdqa  W, W_minus_12
-palignr W, W_minus_16, 8       ;; w[i-14]
-movdqa  W_TMP, W_minus_04
-psrldq  W_TMP, 4               ;; w[i-3]
-pxor    W, W_minus_08
-%elif ((i & 3) == 1)
-pxor    W_TMP, W_minus_16
-pxor    W, W_TMP
-movdqa  W_TMP2, W
-movdqa  W_TMP, W
-pslldq  W_TMP2, 12
-%elif ((i & 3) == 2)
-psrld   W, 31
-pslld   W_TMP, 1
-por     W_TMP, W
-movdqa  W, W_TMP2
-psrld   W_TMP2, 30
-pslld   W, 2
-%elif ((i & 3) == 3)
-pxor    W_TMP, W
-pxor    W_TMP, W_TMP2
-movdqa  W, W_TMP
-paddd   W_TMP, [K_BASE + K_XMM]
-movdqa  [WK(i&~3)],W_TMP
-
-W_PRECALC_ROTATE
-%endif
-%endmacro
-
-%macro W_PRECALC_32_79 0
-;; in SHA-1 specification: w[i] = (w[i-3] ^ w[i-8]  ^ w[i-14] ^ w[i-16]) rol 1
-;; instead we do equal:    w[i] = (w[i-6] ^ w[i-16] ^ w[i-28] ^ w[i-32]) rol 2
-;; allows more efficient vectorization since w[i]=>w[i-3] dependency is broken
-;;
-%if ((i & 3) == 0)    ;; blended SSE and ALU instruction scheduling, 1 vector iteration per 4 rounds
-movdqa  W_TMP, W_minus_04
-pxor    W, W_minus_28         ;; W is W_minus_32 before xor
-palignr W_TMP, W_minus_08, 8
-%elif ((i & 3) == 1)
-pxor    W, W_minus_16
-pxor    W, W_TMP
-movdqa  W_TMP, W
-%elif ((i & 3) == 2)
-psrld   W, 30
-pslld   W_TMP, 2
-por     W_TMP, W
-%elif ((i & 3) == 3)
-movdqa  W, W_TMP
-paddd   W_TMP, [K_BASE + K_XMM]
-movdqa  [WK(i&~3)],W_TMP
-
-W_PRECALC_ROTATE
-%endif
-%endmacro 
-
-%macro RR 6             ;; RR does two rounds of SHA-1 back to back with W pre-calculation
-
-;;     TEMP = A
-;;     A = F( i, B, C, D ) + E + ROTATE_LEFT( A, 5 ) + W[i] + K(i)
-;;     C = ROTATE_LEFT( B, 30 )
-;;     D = C
-;;     E = D
-;;     B = TEMP
-
-W_PRECALC (%6 + W_PRECALC_AHEAD)
-F    %2, %3, %4     ;; F returns result in T1
-add  %5, [WK(%6)]
-rol  %2, 30
-mov  T2, %1
-add  %4, [WK(%6 + 1)]
-rol  T2, 5
-add  %5, T1
-
-W_PRECALC (%6 + W_PRECALC_AHEAD + 1)
-add  T2, %5
-mov  %5, T2
-rol  T2, 5
-add  %4, T2
-F    %1, %2, %3    ;; F returns result in T1
-add  %4, T1
-rol  %1, 30
-
-;; write:  %1, %2
-;; rotate: %1<=%4, %2<=%5, %3<=%1, %4<=%2, %5<=%3
-%endmacro
-
-
-
-;;----------------------
-section .data
-align 128
-
-%xdefine K1 0x5a827999
-%xdefine K2 0x6ed9eba1
-%xdefine K3 0x8f1bbcdc
-%xdefine K4 0xca62c1d6
-
-align 128
-K_XMM_AR:
-DD K1, K1, K1, K1
-DD K2, K2, K2, K2
-DD K3, K3, K3, K3
-DD K4, K4, K4, K4
-
-align 16
-bswap_shufb_ctl:
-DD 00010203h
-DD 04050607h
-DD 08090a0bh
-DD 0c0d0e0fh
-
-;; dispatch pointer, points to the init routine for the first invocation
-sha1_update_intel_dispatched:
-DQ  sha1_update_intel_init_
-
-;;----------------------
-section .text
-align 4096
-
-SHA1_VECTOR_ASM     sha1_update_intel_ssse3_, multiblock
-
-align 32
-sha1_update_intel_init_:       ;; we get here with the first time invocation
-call    sha1_update_intel_dispacth_init_
-INTEL_SHA1_UPDATE_FUNCNAME:    ;; we get here after init
-jmp     qword [sha1_update_intel_dispatched]
-
-;; CPUID feature flag based dispatch
-sha1_update_intel_dispacth_init_:
-push    rax
-push    rbx
-push    rcx
-push    rdx
-push    rsi
-
-lea     rsi, [INTEL_SHA1_UPDATE_DEFAULT_DISPATCH]
-
-mov     eax, 1
-cpuid
-
-test    ecx, 0200h          ;; SSSE3 support, CPUID.1.ECX[bit 9]
-jz      _done
-
-lea     rsi, [sha1_update_intel_ssse3_]
-
-_done:
-mov     [sha1_update_intel_dispatched], rsi
-
-pop     rsi
-pop     rdx
-pop     rcx
-pop     rbx
-pop     rax
-ret
-
-;;----------------------
-;; in the case a default SHA-1 update function implementation was not provided
-;; and code was invoked on a non-SSSE3 supporting CPU, dispatch handles this 
-;; failure in a safest way - jumps to the stub function with UD2 instruction below
-sha1_intel_non_ssse3_cpu_stub_:
-ud2     ;; in the case no default SHA-1 was provided non-SSSE3 CPUs safely fail here
-ret
-
-; END
-;----------------------
index d15325063d92b6e047ccfb438a7b683d4fb056ab..fbbfdc18d1bdde0a64bd57981c37e3cdb1aa4665 100644 (file)
 /*
  * sha1.c - implementation of the Secure Hash Algorithm version 1 (FIPS 180-1)
  *
- * The following copying information applies to this specific source code file:
+ * Copyright 2022-2023 Eric Biggers
  *
- * Written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
+ * 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:
  *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ * 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.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#include "wimlib/cpu_features.h"
 #include "wimlib/endianness.h"
 #include "wimlib/sha1.h"
 #include "wimlib/unaligned.h"
 
-/* Dummy SHA-1 message digest of all 0's.  This is used in the WIM format to
- * mean "SHA-1 not specified".  */
-const u8 zero_hash[20];
+/*----------------------------------------------------------------------------*
+ *                              Shared helpers                                *
+ *----------------------------------------------------------------------------*/
 
-/*
- * Builds a hexadecimal string representation of a SHA-1 message digest.
- *
- * The output buffer must be at least 41 characters.
- */
-void
-sprint_hash(const u8 hash[SHA1_HASH_SIZE], tchar strbuf[SHA1_HASH_SIZE * 2 + 1])
+static inline u32
+rol32(u32 v, int bits)
 {
-       int i;
-       u8 high, low;
-
-       for (i = 0; i < SHA1_HASH_SIZE; i++) {
-               high = hash[i] >> 4;
-               low = hash[i] & 0xF;
-               strbuf[i * 2 + 0] = (high < 10 ? high + '0' : high - 10 + 'a');
-               strbuf[i * 2 + 1] = (low  < 10 ? low  + '0' : low  - 10 + 'a');
-       }
-       strbuf[i * 2] = 0;
+       return (v << bits) | (v >> (32 - bits));
 }
 
-/* If we use libcrypto (e.g. OpenSSL) then we get all the SHA-1 functions for
- * free.  Otherwise we need to implement them ourselves.  */
+/* Expands to the round constant for the given round */
+#define SHA1_K(i)                      \
+       (((i) < 20) ? 0x5A827999 :      \
+        ((i) < 40) ? 0x6ED9EBA1 :      \
+        ((i) < 60) ? 0x8F1BBCDC :      \
+                     0xCA62C1D6)
 
-#ifndef WITH_LIBCRYPTO
+/* Expands to the computation on b, c, and d for the given round */
+#define SHA1_F(i, b, c, d)                                     \
+       (((i) < 20) ? /* Choice */ (b & (c ^ d)) ^ d :          \
+        ((i) < 40) ? /* Parity */ b ^ c ^ d :                  \
+        ((i) < 60) ? /* Majority */ (c & d) ^ (b & (c ^ d)) :  \
+                     /* Parity */ b ^ c ^ d)
 
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+/*
+ * Expands to a memory barrier for the given array, preventing values of the
+ * array from being cached in registers past the barrier.  Use this to prevent
+ * the compiler from making counter-productive optimizations when there aren't
+ * enough registers available to hold the full array.
+ */
+#define FORCE_NOT_CACHED(array)        asm volatile("" : "+m" (array))
 
-#define blk0(i) (tmp[i] = be32_to_cpu(load_be32_unaligned(&(block)[(i) * 4])))
+/*
+ * Expands to FORCE_NOT_CACHED() if the architecture has 16 or fewer general
+ * purpose registers, otherwise does nothing.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
+#  define FORCE_NOT_CACHED_IF_FEW_REGS(array)  FORCE_NOT_CACHED(array)
+#else
+#  define FORCE_NOT_CACHED_IF_FEW_REGS(array)  (void)(array)
+#endif
 
-#define blk(i) (tmp[i & 15] = rol(tmp[(i + 13) & 15] ^ \
-                                 tmp[(i +  8) & 15] ^ \
-                                 tmp[(i +  2) & 15] ^ \
-                                 tmp[(i +  0) & 15], 1))
+/*----------------------------------------------------------------------------*
+ *                         Generic implementation                             *
+ *----------------------------------------------------------------------------*/
 
-#define R0(v, w, x, y, z, i) \
-       z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
-       w = rol(w, 30);
+/*
+ * This is SHA-1 in portable C code.  It computes the message schedule
+ * just-in-time, in a rolling window of length 16.
+ */
 
-#define R1(v, w, x, y, z, i) \
-       z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
-       w = rol(w, 30);
+#define SHA1_GENERIC_ROUND(i, a, b, c, d, e)                           \
+       FORCE_NOT_CACHED_IF_FEW_REGS(w);                                \
+       if ((i) < 16)                                                   \
+               w[i] = get_unaligned_be32(data + ((i) * 4));            \
+       else                                                            \
+               w[(i) % 16] = rol32(w[((i) - 16) % 16] ^                \
+                                   w[((i) - 14) % 16] ^                \
+                                   w[((i) -  8) % 16] ^                \
+                                   w[((i) -  3) % 16], 1);             \
+       e += w[(i) % 16] + rol32(a, 5) + SHA1_F((i), b, c, d) + SHA1_K(i); \
+       b = rol32(b, 30);
+       /* implicit: the new (a, b, c, d, e) is the old (e, a, b, c, d) */
+
+#define SHA1_GENERIC_5ROUNDS(i)                                \
+       SHA1_GENERIC_ROUND((i) + 0, a, b, c, d, e);     \
+       SHA1_GENERIC_ROUND((i) + 1, e, a, b, c, d);     \
+       SHA1_GENERIC_ROUND((i) + 2, d, e, a, b, c);     \
+       SHA1_GENERIC_ROUND((i) + 3, c, d, e, a, b);     \
+       SHA1_GENERIC_ROUND((i) + 4, b, c, d, e, a);
+
+#define SHA1_GENERIC_20ROUNDS(i)       \
+       SHA1_GENERIC_5ROUNDS((i) +  0); \
+       SHA1_GENERIC_5ROUNDS((i) +  5); \
+       SHA1_GENERIC_5ROUNDS((i) + 10); \
+       SHA1_GENERIC_5ROUNDS((i) + 15);
 
-#define R2(v, w, x, y, z, i) \
-       z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
-       w = rol(w, 30);
+static void
+sha1_blocks_generic(u32 h[5], const void *data, size_t num_blocks)
+{
+       do {
+               u32 a = h[0];
+               u32 b = h[1];
+               u32 c = h[2];
+               u32 d = h[3];
+               u32 e = h[4];
+               u32 w[16];
+
+               SHA1_GENERIC_20ROUNDS(0);
+               SHA1_GENERIC_20ROUNDS(20);
+               SHA1_GENERIC_20ROUNDS(40);
+               SHA1_GENERIC_20ROUNDS(60);
+
+               h[0] += a;
+               h[1] += b;
+               h[2] += c;
+               h[3] += d;
+               h[4] += e;
+               data += SHA1_BLOCK_SIZE;
+       } while (--num_blocks);
+}
 
-#define R3(v, w, x, y, z, i) \
-       z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
-       w = rol(w, 30);
+/*----------------------------------------------------------------------------*
+ *                    x86 SSSE3 (and AVX+BMI2) implementation                 *
+ *----------------------------------------------------------------------------*/
 
-#define R4(v, w, x, y, z, i) \
-       z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
-       w = rol(w, 30);
+/*
+ * This is SHA-1 using the x86 SSSE3 instructions.  A copy of it is also
+ * compiled with AVX and BMI2 code generation enabled for improved performance.
+ *
+ * Unfortunately this isn't actually much faster than the generic
+ * implementation, since only the message schedule can be vectorized, not the
+ * SHA itself.  The vectorized computation of the message schedule is
+ * interleaved with the scalar computation of the SHA itself.
+ *
+ * Specifically, 16 rounds ahead of time, the words of the message schedule are
+ * calculated, the round constants are added to them, and they are stored in a
+ * temporary array that the scalar code reads from later.  This is done 4 words
+ * at a time, but split into 4 steps, so that one step is executed during each
+ * round.  Rounds 16-31 use the usual formula 'w[i] = rol32(w[i-16] ^ w[i-14] ^
+ * w[i-8] ^ w[i-3], 1)', while rounds 32-79 use the equivalent formula 'w[i] =
+ * rol32(w[i-32] ^ w[i-28] ^ w[i-16] ^ w[i-6], 2)' for improved vectorization.
+ *
+ * During rounds 80-95, the first 16 message schedule words for the next block
+ * are prepared.
+ */
+#if defined(__i386__) || defined(__x86_64__)
+#include <immintrin.h>
+
+#define SHA1_SSSE3_PRECALC(i, w0, w1, w2, w3, w4, w5, w6, w7)          \
+       if ((i) % 20 == 0)                                              \
+               k = _mm_set1_epi32(SHA1_K((i) % 80));                   \
+       if ((i) < 32) {                                                 \
+               /*
+                * Vectorized computation of w[i] = rol32(w[i-16] ^ w[i-14] ^
+                * w[i-8] ^ w[i-3], 1) for i...i+3, split into 4 steps.
+                * w[i-16..i+3] are in (w0, w1, w2, w3, w4).
+                */                                                     \
+               if ((i) % 4 == 0) {                                     \
+                       w4 = _mm_alignr_epi8(w1, w0, 8) ^ w2;           \
+                       t0 = _mm_srli_si128(w3, 4);                     \
+               } else if ((i) % 4 == 1) {                              \
+                       t0 ^= w4 ^ w0;                                  \
+                       t1 = _mm_slli_si128(t0, 12);                    \
+               } else if ((i) % 4 == 2) {                              \
+                       t2 = _mm_slli_epi32(t1, 2);                     \
+                       w4 = _mm_slli_epi32(t0, 1);                     \
+                       t0 = _mm_srli_epi32(t0, 31);                    \
+                       t2 ^= _mm_srli_epi32(t1, 30);                   \
+               } else {                                                \
+                       w4 ^= t0 ^ t2;                                  \
+                       t0 = _mm_add_epi32(w4, k);                      \
+                       _mm_store_si128((__m128i *)&tmp[((i) - 3) % 16], t0);   \
+               }                                                       \
+       } else if ((i) < 80) {                                          \
+               /*
+                * Vectorized computation of w[i] = rol32(w[i-32] ^ w[i-28] ^
+                * w[i-16] ^ w[i-6], 2) for i...i+3, split into 4 steps.
+                * w[i-32..i+3] are in (w4, w5, w6, w7, w0, w1, w2, w3, w4);
+                * note the reuse of w4.
+                */                                                     \
+               if ((i) % 4 == 0)                                       \
+                       w4 ^= _mm_alignr_epi8(w3, w2, 8);               \
+               else if ((i) % 4 == 1)                                  \
+                       w4 ^= w5 ^ w0;                                  \
+               else if ((i) % 4 == 2)                                  \
+                       w4 = _mm_slli_epi32(w4, 2) ^                    \
+                            _mm_srli_epi32(w4, 30);                    \
+               else                                                    \
+                       _mm_store_si128((__m128i *)&tmp[((i) - 3) % 16],\
+                                       _mm_add_epi32(w4, k));          \
+       } else if ((i) < 96) {                                          \
+               /* Precomputation of w[0..15] for next block */         \
+               if ((i) == 80 && --num_blocks != 0)                     \
+                       data += SHA1_BLOCK_SIZE;                        \
+               if ((i) % 4 == 0)                                       \
+                       w0 = _mm_loadu_si128(data + (((i) - 80) * 4));  \
+               else if ((i) % 4 == 1)                                  \
+                       w0 = _mm_shuffle_epi8(w0, bswap32_mask);        \
+               else if ((i) % 4 == 2)                                  \
+                       t0 = _mm_add_epi32(w0, k);                      \
+               else                                                    \
+                       _mm_store_si128((__m128i *)&tmp[(i) - 83], t0); \
+       }
 
-/* Hash a single 512-bit block. This is the core of the algorithm.  */
-static void
-sha1_transform_default(u32 state[5], const u8 block[64])
+#define SHA1_SSSE3_2ROUNDS(i, a, b, c, d, e, w0, w1, w2, w3, w4, w5, w6, w7) \
+       FORCE_NOT_CACHED(tmp);                                          \
+       e += tmp[(i) % 16] + rol32(a, 5) + SHA1_F((i), b, c, d);        \
+       b = rol32(b, 30);                                               \
+       SHA1_SSSE3_PRECALC((i) + 16, w0, w1, w2, w3, w4, w5, w6, w7);   \
+       FORCE_NOT_CACHED(tmp);                                          \
+       d += tmp[((i) + 1) % 16] + rol32(e, 5) + SHA1_F((i) + 1, a, b, c); \
+       SHA1_SSSE3_PRECALC((i) + 17, w0, w1, w2, w3, w4, w5, w6, w7);   \
+       a = rol32(a, 30);
+       /* implicit: the new (a, b, c, d, e) is the old (d, e, a, b, c) */
+
+#define SHA1_SSSE3_4ROUNDS(i, a, b, c, d, e, w0, w1, w2, w3, w4, w5, w6, w7)   \
+       SHA1_SSSE3_2ROUNDS((i) + 0, a, b, c, d, e, w0, w1, w2, w3, w4, w5, w6, w7); \
+       SHA1_SSSE3_2ROUNDS((i) + 2, d, e, a, b, c, w0, w1, w2, w3, w4, w5, w6, w7); \
+       /*
+        * implicit: the new (w0-w7) is the old (w1-w7,w0),
+        * and the new (a, b, c, d, e) is the old (b, c, d, e, a)
+        */
+
+#define SHA1_SSSE3_20ROUNDS(i, w0, w1, w2, w3, w4, w5, w6, w7)         \
+       SHA1_SSSE3_4ROUNDS((i) +  0, a, b, c, d, e, w0, w1, w2, w3, w4, w5, w6, w7); \
+       SHA1_SSSE3_4ROUNDS((i) +  4, b, c, d, e, a, w1, w2, w3, w4, w5, w6, w7, w0); \
+       SHA1_SSSE3_4ROUNDS((i) +  8, c, d, e, a, b, w2, w3, w4, w5, w6, w7, w0, w1); \
+       SHA1_SSSE3_4ROUNDS((i) + 12, d, e, a, b, c, w3, w4, w5, w6, w7, w0, w1, w2); \
+       SHA1_SSSE3_4ROUNDS((i) + 16, e, a, b, c, d, w4, w5, w6, w7, w0, w1, w2, w3);
+       /* implicit: the new (w0-w7) is the old (w5-w7,w0-w4) */
+
+#define SHA1_SSSE3_BODY                                                        \
+       const __m128i bswap32_mask =                                    \
+               _mm_setr_epi8( 3,  2,  1,  0,  7,  6,  5,  4,           \
+                             11, 10,  9,  8, 15, 14, 13, 12);          \
+       __m128i w0, w1, w2, w3, w4, w5, w6, w7;                         \
+       __m128i k = _mm_set1_epi32(SHA1_K(0));                          \
+       u32 tmp[16] __attribute__((aligned(16)));                       \
+                                                                       \
+       w0 = _mm_shuffle_epi8(_mm_loadu_si128(data +  0), bswap32_mask); \
+       w1 = _mm_shuffle_epi8(_mm_loadu_si128(data + 16), bswap32_mask); \
+       w2 = _mm_shuffle_epi8(_mm_loadu_si128(data + 32), bswap32_mask); \
+       w3 = _mm_shuffle_epi8(_mm_loadu_si128(data + 48), bswap32_mask); \
+       _mm_store_si128((__m128i *)&tmp[0], _mm_add_epi32(w0, k));      \
+       _mm_store_si128((__m128i *)&tmp[4], _mm_add_epi32(w1, k));      \
+       _mm_store_si128((__m128i *)&tmp[8], _mm_add_epi32(w2, k));      \
+       _mm_store_si128((__m128i *)&tmp[12], _mm_add_epi32(w3, k));     \
+                                                                       \
+       do {                                                            \
+               u32 a = h[0];                                           \
+               u32 b = h[1];                                           \
+               u32 c = h[2];                                           \
+               u32 d = h[3];                                           \
+               u32 e = h[4];                                           \
+               __m128i t0, t1, t2;                                     \
+                                                                       \
+               SHA1_SSSE3_20ROUNDS(0, w0, w1, w2, w3, w4, w5, w6, w7); \
+               SHA1_SSSE3_20ROUNDS(20, w5, w6, w7, w0, w1, w2, w3, w4); \
+               SHA1_SSSE3_20ROUNDS(40, w2, w3, w4, w5, w6, w7, w0, w1); \
+               SHA1_SSSE3_20ROUNDS(60, w7, w0, w1, w2, w3, w4, w5, w6); \
+                                                                       \
+               h[0] += a;                                              \
+               h[1] += b;                                              \
+               h[2] += c;                                              \
+               h[3] += d;                                              \
+               h[4] += e;                                              \
+                                                                       \
+               /* 'data' and 'num_blocks' were updated at start of round 64. */ \
+       } while (num_blocks);
+
+#define HAVE_SHA1_BLOCKS_X86_SSSE3
+static void __attribute__((target("ssse3")))
+sha1_blocks_x86_ssse3(u32 h[5], const void *data, size_t num_blocks)
 {
-       u32 a, b, c, d, e;
-       u32 tmp[16];
-
-       /* Copy ctx->state[] to working vars */
-       a = state[0];
-       b = state[1];
-       c = state[2];
-       d = state[3];
-       e = state[4];
-
-       /* 4 rounds of 20 operations each. Loop unrolled. */
-       R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
-       R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
-       R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
-       R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
-       R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
-       R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
-       R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
-       R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
-       R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
-       R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
-       R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
-       R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
-       R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
-       R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
-       R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
-       R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
-       R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
-       R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
-       R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
-       R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
-
-       /* Add the working vars back into context.state[] */
-       state[0] += a;
-       state[1] += b;
-       state[2] += c;
-       state[3] += d;
-       state[4] += e;
+       SHA1_SSSE3_BODY;
 }
 
-#ifdef ENABLE_SSSE3_SHA1
-extern void
-sha1_transform_blocks_ssse3(u32 state[5], const void *data, size_t num_blocks);
-extern void
-sha1_transform_blocks_default(u32 state[5], const void *data, size_t num_blocks);
-#  define sha1_transform_blocks sha1_transform_blocks_ssse3
-#else
-#  define sha1_transform_blocks sha1_transform_blocks_default
+#define HAVE_SHA1_BLOCKS_X86_AVX_BMI2
+static void __attribute__((target("avx,bmi2")))
+sha1_blocks_x86_avx_bmi2(u32 h[5], const void *data, size_t num_blocks)
+{
+       SHA1_SSSE3_BODY;
+}
+#endif /* x86 SSSE3 (and AVX+BMI2) implementation */
+
+/*----------------------------------------------------------------------------*
+ *                        x86 SHA Extensions implementation                   *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * This is SHA-1 using the x86 SHA extensions.
+ *
+ * The SHA1RNDS4 instruction does most of the work.  It takes in a 128-bit
+ * vector containing 'a', 'b', 'c', and 'd' (high-order to low-order), a 128-bit
+ * vector containing the next 4 words of the message schedule with 'e' added to
+ * the high-order word, and an immediate that identifies the current 20-round
+ * section.  It does 4 rounds and updates 'a', 'b', 'c', and 'd' accordingly.
+ *
+ * Each SHA1RNDS4 is paired with SHA1NEXTE.  It takes in the abcd vector,
+ * calculates the value of 'e' after 4 rounds, and adds it to the high-order
+ * word of a vector that contains the next 4 words of the message schedule.
+ *
+ * Each 4 words of the message schedule for rounds 16-79 is calculated as
+ * rol32(w[i-16] ^ w[i-14] ^ w[i-8] ^ w[i-3], 1) in three steps using the
+ * SHA1MSG1, PXOR, and SHA1MSG2 instructions.  This happens in a rolling window,
+ * so during the j'th set of 4 rounds we do the SHA1MSG2 step for j+1'th set of
+ * message schedule words, PXOR for j+2'th set, and SHA1MSG1 for the j+3'th set.
+ */
+#if defined(__i386__) || defined(__x86_64__)
+#include <immintrin.h>
+
+#define SHA1_NI_4ROUNDS(i, w0, w1, w2, w3, we0, we1)                   \
+       if ((i) < 16)                                                   \
+               w0 = _mm_shuffle_epi8(                                  \
+                       _mm_loadu_si128(data + ((i) * 4)), bswap_mask); \
+       if ((i) == 0)                                                   \
+               we0 = _mm_add_epi32(h_e, w0);                           \
+       else                                                            \
+               we0 = _mm_sha1nexte_epu32(/* old abcd */ we0, w0);      \
+       we1 = abcd;                                                     \
+       if ((i) >= 12 && (i) < 76)                                      \
+               w1 = _mm_sha1msg2_epu32(w1, w0);                        \
+       abcd = _mm_sha1rnds4_epu32(abcd, we0, (i) / 20);                \
+       if ((i) >= 8 && (i) < 72)                                       \
+               w2 ^= w0;                                               \
+       if ((i) >= 4 && (i) < 68)                                       \
+               w3 = _mm_sha1msg1_epu32(w3, w0);                        \
+       /*
+        * implicit: the new (w0, w1, w2, w3) is the old (w1, w2, w3, w0),
+        * and the new (we0, we1) is the old (we1, we0)
+        */
+
+#define SHA1_NI_16ROUNDS(i)                                    \
+       SHA1_NI_4ROUNDS((i) +  0, w0, w1, w2, w3, we0, we1);    \
+       SHA1_NI_4ROUNDS((i) +  4, w1, w2, w3, w0, we1, we0);    \
+       SHA1_NI_4ROUNDS((i) +  8, w2, w3, w0, w1, we0, we1);    \
+       SHA1_NI_4ROUNDS((i) + 12, w3, w0, w1, w2, we1, we0);
+
+#define HAVE_SHA1_BLOCKS_X86_SHA
+static void __attribute__((target("sha,sse4.1")))
+sha1_blocks_x86_sha(u32 h[5], const void *data, size_t num_blocks)
+{
+       const __m128i bswap_mask =
+               _mm_setr_epi8(15, 14, 13, 12, 11, 10,  9,  8,
+                             7,  6,   5,  4,  3,  2,  1,  0);
+       __m128i h_abcd = _mm_shuffle_epi32(
+                               _mm_loadu_si128((__m128i *)h), 0x1B);
+       __m128i h_e = _mm_setr_epi32(0, 0, 0, h[4]);
+
+       do {
+               __m128i abcd = h_abcd;
+               __m128i w0, w1, w2, w3, we0, we1;
+
+               SHA1_NI_16ROUNDS(0);
+               SHA1_NI_16ROUNDS(16);
+               SHA1_NI_16ROUNDS(32);
+               SHA1_NI_16ROUNDS(48);
+               SHA1_NI_16ROUNDS(64);
+
+               h_abcd = _mm_add_epi32(h_abcd, abcd);
+               h_e = _mm_sha1nexte_epu32(we0, h_e);
+               data += SHA1_BLOCK_SIZE;
+       } while (--num_blocks);
+
+       _mm_storeu_si128((__m128i *)h, _mm_shuffle_epi32(h_abcd, 0x1B));
+       h[4] = _mm_extract_epi32(h_e, 3);
+}
+#endif /* x86 SHA Extensions implementation */
+
+/*----------------------------------------------------------------------------*
+ *                     ARMv8 Crypto Extensions implementation                 *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * This is SHA-1 using the ARMv8 Crypto Extensions.
+ *
+ * This does 4 rounds at a time, and it works very similarily to the x86 SHA
+ * Extensions implementation.  The differences are fairly minor:
+ *
+ * - x86 has SHA1RNDS4 that takes an immediate that identifies the set of 20
+ *   rounds, and it handles adding the round constants.  ARM has SHA1C for
+ *   rounds 0-19, SHA1P for rounds 20-39 and 60-79, and SHA1M for rounds 40-59.
+ *   These don't add the round constants, so that must be done separately.
+ *
+ * - ARM needs only two instructions, instead of x86's three, to prepare each
+ *   set of 4 message schedule words: SHA1SU0 which does w[i-16] ^ w[i-14] ^
+ *   w[i-8], and SHA1SU1 which XOR's in w[i-3] and rotates left by 1.
+ */
+#if defined(__aarch64__) && \
+       (defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 5))
+
+/*
+ * clang's arm_neon.h used to have a bug where it only defined the SHA-1
+ * intrinsics when CRYPTO (clang 12 and earlier) or SHA2 (clang 13 and 14) is
+ * enabled in the main target.  This prevents them from being used in target
+ * attribute functions.  Work around this by defining the macros ourselves.
+ */
+#if defined(__clang__) && __clang_major__ <= 15
+#  ifndef __ARM_FEATURE_CRYPTO
+#    define __ARM_FEATURE_CRYPTO 1
+#    define DEFINED_ARM_FEATURE_CRYPTO
+#  endif
+#  ifndef __ARM_FEATURE_SHA2
+#    define __ARM_FEATURE_SHA2 1
+#    define DEFINED_ARM_FEATURE_SHA2
+#  endif
+#endif
+#include <arm_neon.h>
+#ifdef DEFINED_ARM_FEATURE_CRYPTO
+#  undef __ARM_FEATURE_CRYPTO
+#endif
+#ifdef DEFINED_ARM_FEATURE_SHA2
+#  undef __ARM_FEATURE_SHA2
 #endif
 
-#ifndef ENABLE_SSSE3_SHA1
-static
+/* Expands to a vector containing 4 copies of the given round's constant */
+#define SHA1_CE_K(i)           \
+       ((i) < 20 ? k0 :        \
+        (i) < 40 ? k1 :        \
+        (i) < 60 ? k2 :        \
+                   k3)
+
+/* Expands to the appropriate instruction for the given round */
+#define SHA1_CE_OP(i, abcd, e, w)                      \
+       ((i) < 20 ? vsha1cq_u32((abcd), (e), (w)) :     \
+        (i) < 40 ? vsha1pq_u32((abcd), (e), (w)) :     \
+        (i) < 60 ? vsha1mq_u32((abcd), (e), (w)) :     \
+                   vsha1pq_u32((abcd), (e), (w)))
+
+#define SHA1_CE_4ROUNDS(i, w0, w1, w2, w3, e0, e1)     \
+       tmp = w0 + SHA1_CE_K(i);                        \
+       e1 = vsha1h_u32(vgetq_lane_u32(abcd, 0));       \
+       abcd = SHA1_CE_OP((i), abcd, e0, tmp);          \
+       if ((i) >= 12 && (i) < 76)                      \
+               w1 = vsha1su1q_u32(w1, w0);             \
+       if ((i) >= 8 && (i) < 72)                       \
+               w2 = vsha1su0q_u32(w2, w3, w0);
+       /*
+        * implicit: the new (w0, w1, w2, w3) is the old (w1, w2, w3, w0),
+        * and the new (e0, e1) is the old (e1, e0)
+        */
+
+#define SHA1_CE_16ROUNDS(i)                                    \
+       SHA1_CE_4ROUNDS((i) +  0, w0, w1, w2, w3, e0, e1);      \
+       SHA1_CE_4ROUNDS((i) +  4, w1, w2, w3, w0, e1, e0);      \
+       SHA1_CE_4ROUNDS((i) +  8, w2, w3, w0, w1, e0, e1);      \
+       SHA1_CE_4ROUNDS((i) + 12, w3, w0, w1, w2, e1, e0);
+
+#define HAVE_SHA1_BLOCKS_ARM_CE
+static void
+#ifdef __clang__
+       /*
+        * clang has the SHA-1 instructions under "sha2".  "crypto" used to work
+        * too, but only in clang 15 and earlier.  So, use "sha2" here.
+        */
+       __attribute__((target("sha2")))
+#else
+       /* gcc wants "+crypto".  "+sha2" doesn't work. */
+       __attribute__((target("+crypto")))
 #endif
-void
-sha1_transform_blocks_default(u32 state[5], const void *data, size_t num_blocks)
+sha1_blocks_arm_ce(u32 h[5], const void *data, size_t num_blocks)
 {
+       uint32x4_t h_abcd = vld1q_u32(h);
+       uint32x4_t k0 = vdupq_n_u32(SHA1_K(0));
+       uint32x4_t k1 = vdupq_n_u32(SHA1_K(20));
+       uint32x4_t k2 = vdupq_n_u32(SHA1_K(40));
+       uint32x4_t k3 = vdupq_n_u32(SHA1_K(60));
+
        do {
-               sha1_transform_default(state, data);
-               data += 64;
+               uint32x4_t abcd = h_abcd;
+               u32 e0 = h[4], e1;
+               uint32x4_t tmp, w0, w1, w2, w3;
+
+               w0 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 0)));
+               w1 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 16)));
+               w2 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 32)));
+               w3 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 48)));
+
+               SHA1_CE_16ROUNDS(0);
+               SHA1_CE_16ROUNDS(16);
+               SHA1_CE_16ROUNDS(32);
+               SHA1_CE_16ROUNDS(48);
+               SHA1_CE_16ROUNDS(64);
+
+               h_abcd += abcd;
+               h[4] += e0;
+               data += SHA1_BLOCK_SIZE;
        } while (--num_blocks);
+
+       vst1q_u32(h, h_abcd);
+}
+#endif /* ARMv8 Crypto Extensions implementation */
+
+/*----------------------------------------------------------------------------*
+ *                              Everything else                               *
+ *----------------------------------------------------------------------------*/
+
+static void
+sha1_blocks(u32 h[5], const void *data, size_t num_blocks)
+{
+#ifdef HAVE_SHA1_BLOCKS_X86_SHA
+       if ((cpu_features & (X86_CPU_FEATURE_SHA | X86_CPU_FEATURE_SSE4_1)) ==
+           (X86_CPU_FEATURE_SHA | X86_CPU_FEATURE_SSE4_1))
+               return sha1_blocks_x86_sha(h, data, num_blocks);
+#endif
+#ifdef HAVE_SHA1_BLOCKS_X86_AVX_BMI2
+       if ((cpu_features & (X86_CPU_FEATURE_AVX | X86_CPU_FEATURE_BMI2)) ==
+           (X86_CPU_FEATURE_AVX | X86_CPU_FEATURE_BMI2))
+               return sha1_blocks_x86_avx_bmi2(h, data, num_blocks);
+#endif
+#ifdef HAVE_SHA1_BLOCKS_X86_SSSE3
+       if (cpu_features & X86_CPU_FEATURE_SSSE3)
+               return sha1_blocks_x86_ssse3(h, data, num_blocks);
+#endif
+#ifdef HAVE_SHA1_BLOCKS_ARM_CE
+       if (cpu_features & ARM_CPU_FEATURE_SHA1)
+               return sha1_blocks_arm_ce(h, data, num_blocks);
+#endif
+       return sha1_blocks_generic(h, data, num_blocks);
 }
 
-/* Initializes the specified SHA-1 context.
+/*
+ * Initialize the given SHA-1 context.
  *
  * After sha1_init(), call sha1_update() zero or more times to provide the data
- * to be hashed.  Then call sha1_final() to get the final hash.  */
+ * to be hashed.  Then call sha1_final() to get the resulting message digest.
+ */
 void
-sha1_init(SHA_CTX *ctx)
+sha1_init(struct sha1_ctx *ctx)
 {
        ctx->bytecount = 0;
 
-       ctx->state[0] = 0x67452301;
-       ctx->state[1] = 0xEFCDAB89;
-       ctx->state[2] = 0x98BADCFE;
-       ctx->state[3] = 0x10325476;
-       ctx->state[4] = 0xC3D2E1F0;
+       ctx->h[0] = 0x67452301;
+       ctx->h[1] = 0xEFCDAB89;
+       ctx->h[2] = 0x98BADCFE;
+       ctx->h[3] = 0x10325476;
+       ctx->h[4] = 0xC3D2E1F0;
 }
 
-/* Updates the SHA-1 context with @len bytes of data.  */
+/* Update the SHA-1 context with @len bytes of data. */
 void
-sha1_update(SHA_CTX *ctx, const void *data, size_t len)
+sha1_update(struct sha1_ctx *ctx, const void *data, size_t len)
 {
-       unsigned buffered = ctx->bytecount & 63;
+       unsigned buffered = ctx->bytecount % SHA1_BLOCK_SIZE;
+       size_t blocks;
 
        ctx->bytecount += len;
 
        if (buffered) {
-               /* Previous block is unfinished.  */
-               if (len < 64 - buffered) {
+               unsigned remaining = SHA1_BLOCK_SIZE - buffered;
+
+               if (len < remaining) {
                        memcpy(&ctx->buffer[buffered], data, len);
-                       /* Previous block still unfinished.  */
                        return;
-               } else {
-                       memcpy(&ctx->buffer[buffered], data, 64 - buffered);
-                       /* Finished the previous block.  */
-                       sha1_transform_blocks(ctx->state, ctx->buffer, 1);
-                       data += 64 - buffered;
-                       len -= 64 - buffered;
                }
+               memcpy(&ctx->buffer[buffered], data, remaining);
+               sha1_blocks(ctx->h, ctx->buffer, 1);
+               data += remaining;
+               len -= remaining;
        }
 
-       /* Process blocks directly from the input data.  */
-       if (len / 64) {
-               sha1_transform_blocks(ctx->state, data, len / 64);
-               data += len & ~63;
-               len &= 63;
+       blocks = len / SHA1_BLOCK_SIZE;
+       if (blocks) {
+               sha1_blocks(ctx->h, data, blocks);
+               data += blocks * SHA1_BLOCK_SIZE;
+               len -= blocks * SHA1_BLOCK_SIZE;
        }
 
-       /* Copy any remaining bytes to the buffer.  */
        if (len)
                memcpy(ctx->buffer, data, len);
 }
 
-/* Pad the message and generate the final SHA-1 message digest.  */
+/* Finalize the SHA-1 operation and return the resulting message digest. */
 void
-sha1_final(u8 md[20], SHA_CTX *ctx)
+sha1_final(struct sha1_ctx *ctx, u8 hash[SHA1_HASH_SIZE])
 {
-       /* Logically, we must append 1 bit, then a variable number of 0 bits,
-        * then the message length in bits as a big-endian integer, so that the
-        * final length is a multiple of the block size.  */
-       static const u8 padding[64] = {0x80, };
-       be64 finalcount = cpu_to_be64(ctx->bytecount << 3);
-
-       sha1_update(ctx, padding, 64 - ((ctx->bytecount + 8) & 63));
-       sha1_update(ctx, &finalcount, 8);
-
-       for (int i = 0; i < 5; i++)
-               store_be32_unaligned(cpu_to_be32(ctx->state[i]), &md[i * 4]);
+       unsigned buffered = ctx->bytecount % SHA1_BLOCK_SIZE;
+       const be64 bitcount = cpu_to_be64(ctx->bytecount * 8);
+
+       ctx->buffer[buffered++] = 0x80;
+       if (buffered > SHA1_BLOCK_SIZE - 8) {
+               memset(&ctx->buffer[buffered], 0, SHA1_BLOCK_SIZE - buffered);
+               sha1_blocks(ctx->h, ctx->buffer, 1);
+               buffered = 0;
+       }
+       memset(&ctx->buffer[buffered], 0, SHA1_BLOCK_SIZE - 8 - buffered);
+       memcpy(&ctx->buffer[SHA1_BLOCK_SIZE - 8], &bitcount, 8);
+       sha1_blocks(ctx->h, ctx->buffer, 1);
+
+       put_unaligned_be32(ctx->h[0], &hash[0]);
+       put_unaligned_be32(ctx->h[1], &hash[4]);
+       put_unaligned_be32(ctx->h[2], &hash[8]);
+       put_unaligned_be32(ctx->h[3], &hash[12]);
+       put_unaligned_be32(ctx->h[4], &hash[16]);
 }
 
-/* Calculate the SHA-1 message digest of the specified buffer.
- * @len is the buffer length in bytes.  */
+/* Calculate the SHA-1 message digest of the given data. */
 void
-sha1_buffer(const void *buffer, size_t len, u8 md[20])
+sha1(const void *data, size_t len, u8 hash[SHA1_HASH_SIZE])
 {
-       SHA_CTX ctx;
+       struct sha1_ctx ctx;
 
        sha1_init(&ctx);
-       sha1_update(&ctx, buffer, len);
-       sha1_final(md, &ctx);
+       sha1_update(&ctx, data, len);
+       sha1_final(&ctx, hash);
 }
 
-#endif /* !WITH_LIBCRYPTO */
+/* "Null" SHA-1 message digest containing all 0's */
+const u8 zero_hash[SHA1_HASH_SIZE];
+
+/* Build a hexadecimal string representation of a SHA-1 message digest. */
+void
+sprint_hash(const u8 hash[SHA1_HASH_SIZE], tchar strbuf[SHA1_HASH_STRING_LEN])
+{
+       int i;
+       u8 high, low;
+
+       for (i = 0; i < SHA1_HASH_SIZE; i++) {
+               high = hash[i] >> 4;
+               low = hash[i] & 0xF;
+               strbuf[i * 2 + 0] = (high < 10 ? high + '0' : high - 10 + 'a');
+               strbuf[i * 2 + 1] = (low  < 10 ? low  + '0' : low  - 10 + 'a');
+       }
+       strbuf[i * 2] = 0;
+}
index 35f2eef0a20695c3a279af668a1c62ae22a4b3ed..a6aef41835ce93153a7f7fbbf7da31da5461ec78 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -206,7 +206,7 @@ sort_blob_list_for_solid_compression(struct list_head *blob_list)
                                                         blob_table.capacity]);
                        break;
                case BLOB_IN_FILE_ON_DISK:
-       #ifdef __WIN32__
+       #ifdef _WIN32
                case BLOB_IN_WINDOWS_FILE:
        #endif
                        blob_set_solid_sort_name_from_inode(blob, blob->file_inode);
index 1313a59aa10ef03c36101ed99508db9206fc2034..8205c71c577cc4d5fea0e2c1d4e74633f6fbb6a1 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -96,7 +96,6 @@ write_split_wim(WIMStruct *orig_wim, const tchar *swm_name,
 
        for (part_number = 1; part_number <= swm_info->num_parts; part_number++) {
                int part_write_flags;
-               wimlib_progress_func_t progfunc;
 
                if (part_number != 1) {
                        tsprintf(swm_name_buf + swm_base_name_len,
@@ -118,8 +117,6 @@ write_split_wim(WIMStruct *orig_wim, const tchar *swm_name,
                if (part_number != 1)
                        part_write_flags |= WIMLIB_WRITE_FLAG_NO_METADATA;
 
-               progfunc = orig_wim->progfunc;
-               orig_wim->progfunc = NULL;
                ret = write_wim_part(orig_wim,
                                     progress.split.part_name,
                                     WIMLIB_ALL_IMAGES,
@@ -129,7 +126,6 @@ write_split_wim(WIMStruct *orig_wim, const tchar *swm_name,
                                     swm_info->num_parts,
                                     &swm_info->parts[part_number - 1].blob_list,
                                     guid);
-               orig_wim->progfunc = progfunc;
                if (ret)
                        return ret;
 
index 69eb6404a6a0ffc0547f81feb219d5cf8fa364e6..dee065174c882c38880e577c5912bdcb6ef1851f 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -48,7 +48,7 @@ struct tagged_item_header {
        u8 data[0];
 
        /* then zero-padded to an 8-byte boundary */
-} _aligned_attribute(8);
+} __attribute__((aligned(8)));
 
 /*
  * Retrieve from @inode the first metadata item that is tagged with @tag and
index e5413e5fafa8c607ab50a942b17bdde45c41f847..2cface66bbef1240a3c3f12ff40127f0ebb416d3 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -114,7 +114,8 @@ inode_copy_checksums(struct wim_inode *inode,
 
                back_ptr = retrieve_pointer_to_unhashed_blob(blob);
                copy_hash(blob->hash, template_blob->hash);
-               if (after_blob_hashed(blob, back_ptr, blob_table) != blob)
+               if (after_blob_hashed(blob, back_ptr, blob_table,
+                                     inode) != blob)
                        free_blob_descriptor(blob);
        }
 }
index 5c566dcb25db164e5fc9a1d42e550df017e6d6fe..6a438fd9396fb3b34c6e7b53b4132bc4259e65a4 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2015-2018 Eric Biggers
+ * Copyright 2015-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 /*
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#ifdef _WIN32
+#  include <windows.h>
+#  include <sddl.h>
+#  undef ERROR
+#endif
 
 #include "wimlib.h"
 #include "wimlib/endianness.h"
@@ -50,6 +55,7 @@
 #include "wimlib/scan.h"
 #include "wimlib/security_descriptor.h"
 #include "wimlib/test_support.h"
+#include "wimlib/timestamp.h"
 #include "wimlib/unix_data.h"
 #include "wimlib/xattr.h"
 
@@ -63,20 +69,26 @@ struct generation_context {
        bool metadata_only;
 };
 
+static u64 random_state;
+
+WIMLIBAPI void
+wimlib_seed_random(u64 seed)
+{
+       random_state = seed;
+}
+
 static u32
 rand32(void)
 {
-       static u64 state = 0x55DB93D0AB838771;
-
-       /* A simple linear congruential generator  */
-       state = (state * 25214903917 + 11) & ((1ULL << 48) - 1);
-       return state >> 16;
+       /* A simple linear congruential generator */
+       random_state = (random_state * 25214903917 + 11) % (1ULL << 48);
+       return random_state >> 16;
 }
 
 static bool
 randbool(void)
 {
-       return (rand32() & 1) != 0;
+       return rand32() % 2;
 }
 
 static u8
@@ -100,10 +112,18 @@ rand64(void)
 static u64
 generate_random_timestamp(void)
 {
-       /* When setting timestamps on Windows:
+       u64 ts;
+
+       if (randbool())
+               ts = rand64();
+       else
+               ts = time_t_to_wim_timestamp(rand64() % (1ULL << 34));
+       /*
+        * When setting timestamps on Windows:
         * - 0 is a special value meaning "not specified"
-        * - if the high bit is set you get STATUS_INVALID_PARAMETER  */
-       return (1 + rand64()) & ~(1ULL << 63);
+        * - if the high bit is set you get STATUS_INVALID_PARAMETER
+        */
+       return max(1, ts % (1ULL << 63));
 }
 
 static inline bool
@@ -126,7 +146,7 @@ is_valid_windows_filename_char(utf16lechar c)
 static inline bool
 is_valid_filename_char(utf16lechar c)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        return is_valid_windows_filename_char(c);
 #else
        return c != cpu_to_le16('\0') && c != cpu_to_le16('/');
@@ -168,7 +188,7 @@ retry:
        /* Generate the characters in the name. */
        for (int i = 0; i < len; i++) {
                do {
-                       name[i] = rand16();
+                       name[i] = cpu_to_le16(rand16());
                } while (!is_valid_filename_char(name[i]));
        }
 
@@ -385,7 +405,7 @@ generate_random_security_descriptor(void *_desc, struct generation_context *ctx)
 static bool
 am_root(void)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        return false;
 #else
        return (getuid() == 0);
@@ -395,7 +415,7 @@ am_root(void)
 static u32
 generate_uid(void)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        return 0;
 #else
        if (am_root())
@@ -407,7 +427,7 @@ generate_uid(void)
 static u32
 generate_gid(void)
 {
-#ifdef __WIN32__
+#ifdef _WIN32
        return 0;
 #else
        if (am_root())
@@ -416,7 +436,7 @@ generate_gid(void)
 #endif
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  ifndef S_IFLNK
 #    define S_IFLNK  0120000
 #  endif
@@ -475,7 +495,7 @@ set_random_xattrs(struct wim_inode *inode)
        struct wim_xattr_entry *entry = (void *)entries;
        size_t entries_size;
        struct wimlib_unix_data unix_data;
-#ifdef __WIN32__
+#ifdef _WIN32
        const char *prefix = "";
 #else
        const char *prefix = "user.";
@@ -501,7 +521,7 @@ set_random_xattrs(struct wim_inode *inode)
                int value_len = rand32() % 64;
                u8 *p;
 
-       #ifdef __WIN32__
+       #ifdef _WIN32
                if (value_len == 0)
                        value_len++;
        #endif
@@ -523,7 +543,7 @@ set_random_xattrs(struct wim_inode *inode)
                        *p++ = 'A' + i;
                        for (int j = 1; j < name_len; j++) {
                                do {
-                               #ifdef __WIN32__
+                               #ifdef _WIN32
                                        *p = 'A' + rand8() % 26;
                                #else
                                        *p = rand8();
@@ -569,7 +589,7 @@ set_random_metadata(struct wim_inode *inode, struct generation_context *ctx)
 
        /* Security descriptor  */
        if (randbool()) {
-               char desc[8192] _aligned_attribute(8);
+               char desc[8192] __attribute__((aligned(8)));
                size_t size;
 
                size = generate_random_security_descriptor(desc, ctx);
@@ -801,7 +821,8 @@ set_random_streams(struct wim_inode *inode, struct generation_context *ctx)
                        ret = add_random_data_stream(inode, ctx, stream_name);
                        if (ret)
                                return ret;
-                       stream_name[0] += cpu_to_le16(1);
+                       stream_name[0] =
+                               cpu_to_le16(le16_to_cpu(stream_name[0]) + 1);
                }
        }
 
@@ -903,7 +924,7 @@ retry:
         * within the same directory.  */
        hash = 0;
        for (const utf16lechar *p = name; *p; p++)
-               hash = (hash * 31) + *p;
+               hash = (hash * 31) + le16_to_cpu(*p);
        FREE(child->d_short_name);
        child->d_short_name = memdup(name, (name_len + 1) * 2);
        child->d_short_name_nbytes = name_len * 2;
@@ -1247,14 +1268,11 @@ cmp_attributes(const struct wim_inode *inode1,
              !inode_is_symlink(inode1)))
                goto mismatch;
 
-       /* SPARSE_FILE may be cleared in UNIX and NTFS-3G modes, or in Windows
-        * mode if the inode is a directory. */
+       /* SPARSE_FILE may be cleared.  This is true in UNIX and NTFS-3G modes.
+        * In Windows mode it should only be true for directories, but even on
+        * nondirectories it doesn't work 100% of the time for some reason. */
        if ((changed & FILE_ATTRIBUTE_SPARSE_FILE) &&
-           !((cleared & FILE_ATTRIBUTE_SPARSE_FILE) &&
-             ((cmp_flags & (WIMLIB_CMP_FLAG_UNIX_MODE |
-                            WIMLIB_CMP_FLAG_NTFS_3G_MODE)) ||
-              ((cmp_flags & WIMLIB_CMP_FLAG_WINDOWS_MODE) &&
-               (inode1->i_attributes & FILE_ATTRIBUTE_DIRECTORY)))))
+           !(cleared & FILE_ATTRIBUTE_SPARSE_FILE))
                goto mismatch;
 
        /* COMPRESSED may change in UNIX and NTFS-3G modes.  (It *should* be
@@ -1291,6 +1309,73 @@ mismatch:
        return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
 }
 
+static void
+print_security_descriptor(const void *desc, size_t size, FILE *fp)
+{
+       print_byte_field(desc, size, fp);
+#ifdef _WIN32
+       wchar_t *str = NULL;
+       ConvertSecurityDescriptorToStringSecurityDescriptorW(
+                       (void *)desc,
+                       SDDL_REVISION_1,
+                       OWNER_SECURITY_INFORMATION |
+                               GROUP_SECURITY_INFORMATION |
+                               DACL_SECURITY_INFORMATION |
+                               SACL_SECURITY_INFORMATION,
+                       &str,
+                       NULL);
+       if (str) {
+               fprintf(fp, " [ %ls ]", str);
+               LocalFree(str);
+       }
+#endif /* _WIN32 */
+}
+
+static int
+cmp_security(const struct wim_inode *inode1, const struct wim_inode *inode2,
+            const struct wim_image_metadata *imd1,
+            const struct wim_image_metadata *imd2, int cmp_flags)
+{
+       /*
+        * Unfortunately this has to be disabled on Windows for now, since
+        * Windows changes security descriptors upon backup/restore in ways that
+        * are difficult to replicate...
+        */
+       if (cmp_flags & WIMLIB_CMP_FLAG_WINDOWS_MODE)
+               return 0;
+
+       if (inode_has_security_descriptor(inode1)) {
+               if (inode_has_security_descriptor(inode2)) {
+                       const void *desc1 = imd1->security_data->descriptors[inode1->i_security_id];
+                       const void *desc2 = imd2->security_data->descriptors[inode2->i_security_id];
+                       size_t size1 = imd1->security_data->sizes[inode1->i_security_id];
+                       size_t size2 = imd2->security_data->sizes[inode2->i_security_id];
+
+                       if (size1 != size2 || memcmp(desc1, desc2, size1)) {
+                               ERROR("Security descriptor of %"TS" differs!",
+                                     inode_any_full_path(inode1));
+                               fprintf(stderr, "desc1=");
+                               print_security_descriptor(desc1, size1, stderr);
+                               fprintf(stderr, "\ndesc2=");
+                               print_security_descriptor(desc2, size2, stderr);
+                               fprintf(stderr, "\n");
+                               return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
+                       }
+               } else if (!(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) {
+                       ERROR("%"TS" has a security descriptor in the first image but "
+                             "not in the second image!", inode_any_full_path(inode1));
+                       return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
+               }
+       } else if (inode_has_security_descriptor(inode2)) {
+               /* okay --- consider it acceptable if a default security
+                * descriptor was assigned  */
+               /*ERROR("%"TS" has a security descriptor in the second image but "*/
+                     /*"not in the first image!", inode_any_full_path(inode1));*/
+               /*return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;*/
+       }
+       return 0;
+}
+
 static int
 cmp_object_ids(const struct wim_inode *inode1,
               const struct wim_inode *inode2, int cmp_flags)
@@ -1500,26 +1585,60 @@ cmp_xattrs(const struct wim_inode *inode1, const struct wim_inode *inode2,
        }
 }
 
+/*
+ * ext4 only supports timestamps from years 1901 to 2446, more specifically the
+ * range [-0x80000000, 0x380000000) seconds relative to the start of UNIX epoch.
+ */
+static bool
+in_ext4_range(u64 ts)
+{
+       return ts >= time_t_to_wim_timestamp(-0x80000000LL) &&
+               ts < time_t_to_wim_timestamp(0x380000000LL);
+}
+
+static bool
+timestamps_differ(u64 ts1, u64 ts2, int cmp_flags)
+{
+       if (ts1 == ts2)
+               return false;
+       if ((cmp_flags & WIMLIB_CMP_FLAG_EXT4) &&
+           (!in_ext4_range(ts1) || !in_ext4_range(ts2)))
+               return false;
+       return true;
+}
+
 static int
 cmp_timestamps(const struct wim_inode *inode1, const struct wim_inode *inode2,
               int cmp_flags)
 {
-       if (inode1->i_creation_time != inode2->i_creation_time &&
+       if (timestamps_differ(inode1->i_creation_time,
+                             inode2->i_creation_time, cmp_flags) &&
            !(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) {
-               ERROR("Creation time of %"TS" differs",
-                     inode_any_full_path(inode1));
+               ERROR("Creation time of %"TS" differs; %"PRIu64" != %"PRIu64,
+                     inode_any_full_path(inode1),
+                     inode1->i_creation_time, inode2->i_creation_time);
                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
        }
 
-       if (inode1->i_last_write_time != inode2->i_last_write_time) {
-               ERROR("Last write time of %"TS" differs",
-                     inode_any_full_path(inode1));
+       if (timestamps_differ(inode1->i_last_write_time,
+                             inode2->i_last_write_time, cmp_flags)) {
+               ERROR("Last write time of %"TS" differs; %"PRIu64" != %"PRIu64,
+                     inode_any_full_path(inode1),
+                     inode1->i_last_write_time, inode2->i_last_write_time);
                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
        }
 
-       if (inode1->i_last_access_time != inode2->i_last_access_time) {
-               ERROR("Last access time of %"TS" differs",
-                     inode_any_full_path(inode1));
+       if (timestamps_differ(inode1->i_last_access_time,
+                             inode2->i_last_access_time, cmp_flags) &&
+           /*
+            * On Windows, sometimes a file's last access time will end up as
+            * the current time rather than the expected time.  Maybe caused by
+            * some OS process scanning the files?
+            */
+           !(cmp_flags & WIMLIB_CMP_FLAG_WINDOWS_MODE)) {
+               ERROR("Last access time of %"TS" differs; %"PRIu64" != %"PRIu64,
+                     inode_any_full_path(inode1),
+                     inode1->i_last_access_time, inode2->i_last_access_time);
                return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
        }
 
@@ -1539,30 +1658,9 @@ cmp_inodes(const struct wim_inode *inode1, const struct wim_inode *inode2,
                return ret;
 
        /* Compare security descriptors  */
-       if (inode_has_security_descriptor(inode1)) {
-               if (inode_has_security_descriptor(inode2)) {
-                       const void *desc1 = imd1->security_data->descriptors[inode1->i_security_id];
-                       const void *desc2 = imd2->security_data->descriptors[inode2->i_security_id];
-                       size_t size1 = imd1->security_data->sizes[inode1->i_security_id];
-                       size_t size2 = imd2->security_data->sizes[inode2->i_security_id];
-
-                       if (size1 != size2 || memcmp(desc1, desc2, size1)) {
-                               ERROR("Security descriptor of %"TS" differs!",
-                                     inode_any_full_path(inode1));
-                               return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
-                       }
-               } else if (!(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) {
-                       ERROR("%"TS" has a security descriptor in the first image but "
-                             "not in the second image!", inode_any_full_path(inode1));
-                       return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;
-               }
-       } else if (inode_has_security_descriptor(inode2)) {
-               /* okay --- consider it acceptable if a default security
-                * descriptor was assigned  */
-               /*ERROR("%"TS" has a security descriptor in the second image but "*/
-                     /*"not in the first image!", inode_any_full_path(inode1));*/
-               /*return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;*/
-       }
+       ret = cmp_security(inode1, inode2, imd1, imd2, cmp_flags);
+       if (ret)
+               return ret;
 
        /* Compare streams  */
        for (unsigned i = 0; i < inode1->i_num_streams; i++) {
index 2e57e2f669d067985aaf749a55f920769ac5dbf1..d41464cd8cbd17016cdb2b22e9cf2539e9a13623 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -382,3 +382,23 @@ load_text_file(const tchar *path, const void *buf, size_t bufsize,
        *mem_ret = tstr;
        return 0;
 }
+
+/* API function documented in wimlib.h */
+WIMLIBAPI int
+wimlib_load_text_file(const tchar *path,
+                     tchar **tstr_ret, size_t *tstr_nchars_ret)
+{
+       void *buf;
+       size_t bufsize;
+       int ret;
+
+       if (path == NULL || (path[0] == '-' && path[1] == '\0'))
+               ret = stdin_get_contents(&buf, &bufsize);
+       else
+               ret = read_file_contents(path, &buf, &bufsize);
+       if (ret)
+               return ret;
+       ret = translate_text_buffer(buf, bufsize, tstr_ret, tstr_nchars_ret);
+       FREE(buf);
+       return ret;
+}
diff --git a/src/threads.c b/src/threads.c
new file mode 100644 (file)
index 0000000..1b1028a
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * threads.c - Thread, mutex, and condition variable support.  Wraps around
+ *             pthreads or Windows native threads.
+ */
+
+/*
+ * Copyright 2016-2023 Eric Biggers
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see https://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef _WIN32
+#  include "wimlib/win32_common.h"
+#else
+#  include <errno.h>
+#  include <pthread.h>
+#endif
+
+#include "wimlib/assert.h"
+#include "wimlib/error.h"
+#include "wimlib/threads.h"
+#include "wimlib/util.h"
+
+#ifdef _WIN32
+
+static WINAPI DWORD
+win32_thrproc(LPVOID lpParameter)
+{
+       struct thread *t = (struct thread *)lpParameter;
+
+       (*t->thrproc)(t->arg);
+       return 0;
+}
+
+bool thread_create(struct thread *t, void *(*thrproc)(void *), void *arg)
+{
+       HANDLE h;
+
+       t->thrproc = thrproc;
+       t->arg = arg;
+       h = CreateThread(NULL, 0, win32_thrproc, (LPVOID)t, 0, NULL);
+       if (h == NULL) {
+               win32_error(GetLastError(), L"Failed to create thread");
+               return false;
+       }
+       t->win32_thread = (void *)h;
+       return true;
+}
+
+void thread_join(struct thread *t)
+{
+       DWORD res = WaitForSingleObject((HANDLE)t->win32_thread, INFINITE);
+
+       wimlib_assert(res == WAIT_OBJECT_0);
+}
+
+bool mutex_init(struct mutex *m)
+{
+       CRITICAL_SECTION *crit = MALLOC(sizeof(*crit));
+
+       if (!crit)
+               return false;
+       InitializeCriticalSection(crit);
+       m->win32_crit = crit;
+       return true;
+}
+
+void mutex_destroy(struct mutex *m)
+{
+       DeleteCriticalSection(m->win32_crit);
+       FREE(m->win32_crit);
+       m->win32_crit = NULL;
+}
+
+void mutex_lock(struct mutex *m)
+{
+       CRITICAL_SECTION *crit = m->win32_crit;
+
+       if (unlikely(!crit)) {
+               CRITICAL_SECTION *old;
+
+               crit = MALLOC(sizeof(*crit));
+               wimlib_assert(crit != NULL);
+               InitializeCriticalSection(crit);
+               old = InterlockedCompareExchangePointer(&m->win32_crit, crit,
+                                                       NULL);
+               if (old) {
+                       DeleteCriticalSection(crit);
+                       FREE(crit);
+                       crit = old;
+               }
+       }
+       EnterCriticalSection(crit);
+}
+
+void mutex_unlock(struct mutex *m)
+{
+       LeaveCriticalSection(m->win32_crit);
+}
+
+bool condvar_init(struct condvar *c)
+{
+       CONDITION_VARIABLE *cond = MALLOC(sizeof(*cond));
+
+       if (!cond)
+               return false;
+       InitializeConditionVariable(cond);
+       c->win32_cond = cond;
+       return true;
+}
+
+void condvar_destroy(struct condvar *c)
+{
+       FREE(c->win32_cond);
+       c->win32_cond = NULL;
+}
+
+void condvar_wait(struct condvar *c, struct mutex *m)
+{
+       BOOL ok = SleepConditionVariableCS(c->win32_cond, m->win32_crit,
+                                          INFINITE);
+       wimlib_assert(ok);
+}
+
+void condvar_signal(struct condvar *c)
+{
+       WakeConditionVariable(c->win32_cond);
+}
+
+void condvar_broadcast(struct condvar *c)
+{
+       WakeAllConditionVariable(c->win32_cond);
+}
+
+#else /* _WIN32 */
+
+bool thread_create(struct thread *t, void *(*thrproc)(void *), void *arg)
+{
+       int err = pthread_create(&t->pthread, NULL, thrproc, arg);
+
+       if (err) {
+               errno = err;
+               ERROR_WITH_ERRNO("Failed to create thread");
+               return false;
+       }
+       return true;
+}
+
+void thread_join(struct thread *t)
+{
+       int err = pthread_join(t->pthread, NULL);
+
+       wimlib_assert(err == 0);
+}
+
+bool mutex_init(struct mutex *m)
+{
+       int err = pthread_mutex_init(&m->pthread_mutex, NULL);
+
+       if (err) {
+               errno = err;
+               ERROR_WITH_ERRNO("Failed to initialize mutex");
+               return false;
+       }
+       return true;
+}
+
+void mutex_destroy(struct mutex *m)
+{
+       int err = pthread_mutex_destroy(&m->pthread_mutex);
+
+       wimlib_assert(err == 0);
+}
+
+void mutex_lock(struct mutex *m)
+{
+       int err = pthread_mutex_lock(&m->pthread_mutex);
+
+       wimlib_assert(err == 0);
+}
+
+void mutex_unlock(struct mutex *m)
+{
+       int err = pthread_mutex_unlock(&m->pthread_mutex);
+
+       wimlib_assert(err == 0);
+}
+
+bool condvar_init(struct condvar *c)
+{
+       int err = pthread_cond_init(&c->pthread_cond, NULL);
+
+       if (err) {
+               errno = err;
+               ERROR_WITH_ERRNO("Failed to initialize condition variable");
+               return false;
+       }
+       return true;
+}
+
+void condvar_destroy(struct condvar *c)
+{
+       int err = pthread_cond_destroy(&c->pthread_cond);
+
+       wimlib_assert(err == 0);
+}
+
+void condvar_wait(struct condvar *c, struct mutex *m)
+{
+       int err = pthread_cond_wait(&c->pthread_cond, &m->pthread_mutex);
+
+       wimlib_assert(err == 0);
+}
+
+void condvar_signal(struct condvar *c)
+{
+       int err = pthread_cond_signal(&c->pthread_cond);
+
+       wimlib_assert(err == 0);
+}
+
+void condvar_broadcast(struct condvar *c)
+{
+       int err = pthread_cond_broadcast(&c->pthread_cond);
+
+       wimlib_assert(err == 0);
+}
+
+#endif /* !_WIN32 */
index 51046cdb68084048df29ab185ecd6f03cbeaba33..70b2e5f37eac4ea6eadb1ef67ca7da4d026aef66 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -70,8 +70,8 @@ wim_timestamp_to_wimlib_timespec(u64 timestamp, struct wimlib_timespec *wts,
                *high_part_ret = sec >> 32;
 }
 
-#ifdef __WIN32__
-static _unused_attribute void
+#ifdef _WIN32
+static void __attribute__((unused))
 check_sizeof_time_t(void)
 {
        /* Windows builds should always be using 64-bit time_t now. */
@@ -95,6 +95,7 @@ wim_timestamp_to_timespec(u64 timestamp)
                .tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK,
        };
 }
+#endif /* !_WIN32 */
 
 /* UNIX timestamps to Windows NT timestamps  */
 
@@ -104,6 +105,7 @@ time_t_to_wim_timestamp(time_t t)
        return ((u64)t + EPOCH_DISTANCE) * TICKS_PER_SECOND;
 }
 
+#ifndef _WIN32
 u64
 timeval_to_wim_timestamp(const struct timeval *tv)
 {
@@ -127,7 +129,7 @@ now_as_wim_timestamp(void)
        gettimeofday(&tv, NULL);
        return timeval_to_wim_timestamp(&tv);
 }
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
 /* Translate a WIM timestamp into a human-readable string.  */
 void
index 43215e03d094ad945507847ecc1b25719002b38b..89b6f5f6a8465ca51ab1c76963edee457e351592 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index f9fad4a33ce1a1ece10237d9c90d452743019a3e..70a81c43e08880137cab699381b6bad5dfe8b623 100644 (file)
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifndef __WIN32__
+#ifndef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
@@ -688,4 +688,4 @@ unix_build_dentry_tree(struct wim_dentry **root_ret,
                                                root_disk_path, params);
 }
 
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
index 2247a7f6840307751936b69f6ad618c30ecabf2a..c4a2d571e2e7d4febd98963d942ce683fc639d0e 100644 (file)
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 /*
@@ -742,9 +742,12 @@ get_capture_config(const tchar *config_file, struct capture_config *config,
 
                size_t len = tstrlen(fs_source_path) +
                             tstrlen(wimboot_cfgfile);
-               tmp_config_file = MALLOC((len + 1) * sizeof(tchar));
                struct stat st;
 
+               tmp_config_file = MALLOC((len + 1) * sizeof(tchar));
+               if (!tmp_config_file)
+                       return WIMLIB_ERR_NOMEM;
+
                tsprintf(tmp_config_file, T("%"TS"%"TS),
                         fs_source_path, wimboot_cfgfile);
                if (!tstat(tmp_config_file, &st)) {
@@ -955,7 +958,7 @@ is_ancestor(const struct wim_dentry *d1, const struct wim_dentry *d2)
  */
 int
 rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to,
-               CASE_SENSITIVITY_TYPE case_type,
+               CASE_SENSITIVITY_TYPE case_type, bool noreplace,
                struct update_command_journal *j)
 {
        struct wim_dentry *src;
@@ -975,6 +978,9 @@ rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to,
        if (dst) {
                /* Destination file exists */
 
+               if (noreplace)
+                       return -EEXIST;
+
                if (src == dst) /* Same file */
                        return 0;
 
@@ -1042,7 +1048,7 @@ execute_rename_command(struct update_command_journal *j,
 
        ret = rename_wim_path(wim, rename_cmd->rename.wim_source_path,
                              rename_cmd->rename.wim_target_path,
-                             WIMLIB_CASE_PLATFORM_DEFAULT, j);
+                             WIMLIB_CASE_PLATFORM_DEFAULT, false, j);
        if (ret) {
                ret = -ret;
                errno = ret;
@@ -1231,7 +1237,7 @@ check_add_command(struct wimlib_update_command *cmd,
        }
 #endif
 
-#ifdef __WIN32__
+#ifdef _WIN32
        /* Check for flags not supported on Windows.  */
        if (add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) {
                ERROR("Capturing UNIX-specific data is not supported on Windows");
index 0fe3d4e4582026b3ea9aaae6f375fdb7f8a73578..52af147d3f122fe5240cced3ea3484885017234b 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -43,7 +43,6 @@
 #include "wimlib/error.h"
 #include "wimlib/timestamp.h"
 #include "wimlib/util.h"
-#include "wimlib/xml.h"
 
 /*******************
  * Memory allocation
@@ -106,7 +105,7 @@ wimlib_strdup(const char *str)
        return memdup(str, strlen(str) + 1);
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 wchar_t *
 wimlib_wcsdup(const wchar_t *str)
 {
@@ -153,9 +152,6 @@ wimlib_set_memory_allocator(void *(*malloc_func)(size_t),
        wimlib_malloc_func  = malloc_func  ? malloc_func  : malloc;
        wimlib_free_func    = free_func    ? free_func    : free;
        wimlib_realloc_func = realloc_func ? realloc_func : realloc;
-
-       xml_set_memory_allocator(wimlib_malloc_func, wimlib_free_func,
-                                wimlib_realloc_func);
        return 0;
 }
 
@@ -174,7 +170,7 @@ void *mempcpy(void *dst, const void *src, size_t n)
  * Random number generation
  **************************/
 
-#ifndef __WIN32__
+#ifndef _WIN32
 /*
  * Generate @n cryptographically secure random bytes (thread-safe)
  *
@@ -186,7 +182,7 @@ get_random_bytes(void *p, size_t n)
 {
        if (n == 0)
                return;
-#ifdef HAVE_NR_GETRANDOM
+#ifdef __NR_getrandom
        static bool getrandom_unavailable;
 
        if (getrandom_unavailable)
@@ -211,7 +207,7 @@ get_random_bytes(void *p, size_t n)
 
 try_dev_urandom:
        ;
-#endif /* HAVE_NR_GETRANDOM */
+#endif /* __NR_getrandom */
        int fd = open("/dev/urandom", O_RDONLY);
        if (fd < 0) {
                ERROR_WITH_ERRNO("Unable to open /dev/urandom");
@@ -231,7 +227,7 @@ try_dev_urandom:
        } while (n != 0);
        close(fd);
 }
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
 /*
  * Generate @n cryptographically secure random alphanumeric characters
@@ -275,7 +271,7 @@ get_random_alnum_chars(tchar *p, size_t n)
  * System information
  ************************/
 
-#ifndef __WIN32__
+#ifndef _WIN32
 unsigned
 get_available_cpus(void)
 {
@@ -286,9 +282,9 @@ get_available_cpus(void)
        }
        return n;
 }
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
-#ifndef __WIN32__
+#ifndef _WIN32
 u64
 get_available_memory(void)
 {
@@ -311,4 +307,4 @@ default_size:
        WARNING("Failed to determine available memory; assuming 1 GiB");
        return (u64)1 << 30;
 }
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
index 41fcff789919f1b60a0e888ad3598a8a21fb4e37..00da39709733fdc7ce6214262b5b19ea18807102 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
index ca40e53a47a303b4dbe3cb44e2172e2976e91da5..3a2a216a4c0db0c682be476484deacca754f438e 100644 (file)
--- a/src/wim.c
+++ b/src/wim.c
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -16,7 +16,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include <errno.h>
 #include <fcntl.h>
-#include <pthread.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "wimlib.h"
 #include "wimlib/assert.h"
 #include "wimlib/blob_table.h"
+#include "wimlib/cpu_features.h"
 #include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
 #include "wimlib/file_io.h"
 #include "wimlib/integrity.h"
 #include "wimlib/metadata.h"
 #include "wimlib/security.h"
+#include "wimlib/threads.h"
 #include "wimlib/wim.h"
 #include "wimlib/xml.h"
 #include "wimlib/win32.h"
@@ -643,11 +645,17 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags)
                filedes_init(&wim->in_fd, *(const int*)wim_filename_or_fd);
                wim->in_fd.is_pipe = 1;
        } else {
+               struct stat stbuf;
+
                wimfile = wim_filename_or_fd;
                ret = open_wim_file(wimfile, &wim->in_fd);
                if (ret)
                        return ret;
 
+               /* The file size is needed for enforcing some limits later. */
+               if (fstat(wim->in_fd.fd, &stbuf) == 0)
+                       wim->file_size = stbuf.st_size;
+
                /* The absolute path to the WIM is requested so that
                 * wimlib_overwrite() still works even if the process changes
                 * its working directory.  This actually happens if a WIM is
@@ -942,7 +950,7 @@ wimlib_get_version_string(void)
 }
 
 static bool lib_initialized = false;
-static pthread_mutex_t lib_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct mutex lib_initialization_mutex = MUTEX_INITIALIZER;
 
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
@@ -953,15 +961,13 @@ wimlib_global_init(int init_flags)
        if (lib_initialized)
                goto out;
 
-       pthread_mutex_lock(&lib_initialization_mutex);
+       mutex_lock(&lib_initialization_mutex);
 
        if (lib_initialized)
                goto out_unlock;
 
-#ifdef ENABLE_ERROR_MESSAGES
        if (!wimlib_error_file)
                wimlib_error_file = stderr;
-#endif
 
        ret = WIMLIB_ERR_INVALID_PARAM;
        if (init_flags & ~(WIMLIB_INIT_FLAG_ASSUME_UTF8 |
@@ -979,8 +985,8 @@ wimlib_global_init(int init_flags)
                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
                goto out_unlock;
 
-       xml_global_init();
-#ifdef __WIN32__
+       init_cpu_features();
+#ifdef _WIN32
        ret = win32_global_init(init_flags);
        if (ret)
                goto out_unlock;
@@ -993,7 +999,7 @@ wimlib_global_init(int init_flags)
        lib_initialized = true;
        ret = 0;
 out_unlock:
-       pthread_mutex_unlock(&lib_initialization_mutex);
+       mutex_unlock(&lib_initialization_mutex);
 out:
        return ret;
 }
@@ -1005,13 +1011,12 @@ wimlib_global_cleanup(void)
        if (!lib_initialized)
                return;
 
-       pthread_mutex_lock(&lib_initialization_mutex);
+       mutex_lock(&lib_initialization_mutex);
 
        if (!lib_initialized)
                goto out_unlock;
 
-       xml_global_cleanup();
-#ifdef __WIN32__
+#ifdef _WIN32
        win32_global_cleanup();
 #endif
 
@@ -1019,5 +1024,5 @@ wimlib_global_cleanup(void)
        lib_initialized = false;
 
 out_unlock:
-       pthread_mutex_unlock(&lib_initialization_mutex);
+       mutex_unlock(&lib_initialization_mutex);
 }
index 16ea1c6da0ebaba7f639b3b728876562291b1068..115a56996e39759832ae62b3265f9a2bacfee725 100644 (file)
@@ -3,15 +3,15 @@
  *
  * Support for creating WIMBoot pointer files.
  *
- * See http://technet.microsoft.com/en-us/library/dn594399.aspx for general
- * information about WIMBoot.
+ * For general information about WIMBoot, see
+ * https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/dn594399(v=win.10)
  *
  * Note that WIMBoot pointer files are actually implemented on top of the
  * Windows Overlay Filesystem filter (WOF).  See wof.h for more info.
  */
 
 /*
- * Copyright (C) 2014-2016 Eric Biggers
+ * Copyright (C) 2014-2021 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
@@ -511,6 +511,21 @@ prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr,
        return 0;
 }
 
+static bool
+valid_wim_filename(const struct WimOverlay_dat_entry_2 *entry, size_t name_len)
+{
+       size_t i;
+
+       if (name_len % sizeof(wchar_t))
+               return false;
+       name_len /= sizeof(wchar_t);
+       if (name_len < 2)
+               return false;
+       for (i = 0; i < name_len && entry->wim_file_name[i] != 0; i++)
+               ;
+       return i == name_len - 1;
+}
+
 /*
  * Reads and validates a WimOverlay.dat file.
  *
@@ -686,13 +701,7 @@ retry:
 
                wim_file_name_length = entry_1->entry_2_length -
                                        sizeof(struct WimOverlay_dat_entry_2);
-               if ((wim_file_name_length < 2 * sizeof(wchar_t)) ||
-                   (wim_file_name_length % sizeof(wchar_t) != 0) ||
-                   (wmemchr(entry_2->wim_file_name, L'\0',
-                            wim_file_name_length / sizeof(wchar_t))
-                    != &entry_2->wim_file_name[wim_file_name_length /
-                                               sizeof(wchar_t) - 1]))
-               {
+               if (!valid_wim_filename(entry_2, wim_file_name_length)) {
                        ERROR("\"%ls\": entry %"PRIu32" (2) "
                              "(data source ID 0x%016"PRIx64") "
                              "has invalid WIM file name",
@@ -878,8 +887,9 @@ wimboot_alloc_data_source_id(const wchar_t *wim_path,
        size_t wim_file_name_length;
        void *in;
        size_t insize;
-       struct wof_external_info *wof_info;
-       struct wim_provider_add_overlay_input *wim_info;
+       WOF_EXTERNAL_INFO *wof_info;
+       WIM_PROVIDER_ADD_OVERLAY_INPUT *wim_info;
+       wchar_t *WimFileName;
        HANDLE h;
        u64 data_source_id;
        DWORD bytes_returned;
@@ -899,28 +909,25 @@ wimboot_alloc_data_source_id(const wchar_t *wim_path,
        wim_file_name_length = sizeof(wchar_t) *
                               (wim_path_nchars + prefix_nchars);
 
-       insize = sizeof(struct wof_external_info) +
-                sizeof(struct wim_provider_add_overlay_input) +
-                wim_file_name_length;
-
-       in = MALLOC(insize);
+       insize = sizeof(*wof_info) + sizeof(*wim_info) + wim_file_name_length;
+       in = CALLOC(1, insize);
        if (!in) {
                ret = WIMLIB_ERR_NOMEM;
                goto out;
        }
 
-       wof_info = (struct wof_external_info *)in;
-       wof_info->version = WOF_CURRENT_VERSION;
-       wof_info->provider = WOF_PROVIDER_WIM;
+       wof_info = (WOF_EXTERNAL_INFO *)in;
+       wof_info->Version = WOF_CURRENT_VERSION;
+       wof_info->Provider = WOF_PROVIDER_WIM;
 
-       wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1);
-       wim_info->wim_type = WIM_BOOT_NOT_OS_WIM;
-       wim_info->wim_index = image;
-       wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input,
-                                                 wim_file_name);
-       wim_info->wim_file_name_length = wim_file_name_length;
-       wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars);
-       wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars);
+       wim_info = (WIM_PROVIDER_ADD_OVERLAY_INPUT *)(wof_info + 1);
+       wim_info->WimType = WIM_BOOT_NOT_OS_WIM;
+       wim_info->WimIndex = image;
+       wim_info->WimFileNameOffset = sizeof(*wim_info);
+       wim_info->WimFileNameLength = wim_file_name_length;
+       WimFileName = (wchar_t *)(wim_info + 1);
+       wmemcpy(WimFileName, prefix, prefix_nchars);
+       wmemcpy(&WimFileName[prefix_nchars], wim_path, wim_path_nchars);
 
 retry_ioctl:
        h = open_file(drive_path, GENERIC_WRITE);
@@ -1018,20 +1025,20 @@ wimboot_set_pointer(HANDLE h,
                 * using FSCTL_SET_EXTERNAL_BACKING.  */
                unsigned int max_retries = 4;
                struct {
-                       struct wof_external_info wof_info;
-                       struct wim_provider_external_info wim_info;
+                       WOF_EXTERNAL_INFO wof_info;
+                       WIM_PROVIDER_EXTERNAL_INFO wim_info;
                } in;
 
        retry:
                memset(&in, 0, sizeof(in));
 
-               in.wof_info.version = WOF_CURRENT_VERSION;
-               in.wof_info.provider = WOF_PROVIDER_WIM;
+               in.wof_info.Version = WOF_CURRENT_VERSION;
+               in.wof_info.Provider = WOF_PROVIDER_WIM;
 
-               in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION;
-               in.wim_info.flags = 0;
-               in.wim_info.data_source_id = data_source_id;
-               copy_hash(in.wim_info.unnamed_data_stream_hash, blob->hash);
+               in.wim_info.Version = WIM_PROVIDER_CURRENT_VERSION;
+               in.wim_info.Flags = 0;
+               in.wim_info.DataSourceId.QuadPart = data_source_id;
+               copy_hash(in.wim_info.ResourceHash, blob->hash);
 
                /* blob_table_hash is not necessary  */
 
@@ -1065,20 +1072,20 @@ wimboot_set_pointer(HANDLE h,
                                le16 rpdatalen;
                                le16 rpreserved;
                        } hdr;
-                       struct wof_external_info wof_info;
+                       WOF_EXTERNAL_INFO wof_info;
                        struct wim_provider_rpdata wim_info;
                } in;
 
                STATIC_ASSERT(sizeof(in) == 8 +
-                             sizeof(struct wof_external_info) +
+                             sizeof(WOF_EXTERNAL_INFO) +
                              sizeof(struct wim_provider_rpdata));
 
                in.hdr.rptag = WIM_IO_REPARSE_TAG_WOF;
                in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr);
                in.hdr.rpreserved = 0;
 
-               in.wof_info.version = WOF_CURRENT_VERSION;
-               in.wof_info.provider = WOF_PROVIDER_WIM;
+               in.wof_info.Version = WOF_CURRENT_VERSION;
+               in.wof_info.Provider = WOF_PROVIDER_WIM;
 
                in.wim_info.version = 2;
                in.wim_info.flags = 0;
@@ -1113,4 +1120,4 @@ wimboot_set_pointer(HANDLE h,
        return true;
 }
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
index c55847d23147d8f4ed5ec826e10e0c929d0cb717..e493ecfe298fcd9bb24167586011c5353673159b 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2013-2018 Eric Biggers
+ * Copyright 2013-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
@@ -244,14 +244,6 @@ get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
        }
 }
 
-/* Is the image being extracted an OS image for Windows 10 or later?  */
-static bool
-is_image_windows_10_or_later(struct win32_apply_ctx *ctx)
-{
-       /* Note: if no build number is available, this returns false.  */
-       return ctx->windows_build_number >= 10240;
-}
-
 static const wchar_t *
 current_path(struct win32_apply_ctx *ctx);
 
@@ -1094,7 +1086,7 @@ prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
 
        path_max = compute_path_max(dentry_list);
        /* Add some extra for building Win32 paths for the file encryption APIs,
-        * and ensure we have at least enough to potentially use a 8.3 name for
+        * and ensure we have at least enough to potentially use an 8.3 name for
         * the last component.  */
        path_max += max(2 + (ctx->target_ntpath.Length / sizeof(wchar_t)),
                        8 + 1 + 3);
@@ -1252,7 +1244,7 @@ remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_appl
        HANDLE h;
        size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
                         (13 * sizeof(wchar_t));
-       u8 buf[bufsize] _aligned_attribute(8);
+       u8 buf[bufsize] __attribute__((aligned(8)));
        bool retried = false;
        FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
 
@@ -1334,7 +1326,7 @@ set_short_name(HANDLE h, const struct wim_dentry *dentry,
        size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
                         max(dentry->d_short_name_nbytes, sizeof(wchar_t)) +
                         sizeof(wchar_t);
-       u8 buf[bufsize] _aligned_attribute(8);
+       u8 buf[bufsize] __attribute__((aligned(8)));
        FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
        NTSTATUS status;
        bool tried_to_remove_existing = false;
@@ -1488,7 +1480,7 @@ retry:
        if (unlikely(!NT_SUCCESS(status))) {
                winnt_error(status, L"Can't open \"%ls\" for deletion "
                            "(perms=%x, flags=%x)",
-                           current_path(ctx), perms, flags);
+                           current_path(ctx), (u32)perms, (u32)flags);
                return WIMLIB_ERR_OPEN;
        }
 
@@ -1642,7 +1634,7 @@ create_empty_streams(const struct wim_dentry *dentry,
                if (strm->stream_type == STREAM_TYPE_REPARSE_POINT &&
                    ctx->common.supported_features.reparse_points)
                {
-                       u8 buf[REPARSE_DATA_OFFSET] _aligned_attribute(8);
+                       u8 buf[REPARSE_DATA_OFFSET] __attribute__((aligned(8)));
                        struct reparse_buffer_disk *rpbuf =
                                (struct reparse_buffer_disk *)buf;
                        complete_reparse_point(rpbuf, inode, 0);
@@ -1851,7 +1843,7 @@ create_link(HANDLE h, const struct wim_dentry *dentry,
 
                size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
                                 ctx->pathbuf.Length + sizeof(wchar_t);
-               u8 buf[bufsize] _aligned_attribute(8);
+               u8 buf[bufsize] __attribute__((aligned(8)));
                FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
                NTSTATUS status;
 
@@ -1860,16 +1852,25 @@ create_link(HANDLE h, const struct wim_dentry *dentry,
                info->FileNameLength = ctx->pathbuf.Length;
                memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
                info->FileName[info->FileNameLength / 2] = L'\0';
+               /*
+                * Note: the null terminator isn't actually necessary, but if
+                * you don't add the extra character, you get
+                * STATUS_INFO_LENGTH_MISMATCH when FileNameLength is 2.
+                */
 
-               /* Note: the null terminator isn't actually necessary,
-                * but if you don't add the extra character, you get
-                * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
-                * happens to be 2  */
-
-               status = NtSetInformationFile(h, &ctx->iosb, info, bufsize,
-                                             FileLinkInformation);
-               if (NT_SUCCESS(status))
-                       return 0;
+               /*
+                * When fuzzing with wlfuzz.exe, creating a hard link sometimes
+                * fails with STATUS_ACCESS_DENIED.  However, it eventually
+                * succeeds when re-attempted...
+                */
+               int i = 0;
+               do {
+                       status = NtSetInformationFile(h, &ctx->iosb, info,
+                                                     bufsize,
+                                                     FileLinkInformation);
+                       if (NT_SUCCESS(status))
+                               return 0;
+               } while (++i < 32);
                winnt_error(status, L"Failed to create link \"%ls\"",
                            current_path(ctx));
                return WIMLIB_ERR_LINK;
@@ -2432,15 +2433,15 @@ static int
 get_system_compression_format(int extract_flags)
 {
        if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K)
-               return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K;
+               return FILE_PROVIDER_COMPRESSION_XPRESS4K;
 
        if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K)
-               return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K;
+               return FILE_PROVIDER_COMPRESSION_XPRESS8K;
 
        if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K)
-               return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K;
+               return FILE_PROVIDER_COMPRESSION_XPRESS16K;
 
-       return FILE_PROVIDER_COMPRESSION_FORMAT_LZX;
+       return FILE_PROVIDER_COMPRESSION_LZX;
 }
 
 
@@ -2448,11 +2449,11 @@ static const wchar_t *
 get_system_compression_format_string(int format)
 {
        switch (format) {
-       case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K:
+       case FILE_PROVIDER_COMPRESSION_XPRESS4K:
                return L"XPRESS4K";
-       case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K:
+       case FILE_PROVIDER_COMPRESSION_XPRESS8K:
                return L"XPRESS8K";
-       case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K:
+       case FILE_PROVIDER_COMPRESSION_XPRESS16K:
                return L"XPRESS16K";
        default:
                return L"LZX";
@@ -2464,16 +2465,16 @@ set_system_compression(HANDLE h, int format)
 {
        NTSTATUS status;
        struct {
-               struct wof_external_info wof_info;
-               struct file_provider_external_info file_info;
+               WOF_EXTERNAL_INFO wof_info;
+               FILE_PROVIDER_EXTERNAL_INFO_V1 file_info;
        } in = {
                .wof_info = {
-                       .version = WOF_CURRENT_VERSION,
-                       .provider = WOF_PROVIDER_FILE,
+                       .Version = WOF_CURRENT_VERSION,
+                       .Provider = WOF_PROVIDER_FILE,
                },
                .file_info = {
-                       .version = FILE_PROVIDER_CURRENT_VERSION,
-                       .compression_format = format,
+                       .Version = FILE_PROVIDER_CURRENT_VERSION,
+                       .Algorithm = format,
                },
        };
 
@@ -2530,6 +2531,22 @@ static const struct string_list bootloader_patterns = {
        .num_strings = ARRAY_LEN(bootloader_pattern_strings),
 };
 
+/* Returns true if the specified system compression format is supported by the
+ * bootloader of the image being applied.  */
+static bool
+bootloader_supports_compression_format(struct win32_apply_ctx *ctx, int format)
+{
+       /* Windows 10 and later support XPRESS4K */
+       if (format == FILE_PROVIDER_COMPRESSION_XPRESS4K)
+               return ctx->windows_build_number >= 10240;
+
+       /*
+        * Windows 10 version 1903 and later support the other formats;
+        * see https://wimlib.net/forums/viewtopic.php?f=1&t=444
+        */
+       return ctx->windows_build_number >= 18362;
+}
+
 static NTSTATUS
 set_system_compression_on_inode(struct wim_inode *inode, int format,
                                struct win32_apply_ctx *ctx)
@@ -2539,12 +2556,8 @@ set_system_compression_on_inode(struct wim_inode *inode, int format,
        HANDLE h;
 
        /* If it may be needed for compatibility with the Windows bootloader,
-        * force this file to XPRESS4K or uncompressed format.  The bootloader
-        * of Windows 10 supports XPRESS4K only; older versions don't support
-        * system compression at all.  */
-       if (!is_image_windows_10_or_later(ctx) ||
-           format != FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K)
-       {
+        * force this file to XPRESS4K or uncompressed format.  */
+       if (!bootloader_supports_compression_format(ctx, format)) {
                /* We need to check the patterns against every name of the
                 * inode, in case any of them match.  */
                struct wim_dentry *dentry;
@@ -2568,7 +2581,9 @@ set_system_compression_on_inode(struct wim_inode *inode, int format,
 
                        warned = (ctx->num_system_compression_exclusions++ > 0);
 
-                       if (is_image_windows_10_or_later(ctx)) {
+                       if (bootloader_supports_compression_format(ctx,
+                                  FILE_PROVIDER_COMPRESSION_XPRESS4K))
+                       {
                                /* Force to XPRESS4K  */
                                if (!warned) {
                                        WARNING("For compatibility with the "
@@ -2580,7 +2595,7 @@ set_system_compression_on_inode(struct wim_inode *inode, int format,
                                                "          you requested.",
                                                get_system_compression_format_string(format));
                                }
-                               format = FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K;
+                               format = FILE_PROVIDER_COMPRESSION_XPRESS4K;
                                break;
                        } else {
                                /* Force to uncompressed  */
@@ -2826,7 +2841,7 @@ set_xattrs(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx)
        u32 len;
        const struct wim_xattr_entry *entry;
        size_t bufsize = 0;
-       u8 _buf[1024] _aligned_attribute(4);
+       u8 _buf[1024] __attribute__((aligned(4)));
        u8 *buf = _buf;
        FILE_FULL_EA_INFORMATION *ea, *ea_prev;
        NTSTATUS status;
@@ -3344,4 +3359,4 @@ const struct apply_operations win32_apply_ops = {
        .context_size           = sizeof(struct win32_apply_ctx),
 };
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
index d62f7d07ef20c08c9bec93f261131033e39b159b..958147e65cab03c201a556f98a440dee67260c4b 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) 2013-2018 Eric Biggers
+ * Copyright (C) 2013-2021 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
@@ -330,7 +330,7 @@ read_winnt_stream_prefix(const struct windows_file *file,
        };
        HANDLE h;
        NTSTATUS status;
-       u8 buf[BUFFER_SIZE] _aligned_attribute(8);
+       u8 buf[BUFFER_SIZE] __attribute__((aligned(8)));
        u64 bytes_remaining;
        int ret;
 
@@ -488,7 +488,8 @@ read_win32_encrypted_file_prefix(const wchar_t *path, bool is_dir, u64 size,
  * described by @blob.  */
 int
 read_windows_file_prefix(const struct blob_descriptor *blob, u64 size,
-                        const struct consume_chunk_callback *cb)
+                        const struct consume_chunk_callback *cb,
+                        bool recover_data)
 {
        const struct windows_file *file = blob->windows_file;
 
@@ -511,7 +512,7 @@ winnt_get_short_name(HANDLE h, struct wim_dentry *dentry)
         * course has to create its own handle.  */
        NTSTATUS status;
        IO_STATUS_BLOCK iosb;
-       u8 buf[128] _aligned_attribute(8);
+       u8 buf[128] __attribute__((aligned(8)));
        const FILE_NAME_INFORMATION *info;
 
        status = NtQueryInformationFile(h, &iosb, buf, sizeof(buf),
@@ -536,7 +537,7 @@ winnt_load_security_descriptor(HANDLE h, struct wim_inode *inode,
                               struct winnt_scan_ctx *ctx)
 {
        SECURITY_INFORMATION requestedInformation;
-       u8 _buf[4096] _aligned_attribute(8);
+       u8 _buf[4096] __attribute__((aligned(8)));
        u8 *buf;
        ULONG bufsize;
        ULONG len_needed;
@@ -704,7 +705,7 @@ winnt_load_xattrs(HANDLE h, struct wim_inode *inode,
 {
        IO_STATUS_BLOCK iosb;
        NTSTATUS status;
-       u8 _buf[1024] _aligned_attribute(4);
+       u8 _buf[1024] __attribute__((aligned(4)));
        u8 *buf = _buf;
        const FILE_FULL_EA_INFORMATION *ea;
        struct wim_xattr_entry *entry;
@@ -803,12 +804,13 @@ out:
 }
 
 static int
-winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
-                                 HANDLE cur_dir,
-                                 const wchar_t *relative_path,
-                                 size_t relative_path_nchars,
-                                 const wchar_t *filename,
-                                 struct winnt_scan_ctx *ctx);
+winnt_build_dentry_tree(struct wim_dentry **root_ret,
+                       HANDLE cur_dir,
+                       const wchar_t *relative_path,
+                       size_t relative_path_nchars,
+                       const wchar_t *filename,
+                       struct winnt_scan_ctx *ctx,
+                       bool recursive);
 
 static int
 winnt_recurse_directory(HANDLE h,
@@ -850,13 +852,14 @@ winnt_recurse_directory(HANDLE h,
                                if (!filename)
                                        goto out_free_buf;
 
-                               ret = winnt_build_dentry_tree_recursive(
+                               ret = winnt_build_dentry_tree(
                                                        &child,
                                                        h,
                                                        filename,
                                                        info->FileNameLength / 2,
                                                        filename,
-                                                       ctx);
+                                                       ctx,
+                                                       true);
 
                                pathbuf_truncate(ctx->params, orig_path_nchars);
 
@@ -1318,22 +1321,17 @@ winnt_scan_data_stream(wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
  * Load information about the data streams of an open file into a WIM inode.
  *
  * We use the NtQueryInformationFile() system call instead of FindFirstStream()
- * and FindNextStream().  This is done for two reasons:
- *
- * - FindFirstStream() opens its own handle to the file or directory and
- *   apparently does so without specifying FILE_FLAG_BACKUP_SEMANTICS, thereby
- *   causing access denied errors on certain files (even when running as the
- *   Administrator).
- * - FindFirstStream() and FindNextStream() is only available on Windows Vista
- *   and later, whereas the stream support in NtQueryInformationFile() was
- *   already present in Windows XP.
+ * and FindNextStream(), since FindFirstStream() opens its own handle to the
+ * file or directory and apparently does so without specifying
+ * FILE_FLAG_BACKUP_SEMANTICS.  This causing access denied errors on certain
+ * files, even when running as the Administrator.
  */
 static noinline_for_stack int
 winnt_scan_data_streams(HANDLE h, struct wim_inode *inode, u64 file_size,
                        struct winnt_scan_ctx *ctx)
 {
        int ret;
-       u8 _buf[4096] _aligned_attribute(8);
+       u8 _buf[4096] __attribute__((aligned(8)));
        u8 *buf;
        size_t bufsize;
        IO_STATUS_BLOCK iosb;
@@ -1515,7 +1513,7 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode,
        if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                struct reparse_buffer_disk rpbuf;
                struct {
-                       struct wof_external_info wof_info;
+                       WOF_EXTERNAL_INFO wof_info;
                        struct wim_provider_rpdata wim_info;
                } *rpdata = (void *)rpbuf.rpdata;
                struct blob_descriptor *reparse_blob;
@@ -1533,8 +1531,8 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode,
                if (ret)
                        return ret;
 
-               if (rpdata->wof_info.version != WOF_CURRENT_VERSION ||
-                   rpdata->wof_info.provider != WOF_PROVIDER_WIM ||
+               if (rpdata->wof_info.Version != WOF_CURRENT_VERSION ||
+                   rpdata->wof_info.Provider != WOF_PROVIDER_WIM ||
                    rpdata->wim_info.version != 2)
                        return 0;  /* Not a WIM-backed file  */
 
@@ -1542,8 +1540,8 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode,
                copy_hash(hash, rpdata->wim_info.unnamed_data_stream_hash);
        } else {
                struct {
-                       struct wof_external_info wof_info;
-                       struct wim_provider_external_info wim_info;
+                       WOF_EXTERNAL_INFO wof_info;
+                       WIM_PROVIDER_EXTERNAL_INFO wim_info;
                } out;
                NTSTATUS status;
 
@@ -1577,13 +1575,13 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode,
                }
 
                /* Is this file backed by a WIM?  */
-               if (out.wof_info.version != WOF_CURRENT_VERSION ||
-                   out.wof_info.provider != WOF_PROVIDER_WIM ||
-                   out.wim_info.version != WIM_PROVIDER_CURRENT_VERSION)
+               if (out.wof_info.Version != WOF_CURRENT_VERSION ||
+                   out.wof_info.Provider != WOF_PROVIDER_WIM ||
+                   out.wim_info.Version != WIM_PROVIDER_CURRENT_VERSION)
                        return 0;
 
                /* Okay, this is a WIM backed file.  Get its SHA-1 hash.  */
-               copy_hash(hash, out.wim_info.unnamed_data_stream_hash);
+               copy_hash(hash, out.wim_info.ResourceHash);
        }
 
        /* If the file's unnamed data stream is nonempty, then fill in its hash
@@ -1601,7 +1599,8 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode,
                        return 0;
                back_ptr = retrieve_pointer_to_unhashed_blob(blob);
                copy_hash(blob->hash, hash);
-               if (after_blob_hashed(blob, back_ptr, blob_table) != blob)
+               if (after_blob_hashed(blob, back_ptr, blob_table,
+                                     inode) != blob)
                        free_blob_descriptor(blob);
        }
 
@@ -1655,7 +1654,8 @@ get_file_info(HANDLE h, struct file_info *info)
 static void
 get_volume_information(HANDLE h, struct winnt_scan_ctx *ctx)
 {
-       u8 _attr_info[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 128] _aligned_attribute(8);
+       u8 _attr_info[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 128]
+               __attribute__((aligned(8)));
        FILE_FS_ATTRIBUTE_INFORMATION *attr_info = (void *)_attr_info;
        FILE_FS_VOLUME_INFORMATION vol_info;
        struct file_info file_info;
@@ -1701,12 +1701,13 @@ get_volume_information(HANDLE h, struct winnt_scan_ctx *ctx)
 }
 
 static int
-winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
-                                 HANDLE cur_dir,
-                                 const wchar_t *relative_path,
-                                 size_t relative_path_nchars,
-                                 const wchar_t *filename,
-                                 struct winnt_scan_ctx *ctx)
+winnt_build_dentry_tree(struct wim_dentry **root_ret,
+                       HANDLE cur_dir,
+                       const wchar_t *relative_path,
+                       size_t relative_path_nchars,
+                       const wchar_t *filename,
+                       struct winnt_scan_ctx *ctx,
+                       bool recursive)
 {
        struct wim_dentry *root = NULL;
        struct wim_inode *inode = NULL;
@@ -1883,7 +1884,7 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
 
        set_sort_key(inode, sort_key);
 
-       if (inode_is_directory(inode)) {
+       if (inode_is_directory(inode) && recursive) {
 
                /* Directory: recurse to children.  */
 
@@ -1907,10 +1908,16 @@ winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        }
 
 out_progress:
-       if (likely(root))
-               ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
-       else
-               ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
+       ret = 0;
+       if (recursive) { /* if !recursive, caller handles progress */
+               if (likely(root))
+                       ret = do_scan_progress(ctx->params,
+                                              WIMLIB_SCAN_DENTRY_OK, inode);
+               else
+                       ret = do_scan_progress(ctx->params,
+                                              WIMLIB_SCAN_DENTRY_EXCLUDED,
+                                              NULL);
+       }
 out:
        if (likely(h))
                NtClose(h);
@@ -2710,10 +2717,10 @@ security_map_destroy(struct security_map *map)
  *     ntfs_stream     => wim_inode_stream
  *
  * This also handles things such as exclusions and issuing progress messages.
- * It's similar to winnt_build_dentry_tree_recursive(), but this is much faster
- * because almost all information we need is already loaded in memory in the
- * ntfs_* structures.  However, in some cases we still fall back to
- * winnt_build_dentry_tree_recursive() and/or opening the file.
+ * It's similar to winnt_build_dentry_tree(), but this is much faster because
+ * almost all information we need is already loaded in memory in the ntfs_*
+ * structures.  However, in some cases we still fall back to
+ * winnt_build_dentry_tree() and/or opening the file.
  */
 static int
 generate_wim_structures_recursive(struct wim_dentry **root_ret,
@@ -2732,7 +2739,7 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
        if (NTFS_IS_SPECIAL_FILE(ni->ino))
                goto out;
 
-       /* Fall back to a recursive scan for unhandled cases.  Reparse points,
+       /* Fall back to the standard scan for unhandled cases.  Reparse points,
         * in particular, can't be properly handled here because a commonly used
         * filter driver (WOF) hides reparse points from regular filesystem APIs
         * but not from FSCTL_QUERY_FILE_LAYOUT.  */
@@ -2740,13 +2747,16 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
                              FILE_ATTRIBUTE_ENCRYPTED) ||
            ni->special_streams != 0)
        {
-               ret = winnt_build_dentry_tree_recursive(&root,
-                                                       NULL,
-                                                       ctx->params->cur_path,
-                                                       ctx->params->cur_path_nchars,
-                                                       filename,
-                                                       ctx);
-               goto out;
+               ret = winnt_build_dentry_tree(&root, NULL,
+                                             ctx->params->cur_path,
+                                             ctx->params->cur_path_nchars,
+                                             filename, ctx, false);
+               if (ret) /* Error? */
+                       goto out;
+               if (!root) /* Excluded? */
+                       goto out_progress;
+               inode = root->d_inode;
+               goto process_children;
        }
 
        /* Test for exclusion based on path.  */
@@ -2866,6 +2876,7 @@ generate_wim_structures_recursive(struct wim_dentry **root_ret,
        /* If processing a directory, then recurse to its children.  In this
         * version there is no need to go to disk, as we already have the list
         * of children cached from the MFT.  */
+process_children:
        if (inode_is_directory(inode)) {
                const struct ntfs_dentry *nd = ni->first_child;
 
@@ -3027,10 +3038,8 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
                }
        }
 #endif
-       ret = winnt_build_dentry_tree_recursive(root_ret, NULL,
-                                               params->cur_path,
-                                               params->cur_path_nchars,
-                                               L"", &ctx);
+       ret = winnt_build_dentry_tree(root_ret, NULL, params->cur_path,
+                                     params->cur_path_nchars, L"", &ctx, true);
 out:
        vss_put_snapshot(ctx.snapshot);
        if (ret == 0)
@@ -3038,4 +3047,4 @@ out:
        return ret;
 }
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
index 47f2df307375b5591a770e97b42ff41774f2160b..408650b0d4a942df93f6e463cc2e2374007a8a62 100644 (file)
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
@@ -407,4 +407,4 @@ winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size,
        return status;
 }
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
index c2fb556d15553228412a535a606960118989e2ea..5df6c78eefaabf5fe763dd0d6e319d464e685411 100644 (file)
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
 #include <errno.h>
-#include <pthread.h>
 #include <io.h>        /* for _get_osfhandle()  */
 #include <fcntl.h>
 
@@ -469,21 +468,6 @@ err_set_errno:
        return -1;
 }
 
-/* This really could be replaced with _wcserror_s, but this doesn't seem to
- * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
- * linked in by Visual Studio...?). */
-int
-win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
-{
-       static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
-
-       pthread_mutex_lock(&strerror_lock);
-       mbstowcs(buf, strerror(errnum), buflen);
-       buf[buflen - 1] = '\0';
-       pthread_mutex_unlock(&strerror_lock);
-       return 0;
-}
-
 #define MAX_IO_AMOUNT 1048576
 
 static int
@@ -692,7 +676,7 @@ win32_wglob(const wchar_t *pattern, int flags,
                pglob->gl_pathv[pglob->gl_pathc++] = path;
        } while (FindNextFileW(hFind, &dat));
        err = GetLastError();
-       CloseHandle(hFind);
+       FindClose(hFind);
        if (err != ERROR_NO_MORE_FILES) {
                set_errno_from_win32_error(err);
                ret = GLOB_ABORTED;
@@ -701,7 +685,7 @@ win32_wglob(const wchar_t *pattern, int flags,
        return 0;
 
 oom:
-       CloseHandle(hFind);
+       FindClose(hFind);
        errno = ENOMEM;
        ret = GLOB_NOSPACE;
 fail_globfree:
@@ -786,4 +770,4 @@ now_as_wim_timestamp(void)
        return ((u64)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
 }
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
index 2270feeac6814fc63e06c4865cf6f2b998238f00..6f5a28eaf128728f7675d22186cd9b69ba7296bb 100644 (file)
@@ -4,7 +4,7 @@
  */
 
 /*
- * Copyright (C) 2015-2016 Eric Biggers
+ * Copyright (C) 2015-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
@@ -29,9 +29,9 @@
 #include "wimlib/win32_common.h"
 
 #include <cguid.h>
-#include <pthread.h>
 
 #include "wimlib/error.h"
+#include "wimlib/threads.h"
 #include "wimlib/util.h"
 #include "wimlib/win32_vss.h"
 
@@ -131,21 +131,14 @@ struct IVssAsyncVTable {
        void *AddRef;
        ULONG (WINAPI *Release)(IVssAsync *this);
        void *Cancel;
-       union {
-               HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
-
-               /* Pre-Vista version */
-               HRESULT (WINAPI *OldWait)(IVssAsync *this);
-       };
+       HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
        void *QueryStatus;
 };
 
 typedef struct IVssBackupComponentsVTable IVssBackupComponentsVTable;
-typedef struct IVssBackupComponentsVTable_old IVssBackupComponentsVTable_old;
 
-typedef union {
+typedef struct {
        IVssBackupComponentsVTable *vtable;
-       IVssBackupComponentsVTable_old *old_vtable;
 } IVssBackupComponents;
 
 struct IVssBackupComponentsVTable {
@@ -203,7 +196,6 @@ struct IVssBackupComponentsVTable {
                                        IVssAsync **ppAsync);
        void *DeleteSnapshots;
        void *ImportSnapshots;
-       /*void *RemountReadWrite;*/     /* Old API only  */
        void *BreakSnapshotSet;
        HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
                                                VSS_ID SnapshotId,
@@ -218,100 +210,17 @@ struct IVssBackupComponentsVTable {
        void *QueryRevertStatus;
 };
 
-/* Pre-Vista version  */
-struct IVssBackupComponentsVTable_old {
-       void *QueryInterface;
-       void *AddRef;
-       ULONG (WINAPI *Release)(IVssBackupComponents *this);
-       void *GetWriterComponentsCount;
-       void *GetWriterComponents;
-       HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this,
-                                             BSTR bstrXML);
-       HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this,
-                                        BOOLEAN bSelectComponents,
-                                        BOOLEAN bBackupBootableSystemState,
-                                        VSS_BACKUP_TYPE backupType,
-                                        BOOLEAN bPartialFileSupport);
-       void *InitializeForRestore;
-       /*void *SetRestoreState;*/      /* New API only */
-       HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this,
-                                              IVssAsync **ppAsync);
-       void *GetWriterMetadataCount;
-       void *GetWriterMetadata;
-       void *FreeWriterMetadata;
-       void *AddComponent;
-       HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this,
-                                          IVssAsync **ppAsync);
-       void *AbortBackup;
-       void *GatherWriterStatus;
-       void *GetWriterStatusCount;
-       void *FreeWriterStatus;
-       void *GetWriterStatus;
-       void *SetBackupSucceeded;
-       void *SetBackupOptions;
-       void *SetSelectedForRestore;
-       void *SetRestoreOptions;
-       void *SetAdditionalRestores;
-       void *SetPreviousBackupStamp;
-       void *SaveAsXML;
-       void *BackupComplete;
-       void *AddAlternativeLocationMapping;
-       void *AddRestoreSubcomponent;
-       void *SetFileRestoreStatus;
-       /*void *AddNewTarget;*/         /* New API only */
-       /*void *SetRangesFilePath;*/    /* New API only */
-       void *PreRestore;
-       void *PostRestore;
-       HRESULT (WINAPI *SetContext)(IVssBackupComponents *this,
-                                    LONG lContext);
-       HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this,
-                                          VSS_ID *pSnapshotSetId);
-       HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this,
-                                          VSS_PWSZ pwszVolumeName,
-                                          VSS_ID ProviderId,
-                                          VSS_ID *pidSnapshot);
-       HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this,
-                                       IVssAsync **ppAsync);
-       void *DeleteSnapshots;
-       void *ImportSnapshots;
-       void *RemountReadWrite;
-       void *BreakSnapshotSet;
-       HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
-                                               VSS_ID SnapshotId,
-                                               VSS_SNAPSHOT_PROP *pprop);
-       void *Query;
-       void *IsVolumeSupported;
-       void *DisableWriterClasses;
-       void *EnableWriterClasses;
-       void *DisableWriterInstances;
-       void *ExposeSnapshot;
-       /*void *RevertToSnapshot;*/     /* New API only */
-       /*void *QueryRevertStatus;*/    /* New API only */
-};
-
-/* Call a method, assuming its signature is identical in the old and new APIs */
-#define CALL_METHOD(obj, method, ...)                                  \
-({                                                                     \
-       HRESULT res;                                                    \
-       if (is_old_api)                                                 \
-               res = (obj)->old_vtable->method((obj), ##__VA_ARGS__);  \
-       else                                                            \
-               res = (obj)->vtable->method((obj), ##__VA_ARGS__);      \
-       res;                                                            \
-})
-
 /*----------------------------------------------------------------------------*
  *                             VSS API initialization                         *
  *----------------------------------------------------------------------------*/
 
 static bool vss_initialized;
-static pthread_mutex_t vss_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct mutex vss_initialization_mutex = MUTEX_INITIALIZER;
 
 /* vssapi.dll  */
-static bool is_old_api;                /* old VSS API (pre-Vista)?  */
 static HANDLE hVssapi;
-static HRESULT (WINAPI *func_CreateVssBackupComponents)(IVssBackupComponents **ppBackup);
-static void (WINAPI *func_VssFreeSnapshotProperties)(VSS_SNAPSHOT_PROP *pProp);
+static HRESULT (WINAPI *func_CreateVssBackupComponentsInternal)(IVssBackupComponents **ppBackup);
+static void (WINAPI *func_VssFreeSnapshotPropertiesInternal)(VSS_SNAPSHOT_PROP *pProp);
 
 /* ole32.dll  */
 static HANDLE hOle32;
@@ -327,29 +236,18 @@ vss_global_init_impl(void)
                goto err;
        }
 
-       func_CreateVssBackupComponents =
+       func_CreateVssBackupComponentsInternal =
                (void *)GetProcAddress(hVssapi, "CreateVssBackupComponentsInternal");
-       if (!func_CreateVssBackupComponents) {
-               func_CreateVssBackupComponents =
-                       (void *)GetProcAddress(hVssapi, "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z");
-               if (!func_CreateVssBackupComponents) {
-                       ERROR("CreateVssBackupComponents() not found in vssapi.dll");
-                       goto err_vssapi;
-               }
-               is_old_api = true;
-       } else {
-               is_old_api = false;
+       if (!func_CreateVssBackupComponentsInternal) {
+               ERROR("CreateVssBackupComponentsInternal() not found in vssapi.dll");
+               goto err_vssapi;
        }
 
-       func_VssFreeSnapshotProperties =
+       func_VssFreeSnapshotPropertiesInternal =
                (void *)GetProcAddress(hVssapi, "VssFreeSnapshotPropertiesInternal");
-       if (!func_VssFreeSnapshotProperties) {
-               func_VssFreeSnapshotProperties =
-                       (void *)GetProcAddress(hVssapi, "VssFreeSnapshotProperties");
-               if (!func_VssFreeSnapshotProperties) {
-                       ERROR("VssFreeSnapshotProperties() not found in vssapi.dll");
-                       goto err_vssapi;
-               }
+       if (!func_VssFreeSnapshotPropertiesInternal) {
+               ERROR("VssFreeSnapshotPropertiesInternal() not found in vssapi.dll");
+               goto err_vssapi;
        }
 
        hOle32 = LoadLibrary(L"ole32.dll");
@@ -387,15 +285,15 @@ vss_global_init(void)
        if (vss_initialized)
                return true;
 
-       pthread_mutex_lock(&vss_initialization_mutex);
+       mutex_lock(&vss_initialization_mutex);
        if (!vss_initialized)
                vss_initialized = vss_global_init_impl();
-       pthread_mutex_unlock(&vss_initialization_mutex);
+       mutex_unlock(&vss_initialization_mutex);
 
        if (vss_initialized)
                return true;
        ERROR("The Volume Shadow Copy Service (VSS) API could not be "
-             "initialized. Probably it isn't supported on this computer.");
+             "initialized.");
        return false;
 }
 
@@ -405,14 +303,14 @@ vss_global_cleanup(void)
        if (!vss_initialized)
                return;
 
-       pthread_mutex_lock(&vss_initialization_mutex);
+       mutex_lock(&vss_initialization_mutex);
        if (vss_initialized) {
                (*func_CoUninitialize)();
                FreeLibrary(hOle32);
                FreeLibrary(hVssapi);
                vss_initialized = false;
        }
-       pthread_mutex_unlock(&vss_initialization_mutex);
+       mutex_unlock(&vss_initialization_mutex);
 }
 
 /*----------------------------------------------------------------------------*
@@ -434,20 +332,17 @@ vss_delete_snapshot(struct vss_snapshot *snapshot)
        internal = container_of(snapshot, struct vss_snapshot_internal, base);
 
        if (internal->props.m_pwszSnapshotDeviceObject)
-               (*func_VssFreeSnapshotProperties)(&internal->props);
+               (*func_VssFreeSnapshotPropertiesInternal)(&internal->props);
        if (internal->vss)
-               CALL_METHOD(internal->vss, Release);
+               internal->vss->vtable->Release(internal->vss);
        FREE(internal);
 }
 
 static HRESULT
 wait_and_release(IVssAsync *async)
 {
-       HRESULT res;
-       if (is_old_api)
-               res = async->vtable->OldWait(async);
-       else
-               res = async->vtable->Wait(async, INFINITE);
+       HRESULT res = async->vtable->Wait(async, INFINITE);
+
        async->vtable->Release(async);
        return res;
 }
@@ -459,49 +354,57 @@ request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
        HRESULT res;
        IVssAsync *async;
 
-       res = CALL_METHOD(vss, InitializeForBackup, NULL);
+       res = vss->vtable->InitializeForBackup(vss, NULL);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.InitializeForBackup() error: %x", res);
+               ERROR("IVssBackupComponents.InitializeForBackup() error: %x",
+                     (u32)res);
                return false;
        }
 
-       res = CALL_METHOD(vss, SetBackupState, FALSE, TRUE, VSS_BT_COPY, FALSE);
+       res = vss->vtable->SetBackupState(vss, FALSE, TRUE, VSS_BT_COPY, FALSE);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.SetBackupState() error: %x", res);
+               ERROR("IVssBackupComponents.SetBackupState() error: %x",
+                     (u32)res);
                return false;
        }
 
-       res = CALL_METHOD(vss, StartSnapshotSet, snapshot_id);
+       res = vss->vtable->StartSnapshotSet(vss, snapshot_id);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.StartSnapshotSet() error: %x", res);
+               ERROR("IVssBackupComponents.StartSnapshotSet() error: %x",
+                     (u32)res);
                return false;
        }
 
-       res = CALL_METHOD(vss, AddToSnapshotSet, volume, (GUID){}, snapshot_id);
+       res = vss->vtable->AddToSnapshotSet(vss, volume, (GUID){}, snapshot_id);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x", res);
+               ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x",
+                     (u32)res);
                return false;
        }
 
-       res = CALL_METHOD(vss, PrepareForBackup, &async);
+       res = vss->vtable->PrepareForBackup(vss, &async);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.PrepareForBackup() error: %x", res);
+               ERROR("IVssBackupComponents.PrepareForBackup() error: %x",
+                     (u32)res);
                return false;
        }
        res = wait_and_release(async);
        if (FAILED(res)) {
-               ERROR("IVssAsync.Wait() error while preparing for backup: %x", res);
+               ERROR("IVssAsync.Wait() error while preparing for backup: %x",
+                     (u32)res);
                return false;
        }
 
-       res = CALL_METHOD(vss, DoSnapshotSet, &async);
+       res = vss->vtable->DoSnapshotSet(vss, &async);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.DoSnapshotSet() error: %x", res);
+               ERROR("IVssBackupComponents.DoSnapshotSet() error: %x",
+                     (u32)res);
                return false;
        }
        res = wait_and_release(async);
        if (FAILED(res)) {
-               ERROR("IVssAsync.Wait() error while doing snapshot set: %x", res);
+               ERROR("IVssAsync.Wait() error while doing snapshot set: %x",
+                     (u32)res);
                return false;
        }
 
@@ -559,9 +462,9 @@ vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
        if (!vss_global_init())
                goto vss_err;
 
-       res = (*func_CreateVssBackupComponents)(&vss);
+       res = (*func_CreateVssBackupComponentsInternal)(&vss);
        if (FAILED(res)) {
-               ERROR("CreateVssBackupComponents error: %x", res);
+               ERROR("CreateVssBackupComponents error: %x", (u32)res);
                goto vss_err;
        }
 
@@ -570,9 +473,10 @@ vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
        if (!request_vss_snapshot(vss, volume, &snapshot_id))
                goto vss_err;
 
-       res = CALL_METHOD(vss, GetSnapshotProperties, snapshot_id, &snapshot->props);
+       res = vss->vtable->GetSnapshotProperties(vss, snapshot_id, &snapshot->props);
        if (FAILED(res)) {
-               ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x", res);
+               ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x",
+                     (u32)res);
                goto vss_err;
        }
 
@@ -621,4 +525,4 @@ out:
        return ret;
 }
 
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
index 4805913de376fab95c1982eae45f0253a6dd13ec..4bf1b2bedf5e181fa9f975fec74f66ba7fdc47b9 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -301,7 +301,8 @@ struct write_blobs_progress_data {
 
 static int
 do_write_blobs_progress(struct write_blobs_progress_data *progress_data,
-                       u64 complete_size, u32 complete_count, bool discarded)
+                       u64 complete_size, u64 complete_compressed_size,
+                       u32 complete_count, bool discarded)
 {
        union wimlib_progress_info *progress = &progress_data->progress;
        int ret;
@@ -316,6 +317,8 @@ do_write_blobs_progress(struct write_blobs_progress_data *progress_data,
                }
        } else {
                progress->write_streams.completed_bytes += complete_size;
+               progress->write_streams.completed_compressed_bytes +=
+                       complete_compressed_size;
                progress->write_streams.completed_streams += complete_count;
        }
 
@@ -503,8 +506,8 @@ end_chunk_table(struct write_blobs_ctx *ctx, u64 res_actual_size,
                                                0 != (ctx->write_resource_flags &
                                                      WRITE_RESOURCE_FLAG_SOLID));
 
-       typedef le64 _may_alias_attribute aliased_le64_t;
-       typedef le32 _may_alias_attribute aliased_le32_t;
+       typedef le64 __attribute__((may_alias)) aliased_le64_t;
+       typedef le32 __attribute__((may_alias)) aliased_le32_t;
 
        if (chunk_entry_size == 4) {
                aliased_le32_t *entries = (aliased_le32_t*)ctx->chunk_csizes;
@@ -713,7 +716,9 @@ write_blob_begin_read(struct blob_descriptor *blob, void *_ctx)
                                 * output reference count to the duplicate blob
                                 * in the former case.  */
                                ret = do_write_blobs_progress(&ctx->progress_data,
-                                                             blob->size, 1, true);
+                                                             blob->size,
+                                                             blob->size,
+                                                             1, true);
                                list_del(&blob->write_blobs_list);
                                list_del(&blob->blob_table_list);
                                if (new_blob->will_be_in_output_wim)
@@ -762,7 +767,7 @@ write_blob_uncompressed(struct blob_descriptor *blob, struct filedes *out_fd)
        if (filedes_seek(out_fd, begin_offset) == -1)
                return 0;
 
-       ret = extract_blob_to_fd(blob, out_fd);
+       ret = extract_blob_to_fd(blob, out_fd, false);
        if (ret) {
                /* Error reading the uncompressed data.  */
                if (out_fd->offset == begin_offset &&
@@ -867,8 +872,7 @@ write_chunk(struct write_blobs_ctx *ctx, const void *cchunk,
 {
        int ret;
        struct blob_descriptor *blob;
-       u32 completed_blob_count;
-       u32 completed_size;
+       u32 completed_blob_count = 0;
 
        blob = list_entry(ctx->blobs_being_compressed.next,
                          struct blob_descriptor, write_blobs_list);
@@ -915,8 +919,6 @@ write_chunk(struct write_blobs_ctx *ctx, const void *cchunk,
 
        ctx->cur_write_blob_offset += usize;
 
-       completed_size = usize;
-       completed_blob_count = 0;
        if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) {
                /* Wrote chunk in solid mode.  It may have finished multiple
                 * blobs.  */
@@ -973,7 +975,7 @@ write_chunk(struct write_blobs_ctx *ctx, const void *cchunk,
                }
        }
 
-       return do_write_blobs_progress(&ctx->progress_data, completed_size,
+       return do_write_blobs_progress(&ctx->progress_data, usize, csize,
                                       completed_blob_count, false);
 
 write_error:
@@ -1287,15 +1289,18 @@ write_raw_copy_resources(struct list_head *raw_copy_blobs,
                blob->rdesc->raw_copy_ok = 1;
 
        list_for_each_entry(blob, raw_copy_blobs, write_blobs_list) {
+               u64 compressed_size = 0;
+
                if (blob->rdesc->raw_copy_ok) {
                        /* Write each solid resource only one time.  */
                        ret = write_raw_copy_resource(blob->rdesc, out_fd);
                        if (ret)
                                return ret;
                        blob->rdesc->raw_copy_ok = 0;
+                       compressed_size = blob->rdesc->size_in_wim;
                }
                ret = do_write_blobs_progress(progress_data, blob->size,
-                                             1, false);
+                                             compressed_size, 1, false);
                if (ret)
                        return ret;
        }
@@ -1546,7 +1551,6 @@ write_blob_list(struct list_head *blob_list,
         * specified number of threads, unless the upper bound on the number
         * bytes needing to be compressed is less than a heuristic value.  */
        if (num_nonraw_bytes != 0 && out_ctype != WIMLIB_COMPRESSION_TYPE_NONE) {
-       #ifdef ENABLE_MULTITHREADED_COMPRESSION
                if (num_nonraw_bytes > max(2000000, out_chunk_size)) {
                        ret = new_parallel_chunk_compressor(out_ctype,
                                                            out_chunk_size,
@@ -1558,7 +1562,6 @@ write_blob_list(struct list_head *blob_list,
                                        wimlib_get_error_string(ret));
                        }
                }
-       #endif
 
                if (ctx.compressor == NULL) {
                        ret = new_serial_chunk_compressor(out_ctype, out_chunk_size,
@@ -1734,7 +1737,7 @@ write_wim_resource_from_buffer(const void *buf,
        }
 
        blob_set_is_located_in_attached_buffer(&blob, (void *)buf, buf_size);
-       sha1_buffer(buf, buf_size, blob.hash);
+       sha1(buf, buf_size, blob.hash);
        blob.unhashed = 0;
        blob.is_metadata = is_metadata;
 
@@ -3271,7 +3274,7 @@ overwrite_wim_via_tmpfile(WIMStruct *wim, int write_flags, unsigned num_threads)
        if (ret) {
                ERROR_WITH_ERRNO("Failed to rename `%"TS"' to `%"TS"'",
                                 tmpfile, wim->filename);
-       #ifdef __WIN32__
+       #ifdef _WIN32
                if (ret < 0)
        #endif
                {
diff --git a/src/x86_cpu_features.c b/src/x86_cpu_features.c
deleted file mode 100644 (file)
index 3995413..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * x86_cpu_features.c - feature detection for x86 processors
- *
- * The following copying information applies to this specific source code file:
- *
- * Written in 2015 by Eric Biggers <ebiggers3@gmail.com>
- *
- * To the extent possible under law, the author(s) have dedicated all copyright
- * and related and neighboring rights to this software to the public domain
- * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
- * Dedication (the "CC0").
- *
- * This software is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
- *
- * You should have received a copy of the CC0 along with this software; if not
- * see <http://creativecommons.org/publicdomain/zero/1.0/>.
- */
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include "wimlib/x86_cpu_features.h"
-
-#if defined(__i386__) || defined(__x86_64__)
-
-#define DEBUG 0
-
-#if DEBUG
-#  include <stdio.h>
-#endif
-
-u32 _x86_cpu_features = 0;
-
-/* With old GCC versions we have to manually save and restore the x86_32 PIC
- * register (ebx).  See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602  */
-#if defined(__i386__) && defined(__PIC__)
-#  define EBX_CONSTRAINT "=r"
-#else
-#  define EBX_CONSTRAINT "=b"
-#endif
-
-/* Execute the CPUID instruction.  */
-static inline void
-cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d)
-{
-       __asm__(".ifnc %%ebx, %1; mov  %%ebx, %1; .endif\n"
-               "cpuid                                  \n"
-               ".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n"
-               : "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d)
-               : "a" (leaf), "c" (subleaf));
-}
-
-/* Read an extended control register.  */
-static inline u64
-read_xcr(u32 index)
-{
-       u32 edx, eax;
-
-       /* Execute the "xgetbv" instruction.  Old versions of binutils do not
-        * recognize this instruction, so list the raw bytes instead.  */
-       __asm__ (".byte 0x0f, 0x01, 0xd0" : "=d" (edx), "=a" (eax) : "c" (index));
-
-       return ((u64)edx << 32) | eax;
-}
-
-#define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit)))
-
-/* Initialize _x86_cpu_features with bits for interesting processor features. */
-void
-x86_setup_cpu_features(void)
-{
-       u32 features = 0;
-       u32 dummy1, dummy2, dummy3, dummy4;
-       u32 max_function;
-       u32 features_1, features_2, features_3, features_4;
-       bool os_saves_ymm_regs = false;
-
-       /* Get maximum supported function  */
-       cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4);
-       if (max_function < 1)
-               goto out;
-
-       /* Standard feature flags  */
-       cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1);
-
-       if (IS_SET(features_1, 25))
-               features |= X86_CPU_FEATURE_SSE;
-
-       if (IS_SET(features_1, 26))
-               features |= X86_CPU_FEATURE_SSE2;
-
-       if (IS_SET(features_2, 0))
-               features |= X86_CPU_FEATURE_SSE3;
-
-       if (IS_SET(features_2, 9))
-               features |= X86_CPU_FEATURE_SSSE3;
-
-       if (IS_SET(features_2, 19))
-               features |= X86_CPU_FEATURE_SSE4_1;
-
-       if (IS_SET(features_2, 20))
-               features |= X86_CPU_FEATURE_SSE4_2;
-
-       if (IS_SET(features_2, 27)) /* OSXSAVE set?  */
-               if ((read_xcr(0) & 0x6) == 0x6)
-                       os_saves_ymm_regs = true;
-
-       if (os_saves_ymm_regs && IS_SET(features_2, 28))
-               features |= X86_CPU_FEATURE_AVX;
-
-       if (max_function < 7)
-               goto out;
-
-       /* Extended feature flags  */
-       cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4);
-
-       if (IS_SET(features_3, 3))
-               features |= X86_CPU_FEATURE_BMI;
-
-       if (os_saves_ymm_regs && IS_SET(features_3, 5))
-               features |= X86_CPU_FEATURE_AVX2;
-
-       if (IS_SET(features_3, 8))
-               features |= X86_CPU_FEATURE_BMI2;
-
-out:
-
-#if DEBUG
-       printf("Detected x86 CPU features: ");
-       if (features & X86_CPU_FEATURE_SSE)
-               printf("SSE ");
-       if (features & X86_CPU_FEATURE_SSE2)
-               printf("SSE2 ");
-       if (features & X86_CPU_FEATURE_SSE3)
-               printf("SSE3 ");
-       if (features & X86_CPU_FEATURE_SSSE3)
-               printf("SSSE3 ");
-       if (features & X86_CPU_FEATURE_SSE4_1)
-               printf("SSE4.1 ");
-       if (features & X86_CPU_FEATURE_SSE4_2)
-               printf("SSE4.2 ");
-       if (features & X86_CPU_FEATURE_BMI)
-               printf("BMI ");
-       if (features & X86_CPU_FEATURE_AVX)
-               printf("AVX ");
-       if (features & X86_CPU_FEATURE_BMI2)
-               printf("BMI2 ");
-       if (features & X86_CPU_FEATURE_AVX2)
-               printf("AVX2 ");
-       printf("\n");
-#endif /* DEBUG */
-
-       _x86_cpu_features = features | X86_CPU_FEATURES_KNOWN;
-}
-
-#endif /* __i386__ || __x86_64__ */
index 3812ce1e19d7926e499a9c230e0bddb9664d02ea..d4400ee6e80a0c27d40e6e09c087939e223aa017 100644 (file)
--- a/src/xml.c
+++ b/src/xml.c
@@ -1,11 +1,9 @@
 /*
- * xml.c
- *
- * Deals with the XML information in WIM files.  Uses the C library libxml2.
+ * xml.c - deals with the XML information in WIM files
  */
 
 /*
- * Copyright (C) 2012-2016 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "wimlib/blob_table.h"
@@ -39,6 +35,7 @@
 #include "wimlib/resource.h"
 #include "wimlib/timestamp.h"
 #include "wimlib/xml.h"
+#include "wimlib/xmlproc.h"
 #include "wimlib/write.h"
 
 /*
  */
 struct wim_xml_info {
 
-       /* The parsed XML document as a libxml2 document tree  */
-       xmlDocPtr doc;
-
-       /* The root element of the document.  This is a cached value, equal to
-        * xmlDocGetRootElement(doc).  */
-       xmlNode *root;
+       /* The XML document in tree form */
+       struct xml_node *root;
 
        /* A malloc()ed array containing a pointer to the IMAGE element for each
         * WIM image.  The image with 1-based index 'i' is at index 'i - 1' in
         * this array.  Note: these pointers are cached values, since they could
         * also be found by searching the document.  */
-       xmlNode **images;
+       struct xml_node **images;
 
        /* The number of WIM images (the length of 'images')  */
        int image_count;
-
-#if TCHAR_IS_UTF16LE
-       /* Temporary memory for UTF-8 => 'tchar' string translations.  When an
-        * API function needs to return a 'tchar' string, it uses one of these
-        * array slots to hold the string and returns a pointer to it.  */
-       tchar *strings[128];
-       size_t next_string_idx;
-       size_t num_strings;
-#endif
 };
 
-/*----------------------------------------------------------------------------*
- *                            Internal functions                              *
- *----------------------------------------------------------------------------*/
-
-/* Iterate through the children of an xmlNode.  */
-#define node_for_each_child(parent, child)     \
-       for (child = (parent)->children; child != NULL; child = child->next)
-
-/* Is the specified node an element of the specified name?  */
-static bool
-node_is_element(const xmlNode *node, const xmlChar *name)
-{
-       return node->type == XML_ELEMENT_NODE && xmlStrEqual(node->name, name);
-}
-
-/* Retrieve a pointer to the UTF-8 text contents of the specified node, or NULL
- * if the node has no text contents.  This assumes the simple case where the
- * node has a single TEXT child node.  */
-static const xmlChar *
-node_get_text(const xmlNode *node)
-{
-       const xmlNode *child;
-
-       if (!node)
-               return NULL;
-       node_for_each_child(node, child)
-               if (child->type == XML_TEXT_NODE && child->content)
-                       return child->content;
-       return NULL;
-}
-
-/* Retrieve an unsigned integer from the contents of the specified node,
- * decoding it using the specified base.  If the node has no contents or does
- * not contain a valid number, returns 0.  */
 static u64
-node_get_number(const xmlNode *node, int base)
+parse_number(const tchar *str, int base)
 {
-       const xmlChar *str = node_get_text(node);
-       char *end;
+       tchar *end;
        unsigned long long v;
 
        if (!str)
                return 0;
-       v = strtoull(str, &end, base);
-       if ((xmlChar *)end == str || *end || v >= UINT64_MAX)
+       v = tstrtoull(str, &end, base);
+       if (end == str || *end || v >= UINT64_MAX)
                return 0;
        return v;
 }
 
-/* Retrieve the timestamp from a time node.  This node should have child
- * elements HIGHPART and LOWPART; these elements will be used to construct a
- * Windows-style timestamp.  */
+/*
+ * Retrieve an unsigned integer from the contents of the specified element,
+ * decoding it using the specified base.  If the element has no contents or does
+ * not contain a valid number, returns 0.
+ */
 static u64
-node_get_timestamp(const xmlNode *node)
-{
-       u64 timestamp = 0;
-       xmlNode *child;
-
-       if (!node)
-               return 0;
-       node_for_each_child(node, child) {
-               if (node_is_element(child, "HIGHPART"))
-                       timestamp |= node_get_number(child, 16) << 32;
-               else if (node_is_element(child, "LOWPART"))
-                       timestamp |= node_get_number(child, 16);
-       }
-       return timestamp;
-}
-
-static int
-tstr_get_utf8(const tchar *tstr, const xmlChar **utf8_ret)
-{
-#if TCHAR_IS_UTF16LE
-       return utf16le_to_utf8(tstr, tstrlen(tstr) * sizeof(tchar),
-                              (char **)utf8_ret, NULL);
-#else
-       *utf8_ret = (const xmlChar *)tstr;
-       return 0;
-#endif
-}
-
-static void
-tstr_put_utf8(const xmlChar *utf8)
-{
-#if TCHAR_IS_UTF16LE
-       FREE((char *)utf8);
-#endif
-}
-
-/* Retrieve the text contents of an XML element as a 'tchar' string.  If not
- * found or if the text could not be translated, returns NULL.  */
-static const tchar *
-node_get_ttext(struct wim_xml_info *info, xmlNode *node)
-{
-       const xmlChar *text = node_get_text(node);
-
-#if TCHAR_IS_UTF16LE
-       tchar **ttext_p;
-
-       if (!text)
-               return NULL;
-
-       ttext_p = &info->strings[info->next_string_idx];
-       if (info->num_strings >= ARRAY_LEN(info->strings)) {
-               FREE(*ttext_p);
-               *ttext_p = NULL;
-       }
-       if (utf8_to_tstr(text, strlen(text), ttext_p, NULL))
-               return NULL;
-       if (info->num_strings < ARRAY_LEN(info->strings))
-               info->num_strings++;
-       info->next_string_idx++;
-       info->next_string_idx %= ARRAY_LEN(info->strings);
-       return *ttext_p;
-#else
-       return text;
-#endif
-}
-
-/* Unlink the specified node from its parent, then free it (recursively).  */
-static void
-unlink_and_free_tree(xmlNode *node)
+xml_element_get_number(const struct xml_node *element, int base)
 {
-       xmlUnlinkNode(node);
-       xmlFreeNode(node);
+       return parse_number(xml_element_get_text(element), base);
 }
 
-/* Unlink and free (recursively) all children of the specified node.  */
-static void
-unlink_and_free_children(xmlNode *node)
-{
-       xmlNode *child;
-
-       while ((child = node->last) != NULL)
-               unlink_and_free_tree(child);
-}
-
-/* Add the new child element 'replacement' to 'parent', replacing any same-named
- * element that may already exist.  */
-static void
-node_replace_child_element(xmlNode *parent, xmlNode *replacement)
-{
-       xmlNode *child;
-
-       node_for_each_child(parent, child) {
-               if (node_is_element(child, replacement->name)) {
-                       xmlReplaceNode(child, replacement);
-                       xmlFreeNode(child);
-                       return;
-               }
-       }
-
-       xmlAddChild(parent, replacement);
-}
-
-/* Set the text contents of the specified element to the specified string,
- * replacing the existing contents (if any).  The string is "raw" and is
- * permitted to contain characters that have special meaning in XML.  */
-static int
-node_set_text(xmlNode *node, const xmlChar *text)
-{
-       xmlNode *text_node = xmlNewText(text);
-       if (!text_node)
-               return WIMLIB_ERR_NOMEM;
-       unlink_and_free_children(node);
-       xmlAddChild(node, text_node);
-       return 0;
-}
-
-/* Like 'node_set_text()', but takes in a 'tchar' string.  */
-static int
-node_set_ttext(xmlNode *node, const tchar *ttext)
-{
-       const xmlChar *text;
-       int ret;
-
-       ret = tstr_get_utf8(ttext, &text);
-       if (ret)
-               return ret;
-       ret = node_set_text(node, text);
-       tstr_put_utf8(text);
-       return ret;
-}
-
-/* Create a new element containing text and optionally link it into a tree.  */
-static xmlNode *
-new_element_with_text(xmlNode *parent, const xmlChar *name, const xmlChar *text)
+/*
+ * Retrieve the timestamp from a time element.  This element should have child
+ * elements HIGHPART and LOWPART; these elements will be used to construct a
+ * Windows-style timestamp.
+ */
+static u64
+xml_element_get_timestamp(const struct xml_node *element)
 {
-       xmlNode *node;
-
-       node = xmlNewNode(NULL, name);
-       if (!node)
-               return NULL;
+       u64 timestamp = 0;
+       const struct xml_node *child;
 
-       if (node_set_text(node, text)) {
-               xmlFreeNode(node);
-               return NULL;
+       xml_node_for_each_child(element, child) {
+               if (xml_node_is_element(child, T("HIGHPART")))
+                       timestamp |= xml_element_get_number(child, 16) << 32;
+               else if (xml_node_is_element(child, T("LOWPART")))
+                       timestamp |= xml_element_get_number(child, 16);
        }
-
-       if (parent)
-               xmlAddChild(parent, node);
-       return node;
-}
-
-/* Create a new element containing text and optionally link it into a tree.  */
-static int
-new_element_with_ttext(xmlNode *parent, const xmlChar *name, const tchar *ttext,
-                      xmlNode **node_ret)
-{
-       const xmlChar *text;
-       int ret;
-       xmlNode *node;
-
-       ret = tstr_get_utf8(ttext, &text);
-       if (ret)
-               return ret;
-       node = new_element_with_text(parent, name, text);
-       tstr_put_utf8(text);
-       if (!node)
-               return WIMLIB_ERR_NOMEM;
-       if (node_ret)
-               *node_ret = node;
-       return 0;
+       return timestamp;
 }
 
 /* Create a new timestamp element and optionally link it into a tree.  */
-static xmlNode *
-new_element_with_timestamp(xmlNode *parent, const xmlChar *name, u64 timestamp)
+static struct xml_node *
+xml_new_element_with_timestamp(struct xml_node *parent, const tchar *name,
+                              u64 timestamp)
 {
-       xmlNode *node;
-       char buf[32];
+       struct xml_node *element;
+       tchar buf[32];
 
-       node = xmlNewNode(NULL, name);
-       if (!node)
+       element = xml_new_element(NULL, name);
+       if (!element)
                goto err;
 
-       sprintf(buf, "0x%08"PRIX32, (u32)(timestamp >> 32));
-       if (!new_element_with_text(node, "HIGHPART", buf))
+       tsprintf(buf, T("0x%08"PRIX32), (u32)(timestamp >> 32));
+       if (!xml_new_element_with_text(element, T("HIGHPART"), buf))
                goto err;
 
-       sprintf(buf, "0x%08"PRIX32, (u32)timestamp);
-       if (!new_element_with_text(node, "LOWPART", buf))
+       tsprintf(buf, T("0x%08"PRIX32), (u32)timestamp);
+       if (!xml_new_element_with_text(element, T("LOWPART"), buf))
                goto err;
 
        if (parent)
-               xmlAddChild(parent, node);
-       return node;
+               xml_add_child(parent, element);
+       return element;
 
 err:
-       xmlFreeNode(node);
+       xml_free_node(element);
        return NULL;
 }
 
 /* Create a new number element and optionally link it into a tree.  */
-static xmlNode *
-new_element_with_u64(xmlNode *parent, const xmlChar *name, u64 value)
+static struct xml_node *
+xml_new_element_with_u64(struct xml_node *parent, const tchar *name, u64 value)
 {
-       char buf[32];
+       tchar buf[32];
 
-       sprintf(buf, "%"PRIu64, value);
-       return new_element_with_text(parent, name, buf);
-}
-
-/* Allocate a 'struct wim_xml_info'.  The caller is responsible for initializing
- * the document and the images array.  */
-static struct wim_xml_info *
-alloc_wim_xml_info(void)
-{
-       struct wim_xml_info *info = MALLOC(sizeof(*info));
-#if TCHAR_IS_UTF16LE
-       if (info) {
-               info->next_string_idx = 0;
-               info->num_strings = 0;
-       }
-#endif
-       return info;
+       tsprintf(buf, T("%"PRIu64), value);
+       return xml_new_element_with_text(parent, name, buf);
 }
 
 static bool
-parse_index(xmlChar **pp, u32 *index_ret)
+parse_index(tchar **pp, u32 *index_ret)
 {
-       xmlChar *p = *pp;
+       tchar *p = *pp;
        u32 index = 0;
 
        *p++ = '\0'; /* overwrite '[' */
@@ -379,21 +169,21 @@ parse_index(xmlChar **pp, u32 *index_ret)
 }
 
 static int
-do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create,
-                xmlNode **result_ret)
+do_xml_path_walk(struct xml_node *element, const tchar *path, bool create,
+                struct xml_node **result_ret)
 {
-       size_t n = strlen(path) + 1;
-       xmlChar buf[n];
-       xmlChar *p;
-       xmlChar c;
+       size_t n = tstrlen(path) + 1;
+       tchar buf[n];
+       tchar *p;
+       tchar c;
 
        *result_ret = NULL;
 
-       if (!node)
+       if (!element)
                return 0;
 
        /* Copy the path to a temporary buffer.  */
-       memcpy(buf, path, n);
+       tmemcpy(buf, path, n);
        p = buf;
 
        if (*p == '/')
@@ -401,8 +191,8 @@ do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create,
        c = *p;
 
        while (c != '\0') {
-               const xmlChar *name;
-               xmlNode *child;
+               const tchar *name;
+               struct xml_node *child;
                u32 index = 1;
 
                /* We have another path component.  */
@@ -422,8 +212,8 @@ do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create,
                *p = '\0';
 
                /* Look for a matching child.  */
-               node_for_each_child(node, child)
-                       if (node_is_element(child, name) && !--index)
+               xml_node_for_each_child(element, child)
+                       if (xml_node_is_element(child, name) && !--index)
                                goto next_step;
 
                /* No child matched the path.  If create=false, the lookup
@@ -436,99 +226,98 @@ do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create,
                if (index != 1)
                        return WIMLIB_ERR_INVALID_PARAM;
 
-               child = xmlNewChild(node, NULL, name, NULL);
+               child = xml_new_element(element, name);
                if (!child)
                        return WIMLIB_ERR_NOMEM;
        next_step:
                /* Continue to the next path component, if there is one.  */
-               node = child;
+               element = child;
                p++;
        }
 
-       *result_ret = node;
+       *result_ret = element;
        return 0;
 
 bad_syntax:
-       ERROR("The XML path \"%s\" has invalid syntax.", path);
+       ERROR("The XML path \"%"TS"\" has invalid syntax.", path);
        return WIMLIB_ERR_INVALID_PARAM;
 }
 
 /* Retrieve the XML element, if any, at the specified 'path'.  This supports a
  * simple filesystem-like syntax.  If the element was found, returns a pointer
  * to it; otherwise returns NULL.  */
-static xmlNode *
-xml_get_node_by_path(xmlNode *root, const xmlChar *path)
+static struct xml_node *
+xml_get_element_by_path(struct xml_node *root, const tchar *path)
 {
-       xmlNode *node;
-       do_xml_path_walk(root, path, false, &node);
-       return node;
+       struct xml_node *element;
+
+       do_xml_path_walk(root, path, false, &element);
+       return element;
 }
 
-/* Similar to xml_get_node_by_path(), but creates the element and any requisite
- * ancestor elements as needed.   If successful, 0 is returned and *node_ret is
- * set to a pointer to the resulting element.  If unsuccessful, an error code is
- * returned and *node_ret is set to NULL.  */
+/*
+ * Similar to xml_get_element_by_path(), but creates the element and any
+ * requisite ancestor elements as needed.   If successful, 0 is returned and
+ * *element_ret is set to a pointer to the resulting element.  If unsuccessful,
+ * an error code is returned and *element_ret is set to NULL.
+ */
 static int
-xml_ensure_node_by_path(xmlNode *root, const xmlChar *path, xmlNode **node_ret)
+xml_ensure_element_by_path(struct xml_node *root, const tchar *path,
+                          struct xml_node **element_ret)
 {
-       return do_xml_path_walk(root, path, true, node_ret);
+       return do_xml_path_walk(root, path, true, element_ret);
 }
 
 static u64
-xml_get_number_by_path(xmlNode *root, const xmlChar *path)
+xml_get_number_by_path(struct xml_node *root, const tchar *path)
 {
-       return node_get_number(xml_get_node_by_path(root, path), 10);
+       return xml_element_get_number(xml_get_element_by_path(root, path), 10);
 }
 
 static u64
-xml_get_timestamp_by_path(xmlNode *root, const xmlChar *path)
+xml_get_timestamp_by_path(struct xml_node *root, const tchar *path)
 {
-       return node_get_timestamp(xml_get_node_by_path(root, path));
-}
-
-static const xmlChar *
-xml_get_text_by_path(xmlNode *root, const xmlChar *path)
-{
-       return node_get_text(xml_get_node_by_path(root, path));
+       return xml_element_get_timestamp(xml_get_element_by_path(root, path));
 }
 
 static const tchar *
-xml_get_ttext_by_path(struct wim_xml_info *info, xmlNode *root,
-                     const xmlChar *path)
+xml_get_text_by_path(struct xml_node *root, const tchar *path)
 {
-       return node_get_ttext(info, xml_get_node_by_path(root, path));
+       return xml_element_get_text(xml_get_element_by_path(root, path));
 }
 
-/* Creates/replaces (if ttext is not NULL and not empty) or removes (if ttext is
- * NULL or empty) an element containing text.  */
+/*
+ * Create/replace (if text is not NULL and not empty) or remove (if text is NULL
+ * or empty) an element containing text.
+ */
 static int
-xml_set_ttext_by_path(xmlNode *root, const xmlChar *path, const tchar *ttext)
+xml_set_text_by_path(struct xml_node *root, const tchar *path,
+                    const tchar *text)
 {
        int ret;
-       xmlNode *node;
+       struct xml_node *element;
 
-       if (ttext && *ttext) {
+       if (text && *text) {
                /* Create or replace  */
-               ret = xml_ensure_node_by_path(root, path, &node);
+               ret = xml_ensure_element_by_path(root, path, &element);
                if (ret)
                        return ret;
-               return node_set_ttext(node, ttext);
+               return xml_element_set_text(element, text);
        } else {
                /* Remove  */
-               node = xml_get_node_by_path(root, path);
-               if (node)
-                       unlink_and_free_tree(node);
+               xml_free_node(xml_get_element_by_path(root, path));
                return 0;
        }
 }
 
 /* Unlink and return the node which represents the INDEX attribute of the
  * specified IMAGE element.  */
-static xmlAttr *
-unlink_index_attribute(xmlNode *image_node)
+static struct xml_node *
+unlink_index_attribute(struct xml_node *image_node)
 {
-       xmlAttr *attr = xmlHasProp(image_node, "INDEX");
-       xmlUnlinkNode((xmlNode *)attr);
+       struct xml_node *attr = xml_get_attrib(image_node, T("INDEX"));
+
+       xml_unlink_node(attr);
        return attr;
 }
 
@@ -550,19 +339,21 @@ inode_sum_stream_sizes(const struct wim_inode *inode,
 }
 
 static int
-append_image_node(struct wim_xml_info *info, xmlNode *image_node)
+append_image_node(struct wim_xml_info *info, struct xml_node *image_node)
 {
-       char buf[32];
-       xmlNode **images;
+       tchar buf[32];
+       struct xml_node **images;
+       int ret;
 
        /* Limit exceeded?  */
        if (unlikely(info->image_count >= MAX_IMAGES))
                return WIMLIB_ERR_IMAGE_COUNT;
 
-       /* Add the INDEX attribute.  */
-       sprintf(buf, "%d", info->image_count + 1);
-       if (!xmlNewProp(image_node, "INDEX", buf))
-               return WIMLIB_ERR_NOMEM;
+       /* Set the INDEX attribute. */
+       tsprintf(buf, T("%d"), info->image_count + 1);
+       ret = xml_set_attrib(image_node, T("INDEX"), buf);
+       if (ret)
+               return ret;
 
        /* Append the IMAGE element to the 'images' array.  */
        images = REALLOC(info->images,
@@ -573,7 +364,7 @@ append_image_node(struct wim_xml_info *info, xmlNode *image_node)
        images[info->image_count++] = image_node;
 
        /* Add the IMAGE element to the document.  */
-       xmlAddChild(info->root, image_node);
+       xml_add_child(info->root, image_node);
        return 0;
 }
 
@@ -585,31 +376,17 @@ append_image_node(struct wim_xml_info *info, xmlNode *image_node)
 struct wim_xml_info *
 xml_new_info_struct(void)
 {
-       struct wim_xml_info *info;
+       struct wim_xml_info *info = CALLOC(1, sizeof(*info));
 
-       info = alloc_wim_xml_info();
        if (!info)
-               goto err;
-
-       info->doc = xmlNewDoc("1.0");
-       if (!info->doc)
-               goto err_free_info;
-
-       info->root = xmlNewNode(NULL, "WIM");
-       if (!info->root)
-               goto err_free_doc;
-       xmlDocSetRootElement(info->doc, info->root);
+               return NULL;
 
-       info->images = NULL;
-       info->image_count = 0;
+       info->root = xml_new_element(NULL, T("WIM"));
+       if (!info->root) {
+               FREE(info);
+               return NULL;
+       }
        return info;
-
-err_free_doc:
-       xmlFreeDoc(info->doc);
-err_free_info:
-       FREE(info);
-err:
-       return NULL;
 }
 
 /* Free a 'struct wim_xml_info'.  */
@@ -617,12 +394,8 @@ void
 xml_free_info_struct(struct wim_xml_info *info)
 {
        if (info) {
-               xmlFreeDoc(info->doc);
+               xml_free_node(info->root);
                FREE(info->images);
-       #if TCHAR_IS_UTF16LE
-               for (size_t i = 0; i < info->num_strings; i++)
-                       FREE(info->strings[i]);
-       #endif
                FREE(info);
        }
 }
@@ -640,7 +413,7 @@ xml_get_image_count(const struct wim_xml_info *info)
 u64
 xml_get_total_bytes(const struct wim_xml_info *info)
 {
-       return xml_get_number_by_path(info->root, "TOTALBYTES");
+       return xml_get_number_by_path(info->root, T("TOTALBYTES"));
 }
 
 /* Retrieve the TOTALBYTES value for the specified image, or 0 if this value is
@@ -648,7 +421,7 @@ xml_get_total_bytes(const struct wim_xml_info *info)
 u64
 xml_get_image_total_bytes(const struct wim_xml_info *info, int image)
 {
-       return xml_get_number_by_path(info->images[image - 1], "TOTALBYTES");
+       return xml_get_number_by_path(info->images[image - 1], T("TOTALBYTES"));
 }
 
 /* Retrieve the HARDLINKBYTES value for the specified image, or 0 if this value
@@ -656,7 +429,8 @@ xml_get_image_total_bytes(const struct wim_xml_info *info, int image)
 u64
 xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image)
 {
-       return xml_get_number_by_path(info->images[image - 1], "HARDLINKBYTES");
+       return xml_get_number_by_path(info->images[image - 1],
+                                     T("HARDLINKBYTES"));
 }
 
 /* Retrieve the WIMBOOT value for the specified image, or false if this value is
@@ -664,7 +438,7 @@ xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image)
 bool
 xml_get_wimboot(const struct wim_xml_info *info, int image)
 {
-       return xml_get_number_by_path(info->images[image - 1], "WIMBOOT");
+       return xml_get_number_by_path(info->images[image - 1], T("WIMBOOT"));
 }
 
 /* Retrieve the Windows build number for the specified image, or 0 if this
@@ -673,14 +447,15 @@ u64
 xml_get_windows_build_number(const struct wim_xml_info *info, int image)
 {
        return xml_get_number_by_path(info->images[image - 1],
-                                     "WINDOWS/VERSION/BUILD");
+                                     T("WINDOWS/VERSION/BUILD"));
 }
 
 /* Set the WIMBOOT value for the specified image.  */
 int
 xml_set_wimboot(struct wim_xml_info *info, int image)
 {
-       return xml_set_ttext_by_path(info->images[image - 1], "WIMBOOT", T("1"));
+       return xml_set_text_by_path(info->images[image - 1],
+                                   T("WIMBOOT"), T("1"));
 }
 
 /*
@@ -694,18 +469,18 @@ int
 xml_update_image_info(WIMStruct *wim, int image)
 {
        const struct wim_image_metadata *imd = wim->image_metadata[image - 1];
-       xmlNode *image_node = wim->xml_info->images[image - 1];
+       struct xml_node *image_node = wim->xml_info->images[image - 1];
        const struct wim_inode *inode;
        u64 dir_count = 0;
        u64 file_count = 0;
        u64 total_bytes = 0;
        u64 hard_link_bytes = 0;
        u64 size;
-       xmlNode *dircount_node;
-       xmlNode *filecount_node;
-       xmlNode *totalbytes_node;
-       xmlNode *hardlinkbytes_node;
-       xmlNode *lastmodificationtime_node;
+       struct xml_node *dircount_node;
+       struct xml_node *filecount_node;
+       struct xml_node *totalbytes_node;
+       struct xml_node *hardlinkbytes_node;
+       struct xml_node *lastmodificationtime_node;
 
        image_for_each_inode(inode, imd) {
                if (inode_is_directory(inode))
@@ -717,30 +492,32 @@ xml_update_image_info(WIMStruct *wim, int image)
                hard_link_bytes += size * (inode->i_nlink - 1);
        }
 
-       dircount_node = new_element_with_u64(NULL, "DIRCOUNT", dir_count);
-       filecount_node = new_element_with_u64(NULL, "FILECOUNT", file_count);
-       totalbytes_node = new_element_with_u64(NULL, "TOTALBYTES", total_bytes);
-       hardlinkbytes_node = new_element_with_u64(NULL, "HARDLINKBYTES",
-                                                 hard_link_bytes);
-       lastmodificationtime_node =
-               new_element_with_timestamp(NULL, "LASTMODIFICATIONTIME",
-                                          now_as_wim_timestamp());
+       dircount_node = xml_new_element_with_u64(NULL, T("DIRCOUNT"),
+                                                dir_count);
+       filecount_node = xml_new_element_with_u64(NULL, T("FILECOUNT"),
+                                                 file_count);
+       totalbytes_node = xml_new_element_with_u64(NULL, T("TOTALBYTES"),
+                                                  total_bytes);
+       hardlinkbytes_node = xml_new_element_with_u64(NULL, T("HARDLINKBYTES"),
+                                                     hard_link_bytes);
+       lastmodificationtime_node = xml_new_element_with_timestamp(NULL,
+                       T("LASTMODIFICATIONTIME"), now_as_wim_timestamp());
 
        if (unlikely(!dircount_node || !filecount_node || !totalbytes_node ||
                     !hardlinkbytes_node || !lastmodificationtime_node)) {
-               xmlFreeNode(dircount_node);
-               xmlFreeNode(filecount_node);
-               xmlFreeNode(totalbytes_node);
-               xmlFreeNode(hardlinkbytes_node);
-               xmlFreeNode(lastmodificationtime_node);
+               xml_free_node(dircount_node);
+               xml_free_node(filecount_node);
+               xml_free_node(totalbytes_node);
+               xml_free_node(hardlinkbytes_node);
+               xml_free_node(lastmodificationtime_node);
                return WIMLIB_ERR_NOMEM;
        }
 
-       node_replace_child_element(image_node, dircount_node);
-       node_replace_child_element(image_node, filecount_node);
-       node_replace_child_element(image_node, totalbytes_node);
-       node_replace_child_element(image_node, hardlinkbytes_node);
-       node_replace_child_element(image_node, lastmodificationtime_node);
+       xml_replace_child(image_node, dircount_node);
+       xml_replace_child(image_node, filecount_node);
+       xml_replace_child(image_node, totalbytes_node);
+       xml_replace_child(image_node, hardlinkbytes_node);
+       xml_replace_child(image_node, lastmodificationtime_node);
        return 0;
 }
 
@@ -749,31 +526,33 @@ int
 xml_add_image(struct wim_xml_info *info, const tchar *name)
 {
        const u64 now = now_as_wim_timestamp();
-       xmlNode *image_node;
+       struct xml_node *image_node;
        int ret;
 
+       if (name && !xml_legal_value(name)) {
+               ERROR("Name of new image contains illegal characters");
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+
        ret = WIMLIB_ERR_NOMEM;
-       image_node = xmlNewNode(NULL, "IMAGE");
+       image_node = xml_new_element(NULL, T("IMAGE"));
        if (!image_node)
                goto err;
-
-       if (name && *name) {
-               ret = new_element_with_ttext(image_node, "NAME", name, NULL);
-               if (ret)
-                       goto err;
-       }
-       ret = WIMLIB_ERR_NOMEM;
-       if (!new_element_with_u64(image_node, "DIRCOUNT", 0))
+       if (name && *name &&
+           !xml_new_element_with_text(image_node, T("NAME"), name))
+               goto err;
+       if (!xml_new_element_with_u64(image_node, T("DIRCOUNT"), 0))
                goto err;
-       if (!new_element_with_u64(image_node, "FILECOUNT", 0))
+       if (!xml_new_element_with_u64(image_node, T("FILECOUNT"), 0))
                goto err;
-       if (!new_element_with_u64(image_node, "TOTALBYTES", 0))
+       if (!xml_new_element_with_u64(image_node, T("TOTALBYTES"), 0))
                goto err;
-       if (!new_element_with_u64(image_node, "HARDLINKBYTES", 0))
+       if (!xml_new_element_with_u64(image_node, T("HARDLINKBYTES"), 0))
                goto err;
-       if (!new_element_with_timestamp(image_node, "CREATIONTIME", now))
+       if (!xml_new_element_with_timestamp(image_node, T("CREATIONTIME"), now))
                goto err;
-       if (!new_element_with_timestamp(image_node, "LASTMODIFICATIONTIME", now))
+       if (!xml_new_element_with_timestamp(image_node,
+                                           T("LASTMODIFICATIONTIME"), now))
                goto err;
        ret = append_image_node(info, image_node);
        if (ret)
@@ -781,7 +560,7 @@ xml_add_image(struct wim_xml_info *info, const tchar *name)
        return 0;
 
 err:
-       xmlFreeNode(image_node);
+       xml_free_node(image_node);
        return ret;
 }
 
@@ -799,39 +578,46 @@ xml_export_image(const struct wim_xml_info *src_info, int src_image,
                 struct wim_xml_info *dest_info, const tchar *dest_image_name,
                 const tchar *dest_image_description, bool wimboot)
 {
-       xmlNode *dest_node;
+       struct xml_node *dest_node;
        int ret;
 
+       if (dest_image_name && !xml_legal_value(dest_image_name)) {
+               ERROR("Destination image name contains illegal characters");
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+       if (dest_image_description &&
+           !xml_legal_value(dest_image_description)) {
+               ERROR("Destination image description contains illegal characters");
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+
        ret = WIMLIB_ERR_NOMEM;
-       dest_node = xmlDocCopyNode(src_info->images[src_image - 1],
-                                  dest_info->doc, 1);
+       dest_node = xml_clone_tree(src_info->images[src_image - 1]);
        if (!dest_node)
                goto err;
 
-       ret = xml_set_ttext_by_path(dest_node, "NAME", dest_image_name);
+       ret = xml_set_text_by_path(dest_node, T("NAME"), dest_image_name);
        if (ret)
                goto err;
 
-       ret = xml_set_ttext_by_path(dest_node, "DESCRIPTION",
-                                   dest_image_description);
+       ret = xml_set_text_by_path(dest_node, T("DESCRIPTION"),
+                                  dest_image_description);
        if (ret)
                goto err;
 
        if (wimboot) {
-               ret = xml_set_ttext_by_path(dest_node, "WIMBOOT", T("1"));
+               ret = xml_set_text_by_path(dest_node, T("WIMBOOT"), T("1"));
                if (ret)
                        goto err;
        }
 
-       xmlFreeProp(unlink_index_attribute(dest_node));
-
        ret = append_image_node(dest_info, dest_node);
        if (ret)
                goto err;
        return 0;
 
 err:
-       xmlFreeNode(dest_node);
+       xml_free_node(dest_node);
        return ret;
 }
 
@@ -839,8 +625,8 @@ err:
 void
 xml_delete_image(struct wim_xml_info *info, int image)
 {
-       xmlNode *next_image;
-       xmlAttr *index_attr, *next_index_attr;
+       struct xml_node *next_image;
+       struct xml_node *index_attr, *next_index_attr;
 
        /* Free the IMAGE element for the deleted image.  Then, shift all
         * higher-indexed IMAGE elements down by 1, in the process re-assigning
@@ -848,18 +634,18 @@ xml_delete_image(struct wim_xml_info *info, int image)
 
        next_image = info->images[image - 1];
        next_index_attr = unlink_index_attribute(next_image);
-       unlink_and_free_tree(next_image);
+       xml_free_node(next_image);
 
        while (image < info->image_count) {
                index_attr = next_index_attr;
                next_image = info->images[image];
                next_index_attr = unlink_index_attribute(next_image);
-               xmlAddChild(next_image, (xmlNode *)index_attr);
+               xml_add_child(next_image, index_attr);
                info->images[image - 1] = next_image;
                image++;
        }
 
-       xmlFreeProp(next_index_attr);
+       xml_free_node(next_index_attr);
        info->image_count--;
 }
 
@@ -897,80 +683,80 @@ describe_arch(u64 arch)
 
 /* Print information from the WINDOWS element, if present.  */
 static void
-print_windows_info(struct wim_xml_info *info, xmlNode *image_node)
+print_windows_info(struct xml_node *image_node)
 {
-       xmlNode *windows_node;
-       xmlNode *langs_node;
-       xmlNode *version_node;
+       struct xml_node *windows_node;
+       struct xml_node *langs_node;
+       struct xml_node *version_node;
        const tchar *text;
 
-       windows_node = xml_get_node_by_path(image_node, "WINDOWS");
+       windows_node = xml_get_element_by_path(image_node, T("WINDOWS"));
        if (!windows_node)
                return;
 
        tprintf(T("Architecture:           %"TS"\n"),
-               describe_arch(xml_get_number_by_path(windows_node, "ARCH")));
+               describe_arch(xml_get_number_by_path(windows_node, T("ARCH"))));
 
-       text = xml_get_ttext_by_path(info, windows_node, "PRODUCTNAME");
+       text = xml_get_text_by_path(windows_node, T("PRODUCTNAME"));
        if (text)
                tprintf(T("Product Name:           %"TS"\n"), text);
 
-       text = xml_get_ttext_by_path(info, windows_node, "EDITIONID");
+       text = xml_get_text_by_path(windows_node, T("EDITIONID"));
        if (text)
                tprintf(T("Edition ID:             %"TS"\n"), text);
 
-       text = xml_get_ttext_by_path(info, windows_node, "INSTALLATIONTYPE");
+       text = xml_get_text_by_path(windows_node, T("INSTALLATIONTYPE"));
        if (text)
                tprintf(T("Installation Type:      %"TS"\n"), text);
 
-       text = xml_get_ttext_by_path(info, windows_node, "HAL");
+       text = xml_get_text_by_path(windows_node, T("HAL"));
        if (text)
                tprintf(T("HAL:                    %"TS"\n"), text);
 
-       text = xml_get_ttext_by_path(info, windows_node, "PRODUCTTYPE");
+       text = xml_get_text_by_path(windows_node, T("PRODUCTTYPE"));
        if (text)
                tprintf(T("Product Type:           %"TS"\n"), text);
 
-       text = xml_get_ttext_by_path(info, windows_node, "PRODUCTSUITE");
+       text = xml_get_text_by_path(windows_node, T("PRODUCTSUITE"));
        if (text)
                tprintf(T("Product Suite:          %"TS"\n"), text);
 
-       langs_node = xml_get_node_by_path(windows_node, "LANGUAGES");
+       langs_node = xml_get_element_by_path(windows_node, T("LANGUAGES"));
        if (langs_node) {
-               xmlNode *lang_node;
+               struct xml_node *lang_node;
 
                tprintf(T("Languages:              "));
-               node_for_each_child(langs_node, lang_node) {
-                       if (!node_is_element(lang_node, "LANGUAGE"))
+               xml_node_for_each_child(langs_node, lang_node) {
+                       if (!xml_node_is_element(lang_node, T("LANGUAGE")))
                                continue;
-                       text = node_get_ttext(info, lang_node);
+                       text = xml_element_get_text(lang_node);
                        if (!text)
                                continue;
                        tprintf(T("%"TS" "), text);
                }
                tputchar(T('\n'));
 
-               text = xml_get_ttext_by_path(info, langs_node, "DEFAULT");
+               text = xml_get_text_by_path(langs_node, T("DEFAULT"));
                if (text)
                        tprintf(T("Default Language:       %"TS"\n"), text);
        }
 
-       text = xml_get_ttext_by_path(info, windows_node, "SYSTEMROOT");
+       text = xml_get_text_by_path(windows_node, T("SYSTEMROOT"));
        if (text)
                tprintf(T("System Root:            %"TS"\n"), text);
 
-       version_node = xml_get_node_by_path(windows_node, "VERSION");
+       version_node = xml_get_element_by_path(windows_node, T("VERSION"));
        if (version_node) {
                tprintf(T("Major Version:          %"PRIu64"\n"),
-                       xml_get_number_by_path(version_node, "MAJOR"));
+                       xml_get_number_by_path(version_node, T("MAJOR")));
                tprintf(T("Minor Version:          %"PRIu64"\n"),
-                       xml_get_number_by_path(version_node, "MINOR"));
+                       xml_get_number_by_path(version_node, T("MINOR")));
                tprintf(T("Build:                  %"PRIu64"\n"),
-                       xml_get_number_by_path(version_node, "BUILD"));
+                       xml_get_number_by_path(version_node, T("BUILD")));
                tprintf(T("Service Pack Build:     %"PRIu64"\n"),
-                       xml_get_number_by_path(version_node, "SPBUILD"));
+                       xml_get_number_by_path(version_node, T("SPBUILD")));
                tprintf(T("Service Pack Level:     %"PRIu64"\n"),
-                       xml_get_number_by_path(version_node, "SPLEVEL"));
+                       xml_get_number_by_path(version_node, T("SPLEVEL")));
        }
 }
 
@@ -978,7 +764,7 @@ print_windows_info(struct wim_xml_info *info, xmlNode *image_node)
 void
 xml_print_image_info(struct wim_xml_info *info, int image)
 {
-       xmlNode * const image_node = info->images[image - 1];
+       struct xml_node * const image_node = info->images[image - 1];
        const tchar *text;
        tchar timebuf[64];
 
@@ -986,49 +772,49 @@ xml_print_image_info(struct wim_xml_info *info, int image)
 
        /* Always print the Name and Description, even if the corresponding XML
         * elements are not present.  */
-       text = xml_get_ttext_by_path(info, image_node, "NAME");
+       text = xml_get_text_by_path(image_node, T("NAME"));
        tprintf(T("Name:                   %"TS"\n"), text ? text : T(""));
-       text = xml_get_ttext_by_path(info, image_node, "DESCRIPTION");
+       text = xml_get_text_by_path(image_node, T("DESCRIPTION"));
        tprintf(T("Description:            %"TS"\n"), text ? text : T(""));
 
-       text = xml_get_ttext_by_path(info, image_node, "DISPLAYNAME");
+       text = xml_get_text_by_path(image_node, T("DISPLAYNAME"));
        if (text)
                tprintf(T("Display Name:           %"TS"\n"), text);
 
-       text = xml_get_ttext_by_path(info, image_node, "DISPLAYDESCRIPTION");
+       text = xml_get_text_by_path(image_node, T("DISPLAYDESCRIPTION"));
        if (text)
                tprintf(T("Display Description:    %"TS"\n"), text);
 
        tprintf(T("Directory Count:        %"PRIu64"\n"),
-               xml_get_number_by_path(image_node, "DIRCOUNT"));
+               xml_get_number_by_path(image_node, T("DIRCOUNT")));
 
        tprintf(T("File Count:             %"PRIu64"\n"),
-               xml_get_number_by_path(image_node, "FILECOUNT"));
+               xml_get_number_by_path(image_node, T("FILECOUNT")));
 
        tprintf(T("Total Bytes:            %"PRIu64"\n"),
-               xml_get_number_by_path(image_node, "TOTALBYTES"));
+               xml_get_number_by_path(image_node, T("TOTALBYTES")));
 
        tprintf(T("Hard Link Bytes:        %"PRIu64"\n"),
-               xml_get_number_by_path(image_node, "HARDLINKBYTES"));
+               xml_get_number_by_path(image_node, T("HARDLINKBYTES")));
 
        wim_timestamp_to_str(xml_get_timestamp_by_path(image_node,
-                                                      "CREATIONTIME"),
+                                                      T("CREATIONTIME")),
                             timebuf, ARRAY_LEN(timebuf));
        tprintf(T("Creation Time:          %"TS"\n"), timebuf);
 
        wim_timestamp_to_str(xml_get_timestamp_by_path(image_node,
-                                                      "LASTMODIFICATIONTIME"),
-                            timebuf, ARRAY_LEN(timebuf));
+                                       T("LASTMODIFICATIONTIME")),
+                                       timebuf, ARRAY_LEN(timebuf));
        tprintf(T("Last Modification Time: %"TS"\n"), timebuf);
 
-       print_windows_info(info, image_node);
+       print_windows_info(image_node);
 
-       text = xml_get_ttext_by_path(info, image_node, "FLAGS");
+       text = xml_get_text_by_path(image_node, T("FLAGS"));
        if (text)
                tprintf(T("Flags:                  %"TS"\n"), text);
 
        tprintf(T("WIMBoot compatible:     %"TS"\n"),
-               xml_get_number_by_path(image_node, "WIMBOOT") ?
+               xml_get_number_by_path(image_node, T("WIMBOOT")) ?
                        T("yes") : T("no"));
 
        tputchar('\n');
@@ -1039,28 +825,28 @@ xml_print_image_info(struct wim_xml_info *info, int image)
  *----------------------------------------------------------------------------*/
 
 static int
-image_node_get_index(xmlNode *node)
+image_element_get_index(struct xml_node *element)
 {
-       u64 v = node_get_number((const xmlNode *)xmlHasProp(node, "INDEX"), 10);
-       return min(v, INT_MAX);
+       struct xml_node *attrib = xml_get_attrib(element, T("INDEX"));
+
+       if (!attrib)
+               return 0;
+       return min(INT_MAX, parse_number(attrib->value, 10));
 }
 
 /* Prepare the 'images' array from the XML document tree.  */
 static int
-setup_images(struct wim_xml_info *info, xmlNode *root)
+setup_images(struct wim_xml_info *info, struct xml_node *root)
 {
-       xmlNode *child;
+       struct xml_node *child;
        int index;
        int max_index = 0;
        int ret;
 
-       info->images = NULL;
-       info->image_count = 0;
-
-       node_for_each_child(root, child) {
-               if (!node_is_element(child, "IMAGE"))
+       xml_node_for_each_child(root, child) {
+               if (!xml_node_is_element(child, T("IMAGE")))
                        continue;
-               index = image_node_get_index(child);
+               index = image_element_get_index(child);
                if (unlikely(index < 1 || info->image_count >= MAX_IMAGES))
                        goto err_indices;
                max_index = max(max_index, index);
@@ -1072,10 +858,10 @@ setup_images(struct wim_xml_info *info, xmlNode *root)
        info->images = CALLOC(info->image_count, sizeof(info->images[0]));
        if (unlikely(!info->images))
                goto err;
-       node_for_each_child(root, child) {
-               if (!node_is_element(child, "IMAGE"))
+       xml_node_for_each_child(root, child) {
+               if (!xml_node_is_element(child, T("IMAGE")))
                        continue;
-               index = image_node_get_index(child);
+               index = image_element_get_index(child);
                if (unlikely(info->images[index - 1]))
                        goto err_indices;
                info->images[index - 1] = child;
@@ -1091,97 +877,108 @@ err:
        return ret;
 }
 
+static int
+parse_wim_xml_document(const utf16lechar *raw_doc, size_t raw_doc_size,
+                      struct xml_node **root_ret)
+{
+       tchar *doc;
+       int ret;
+
+       ret = utf16le_to_tstr(raw_doc, raw_doc_size, &doc, NULL);
+       if (ret)
+               return ret;
+       ret = xml_parse_document(doc, root_ret);
+       FREE(doc);
+       return ret;
+}
+
 /* Reads the XML data from a WIM file.  */
 int
 read_wim_xml_data(WIMStruct *wim)
 {
        struct wim_xml_info *info;
-       void *buf;
-       size_t bufsize;
-       xmlDoc *doc;
-       xmlNode *root;
+       void *raw_doc;
+       size_t raw_doc_size;
+       struct xml_node *root;
        int ret;
 
        /* Allocate the 'struct wim_xml_info'.  */
        ret = WIMLIB_ERR_NOMEM;
-       info = alloc_wim_xml_info();
+       info = CALLOC(1, sizeof(*info));
        if (!info)
                goto err;
 
-       /* Read the raw UTF-16LE bytes.  */
-       ret = wimlib_get_xml_data(wim, &buf, &bufsize);
+       /* Read the raw UTF-16LE XML document.  */
+       ret = wimlib_get_xml_data(wim, &raw_doc, &raw_doc_size);
        if (ret)
-               goto err_free_info;
+               goto err;
 
-       /* Parse the document with libxml2, creating the document tree.  */
-       doc = xmlReadMemory(buf, bufsize, NULL, "UTF-16LE", XML_PARSE_NONET);
-       FREE(buf);
-       buf = NULL;
-       if (!doc) {
+       /* Parse the document, creating the document tree.  */
+       ret = parse_wim_xml_document(raw_doc, raw_doc_size, &info->root);
+       FREE(raw_doc);
+       raw_doc = NULL;
+       if (ret) {
+               if (ret != WIMLIB_ERR_NOMEM)
+                       ret = WIMLIB_ERR_XML;
                ERROR("Unable to parse the WIM file's XML document!");
-               ret = WIMLIB_ERR_XML;
-               goto err_free_info;
+               goto err;
        }
+       root = info->root;
 
        /* Verify the root element.  */
-       root = xmlDocGetRootElement(doc);
-       if (!node_is_element(root, "WIM")) {
+       if (!xml_node_is_element(root, T("WIM"))) {
                ERROR("The WIM file's XML document has an unexpected format!");
                ret = WIMLIB_ERR_XML;
-               goto err_free_doc;
+               goto err;
        }
 
        /* Verify the WIM file is not encrypted.  */
-       if (xml_get_node_by_path(root, "ESD/ENCRYPTED")) {
+       if (xml_get_element_by_path(root, T("ESD/ENCRYPTED"))) {
                ret = WIMLIB_ERR_WIM_IS_ENCRYPTED;
-               goto err_free_doc;
+               goto err;
        }
 
        /* Validate the image elements and set up the images[] array.  */
        ret = setup_images(info, root);
        if (ret)
-               goto err_free_doc;
+               goto err;
 
-       /* Save the document and return.  */
-       info->doc = doc;
-       info->root = root;
+       /* Success!  */
        wim->xml_info = info;
        return 0;
 
-err_free_doc:
-       xmlFreeDoc(doc);
-err_free_info:
-       FREE(info);
 err:
+       xml_free_info_struct(info);
        return ret;
 }
 
 /* Swap the INDEX attributes of two IMAGE elements.  */
 static void
-swap_index_attributes(xmlNode *image_node_1, xmlNode *image_node_2)
+swap_index_attributes(struct xml_node *image_element_1,
+                     struct xml_node *image_element_2)
 {
-       xmlAttr *attr_1, *attr_2;
+       struct xml_node *attr_1, *attr_2;
 
-       if (image_node_1 != image_node_2) {
-               attr_1 = unlink_index_attribute(image_node_1);
-               attr_2 = unlink_index_attribute(image_node_2);
-               xmlAddChild(image_node_1, (xmlNode *)attr_2);
-               xmlAddChild(image_node_2, (xmlNode *)attr_1);
+       if (image_element_1 != image_element_2) {
+               attr_1 = unlink_index_attribute(image_element_1);
+               attr_2 = unlink_index_attribute(image_element_2);
+               xml_add_child(image_element_1, attr_2);
+               xml_add_child(image_element_2, attr_1);
        }
 }
 
 static int
 prepare_document_for_write(struct wim_xml_info *info, int image, u64 total_bytes,
-                          xmlNode **orig_totalbytes_node_ret)
+                          struct xml_node **orig_totalbytes_element_ret)
 {
-       xmlNode *totalbytes_node = NULL;
+       struct xml_node *totalbytes_element = NULL;
 
        /* Allocate the new TOTALBYTES element if needed.  */
        if (total_bytes != WIM_TOTALBYTES_USE_EXISTING &&
            total_bytes != WIM_TOTALBYTES_OMIT) {
-               totalbytes_node = new_element_with_u64(NULL, "TOTALBYTES",
-                                                      total_bytes);
-               if (!totalbytes_node)
+               totalbytes_element = xml_new_element_with_u64(
+                                       NULL, T("TOTALBYTES"), total_bytes);
+               if (!totalbytes_element)
                        return WIMLIB_ERR_NOMEM;
        }
 
@@ -1191,7 +988,7 @@ prepare_document_for_write(struct wim_xml_info *info, int image, u64 total_bytes
                 * other IMAGE elements from the document.  */
                for (int i = 0; i < info->image_count; i++)
                        if (i + 1 != image)
-                               xmlUnlinkNode(info->images[i]);
+                               xml_unlink_node(info->images[i]);
 
                /* Temporarily set the INDEX attribute of the needed IMAGE
                 * element to 1.  */
@@ -1199,24 +996,24 @@ prepare_document_for_write(struct wim_xml_info *info, int image, u64 total_bytes
        }
 
        /* Adjust (add, change, or remove) the TOTALBYTES element if needed.  */
-       *orig_totalbytes_node_ret = NULL;
+       *orig_totalbytes_element_ret = NULL;
        if (total_bytes != WIM_TOTALBYTES_USE_EXISTING) {
                /* Unlink the previous TOTALBYTES element, if any.  */
-               *orig_totalbytes_node_ret = xml_get_node_by_path(info->root,
-                                                                "TOTALBYTES");
-               if (*orig_totalbytes_node_ret)
-                       xmlUnlinkNode(*orig_totalbytes_node_ret);
+               *orig_totalbytes_element_ret = xml_get_element_by_path(
+                                               info->root, T("TOTALBYTES"));
+               if (*orig_totalbytes_element_ret)
+                       xml_unlink_node(*orig_totalbytes_element_ret);
 
                /* Link in the new TOTALBYTES element, if any.  */
-               if (totalbytes_node)
-                       xmlAddChild(info->root, totalbytes_node);
+               if (totalbytes_element)
+                       xml_add_child(info->root, totalbytes_element);
        }
        return 0;
 }
 
 static void
 restore_document_after_write(struct wim_xml_info *info, int image,
-                            xmlNode *orig_totalbytes_node)
+                            struct xml_node *orig_totalbytes_element)
 {
        /* Restore the IMAGE elements if needed.  */
        if (image != WIMLIB_ALL_IMAGES) {
@@ -1224,15 +1021,15 @@ restore_document_after_write(struct wim_xml_info *info, int image,
                 * elements to the document.  */
                for (int i = 0; i < info->image_count; i++)
                        if (i + 1 != image)
-                               xmlAddChild(info->root, info->images[i]);
+                               xml_add_child(info->root, info->images[i]);
 
                /* Restore the original INDEX attributes.  */
                swap_index_attributes(info->images[0], info->images[image - 1]);
        }
 
        /* Restore the original TOTALBYTES element if needed.  */
-       if (orig_totalbytes_node)
-               node_replace_child_element(info->root, orig_totalbytes_node);
+       if (orig_totalbytes_element)
+               xml_replace_child(info->root, orig_totalbytes_element);
 }
 
 /*
@@ -1250,45 +1047,29 @@ write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes,
                   struct wim_reshdr *out_reshdr, int write_resource_flags)
 {
        struct wim_xml_info *info = wim->xml_info;
-       long ret;
-       long ret2;
-       xmlBuffer *buffer;
-       xmlNode *orig_totalbytes_node;
-       xmlSaveCtxt *save_ctx;
+       int ret;
+       struct xml_node *orig_totalbytes_element;
+       struct xml_out_buf buf = {};
+       const utf16lechar *raw_doc;
+       size_t raw_doc_size;
 
        /* Make any needed temporary changes to the document.  */
        ret = prepare_document_for_write(info, image, total_bytes,
-                                        &orig_totalbytes_node);
+                                        &orig_totalbytes_element);
        if (ret)
                goto out;
 
-       /* Create an in-memory buffer to hold the encoded document.  */
-       ret = WIMLIB_ERR_NOMEM;
-       buffer = xmlBufferCreate();
-       if (!buffer)
+       ret = xml_write_document(info->root, &buf);
+       if (ret)
                goto out_restore_document;
 
-       /* Encode the document in UTF-16LE, with a byte order mark, and with no
-        * XML declaration.  Some other WIM software requires all of these
-        * characteristics.  */
-       ret = WIMLIB_ERR_NOMEM;
-       if (xmlBufferCat(buffer, "\xff\xfe"))
-               goto out_free_buffer;
-       save_ctx = xmlSaveToBuffer(buffer, "UTF-16LE", XML_SAVE_NO_DECL);
-       if (!save_ctx)
-               goto out_free_buffer;
-       ret = xmlSaveDoc(save_ctx, info->doc);
-       ret2 = xmlSaveClose(save_ctx);
-       if (ret < 0 || ret2 < 0) {
-               ERROR("Unable to serialize the WIM file's XML document!");
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_free_buffer;
-       }
+       ret = tstr_get_utf16le_and_len(buf.buf, &raw_doc, &raw_doc_size);
+       if (ret)
+               goto out_restore_document;
 
        /* Write the XML data uncompressed.  Although wimlib can handle
         * compressed XML data, some other WIM software cannot.  */
-       ret = write_wim_resource_from_buffer(xmlBufferContent(buffer),
-                                            xmlBufferLength(buffer),
+       ret = write_wim_resource_from_buffer(raw_doc, raw_doc_size,
                                             true,
                                             &wim->out_fd,
                                             WIMLIB_COMPRESSION_TYPE_NONE,
@@ -1296,39 +1077,15 @@ write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes,
                                             out_reshdr,
                                             NULL,
                                             write_resource_flags);
-out_free_buffer:
-       xmlBufferFree(buffer);
+       tstr_put_utf16le(raw_doc);
 out_restore_document:
        /* Revert any temporary changes we made to the document.  */
-       restore_document_after_write(info, image, orig_totalbytes_node);
+       restore_document_after_write(info, image, orig_totalbytes_element);
+       FREE(buf.buf);
 out:
        return ret;
 }
 
-/*----------------------------------------------------------------------------*
- *                           Global setup functions                           *
- *----------------------------------------------------------------------------*/
-
-void
-xml_global_init(void)
-{
-       xmlInitParser();
-}
-
-void
-xml_global_cleanup(void)
-{
-       xmlCleanupParser();
-}
-
-void
-xml_set_memory_allocator(void *(*malloc_func)(size_t),
-                        void (*free_func)(void *),
-                        void *(*realloc_func)(void *, size_t))
-{
-       xmlMemSetup(free_func, malloc_func, realloc_func, wimlib_strdup);
-}
-
 /*----------------------------------------------------------------------------*
  *                           Library API functions                            *
  *----------------------------------------------------------------------------*/
@@ -1373,24 +1130,22 @@ static bool
 image_name_in_use(const WIMStruct *wim, const tchar *name, int excluded_image)
 {
        const struct wim_xml_info *info = wim->xml_info;
-       const xmlChar *name_utf8;
-       bool found = false;
+       const tchar *existing_name;
 
        /* Any number of images can have "no name".  */
        if (!name || !*name)
                return false;
 
        /* Check for images that have the specified name.  */
-       if (tstr_get_utf8(name, &name_utf8))
-               return false;
-       for (int i = 0; i < info->image_count && !found; i++) {
+       for (int i = 0; i < info->image_count; i++) {
                if (i + 1 == excluded_image)
                        continue;
-               found = xmlStrEqual(name_utf8, xml_get_text_by_path(
-                                                   info->images[i], "NAME"));
+               existing_name = xml_get_text_by_path(info->images[i],
+                                                    T("NAME"));
+               if (existing_name && !tstrcmp(existing_name, name))
+                       return true;
        }
-       tstr_put_utf8(name_utf8);
-       return found;
+       return false;
 }
 
 WIMLIBAPI bool
@@ -1421,19 +1176,13 @@ WIMLIBAPI const tchar *
 wimlib_get_image_property(const WIMStruct *wim, int image,
                          const tchar *property_name)
 {
-       const xmlChar *name;
-       const tchar *value;
-       struct wim_xml_info *info = wim->xml_info;
+       const struct wim_xml_info *info = wim->xml_info;
 
        if (!property_name || !*property_name)
                return NULL;
        if (image < 1 || image > info->image_count)
                return NULL;
-       if (tstr_get_utf8(property_name, &name))
-               return NULL;
-       value = xml_get_ttext_by_path(info, info->images[image - 1], name);
-       tstr_put_utf8(name);
-       return value;
+       return xml_get_text_by_path(info->images[image - 1], property_name);
 }
 
 WIMLIBAPI int
@@ -1445,7 +1194,8 @@ wimlib_set_image_name(WIMStruct *wim, int image, const tchar *name)
 WIMLIBAPI int
 wimlib_set_image_descripton(WIMStruct *wim, int image, const tchar *description)
 {
-       return wimlib_set_image_property(wim, image, T("DESCRIPTION"), description);
+       return wimlib_set_image_property(wim, image, T("DESCRIPTION"),
+                                        description);
 }
 
 WIMLIBAPI int
@@ -1458,13 +1208,22 @@ WIMLIBAPI int
 wimlib_set_image_property(WIMStruct *wim, int image, const tchar *property_name,
                          const tchar *property_value)
 {
-       const xmlChar *name;
        struct wim_xml_info *info = wim->xml_info;
-       int ret;
 
        if (!property_name || !*property_name)
                return WIMLIB_ERR_INVALID_PARAM;
 
+       if (!xml_legal_path(property_name)) {
+               ERROR("Property name '%"TS"' is illegal in XML", property_name);
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+
+       if (property_value && !xml_legal_value(property_value)) {
+               WARNING("Value of property '%"TS"' contains illegal characters",
+                       property_name);
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+
        if (image < 1 || image > info->image_count)
                return WIMLIB_ERR_INVALID_IMAGE;
 
@@ -1472,10 +1231,6 @@ wimlib_set_image_property(WIMStruct *wim, int image, const tchar *property_name,
            image_name_in_use(wim, property_value, image))
                return WIMLIB_ERR_IMAGE_NAME_COLLISION;
 
-       ret = tstr_get_utf8(property_name, &name);
-       if (ret)
-               return ret;
-       ret = xml_set_ttext_by_path(info->images[image - 1], name, property_value);
-       tstr_put_utf8(name);
-       return ret;
+       return xml_set_text_by_path(info->images[image - 1], property_name,
+                                   property_value);
 }
index a88c56485e050163411d29c0698979541a17fcf5..305d2efa5a77ae39947df91d99768dee6bb09822 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /*
- * Copyright (C) 2016 Eric Biggers
+ * Copyright 2016-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -154,7 +154,7 @@ copy_registry_string(struct windows_info_ctx *ctx, const struct regf *regf,
 static const struct {
        u16 id;
        u16 name_start_offset;
-} language_id_map[452] = {
+} language_id_map[453] = {
        {0x0000,    0}, {0x0001,    6}, {0x0002,   12}, {0x0003,   18},
        {0x0004,   24}, {0x0005,   30}, {0x0006,   36}, {0x0007,   42},
        {0x0008,   48}, {0x0009,   54}, {0x000a,   60}, {0x000b,   66},
@@ -183,96 +183,97 @@ static const struct {
        {0x0064,  647}, {0x0065,  654}, {0x0066,  660}, {0x0067,  667},
        {0x0068,  678}, {0x0069,  689}, {0x006a,  696}, {0x006b,  702},
        {0x006c,  709}, {0x006d,  716}, {0x006e,  722}, {0x006f,  728},
-       {0x0070,  734}, {0x0071,  740}, {0x0072,  746}, {0x0073,  752},
-       {0x0074,  758}, {0x0075,  764}, {0x0076,  771}, {0x0077,  778},
-       {0x0078,  784}, {0x0079,  790}, {0x007a,  798}, {0x007c,  805},
-       {0x007e,  812}, {0x007f,  818}, {0x0080,  819}, {0x0081,  825},
-       {0x0082,  831}, {0x0083,  837}, {0x0084,  843}, {0x0085,  850},
-       {0x0086,  857}, {0x0087,  869}, {0x0088,  875}, {0x008c,  881},
-       {0x0091,  888}, {0x0092,  894}, {0x0400,  905}, {0x0401,  911},
-       {0x0402,  917}, {0x0403,  923}, {0x0404,  929}, {0x0405,  935},
-       {0x0406,  941}, {0x0407,  947}, {0x0408,  953}, {0x0409,  959},
-       {0x040a,  965}, {0x040b,  978}, {0x040c,  984}, {0x040d,  990},
-       {0x040e,  996}, {0x040f, 1002}, {0x0410, 1008}, {0x0411, 1014},
-       {0x0412, 1020}, {0x0413, 1026}, {0x0414, 1032}, {0x0415, 1038},
-       {0x0416, 1044}, {0x0417, 1050}, {0x0418, 1056}, {0x0419, 1062},
-       {0x041a, 1068}, {0x041b, 1074}, {0x041c, 1080}, {0x041d, 1086},
-       {0x041e, 1092}, {0x041f, 1098}, {0x0420, 1104}, {0x0421, 1110},
-       {0x0422, 1116}, {0x0423, 1122}, {0x0424, 1128}, {0x0425, 1134},
-       {0x0426, 1140}, {0x0427, 1146}, {0x0428, 1152}, {0x0429, 1163},
-       {0x042a, 1169}, {0x042b, 1175}, {0x042c, 1181}, {0x042d, 1192},
-       {0x042e, 1198}, {0x042f, 1205}, {0x0430, 1211}, {0x0431, 1217},
-       {0x0432, 1223}, {0x0433, 1229}, {0x0434, 1235}, {0x0435, 1241},
-       {0x0436, 1247}, {0x0437, 1253}, {0x0438, 1259}, {0x0439, 1265},
-       {0x043a, 1271}, {0x043b, 1277}, {0x043d, 1283}, {0x043e, 1290},
-       {0x043f, 1296}, {0x0440, 1302}, {0x0441, 1308}, {0x0442, 1314},
-       {0x0443, 1320}, {0x0444, 1331}, {0x0445, 1337}, {0x0446, 1343},
-       {0x0447, 1349}, {0x0448, 1355}, {0x0449, 1361}, {0x044a, 1367},
-       {0x044b, 1373}, {0x044c, 1379}, {0x044d, 1385}, {0x044e, 1391},
-       {0x044f, 1397}, {0x0450, 1403}, {0x0451, 1409}, {0x0452, 1415},
-       {0x0453, 1421}, {0x0454, 1427}, {0x0455, 1433}, {0x0456, 1439},
-       {0x0457, 1445}, {0x0458, 1452}, {0x0459, 1459}, {0x045a, 1470},
-       {0x045b, 1477}, {0x045c, 1483}, {0x045d, 1495}, {0x045e, 1506},
-       {0x045f, 1512}, {0x0460, 1524}, {0x0461, 1535}, {0x0462, 1541},
-       {0x0463, 1547}, {0x0464, 1553}, {0x0465, 1560}, {0x0466, 1566},
-       {0x0467, 1573}, {0x0468, 1579}, {0x0469, 1590}, {0x046a, 1597},
-       {0x046b, 1603}, {0x046c, 1610}, {0x046d, 1617}, {0x046e, 1623},
-       {0x046f, 1629}, {0x0470, 1635}, {0x0471, 1641}, {0x0472, 1647},
-       {0x0473, 1653}, {0x0474, 1659}, {0x0475, 1665}, {0x0476, 1672},
-       {0x0477, 1679}, {0x0478, 1685}, {0x0479, 1691}, {0x047a, 1699},
-       {0x047c, 1706}, {0x047e, 1713}, {0x0480, 1719}, {0x0481, 1725},
-       {0x0482, 1731}, {0x0483, 1737}, {0x0484, 1743}, {0x0485, 1750},
-       {0x0486, 1757}, {0x0487, 1769}, {0x0488, 1775}, {0x048c, 1781},
-       {0x0491, 1788}, {0x0492, 1794}, {0x0501, 1805}, {0x05fe, 1814},
-       {0x0800, 1824}, {0x0801, 1830}, {0x0803, 1836}, {0x0804, 1851},
-       {0x0807, 1857}, {0x0809, 1863}, {0x080a, 1869}, {0x080c, 1875},
-       {0x0810, 1881}, {0x0813, 1887}, {0x0814, 1893}, {0x0816, 1899},
-       {0x0818, 1905}, {0x0819, 1911}, {0x081a, 1917}, {0x081d, 1928},
-       {0x0820, 1934}, {0x082c, 1940}, {0x082e, 1951}, {0x0832, 1958},
-       {0x083b, 1964}, {0x083c, 1970}, {0x083e, 1976}, {0x0843, 1982},
-       {0x0845, 1993}, {0x0846, 1999}, {0x0849, 2010}, {0x0850, 2016},
-       {0x0859, 2027}, {0x085d, 2038}, {0x085f, 2049}, {0x0860, 2061},
-       {0x0861, 2072}, {0x0867, 2078}, {0x086b, 2089}, {0x0873, 2096},
-       {0x0901, 2102}, {0x09ff, 2116}, {0x0c00, 2126}, {0x0c01, 2132},
-       {0x0c04, 2138}, {0x0c07, 2144}, {0x0c09, 2150}, {0x0c0a, 2156},
-       {0x0c0c, 2162}, {0x0c1a, 2168}, {0x0c3b, 2179}, {0x0c50, 2185},
-       {0x0c51, 2196}, {0x0c6b, 2202}, {0x1000, 2209}, {0x1001, 2220},
-       {0x1004, 2226}, {0x1007, 2232}, {0x1009, 2238}, {0x100a, 2244},
-       {0x100c, 2250}, {0x101a, 2256}, {0x103b, 2262}, {0x105f, 2269},
-       {0x1401, 2281}, {0x1404, 2287}, {0x1407, 2293}, {0x1409, 2299},
-       {0x140a, 2305}, {0x140c, 2311}, {0x141a, 2317}, {0x143b, 2328},
-       {0x1801, 2335}, {0x1809, 2341}, {0x180a, 2347}, {0x180c, 2353},
-       {0x181a, 2359}, {0x183b, 2370}, {0x1c01, 2377}, {0x1c09, 2383},
-       {0x1c0a, 2389}, {0x1c0c, 2395}, {0x1c1a, 2402}, {0x1c3b, 2413},
-       {0x2000, 2420}, {0x2001, 2426}, {0x2009, 2432}, {0x200a, 2438},
-       {0x200c, 2444}, {0x201a, 2450}, {0x203b, 2461}, {0x2400, 2468},
-       {0x2401, 2474}, {0x2409, 2480}, {0x240a, 2487}, {0x240c, 2493},
-       {0x241a, 2499}, {0x243b, 2510}, {0x2800, 2517}, {0x2801, 2523},
-       {0x2809, 2529}, {0x280a, 2535}, {0x280c, 2541}, {0x281a, 2547},
-       {0x2c00, 2558}, {0x2c01, 2564}, {0x2c09, 2570}, {0x2c0a, 2576},
-       {0x2c0c, 2582}, {0x2c1a, 2588}, {0x3000, 2599}, {0x3001, 2605},
-       {0x3009, 2611}, {0x300a, 2617}, {0x300c, 2623}, {0x301a, 2629},
-       {0x3400, 2640}, {0x3401, 2646}, {0x3409, 2652}, {0x340a, 2658},
-       {0x340c, 2664}, {0x3800, 2670}, {0x3801, 2676}, {0x3809, 2682},
-       {0x380a, 2688}, {0x380c, 2694}, {0x3c00, 2700}, {0x3c01, 2706},
-       {0x3c09, 2712}, {0x3c0a, 2718}, {0x3c0c, 2724}, {0x4000, 2730},
-       {0x4001, 2736}, {0x4009, 2742}, {0x400a, 2748}, {0x4400, 2754},
-       {0x4409, 2760}, {0x440a, 2766}, {0x4800, 2772}, {0x4809, 2778},
-       {0x480a, 2784}, {0x4c00, 2790}, {0x4c0a, 2796}, {0x500a, 2802},
-       {0x540a, 2808}, {0x580a, 2814}, {0x5c0a, 2821}, {0x641a, 2827},
-       {0x681a, 2838}, {0x6c1a, 2849}, {0x701a, 2860}, {0x703b, 2871},
-       {0x742c, 2878}, {0x743b, 2889}, {0x7804, 2896}, {0x7814, 2902},
-       {0x781a, 2908}, {0x782c, 2919}, {0x783b, 2930}, {0x7843, 2937},
-       {0x7850, 2948}, {0x785d, 2954}, {0x785f, 2965}, {0x7c04, 2977},
-       {0x7c14, 2983}, {0x7c1a, 2989}, {0x7c28, 3000}, {0x7c2e, 3011},
-       {0x7c3b, 3018}, {0x7c43, 3025}, {0x7c46, 3036}, {0x7c50, 3047},
-       {0x7c59, 3058}, {0x7c5c, 3069}, {0x7c5d, 3081}, {0x7c5f, 3092},
-       {0x7c67, 3104}, {0x7c68, 3115}, {0x7c86, 3126}, {0x7c92, 3138},
+       {0x0070,  734}, {0x0071,  740}, {0x0072,  751}, {0x0073,  757},
+       {0x0074,  763}, {0x0075,  769}, {0x0076,  776}, {0x0077,  783},
+       {0x0078,  789}, {0x0079,  795}, {0x007a,  803}, {0x007c,  810},
+       {0x007e,  817}, {0x007f,  823}, {0x0080,  824}, {0x0081,  830},
+       {0x0082,  836}, {0x0083,  842}, {0x0084,  848}, {0x0085,  855},
+       {0x0086,  862}, {0x0087,  874}, {0x0088,  880}, {0x008c,  886},
+       {0x0091,  893}, {0x0092,  899}, {0x0400,  910}, {0x0401,  916},
+       {0x0402,  922}, {0x0403,  928}, {0x0404,  934}, {0x0405,  940},
+       {0x0406,  946}, {0x0407,  952}, {0x0408,  958}, {0x0409,  964},
+       {0x040a,  970}, {0x040b,  983}, {0x040c,  989}, {0x040d,  995},
+       {0x040e, 1001}, {0x040f, 1007}, {0x0410, 1013}, {0x0411, 1019},
+       {0x0412, 1025}, {0x0413, 1031}, {0x0414, 1037}, {0x0415, 1043},
+       {0x0416, 1049}, {0x0417, 1055}, {0x0418, 1061}, {0x0419, 1067},
+       {0x041a, 1073}, {0x041b, 1079}, {0x041c, 1085}, {0x041d, 1091},
+       {0x041e, 1097}, {0x041f, 1103}, {0x0420, 1109}, {0x0421, 1115},
+       {0x0422, 1121}, {0x0423, 1127}, {0x0424, 1133}, {0x0425, 1139},
+       {0x0426, 1145}, {0x0427, 1151}, {0x0428, 1157}, {0x0429, 1168},
+       {0x042a, 1174}, {0x042b, 1180}, {0x042c, 1186}, {0x042d, 1197},
+       {0x042e, 1203}, {0x042f, 1210}, {0x0430, 1216}, {0x0431, 1222},
+       {0x0432, 1228}, {0x0433, 1234}, {0x0434, 1240}, {0x0435, 1246},
+       {0x0436, 1252}, {0x0437, 1258}, {0x0438, 1264}, {0x0439, 1270},
+       {0x043a, 1276}, {0x043b, 1282}, {0x043d, 1288}, {0x043e, 1295},
+       {0x043f, 1301}, {0x0440, 1307}, {0x0441, 1313}, {0x0442, 1319},
+       {0x0443, 1325}, {0x0444, 1336}, {0x0445, 1342}, {0x0446, 1348},
+       {0x0447, 1354}, {0x0448, 1360}, {0x0449, 1366}, {0x044a, 1372},
+       {0x044b, 1378}, {0x044c, 1384}, {0x044d, 1390}, {0x044e, 1396},
+       {0x044f, 1402}, {0x0450, 1408}, {0x0451, 1414}, {0x0452, 1420},
+       {0x0453, 1426}, {0x0454, 1432}, {0x0455, 1438}, {0x0456, 1444},
+       {0x0457, 1450}, {0x0458, 1457}, {0x0459, 1464}, {0x045a, 1475},
+       {0x045b, 1482}, {0x045c, 1488}, {0x045d, 1500}, {0x045e, 1511},
+       {0x045f, 1517}, {0x0460, 1529}, {0x0461, 1540}, {0x0462, 1546},
+       {0x0463, 1552}, {0x0464, 1558}, {0x0465, 1565}, {0x0466, 1571},
+       {0x0467, 1578}, {0x0468, 1589}, {0x0469, 1600}, {0x046a, 1607},
+       {0x046b, 1613}, {0x046c, 1620}, {0x046d, 1627}, {0x046e, 1633},
+       {0x046f, 1639}, {0x0470, 1645}, {0x0471, 1651}, {0x0472, 1662},
+       {0x0473, 1668}, {0x0474, 1674}, {0x0475, 1680}, {0x0476, 1687},
+       {0x0477, 1694}, {0x0478, 1700}, {0x0479, 1706}, {0x047a, 1714},
+       {0x047c, 1721}, {0x047e, 1728}, {0x0480, 1734}, {0x0481, 1740},
+       {0x0482, 1746}, {0x0483, 1752}, {0x0484, 1758}, {0x0485, 1765},
+       {0x0486, 1772}, {0x0487, 1784}, {0x0488, 1790}, {0x048c, 1796},
+       {0x0491, 1803}, {0x0492, 1809}, {0x0501, 1820}, {0x05fe, 1829},
+       {0x0800, 1839}, {0x0801, 1845}, {0x0803, 1851}, {0x0804, 1866},
+       {0x0807, 1872}, {0x0809, 1878}, {0x080a, 1884}, {0x080c, 1890},
+       {0x0810, 1896}, {0x0813, 1902}, {0x0814, 1908}, {0x0816, 1914},
+       {0x0818, 1920}, {0x0819, 1926}, {0x081a, 1932}, {0x081d, 1943},
+       {0x0820, 1949}, {0x082c, 1955}, {0x082e, 1966}, {0x0832, 1973},
+       {0x083b, 1979}, {0x083c, 1985}, {0x083e, 1991}, {0x0843, 1997},
+       {0x0845, 2008}, {0x0846, 2014}, {0x0849, 2025}, {0x0850, 2031},
+       {0x0859, 2042}, {0x085d, 2053}, {0x085f, 2064}, {0x0860, 2076},
+       {0x0861, 2087}, {0x0867, 2093}, {0x086b, 2104}, {0x0873, 2111},
+       {0x0901, 2117}, {0x09ff, 2131}, {0x0c00, 2141}, {0x0c01, 2147},
+       {0x0c04, 2153}, {0x0c07, 2159}, {0x0c09, 2165}, {0x0c0a, 2171},
+       {0x0c0c, 2177}, {0x0c1a, 2183}, {0x0c3b, 2194}, {0x0c50, 2200},
+       {0x0c51, 2211}, {0x0c6b, 2217}, {0x1000, 2224}, {0x1001, 2235},
+       {0x1004, 2241}, {0x1007, 2247}, {0x1009, 2253}, {0x100a, 2259},
+       {0x100c, 2265}, {0x101a, 2271}, {0x103b, 2277}, {0x105f, 2284},
+       {0x1401, 2296}, {0x1404, 2302}, {0x1407, 2308}, {0x1409, 2314},
+       {0x140a, 2320}, {0x140c, 2326}, {0x141a, 2332}, {0x143b, 2343},
+       {0x1801, 2350}, {0x1809, 2356}, {0x180a, 2362}, {0x180c, 2368},
+       {0x181a, 2374}, {0x183b, 2385}, {0x1c01, 2392}, {0x1c09, 2398},
+       {0x1c0a, 2404}, {0x1c0c, 2410}, {0x1c1a, 2417}, {0x1c3b, 2428},
+       {0x2000, 2435}, {0x2001, 2441}, {0x2009, 2447}, {0x200a, 2453},
+       {0x200c, 2459}, {0x201a, 2465}, {0x203b, 2476}, {0x2400, 2483},
+       {0x2401, 2489}, {0x2409, 2495}, {0x240a, 2502}, {0x240c, 2508},
+       {0x241a, 2514}, {0x243b, 2525}, {0x2800, 2532}, {0x2801, 2538},
+       {0x2809, 2544}, {0x280a, 2550}, {0x280c, 2556}, {0x281a, 2562},
+       {0x2c00, 2573}, {0x2c01, 2579}, {0x2c09, 2585}, {0x2c0a, 2591},
+       {0x2c0c, 2597}, {0x2c1a, 2603}, {0x3000, 2614}, {0x3001, 2620},
+       {0x3009, 2626}, {0x300a, 2632}, {0x300c, 2638}, {0x301a, 2644},
+       {0x3400, 2655}, {0x3401, 2661}, {0x3409, 2667}, {0x340a, 2673},
+       {0x340c, 2679}, {0x3800, 2685}, {0x3801, 2691}, {0x3809, 2697},
+       {0x380a, 2703}, {0x380c, 2709}, {0x3c00, 2715}, {0x3c01, 2721},
+       {0x3c09, 2727}, {0x3c0a, 2733}, {0x3c0c, 2739}, {0x4000, 2745},
+       {0x4001, 2751}, {0x4009, 2757}, {0x400a, 2763}, {0x4400, 2769},
+       {0x4409, 2775}, {0x440a, 2781}, {0x4800, 2787}, {0x4809, 2793},
+       {0x480a, 2799}, {0x4c00, 2805}, {0x4c09, 2811}, {0x4c0a, 2817},
+       {0x500a, 2823}, {0x540a, 2829}, {0x580a, 2835}, {0x5c0a, 2842},
+       {0x641a, 2848}, {0x681a, 2859}, {0x6c1a, 2870}, {0x701a, 2881},
+       {0x703b, 2892}, {0x742c, 2899}, {0x743b, 2910}, {0x7804, 2917},
+       {0x7814, 2923}, {0x781a, 2929}, {0x782c, 2940}, {0x783b, 2951},
+       {0x7843, 2958}, {0x7850, 2969}, {0x785d, 2975}, {0x785f, 2986},
+       {0x7c04, 2998}, {0x7c14, 3004}, {0x7c1a, 3010}, {0x7c28, 3021},
+       {0x7c2e, 3032}, {0x7c3b, 3039}, {0x7c43, 3046}, {0x7c46, 3057},
+       {0x7c50, 3068}, {0x7c59, 3079}, {0x7c5c, 3090}, {0x7c5d, 3102},
+       {0x7c5f, 3113}, {0x7c67, 3125}, {0x7c68, 3136}, {0x7c86, 3147},
+       {0x7c92, 3159},
 };
 
 /* All the language names; generated by tools/generate_language_id_map.c.
  * For compactness, this is a 'char' string rather than a 'tchar' string.  */
-static const char language_names[3149] =
+static const char language_names[3170] =
        "en-US\0ar-SA\0bg-BG\0ca-ES\0zh-CN\0cs-CZ\0da-DK\0de-DE\0el-GR\0en-US\0"
        "es-ES\0fi-FI\0fr-FR\0he-IL\0hu-HU\0is-IS\0it-IT\0ja-JP\0ko-KR\0nl-NL\0"
        "nb-NO\0pl-PL\0pt-BR\0rm-CH\0ro-RO\0ru-RU\0hr-HR\0sk-SK\0sq-AL\0sv-SE\0"
@@ -280,14 +281,14 @@ static const char language_names[3149] =
        "tg-Cyrl-TJ\0fa-IR\0vi-VN\0hy-AM\0az-Latn-AZ\0eu-ES\0hsb-DE\0mk-MK\0"
        "st-ZA\0ts-ZA\0tn-ZA\0ve-ZA\0xh-ZA\0zu-ZA\0af-ZA\0ka-GE\0fo-FO\0hi-IN\0"
        "mt-MT\0se-NO\0ga-IE\0yi-001\0ms-MY\0kk-KZ\0ky-KG\0sw-KE\0tk-TM\0"
-       "uz-Latn-UZ\0tt-RU\0bn-IN\0pa-IN\0gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0"
+       "uz-Latn-UZ\0tt-RU\0bn-BD\0pa-IN\0gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0"
        "ml-IN\0as-IN\0mr-IN\0sa-IN\0mn-MN\0bo-CN\0cy-GB\0km-KH\0lo-LA\0my-MM\0"
        "gl-ES\0kok-IN\0mni-IN\0sd-Arab-PK\0syr-SY\0si-LK\0chr-Cher-US\0"
        "iu-Latn-CA\0am-ET\0tzm-Latn-DZ\0ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0"
        "fil-PH\0dv-MV\0bin-NG\0ff-Latn-SN\0ha-Latn-NG\0ibb-NG\0yo-NG\0quz-BO\0"
-       "nso-ZA\0ba-RU\0lb-LU\0kl-GL\0ig-NG\0kr-NG\0om-ET\0ti-ER\0gn-PY\0"
+       "nso-ZA\0ba-RU\0lb-LU\0kl-GL\0ig-NG\0kr-Latn-NG\0om-ET\0ti-ER\0gn-PY\0"
        "haw-US\0la-001\0so-SO\0ii-CN\0pap-029\0arn-CL\0moh-CA\0br-FR\0\0"
-       "ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-FR\0sah-RU\0quc-Latn-GT\0rw-RW\0"
+       "ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-CH\0sah-RU\0quc-Latn-GT\0rw-RW\0"
        "wo-SN\0prs-AF\0gd-GB\0ku-Arab-IQ\0en-US\0ar-SA\0bg-BG\0ca-ES\0zh-TW\0"
        "cs-CZ\0da-DK\0de-DE\0el-GR\0en-US\0es-ES_tradnl\0fi-FI\0fr-FR\0he-IL\0"
        "hu-HU\0is-IS\0it-IT\0ja-JP\0ko-KR\0nl-NL\0nb-NO\0pl-PL\0pt-BR\0rm-CH\0"
@@ -299,9 +300,9 @@ static const char language_names[3149] =
        "gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0ml-IN\0as-IN\0mr-IN\0sa-IN\0mn-MN\0"
        "bo-CN\0cy-GB\0km-KH\0lo-LA\0my-MM\0gl-ES\0kok-IN\0mni-IN\0sd-Deva-IN\0"
        "syr-SY\0si-LK\0chr-Cher-US\0iu-Cans-CA\0am-ET\0tzm-Arab-MA\0"
-       "ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0fil-PH\0dv-MV\0bin-NG\0ff-NG\0"
+       "ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0fil-PH\0dv-MV\0bin-NG\0ff-Latn-NG\0"
        "ha-Latn-NG\0ibb-NG\0yo-NG\0quz-BO\0nso-ZA\0ba-RU\0lb-LU\0kl-GL\0"
-       "ig-NG\0kr-NG\0om-ET\0ti-ET\0gn-PY\0haw-US\0la-001\0so-SO\0ii-CN\0"
+       "ig-NG\0kr-Latn-NG\0om-ET\0ti-ET\0gn-PY\0haw-US\0la-001\0so-SO\0ii-CN\0"
        "pap-029\0arn-CL\0moh-CA\0br-FR\0ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-FR\0"
        "sah-RU\0quc-Latn-GT\0rw-RW\0wo-SN\0prs-AF\0gd-GB\0ku-Arab-IQ\0"
        "qps-ploc\0qps-ploca\0en-US\0ar-IQ\0ca-ES-valencia\0zh-CN\0de-CH\0"
@@ -320,13 +321,13 @@ static const char language_names[3149] =
        "en-TT\0es-AR\0fr-CM\0sr-Latn-ME\0en-US\0ar-LB\0en-ZW\0es-EC\0fr-CI\0"
        "sr-Cyrl-ME\0en-US\0ar-KW\0en-PH\0es-CL\0fr-ML\0en-US\0ar-AE\0en-ID\0"
        "es-UY\0fr-MA\0en-US\0ar-BH\0en-HK\0es-PY\0fr-HT\0en-US\0ar-QA\0en-IN\0"
-       "es-BO\0en-US\0en-MY\0es-SV\0en-US\0en-SG\0es-HN\0en-US\0es-NI\0es-PR\0"
-       "es-US\0es-419\0es-CU\0bs-Cyrl-BA\0bs-Latn-BA\0sr-Cyrl-RS\0sr-Latn-RS\0"
-       "smn-FI\0az-Cyrl-AZ\0sms-FI\0zh-CN\0nn-NO\0bs-Latn-BA\0az-Latn-AZ\0"
-       "sma-SE\0uz-Cyrl-UZ\0mn-MN\0iu-Cans-CA\0tzm-Tfng-MA\0zh-HK\0nb-NO\0"
-       "sr-Latn-RS\0tg-Cyrl-TJ\0dsb-DE\0smj-SE\0uz-Latn-UZ\0pa-Arab-PK\0"
-       "mn-Mong-CN\0sd-Arab-PK\0chr-Cher-US\0iu-Latn-CA\0tzm-Latn-DZ\0"
-       "ff-Latn-SN\0ha-Latn-NG\0quc-Latn-GT\0ku-Arab-IQ\0";
+       "es-BO\0en-US\0en-MY\0es-SV\0en-US\0en-SG\0es-HN\0en-US\0en-AE\0es-NI\0"
+       "es-PR\0es-US\0es-419\0es-CU\0bs-Cyrl-BA\0bs-Latn-BA\0sr-Cyrl-RS\0"
+       "sr-Latn-RS\0smn-FI\0az-Cyrl-AZ\0sms-FI\0zh-CN\0nn-NO\0bs-Latn-BA\0"
+       "az-Latn-AZ\0sma-SE\0uz-Cyrl-UZ\0mn-MN\0iu-Cans-CA\0tzm-Tfng-MA\0"
+       "zh-HK\0nb-NO\0sr-Latn-RS\0tg-Cyrl-TJ\0dsb-DE\0smj-SE\0uz-Latn-UZ\0"
+       "pa-Arab-PK\0mn-Mong-CN\0sd-Arab-PK\0chr-Cher-US\0iu-Latn-CA\0"
+       "tzm-Latn-DZ\0ff-Latn-SN\0ha-Latn-NG\0quc-Latn-GT\0ku-Arab-IQ\0";
 
 /* Translate a Windows language ID to its name.  Returns NULL if the ID is not
  * recognized.  */
diff --git a/src/xmlproc.c b/src/xmlproc.c
new file mode 100644 (file)
index 0000000..8ce193e
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ * xmlproc.c
+ *
+ * A simple XML 1.0 processor.  This handles all XML features that are used in
+ * WIM files, plus a bit more for futureproofing.  It omits problematic
+ * features, such as expansion of entities other than simple escape sequences.
+ */
+
+/*
+ * Copyright 2023 Eric Biggers
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see https://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+
+#include "wimlib/error.h"
+#include "wimlib/test_support.h"
+#include "wimlib/util.h"
+#include "wimlib/xmlproc.h"
+
+/*----------------------------------------------------------------------------*
+ *                         XML node utility functions                         *
+ *----------------------------------------------------------------------------*/
+
+static tchar *
+tstrdupz(const tchar *str, size_t len)
+{
+       tchar *new_str = CALLOC(len + 1, sizeof(str[0]));
+
+       if (new_str)
+               tmemcpy(new_str, str, len);
+       return new_str;
+}
+
+static struct xml_node *
+xml_new_node(struct xml_node *parent, enum xml_node_type type,
+            const tchar *name, size_t name_len,
+            const tchar *value, size_t value_len)
+{
+       struct xml_node *node = CALLOC(1, sizeof(*node));
+
+       if (!node)
+               return NULL;
+       node->type = type;
+       INIT_LIST_HEAD(&node->children);
+       if (name) {
+               node->name = tstrdupz(name, name_len);
+               if (!node->name)
+                       goto oom;
+       }
+       if (value) {
+               node->value = tstrdupz(value, value_len);
+               if (!node->value)
+                       goto oom;
+       }
+       if (parent)
+               xml_add_child(parent, node);
+       return node;
+
+oom:
+       xml_free_node(node);
+       return NULL;
+}
+
+/*
+ * Create a new ELEMENT node, and if @parent is non-NULL add the new node under
+ * @parent which should be another ELEMENT.
+ */
+struct xml_node *
+xml_new_element(struct xml_node *parent, const tchar *name)
+{
+       return xml_new_node(parent, XML_ELEMENT_NODE, name, tstrlen(name),
+                           NULL, 0);
+}
+
+/*
+ * Create a new ELEMENT node with an attached TEXT node, and if @parent is
+ * non-NULL add the new ELEMENT under @parent which should be another ELEMENT.
+ */
+struct xml_node *
+xml_new_element_with_text(struct xml_node *parent, const tchar *name,
+                         const tchar *text)
+{
+       struct xml_node *element = xml_new_element(parent, name);
+
+       if (element && xml_element_set_text(element, text) != 0) {
+               xml_free_node(element);
+               return NULL;
+       }
+       return element;
+}
+
+/* Append @child to the children list of @parent. */
+void
+xml_add_child(struct xml_node *parent, struct xml_node *child)
+{
+       xml_unlink_node(child); /* Shouldn't be needed, but be safe. */
+       child->parent = parent;
+       list_add_tail(&child->sibling_link, &parent->children);
+}
+
+/* Unlink @node from its parent, if it has one. */
+void
+xml_unlink_node(struct xml_node *node)
+{
+       if (node->parent) {
+               list_del(&node->sibling_link);
+               node->parent = NULL;
+       }
+}
+
+static void
+xml_free_children(struct xml_node *parent)
+{
+       struct xml_node *child, *tmp;
+
+       list_for_each_entry_safe(child, tmp, &parent->children, sibling_link)
+               xml_free_node(child);
+}
+
+/* Recursively free @node, first unlinking it if needed.  @node may be NULL. */
+void
+xml_free_node(struct xml_node *node)
+{
+       if (node) {
+               xml_unlink_node(node);
+               xml_free_children(node);
+               FREE(node->name);
+               FREE(node->value);
+               FREE(node);
+       }
+}
+
+/*
+ * Return the text from the first TEXT child node of @element, or NULL if no
+ * such node exists.  @element may be NULL.
+ */
+const tchar *
+xml_element_get_text(const struct xml_node *element)
+{
+       const struct xml_node *child;
+
+       xml_node_for_each_child(element, child)
+               if (child->type == XML_TEXT_NODE)
+                       return child->value;
+       return NULL;
+}
+
+/*
+ * Set the contents of the given @element to the given @text, replacing the
+ * entire existing contents if any.
+ */
+int
+xml_element_set_text(struct xml_node *element, const tchar *text)
+{
+       struct xml_node *text_node = xml_new_node(NULL, XML_TEXT_NODE, NULL, 0,
+                                                 text, tstrlen(text));
+       if (!text_node)
+               return WIMLIB_ERR_NOMEM;
+       xml_free_children(element);
+       xml_add_child(element, text_node);
+       return 0;
+}
+
+static int
+xml_element_append_text(struct xml_node *element,
+                       const tchar *text, size_t text_len)
+{
+       struct xml_node *last_child;
+
+       if (!list_empty(&element->children) &&
+           (last_child =
+            list_last_entry(&element->children, struct xml_node,
+                            sibling_link))->type == XML_TEXT_NODE) {
+               /*
+                * The new TEXT would directly follow another TEXT, so simplify
+                * the tree by just appending to the existing TEXT.  (This case
+                * can theoretically be reached via the use of CDATA...)
+                */
+               size_t old_len = tstrlen(last_child->value);
+               tchar *new_value = CALLOC(old_len + text_len + 1,
+                                         sizeof(new_value[0]));
+               if (!new_value)
+                       return WIMLIB_ERR_NOMEM;
+               tmemcpy(new_value, last_child->value, old_len);
+               tmemcpy(&new_value[old_len], text, text_len);
+               FREE(last_child->value);
+               last_child->value = new_value;
+               return 0;
+       }
+       if (!xml_new_node(element, XML_TEXT_NODE, NULL, 0, text, text_len))
+               return WIMLIB_ERR_NOMEM;
+       return 0;
+}
+
+/* Find the attribute with the given @name on @element. */
+struct xml_node *
+xml_get_attrib(const struct xml_node *element, const tchar *name)
+{
+       struct xml_node *child;
+
+       xml_node_for_each_child(element, child) {
+               if (child->type == XML_ATTRIBUTE_NODE &&
+                   !tstrcmp(child->name, name))
+                       return child;
+       }
+       return NULL;
+}
+
+/* Set the attribute @name=@value on the given @element. */
+int
+xml_set_attrib(struct xml_node *element, const tchar *name, const tchar *value)
+{
+       struct xml_node *attrib = xml_new_node(NULL, XML_ATTRIBUTE_NODE,
+                                              name, tstrlen(name),
+                                              value, tstrlen(value));
+       if (!attrib)
+               return WIMLIB_ERR_NOMEM;
+       xml_replace_child(element, attrib);
+       return 0;
+}
+
+/*
+ * Add the ELEMENT or ATTRIBUTE node @replacement under the ELEMENT @parent,
+ * replacing any node with the same type and name that already exists.
+ */
+void
+xml_replace_child(struct xml_node *parent, struct xml_node *replacement)
+{
+       struct xml_node *child;
+
+       xml_unlink_node(replacement); /* Shouldn't be needed, but be safe. */
+
+       xml_node_for_each_child(parent, child) {
+               if (child->type == replacement->type &&
+                   !tstrcmp(child->name, replacement->name)) {
+                       list_replace(&child->sibling_link,
+                                    &replacement->sibling_link);
+                       replacement->parent = parent;
+                       child->parent = NULL;
+                       xml_free_node(child);
+                       return;
+               }
+       }
+       xml_add_child(parent, replacement);
+}
+
+struct xml_node *
+xml_clone_tree(struct xml_node *orig)
+{
+       struct xml_node *clone, *orig_child, *clone_child;
+
+       clone = xml_new_node(NULL, orig->type,
+                       orig->name, orig->name ? tstrlen(orig->name) : 0,
+                       orig->value, orig->value ? tstrlen(orig->value) : 0);
+       if (!clone)
+               return NULL;
+       xml_node_for_each_child(orig, orig_child) {
+               clone_child = xml_clone_tree(orig_child);
+               if (!clone_child)
+                       goto oom;
+               xml_add_child(clone, clone_child);
+       }
+       return clone;
+
+oom:
+       xml_free_node(clone);
+       return NULL;
+}
+
+/*----------------------------------------------------------------------------*
+ *                           XML string validation                            *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * Functions that check for legal names and values in XML 1.0.  These are
+ * currently slightly over-lenient, as they allow everything non-ASCII.  These
+ * are also not currently used by the XML parser to reject non-well-formed
+ * documents, but rather just by the user of the XML processor (xml.c) in order
+ * to avoid introducing illegal names and values into the document.
+ */
+
+static inline bool
+is_whitespace(tchar c)
+{
+       return c == ' ' || c == '\n' || c == '\r' || c == '\t';
+}
+
+static inline bool
+is_name_start_char(tchar c)
+{
+       return (c & 0x7f) != c /* overly lenient for now */ ||
+               (c >= 'A' && c <= 'Z') ||
+               (c >= 'a' && c <= 'z') ||
+               c == ':' || c == '_';
+}
+
+static inline bool
+is_name_char(tchar c)
+{
+       return is_name_start_char(c) ||
+               (c >= '0' && c <= '9') || c == '-' || c == '.';
+}
+
+/* Allow characters used in element "paths"; see do_xml_path_walk() */
+static inline bool
+is_path_char(tchar c)
+{
+       return c == '/' || c == '[' || c == ']';
+}
+
+bool
+xml_legal_path(const tchar *p)
+{
+       if (!is_name_start_char(*p) && !is_path_char(*p))
+               return false;
+       for (p = p + 1; *p; p++) {
+               if (!is_name_char(*p) && !is_path_char(*p))
+                       return false;
+       }
+       return true;
+}
+
+bool
+xml_legal_value(const tchar *p)
+{
+       for (; *p; p++) {
+               /* Careful: tchar can be signed. */
+               if (*p > 0 && *p < 0x20 && !is_whitespace(*p))
+                       return false;
+       }
+       return true;
+}
+
+#if TCHAR_IS_UTF16LE
+#define BYTE_ORDER_MARK        (tchar[]){ 0xfeff, 0 }
+#else
+#define BYTE_ORDER_MARK        "\xEF\xBB\xBF"
+#endif
+
+/*----------------------------------------------------------------------------*
+ *                               XML parsing                                  *
+ *----------------------------------------------------------------------------*/
+
+#define CHECK(cond)    if (!(cond)) goto bad
+
+static inline void
+skip_whitespace(const tchar **pp)
+{
+       const tchar *p = *pp;
+
+       while (is_whitespace(*p))
+               p++;
+       *pp = p;
+}
+
+static inline bool
+skip_string(const tchar **pp, const tchar *str)
+{
+       const tchar *p = *pp;
+       size_t len = tstrlen(str);
+
+       if (tstrncmp(p, str, len))
+               return false;
+       *pp = p + len;
+       return true;
+}
+
+static inline bool
+find_and_skip(const tchar **pp, const tchar *str)
+{
+       const tchar *p = *pp;
+
+       p = tstrstr(p, str);
+       if (!p)
+               return false;
+       *pp = p + tstrlen(str);
+       return true;
+}
+
+static bool
+skip_misc(const tchar **pp)
+{
+       const tchar *p = *pp, *prev_p;
+
+       do {
+               prev_p = p;
+               skip_whitespace(&p);
+               /* Discard XML declaration and top-level PIs for now. */
+               if (skip_string(&p, T("<?")) && !find_and_skip(&p, T("?>")))
+                       return false;
+               /* Discard DOCTYPE declaration for now. */
+               if (skip_string(&p, T("<!DOCTYPE")) && !find_and_skip(&p, T(">")))
+                       return false;
+               /* Discard top-level comments for now. */
+               if (skip_string(&p, T("<!--")) && !find_and_skip(&p, T("-->")))
+                       return false;
+       } while (p != prev_p);
+       *pp = p;
+       return true;
+}
+
+static inline const tchar *
+get_escape_seq(tchar c)
+{
+       switch (c) {
+       case '<':
+               return T("&lt;");
+       case '>':
+               return T("&gt;");
+       case '&':
+               return T("&amp;");
+       case '\'':
+               return T("&apos;");
+       case '"':
+               return T("&quot;");
+       }
+       return NULL;
+}
+
+/* Note: 'str' must be NUL-terminated, but only 'len' chars are used. */
+static int
+unescape_string(const tchar *str, size_t len, tchar **unescaped_ret)
+{
+       const tchar *in_p = str;
+       tchar *unescaped, *out_p;
+
+       unescaped = CALLOC(len + 1, sizeof(str[0]));
+       if (!unescaped)
+               return WIMLIB_ERR_NOMEM;
+       out_p = unescaped;
+       while (in_p < &str[len]) {
+               if (*in_p != '&')
+                       *out_p++ = *in_p++;
+               else if (skip_string(&in_p, T("&lt;")))
+                       *out_p++ = '<';
+               else if (skip_string(&in_p, T("&gt;")))
+                       *out_p++ = '>';
+               else if (skip_string(&in_p, T("&amp;")))
+                       *out_p++ = '&';
+               else if (skip_string(&in_p, T("&apos;")))
+                       *out_p++ = '\'';
+               else if (skip_string(&in_p, T("&quot;")))
+                       *out_p++ = '"';
+               else
+                       goto bad;
+       }
+       if (in_p > &str[len])
+               goto bad;
+       *unescaped_ret = unescaped;
+       return 0;
+
+bad:
+       ERROR("Error unescaping string '%.*"TS"'", (int)len, str);
+       FREE(unescaped);
+       return WIMLIB_ERR_XML;
+}
+
+static int
+parse_element(const tchar **pp, struct xml_node *parent, int depth,
+             struct xml_node **node_ret);
+
+static int
+parse_contents(const tchar **pp, struct xml_node *element, int depth)
+{
+       const tchar *p = *pp;
+       int ret;
+
+       for (;;) {
+               const tchar *raw_text = p;
+               tchar *text;
+
+               for (; *p != '<'; p++) {
+                       if (*p == '\0')
+                               return WIMLIB_ERR_XML;
+               }
+               if (p > raw_text) {
+                       ret = unescape_string(raw_text, p - raw_text, &text);
+                       if (ret)
+                               return ret;
+                       ret = xml_element_append_text(element, text,
+                                                     tstrlen(text));
+                       FREE(text);
+                       if (ret)
+                               return ret;
+               }
+               if (p[1] == '/') {
+                       break; /* Reached the end tag of @element */
+               } else if (p[1] == '?') {
+                       /* Discard processing instructions for now. */
+                       p += 2;
+                       if (!find_and_skip(&p, T("?>")))
+                               return WIMLIB_ERR_XML;
+                       continue;
+               } else if (p[1] == '!') {
+                       if (skip_string(&p, T("<![CDATA["))) {
+                               raw_text = p;
+                               if (!find_and_skip(&p, T("]]>")))
+                                       return WIMLIB_ERR_XML;
+                               ret = xml_element_append_text(element, raw_text,
+                                                             p - 3 - raw_text);
+                               if (ret)
+                                       return ret;
+                               continue;
+                       } else if (skip_string(&p, T("<!--"))) {
+                               /* Discard comments for now. */
+                               if (!find_and_skip(&p, T("-->")))
+                                       return WIMLIB_ERR_XML;
+                               continue;
+                       }
+                       return WIMLIB_ERR_XML;
+               }
+               ret = parse_element(&p, element, depth + 1, NULL);
+               if (ret)
+                       return ret;
+       }
+       *pp = p;
+       return 0;
+}
+
+static int
+parse_element(const tchar **pp, struct xml_node *parent, int depth,
+             struct xml_node **element_ret)
+{
+       const tchar *p = *pp;
+       struct xml_node *element = NULL;
+       const tchar *name_start;
+       size_t name_len;
+       int ret;
+
+       /* Parse the start tag. */
+       CHECK(depth < 50);
+       CHECK(*p == '<');
+       p++;
+       name_start = p;
+       while (!is_whitespace(*p) && *p != '>' && *p != '/' && *p != '\0')
+               p++;
+       name_len = p - name_start;
+       CHECK(name_len > 0);
+       element = xml_new_node(parent, XML_ELEMENT_NODE, name_start, name_len,
+                              NULL, 0);
+       if (!element) {
+               ret = WIMLIB_ERR_NOMEM;
+               goto error;
+       }
+       /* Parse the attributes list within the start tag. */
+       while (is_whitespace(*p)) {
+               const tchar *attr_name_start, *attr_value_start;
+               size_t attr_name_len, attr_value_len;
+               tchar *attr_value;
+               tchar quote;
+
+               skip_whitespace(&p);
+               if (*p == '/' || *p == '>')
+                       break;
+               attr_name_start = p;
+               while (*p != '=' && !is_whitespace(*p) && *p != '\0')
+                       p++;
+               attr_name_len = p - attr_name_start;
+               skip_whitespace(&p);
+               CHECK(attr_name_len > 0 && *p == '=');
+               p++;
+               skip_whitespace(&p);
+               quote = *p;
+               CHECK(quote == '\'' || quote == '"');
+               attr_value_start = ++p;
+               while (*p != quote && *p != '\0')
+                       p++;
+               CHECK(*p == quote);
+               attr_value_len = p - attr_value_start;
+               p++;
+               ret = unescape_string(attr_value_start, attr_value_len,
+                                     &attr_value);
+               if (ret)
+                       goto error;
+               ret = xml_new_node(element, XML_ATTRIBUTE_NODE,
+                                  attr_name_start, attr_name_len,
+                                  attr_value, tstrlen(attr_value))
+                       ? 0 : WIMLIB_ERR_NOMEM;
+               FREE(attr_value);
+               if (ret)
+                       goto error;
+       }
+       if (*p == '/') {
+               /* Closing an empty element tag */
+               p++;
+       } else {
+               /* Closing the start tag */
+               CHECK(*p == '>');
+               p++;
+               /* Parse the contents, then the end tag. */
+               ret = parse_contents(&p, element, depth);
+               if (ret)
+                       goto error;
+               CHECK(*p == '<');
+               p++;
+               CHECK(*p == '/');
+               p++;
+               CHECK(!tstrncmp(p, name_start, name_len));
+               p += name_len;
+               skip_whitespace(&p);
+       }
+       CHECK(*p == '>');
+       p++;
+       *pp = p;
+       if (element_ret)
+               *element_ret = element;
+       return 0;
+
+error:
+       xml_free_node(element);
+       return ret;
+
+bad:
+       ret = WIMLIB_ERR_XML;
+       goto error;
+}
+
+/*
+ * Deserialize an XML document and return its root node in @doc_ret.  The
+ * document must be given as a NUL-terminated string of 'tchar', i.e. UTF-16LE
+ * in Windows builds and UTF-8 everywhere else.
+ */
+int
+xml_parse_document(const tchar *p, struct xml_node **doc_ret)
+{
+       int ret;
+       struct xml_node *doc;
+
+       skip_string(&p, BYTE_ORDER_MARK);
+       if (!skip_misc(&p))
+               return WIMLIB_ERR_XML;
+       ret = parse_element(&p, NULL, 0, &doc);
+       if (ret)
+               return ret;
+       if (!skip_misc(&p) || *p) {
+               xml_free_node(doc);
+               return WIMLIB_ERR_XML;
+       }
+       *doc_ret = doc;
+       return 0;
+}
+
+/*----------------------------------------------------------------------------*
+ *                               XML writing                                  *
+ *----------------------------------------------------------------------------*/
+
+static void
+xml_write(struct xml_out_buf *buf, const tchar *str, size_t len)
+{
+       if (buf->count + len + 1 > buf->capacity) {
+               size_t new_capacity = max3(buf->count + len + 1,
+                                          buf->capacity * 2, 4096);
+               tchar *new_buf = REALLOC(buf->buf,
+                                        new_capacity * sizeof(str[0]));
+               if (!new_buf) {
+                       buf->oom = true;
+                       return;
+               }
+               buf->buf = new_buf;
+               buf->capacity = new_capacity;
+       }
+       tmemcpy(&buf->buf[buf->count], str, len);
+       buf->count += len;
+}
+
+static void
+xml_puts(struct xml_out_buf *buf, const tchar *str)
+{
+       xml_write(buf, str, tstrlen(str));
+}
+
+static void
+xml_escape_and_puts(struct xml_out_buf *buf, const tchar *str)
+{
+       const tchar *p = str, *saved, *seq = NULL;
+
+       for (;; p++) {
+               for (saved = p; *p && (seq = get_escape_seq(*p)) == NULL; p++)
+                       ;
+               xml_write(buf, saved, p - saved);
+               if (!*p)
+                       return;
+               xml_puts(buf, seq);
+       }
+}
+
+static void
+xml_write_element(struct xml_node *element, struct xml_out_buf *buf)
+{
+       struct xml_node *child;
+
+       /* Write the start tag. */
+       xml_puts(buf, T("<"));
+       xml_puts(buf, element->name);
+       xml_node_for_each_child(element, child) {
+               if (child->type == XML_ATTRIBUTE_NODE) {
+                       xml_puts(buf, T(" "));
+                       xml_puts(buf, child->name);
+                       xml_puts(buf, T("=\""));
+                       xml_escape_and_puts(buf, child->value);
+                       xml_puts(buf, T("\""));
+               }
+       }
+       xml_puts(buf, T(">"));
+
+       /* Write the contents. */
+       xml_node_for_each_child(element, child) {
+               if (child->type == XML_TEXT_NODE)
+                       xml_escape_and_puts(buf, child->value);
+               else if (child->type == XML_ELEMENT_NODE)
+                       xml_write_element(child, buf);
+       }
+
+       /* Write the end tag. */
+       xml_puts(buf, T("</"));
+       xml_puts(buf, element->name);
+       xml_puts(buf, T(">"));
+}
+
+/*
+ * Serialize the document @doc into @buf as a NUL-terminated string of 'tchar',
+ * i.e. UTF-16LE in Windows builds and UTF-8 everywhere else.  A byte order mark
+ * (BOM) is included, as this is needed for compatibility with WIMGAPI.
+ */
+int
+xml_write_document(struct xml_node *doc, struct xml_out_buf *buf)
+{
+       xml_puts(buf, BYTE_ORDER_MARK);
+       xml_write_element(doc, buf);
+       if (buf->oom)
+               return WIMLIB_ERR_NOMEM;
+       buf->buf[buf->count] = '\0';
+       return 0;
+}
+
+/*----------------------------------------------------------------------------*
+ *                              Test support                                  *
+ *----------------------------------------------------------------------------*/
+
+#ifdef ENABLE_TEST_SUPPORT
+WIMLIBAPI int
+wimlib_parse_and_write_xml_doc(const tchar *in, tchar **out_ret)
+{
+       struct xml_node *doc;
+       struct xml_out_buf buf = {};
+       int ret;
+
+       ret = xml_parse_document(in, &doc);
+       if (ret)
+               return ret;
+       ret = xml_write_document(doc, &buf);
+       xml_free_node(doc);
+       *out_ret = buf.buf;
+       return ret;
+}
+#endif /* ENABLE_TEST_SUPPORT */
index 1b430912de7521d5b2abb622f6b1a694f7ab6281..e192c870a0bdd3cb82d22d85db3f999d7b8824d6 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -544,7 +544,7 @@ xpress_compress_greedy(struct xpress_compressor * restrict c,
 
                length = hc_matchfinder_longest_match(&c->hc_mf,
                                                      in_begin,
-                                                     in_next - in_begin,
+                                                     in_next,
                                                      XPRESS_MIN_MATCH_LEN - 1,
                                                      in_end - in_next,
                                                      min(in_end - in_next, c->nice_match_length),
@@ -558,12 +558,12 @@ xpress_compress_greedy(struct xpress_compressor * restrict c,
                        *next_chosen_item++ =
                                xpress_record_match(c, length, offset);
                        in_next += 1;
-                       hc_matchfinder_skip_positions(&c->hc_mf,
-                                                     in_begin,
-                                                     in_next - in_begin,
-                                                     in_end - in_begin,
-                                                     length - 1,
-                                                     next_hashes);
+                       hc_matchfinder_skip_bytes(&c->hc_mf,
+                                                 in_begin,
+                                                 in_next,
+                                                 in_end,
+                                                 length - 1,
+                                                 next_hashes);
                        in_next += length - 1;
                } else {
                        /* No match found  */
@@ -610,7 +610,7 @@ xpress_compress_lazy(struct xpress_compressor * restrict c,
                /* Find the longest match at the current position.  */
                cur_len = hc_matchfinder_longest_match(&c->hc_mf,
                                                       in_begin,
-                                                      in_next - in_begin,
+                                                      in_next,
                                                       XPRESS_MIN_MATCH_LEN - 1,
                                                       in_end - in_next,
                                                       min(in_end - in_next, c->nice_match_length),
@@ -638,12 +638,12 @@ xpress_compress_lazy(struct xpress_compressor * restrict c,
                        *next_chosen_item++ =
                                xpress_record_match(c, cur_len, cur_offset);
 
-                       hc_matchfinder_skip_positions(&c->hc_mf,
-                                                     in_begin,
-                                                     in_next - in_begin,
-                                                     in_end - in_begin,
-                                                     cur_len - 1,
-                                                     next_hashes);
+                       hc_matchfinder_skip_bytes(&c->hc_mf,
+                                                 in_begin,
+                                                 in_next,
+                                                 in_end,
+                                                 cur_len - 1,
+                                                 next_hashes);
                        in_next += cur_len - 1;
                        continue;
                }
@@ -666,7 +666,7 @@ xpress_compress_lazy(struct xpress_compressor * restrict c,
                 */
                next_len = hc_matchfinder_longest_match(&c->hc_mf,
                                                        in_begin,
-                                                       in_next - in_begin,
+                                                       in_next,
                                                        cur_len,
                                                        in_end - in_next,
                                                        min(in_end - in_next, c->nice_match_length),
@@ -688,12 +688,12 @@ xpress_compress_lazy(struct xpress_compressor * restrict c,
                         * output the current match.  */
                        *next_chosen_item++ =
                                xpress_record_match(c, cur_len, cur_offset);
-                       hc_matchfinder_skip_positions(&c->hc_mf,
-                                                     in_begin,
-                                                     in_next - in_begin,
-                                                     in_end - in_begin,
-                                                     cur_len - 2,
-                                                     next_hashes);
+                       hc_matchfinder_skip_bytes(&c->hc_mf,
+                                                 in_begin,
+                                                 in_next,
+                                                 in_end,
+                                                 cur_len - 2,
+                                                 next_hashes);
                        in_next += cur_len - 2;
                        continue;
                }
@@ -961,12 +961,12 @@ xpress_find_matches(struct xpress_compressor * restrict c,
                                break;
                        --best_len;
                        do {
-                               bt_matchfinder_skip_position(&c->bt_mf,
-                                                            in_begin,
-                                                            in_next - in_begin,
-                                                            nice_len,
-                                                            c->max_search_depth,
-                                                            next_hashes);
+                               bt_matchfinder_skip_byte(&c->bt_mf,
+                                                        in_begin,
+                                                        in_next - in_begin,
+                                                        nice_len,
+                                                        c->max_search_depth,
+                                                        next_hashes);
                                cache_ptr->length = 0;
                                cache_ptr->offset = *in_next++;
                                cache_ptr++;
index d6e606cb962df887311488d554e8e0e0b1bdd1ad..035bcfd03470c85ee511be2d1eecd4833fd04905 100644 (file)
@@ -19,7 +19,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 
@@ -84,7 +84,7 @@ struct xpress_decompressor {
        };
        DECODE_TABLE_WORKING_SPACE(working_space, XPRESS_NUM_SYMBOLS,
                                   XPRESS_MAX_CODEWORD_LEN);
-} _aligned_attribute(DECODE_TABLE_ALIGNMENT);
+} __attribute__((aligned(DECODE_TABLE_ALIGNMENT)));
 
 static int
 xpress_decompress(const void *restrict compressed_data, size_t compressed_size,
index 9c92715b1fca95a100939f7066088a632e535bf4..c5e273f2de9296706ffc19afbdaaff56fcc3fd44 100755 (executable)
@@ -65,6 +65,17 @@ for comp_type in None LZX XPRESS; do
        rm -rf dir.wim tmp
 done
 
+# Test wimverify and the SHA-1 code
+WIMLIB_DISABLE_CPU_FEATURES='*' wimcapture dir dir.wim --compress=none
+disabled=''
+for cpu_feature in '' sha1 bmi2 avx sse4.2 sse4.1 ssse3; do
+       [ -n "$disabled" ] && disabled+=','
+       disabled+="$cpu_feature"
+       if ! WIMLIB_DISABLE_CPU_FEATURES=$disabled wimverify dir.wim; then
+               error "wimverify failed (cpu_features_disabled=$disabled)"
+       fi
+done
+
 # Test wimappend --create
 rm -f dir.wim
 if wimappend dir dir.wim; then
@@ -120,16 +131,26 @@ if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "0"; the
 fi
 rm -rf dir.wim tmp
 
+name_desc_test() {
+       local name=$1
+       local desc=$2
+       if ! wimcapture dir dir.wim "$name" "$desc"; then
+               error "Failed to capture WIM with specified name and description"
+       fi
+       if ! test "`wiminfo dir.wim | grep Name | awk '{print $2}'`" = "$name"; then
+               error "WIM name not set correctly"
+       fi
+       if ! test "`wiminfo dir.wim | grep Description | awk '{print $2}'`" = "$desc"; then
+               error "WIM description not set correctly"
+       fi
+}
+
 echo "Testing capture of WIM with name and description"
-if ! wimcapture dir dir.wim "myname" "mydesc"; then
-       error "Failed to capture WIM with specified name and description"
-fi
-if ! test "`wiminfo dir.wim | grep Name | awk '{print $2}'`" = "myname"; then
-       error "WIM name not set correctly"
-fi
-if ! test "`wiminfo dir.wim | grep Description | awk '{print $2}'`" = "mydesc"; then
-       error "WIM name not set correctly"
-fi
+name_desc_test "myname" "mydesc"
+
+echo "Testing capture of WIM with non-ASCII name and description"
+name_desc_test "áéíóú" "¿?"
+
 echo "Testing printing WIM lookup table"
 if ! wiminfo --lookup-table dir.wim > /dev/null; then
        error "Failed to print WIM lookup table"
index fe8cdca0bf103ff1c2ed30503f38a868c0afed99..be789b8a7a2ab7b8f0a88e9c12e8ce42a59d91fe 100755 (executable)
@@ -142,6 +142,20 @@ mkdir in.dir out.dir
 
 . $srcdir/tests/common_tests.sh
 
+# Test the data recovery mode
+__msg "Testing data recovery mode"
+for file in corrupted_file_1.wim corrupted_file_2.wim; do
+       rm -rf out.dir
+       wimapply $srcdir/tests/wims/$file 1 out.dir 2>/dev/null && \
+               error "Applying $file in default mode unexpectedly succeeded"
+       rm -rf out.dir
+       wimapply --recover-data $srcdir/tests/wims/$file 1 out.dir || \
+               error "Applying $file in data recovery mode unexpectedly failed"
+       if [ ! -e out.dir/file ]; then
+               error "Recovered file not found"
+       fi
+done
+
 # Make sure exclusion list works
 __msg "Testing default capture configuration file"
 touch in.dir/hiberfil.sys
@@ -207,7 +221,15 @@ exclusionlist_test() {
        diff expected_out actual_out
 }
 
+macOS=false
+if [ "$(uname)" = Darwin ]; then
+       macOS=true
+fi
 for t_file in "$srcdir/tests/exclusionlists"/*; do
+       if $macOS && [[ $t_file == */case_*sensitive ]]; then
+               # Exclude test cases that fail on case-insensitive filesystem
+               continue
+       fi
        exclusionlist_test "$t_file"
 done
 
index 8a07325ab0cc746387b360f2aeba88409a61d3fe..83c31c9fd0d63693caf91eb08d6f6fadd43b9cd6 100755 (executable)
@@ -26,7 +26,7 @@ imagex_unmount() {
 }
 
 cleanup() {
-       fusermount -u $TEST_SUBDIR/tmp.mnt &> /dev/null || true
+       fusermount3 -u $TEST_SUBDIR/tmp.mnt &> /dev/null || true
        rm -rf $TEST_SUBDIR
 }
 
@@ -128,7 +128,7 @@ if ! wimmountrw dir.wim dir tmp.mnt; then
        error "Failed to re-mount test WIM read-write"
 fi
 if ! imagex_unmount tmp.mnt --commit --check; then
-       error "Failed to unmount read-write mounted WIM with changes commited (no changes made)"
+       error "Failed to unmount read-write mounted WIM with changes committed (no changes made)"
 fi
 echo "Testing removing file from mounted WIM"
 if ! wimmountrw dir.wim dir tmp.mnt; then
index f22198fb71e4f1c49cc8407f61fb5b1f00132cd5..f1a0f910d0dfcb72b261da362dcbf1db01bd0e83 100755 (executable)
@@ -16,6 +16,9 @@ srcdir="$(cd $srcdir; pwd)"
 
 TEST_SUBDIR=tmpdir_test-imagex-ntfs
 
+# In Debian, mkntfs is at /sbin/mkntfs but /sbin is not on the $PATH by default.
+PATH+=":/sbin"
+
 __do_unmount() {
        for ((i = 0; i < 10; i++)); do
                if fusermount -z -u $1; then
@@ -59,7 +62,7 @@ do_mount() {
 }
 
 do_mkntfs() {
-       if ! mkntfs --force --fast $1 > /dev/null; then
+       if ! mkntfs --force --fast $1 >/dev/null; then
                error "Could not create NTFS volume \"$1\"!  Make sure ntfsprogs are installed."
        fi
 }
index 6c8771669c8c57c3b8fde78f74c1c19aec5240b0..721954df10584d1fd057af21505516431e8d8186 100755 (executable)
@@ -119,6 +119,26 @@ do_apply
 ../tree-cmp file out.dir/newname
 [ ! -e out.dir/file ]
 
+prepare_empty_wim
+msg "Testing UTF-16LE-NOBOM command update file"
+echo -ne 'a\0d\0d\0 \0f\0i\0l\0e\0 \0/\0f\0i\0l\0e\0\n\0' \
+       | wimupdate test.wim
+do_apply
+../tree-cmp file out.dir/file
+
+prepare_empty_wim
+msg "Testing UTF-16LE-BOM command update file"
+echo -ne '\xff\xfea\0d\0d\0 \0f\0i\0l\0e\0 \0/\0f\0i\0l\0e\0\n\0' \
+       | wimupdate test.wim
+do_apply
+../tree-cmp file out.dir/file
+
+prepare_empty_wim
+msg "Testing UTF-8-BOM command update file"
+echo -ne '\xef\xbb\xbfadd file /file' | wimupdate test.wim
+do_apply
+../tree-cmp file out.dir/file
+
 prepare_empty_wim
 msg "Testing adding, then renaming file in WIM image in one command"
 wimupdate test.wim << EOF
index 4b70a8308277c5aa15a3055064a79293e5656fec..674b4b0f559acfd22c97bdf342a93744fa7dea2a 100644 (file)
@@ -28,7 +28,7 @@
 #include <stdbool.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#ifdef HAVE_SYS_XATTR_H
+#ifdef __linux__
 #  include <sys/xattr.h>
 #endif
 #include <assert.h>
@@ -170,7 +170,7 @@ static void cmp(const char *file1, const char *file2, size_t size)
        close(fd2);
 }
 
-#ifdef HAVE_SYS_XATTR_H
+#ifdef __linux__
 /* Compares an extended attribute of the files. */
 static void cmp_xattr(const char *file1, const char *file2,
                      const char *xattr_name, ssize_t max_size,
@@ -275,13 +275,13 @@ static void cmp_ads(const char *file1, const char *file2)
                free(list2);
        }
 }
-#endif /* HAVE_SYS_XATTR_H */
+#endif /* __linux__ */
 
 /* Compares special NTFS data of the files, as accessed through extended
  * attributes. */
 static void special_cmp(const char *file1, const char *file2)
 {
-#ifdef HAVE_SYS_XATTR_H
+#ifdef __linux__
        cmp_xattr(file1, file2, "system.ntfs_acl", 0, false);
        cmp_xattr(file1, file2, "system.ntfs_attrib", 0, false);
        cmp_xattr(file1, file2, "system.ntfs_dos_name", 0, true);
index 07cfc6bbb85d62d32518ed71a6d6e7c27409ef9d..e766f12002a230f5a9487e94e6165bc089810b44 100644 (file)
@@ -1,5 +1,9 @@
 Some fun files:
 
+corrupted_file_1.wim:  This WIM contains a file whose SHA-1 digest doesn't match.
+
+corrupted_file_2.wim:  This WIM contains a file that fails to decompress.
+
 cyclic.wim:  This WIM has an image with a cyclic directory structure and should be
 detected as invalid.
 
diff --git a/tests/wims/corrupted_file_1.wim b/tests/wims/corrupted_file_1.wim
new file mode 100644 (file)
index 0000000..4bb087e
Binary files /dev/null and b/tests/wims/corrupted_file_1.wim differ
diff --git a/tests/wims/corrupted_file_2.wim b/tests/wims/corrupted_file_2.wim
new file mode 100644 (file)
index 0000000..68b5854
Binary files /dev/null and b/tests/wims/corrupted_file_2.wim differ
index fda65ac8181ad8408666eefd01ab0cc6d732dcd9..049a64b19b81354f4e727197b0b7821f2219a457 100644 (file)
@@ -5,14 +5,13 @@ REM win32-test-imagex-capture_and_apply.bat
 REM\r
 REM Run some tests on the Windows version of wimlib-imagex.\r
 REM\r
-REM This must be run on Windows Vista or later in a clean directory, with\r
-REM Administrator privileges.  wimlib-imagex and win32-tree-cmp must be\r
-REM executable using the paths set below.\r
+REM This must be run with Administrator privileges, in a directory containing\r
+REM the wimlib-imagex, win32-tree-cmp, and set_reparse_point programs.\r
 \r
 setlocal EnableDelayedExpansion\r
-set WIMLIB_IMAGEX=wimlib-imagex\r
-set WIN32_TREE_CMP=win32-tree-cmp\r
-set SET_REPARSE_POINT=set_reparse_point\r
+set WIMLIB_IMAGEX=%cd%\wimlib-imagex\r
+set WIN32_TREE_CMP=%cd%\win32-tree-cmp\r
+set SET_REPARSE_POINT=%cd%\set_reparse_point\r
 \r
 if exist in.dir rd /S /Q in.dir\r
 if exist out.dir rd /S /Q out.dir\r
@@ -58,7 +57,8 @@ call :do_test
 call :msg "hard linked empty file"\r
 type nul > file\r
 mklink /h link file > nul\r
-call :do_test\r
+REM Use skip_dism_cmp=1 due to DISM bug\r
+call :do_test_with_params 0 1\r
 \r
 call :msg "various hard linked, identical, different, and empty files"\r
 echo 1 > file\r
@@ -71,7 +71,8 @@ mklink /h emptyfilelink emptyfile > nul
 echo 5 > identicalfile\r
 echo 1 > 1file\r
 mklink /h 1filelink 1file > nul\r
-call :do_test\r
+REM Use skip_dism_cmp=1 due to DISM bug\r
+call :do_test_with_params 0 1\r
 \r
 call :msg "multiple subdirectories, some empty, some not"\r
 md subdir1\r
@@ -84,10 +85,12 @@ md subdir2\subdir2subdir
 type nul > subdir2\emptyfile\r
 call :do_test\r
 \r
-call :msg "file with custom security descriptor"\r
-echo hello > file\r
-icacls file /deny Administrator:F > nul\r
-call :do_test\r
+REM FIXME: win32-tree-cmp can't handle this case.\r
+REM\r
+REM call :msg "file with custom security descriptor"\r
+REM echo hello > file\r
+REM icacls file /deny Administrator:F > nul\r
+REM call :do_test\r
 \r
 call :msg "directory with custom security descriptor (inheritence enabled)"\r
 md subdir\r
@@ -99,7 +102,7 @@ md subdir
 icacls subdir /inheritance:d > nul\r
 call :do_test\r
 \r
-REM            win32-tree-cmp can't handle this case.\r
+REM FIXME: win32-tree-cmp can't handle this case.\r
 REM\r
 REM call :msg "file with custom security descriptor (all inherited ACEs removed)"\r
 REM echo hello > file\r
@@ -109,7 +112,8 @@ REM call :do_test
 call :msg "file with custom integrity level"\r
 echo hello > file\r
 icacls file /setintegritylevel H > nul\r
-call :do_test\r
+REM Use skip_dism_cmp=1 due to DISM bug\r
+call :do_test_with_params 0 1\r
 \r
 call :msg "relative symlink"\r
 mklink relink dest > nul\r
@@ -375,7 +379,10 @@ echo "hello" > encrypted1
 echo "hello" > encrypted2\r
 cipher /e encrypted1 > nul\r
 cipher /e encrypted2 > nul\r
-call :do_test\r
+REM Use skip_dism_cmp=1 due to DISM bug where it fails to preserve short names:\r
+REM DIFFERENCE: in.dir\encrypted1 and out.dir\encrypted1 do not have the same short name (ENCRYP~1 vs. ENCRYP~2)\r
+REM DIFFERENCE: in.dir\encrypted2 and out.dir\encrypted2 do not have the same short name (ENCRYP~2 vs. ENCRYP~1)\r
+call :do_test_with_params 0 1\r
 \r
 call :msg "encrypted directory"\r
 md subdir\r
@@ -396,12 +403,14 @@ cipher /e subdir > nul
 cipher /d subdir\1 > nul\r
 call :do_test\r
 \r
-call :msg "encrypted root directory"\r
-cd ..\r
-cipher /e in.dir > nul\r
-cd in.dir\r
-echo "hello" > encrypted\r
-call :do_test\r
+REM FIXME: apply fails with STATUS_ACCESS_DENIED\r
+REM\r
+REM call :msg "encrypted root directory"\r
+REM cd ..\r
+REM cipher /e in.dir > nul\r
+REM cd in.dir\r
+REM echo "hello" > encrypted\r
+REM call :do_test\r
 \r
 call :msg "unencrypted file in encrypted directory in compressed directory"\r
 md 1\r
@@ -518,7 +527,14 @@ rd /s /q in.dir
 exit /b 0\r
 \r
 :do_test\r
+call :do_test_with_params 0 0\r
+goto :eof\r
+\r
+:do_test_with_params\r
+set skip_dism_apply=%1\r
+set skip_dism_cmp=%2\r
 cd ..\r
+echo   wimlib test (first time)\r
 %WIMLIB_IMAGEX% capture in.dir test.wim --norpfix > NUL\r
 if %errorlevel% neq 0 goto :fail\r
 %WIMLIB_IMAGEX% apply test.wim out.dir > NUL\r
@@ -527,21 +543,30 @@ if %errorlevel% neq 0 goto :fail
 if %errorlevel% neq 0 goto :fail\r
 \r
 REM  apply a second time so we test the case where the files already exist\r
+echo   wimlib test (second time)\r
 %WIMLIB_IMAGEX% apply test.wim out.dir > NUL\r
 if %errorlevel% neq 0 goto :fail\r
 %WIN32_TREE_CMP% in.dir out.dir\r
 if %errorlevel% neq 0 goto :fail\r
 \r
-REM Fun fact: Microsoft's WIMGAPI has bugs that make it fail some of our tests.\r
-REM Even the Windows 8.1 version has incorrect behavior with empty files with\r
-REM multiple links, or files with named data streams and multiple links.\r
+REM  compatibility test: apply the image with DISM\r
+echo   DISM test\r
 rd /S /Q out.dir\r
 md out.dir\r
-REM dism /capture-image /capturedir:in.dir /imagefile:test.wim /name:"test" /norpfix > nul\r
-REM if %errorlevel% neq 0 goto :fail\r
-dism /apply-image /imagefile:test.wim /index:1 /applydir:out.dir > nul\r
-if %errorlevel% neq 0 goto :fail\r
-%WIN32_TREE_CMP% in.dir out.dir\r
+if %skip_dism_apply% neq 1 (\r
+    dism /apply-image /imagefile:test.wim /index:1 /applydir:out.dir > nul\r
+    if !errorlevel! neq 0 (\r
+        echo ERROR: DISM apply failed!\r
+        goto :fail\r
+    )\r
+    if %skip_dism_cmp% neq 1 (\r
+        %WIN32_TREE_CMP% in.dir out.dir\r
+        if !errorlevel! neq 0 (\r
+            echo ERROR: DISM comparison failed!\r
+            goto :fail\r
+        )\r
+    )\r
+)\r
 \r
 rd /S /Q in.dir out.dir\r
 md in.dir\r
diff --git a/tests/win32-test-imagex-capture_and_apply.sh b/tests/win32-test-imagex-capture_and_apply.sh
new file mode 100755 (executable)
index 0000000..4a190c4
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e -u -v
+
+./tools/windows-build.sh
+bindir=$(find . -name 'wimlib-*-bin' | tail -1)
+for helper in win32-tree-cmp set_reparse_point; do
+       cc -O2 -municode -Wall -Werror ./tests/$helper.c -o "$bindir"/$helper.exe
+       chmod 700 "$bindir"/$helper.exe
+done
+cd "$bindir"
+../tests/win32-test-imagex-capture_and_apply.bat
index 35cefbe5c3846d9fcaae2c4d5a8fb762f983dff0..e3fe909dd7d7059720a28a570bf50ce64e9dd4ec 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2015-2016 Eric Biggers
+ * Copyright 2015-2023 Eric Biggers
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 /*
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #ifdef WITH_NTFS_3G
 #  include <sys/wait.h>
 #endif
 #include <unistd.h>
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  include <windows.h>
 #  include <winternl.h>
 #  include <ntstatus.h>
+#else
+#  include <linux/magic.h>
+#  include <sys/vfs.h>
 #endif
 
 #include "wimlib.h"
@@ -89,6 +93,9 @@
 static bool wimfile_in_use[MAX_NUM_WIMS];
 static int in_use_wimfile_indices[MAX_NUM_WIMS];
 static int num_wimfiles_in_use = 0;
+#ifndef _WIN32
+static u32 filesystem_type;
+#endif
 
 static void
 assertion_failed(int line, const char *format, ...)
@@ -119,16 +126,23 @@ assertion_failed(int line, const char *format, ...)
 static void
 change_to_temporary_directory(void)
 {
-#ifdef __WIN32__
-       ASSERT(SetCurrentDirectory(L"E:\\"),
-              "failed to change directory to E:\\");
-#else
-       const char *tmpdir = getenv("TMPDIR");
-       if (!tmpdir)
-               tmpdir = P_tmpdir;
+#ifdef _WIN32
+       const wchar_t *tmpdir = _wgetenv(T("TMPDIR"));
+
+       ASSERT(tmpdir != NULL, "TMPDIR must be set");
+       _wmkdir(tmpdir);
+       ASSERT(!_wchdir(tmpdir),
+              "failed to change to temporary directory '%ls'", tmpdir);
+#else /* _WIN32 */
+       const char *tmpdir = getenv("TMPDIR") ?: P_tmpdir;
+       struct statfs fs;
+
+       mkdir(tmpdir, 0700);
        ASSERT(!chdir(tmpdir),
-              "failed to change to temporary directory \"%s\": %m", tmpdir);
-#endif
+              "failed to change to temporary directory '%s': %m", tmpdir);
+       ASSERT(!statfs(".", &fs), "statfs of '%s' failed: %m", tmpdir);
+       filesystem_type = fs.f_type;
+#endif /* !_WIN32 */
 }
 
 static void __attribute__((unused))
@@ -186,19 +200,19 @@ create_ntfs_volume(const char *name)
 }
 #endif /* WITH_NTFS_3G */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
-extern WINAPI NTSTATUS NtQueryDirectoryFile (HANDLE FileHandle,
-                                            HANDLE Event,
-                                            PIO_APC_ROUTINE ApcRoutine,
-                                            PVOID ApcContext,
-                                            PIO_STATUS_BLOCK IoStatusBlock,
-                                            PVOID FileInformation,
-                                            ULONG Length,
-                                            FILE_INFORMATION_CLASS FileInformationClass,
-                                            BOOLEAN ReturnSingleEntry,
-                                            PUNICODE_STRING FileName,
-                                            BOOLEAN RestartScan);
+WINAPI NTSTATUS NtQueryDirectoryFile(HANDLE FileHandle,
+                                    HANDLE Event,
+                                    PIO_APC_ROUTINE ApcRoutine,
+                                    PVOID ApcContext,
+                                    PIO_STATUS_BLOCK IoStatusBlock,
+                                    PVOID FileInformation,
+                                    ULONG Length,
+                                    FILE_INFORMATION_CLASS FileInformationClass,
+                                    BOOLEAN ReturnSingleEntry,
+                                    PUNICODE_STRING FileName,
+                                    BOOLEAN RestartScan);
 
 static void
 delete_directory_tree_recursive(HANDLE cur_dir, UNICODE_STRING *name)
@@ -300,7 +314,7 @@ delete_directory_tree(const wchar_t *name)
        ASSERT(GetFileAttributes(name) == 0xFFFFFFFF, "Deletion didn't work!");
 }
 
-#else /* __WIN32__ */
+#else /* _WIN32 */
 
 static void
 delete_directory_tree_recursive(int dirfd, const char *name)
@@ -332,22 +346,28 @@ delete_directory_tree(const tchar *name)
        delete_directory_tree_recursive(AT_FDCWD, name);
 }
 
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
-static uint32_t
+static u64 random_state;
+
+static u32
 rand32(void)
 {
-       static uint64_t state;
-
-       /* A simple linear congruential generator  */
-       state = (state * 25214903917 + 11) & (((uint64_t)1 << 48) - 1);
-       return state >> 16;
+       /* A simple linear congruential generator */
+       random_state = (random_state * 25214903917 + 11) % (1ULL << 48);
+       return random_state >> 16;
 }
 
-static inline bool
+static bool
 randbool(void)
 {
-       return rand32() & 1;
+       return rand32() % 2;
+}
+
+static u64
+rand64(void)
+{
+       return ((u64)rand32() << 32) | rand32();
 }
 
 static tchar wimfile[32];
@@ -411,7 +431,7 @@ get_image_count(WIMStruct *wim)
        return info.image_count;
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 static bool
 is_wimboot_capable(WIMStruct *wim)
 {
@@ -426,7 +446,7 @@ is_wimboot_capable(WIMStruct *wim)
                 (info.compression_type == WIMLIB_COMPRESSION_TYPE_LZX &&
                  info.chunk_size == 32768));
 }
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
 
 static void
 overwrite_wim(WIMStruct *wim)
@@ -498,7 +518,7 @@ get_random_write_flags(void)
        return write_flags;
 }
 
-static uint32_t
+static u32
 get_random_chunk_size(int min_order, int max_order)
 {
        return 1 << (min_order + (rand32() % (max_order - min_order + 1)));
@@ -511,8 +531,8 @@ op__create_new_wim(void)
 
        const tchar *wimfile;
        enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_NONE;
-       uint32_t chunk_size = 0;
-       uint32_t solid_chunk_size = 0;
+       u32 chunk_size = 0;
+       u32 solid_chunk_size = 0;
        int write_flags;
        WIMStruct *wim;
 
@@ -778,15 +798,17 @@ op__apply_and_capture_test(void)
        } else
 #endif
        {
-#ifdef __WIN32__
+#ifdef _WIN32
                printf("applying in Windows mode\n");
                cmp_flags |= WIMLIB_CMP_FLAG_WINDOWS_MODE;
-#else /* __WIN32__ */
+#else /* _WIN32 */
                printf("applying in UNIX mode\n");
                extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
                add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
                cmp_flags |= WIMLIB_CMP_FLAG_UNIX_MODE;
-#endif /* !__WIN32__ */
+               if (filesystem_type == EXT4_SUPER_MAGIC)
+                       cmp_flags |= WIMLIB_CMP_FLAG_EXT4;
+#endif /* !_WIN32 */
        }
        add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
        CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
@@ -810,24 +832,33 @@ op__apply_and_capture_test(void)
        wimlib_free(wim);
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
-/* Enumerate and unregister all backing WIMs from the specified volume  */
+/*
+ * Enumerate and unregister all backing WIMs from the volume containing the
+ * current directory.
+ */
 static void
-unregister_all_backing_wims(const tchar drive_letter)
+unregister_all_backing_wims(void)
 {
+       wchar_t full_path[MAX_PATH];
+       DWORD path_len;
        wchar_t volume[7];
        HANDLE h;
        void *overlay_list;
        DWORD bytes_returned;
-       const struct wim_provider_overlay_entry *entry;
+       const WIM_PROVIDER_OVERLAY_ENTRY *entry;
        struct {
-               struct wof_external_info wof_info;
-               struct wim_provider_remove_overlay_input wim;
+               WOF_EXTERNAL_INFO wof_info;
+               WIM_PROVIDER_REMOVE_OVERLAY_INPUT wim;
        } in;
 
-       wsprintf(volume, L"\\\\.\\%lc:", drive_letter);
+       path_len = GetFullPathName(L".", ARRAY_LEN(full_path), full_path, NULL);
+       ASSERT(path_len > 0,
+              "Failed to get full path of current directory; error=%u",
+              (unsigned)GetLastError());
 
+       wsprintf(volume, L"\\\\.\\%lc:", full_path[0]);
        h = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
                       FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS, NULL);
@@ -837,11 +868,11 @@ unregister_all_backing_wims(const tchar drive_letter)
        overlay_list = malloc(32768);
        ASSERT(overlay_list != NULL, "out of memory");
 
-       in.wof_info.version = WOF_CURRENT_VERSION;
-       in.wof_info.provider = WOF_PROVIDER_WIM;
+       in.wof_info.Version = WOF_CURRENT_VERSION;
+       in.wof_info.Provider = WOF_PROVIDER_WIM;
 
        if (!DeviceIoControl(h, FSCTL_ENUM_OVERLAY,
-                            &in, sizeof(struct wof_external_info),
+                            &in, sizeof(WOF_EXTERNAL_INFO),
                             overlay_list, 32768, &bytes_returned, NULL))
        {
                ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
@@ -854,16 +885,16 @@ unregister_all_backing_wims(const tchar drive_letter)
        entry = overlay_list;
        for (;;) {
                printf("Unregistering data source ID %"PRIu64"\n",
-                      entry->data_source_id);
-               in.wim.data_source_id = entry->data_source_id;
+                      entry->DataSourceId.QuadPart);
+               in.wim.DataSourceId = entry->DataSourceId;
                ASSERT(DeviceIoControl(h, FSCTL_REMOVE_OVERLAY, &in, sizeof(in),
                                       NULL, 0, &bytes_returned, NULL),
                       "FSCTL_REMOVE_OVERLAY failed; error=%u",
-                      (unsigned )GetLastError());
-               if (entry->next_entry_offset == 0)
+                      (unsigned)GetLastError());
+               if (entry->NextEntryOffset == 0)
                        break;
-               entry = (const struct wim_provider_overlay_entry *)
-                       ((const uint8_t *)entry + entry->next_entry_offset);
+               entry = (const WIM_PROVIDER_OVERLAY_ENTRY *)
+                       ((const u8 *)entry + entry->NextEntryOffset);
        }
        free(overlay_list);
        CloseHandle(h);
@@ -884,7 +915,7 @@ op__wimboot_test(void)
 
        index = select_random_wimfile_index();
 
-       unregister_all_backing_wims(L'E');
+       unregister_all_backing_wims();
        copy_file(get_wimfile(index), L"wimboot.wim");
 
        CHECK_RET(wimlib_open_wim(L"wimboot.wim", 0, &wim));
@@ -931,7 +962,7 @@ op__wimboot_test(void)
        wimlib_free(wim);
        wimlib_free(wim2);
 }
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
 
 static int
 is_solid_resource(const struct wimlib_resource_entry *resource, void *_ctx)
@@ -953,7 +984,7 @@ op__split_test(void)
        WIMStruct *wim;
        WIMStruct *swm;
        WIMStruct *joined_wim;
-       uint64_t part_size;
+       u64 part_size;
        int write_flags;
        const tchar *globs[] = { T("tmp*.swm") };
        int image_count;
@@ -1039,40 +1070,49 @@ static const operation_func operation_table[] = {
        op__apply_and_capture_test,
        op__split_test,
        op__set_compression_level,
-#ifdef __WIN32__
+#ifdef _WIN32
        op__wimboot_test,
 #endif
 };
 
-#ifdef __WIN32__
-extern int wmain(int argc, wchar_t **argv);
+#ifdef _WIN32
+int wmain(int argc, wchar_t **argv);
 #define main wmain
 #endif
 
 int
 main(int argc, tchar **argv)
 {
-       unsigned long long num_iterations;
-
-       if (argc < 2) {
-               num_iterations = ULLONG_MAX;
-               printf("Starting test runner\n");
-       } else {
-               num_iterations = tstrtoull(argv[1], NULL, 10);
-               printf("Starting test runner with %llu iterations\n",
-                      num_iterations);
-       }
+       unsigned long time_limit = 0;
+       time_t start_time;
+       u64 i;
+
+       /* If you want to make the tests deterministic, delete this line. */
+       random_state = ((u64)time(NULL) << 16) ^ getpid();
+
+       if (argc >= 2)
+               time_limit = tstrtoul(argv[1], NULL, 10);
+
+       if (time_limit == 0)
+               printf("Starting wlfuzz with no time limit\n");
+       else
+               printf("Starting wlfuzz with time limit of %lu seconds\n",
+                      time_limit);
 
-       CHECK_RET(wimlib_global_init(0));
+       CHECK_RET(wimlib_global_init(WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES |
+                                    WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES));
        wimlib_set_print_errors(true);
+       wimlib_seed_random(rand64());
 
        change_to_temporary_directory();
 
-       for (int i = 0; i < MAX_NUM_WIMS; i++)
+       for (i = 0; i < MAX_NUM_WIMS; i++)
                ASSERT(!tunlink(get_wimfile(i)) || errno == ENOENT, "unlink: %m");
 
-       for (unsigned long long i = 0; i < num_iterations; i++) {
-               printf("--> iteration %llu\n", i);
+       i = 0;
+       start_time = time(NULL);
+       while (time_limit == 0 || time(NULL) < start_time + time_limit) {
+               printf("--> iteration %"PRIu64"\n", ++i);
                (*operation_table[rand32() % ARRAY_LEN(operation_table)])();
        }
 
diff --git a/tools/clang-build-with-cfi b/tools/clang-build-with-cfi
deleted file mode 100755 (executable)
index 1a04198..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-set -evu
-
-./configure CC=clang --enable-test-support
-
-ARGS=(-fsanitize=cfi -fsanitize=safe-stack
-       -flto -fvisibility=hidden
-       -O3 -std=c99 -Wall -Wno-pointer-sign
-       -D_GNU_SOURCE -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64
-       -Iinclude -I. -I/usr/include/libxml2
-       -lntfs-3g -lxml2 -lfuse -lpthread -lrt -lcrypto)
-
-clang src/*.c programs/imagex.c -o wimlib-imagex "${ARGS[@]}"
-clang src/*.c tests/wlfuzz.c -o wlfuzz "${ARGS[@]}"
diff --git a/tools/clang-scan-build b/tools/clang-scan-build
deleted file mode 100755 (executable)
index 34b1467..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-exec scan-build --use-analyzer=/usr/bin/clang /usr/bin/clang   \
-       src/*.c programs/imagex.c -o wimlib-imagex              \
-       -std=c99 -Wno-pointer-sign                              \
-       -D_GNU_SOURCE -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64    \
-       -Iinclude -I. -I/usr/include/libxml2                    \
-       -lntfs-3g -lxml2 -lfuse -lpthread -lrt -lcrypto
index f911a4327b2a5e4579a0ff699fa3c44f82f3a00b..5b016099acc51ad4ab2130ce3bcae1e0a8b449cf 100644 (file)
@@ -18,7 +18,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #include <fcntl.h>
index 91ae6f77a88915a0e56a0797a07e975bfb58528c..2c656f45c9bcb51afcc54f5b41bd18f6547c79a9 100644 (file)
@@ -20,7 +20,7 @@
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #define WINVER 0x6000  /* Needed for LCIDToLocaleName() declaration  */
diff --git a/tools/get-version-number.sh b/tools/get-version-number.sh
new file mode 100755 (executable)
index 0000000..2453355
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Get the version number of the project to use in the release filenames
+# and in the --version output.
+
+vers=$(git describe --abbrev=8 --dirty --always 2>/dev/null | \
+       sed 's/^v//')
+if [ -z "$vers" ]; then
+       # Fallback for people who use autoreconf on tarball releases
+       vers="1.14.4"
+fi
+echo "$vers"
diff --git a/tools/libFuzzer/compress/corpus/lzms20 b/tools/libFuzzer/compress/corpus/lzms20
new file mode 100644 (file)
index 0000000..063a7cb
--- /dev/null
@@ -0,0 +1,16 @@
+\ 3\14CHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/lzms50 b/tools/libFuzzer/compress/corpus/lzms50
new file mode 100644 (file)
index 0000000..bcb1bc0
--- /dev/null
@@ -0,0 +1,16 @@
+\ 32CHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/lzms80 b/tools/libFuzzer/compress/corpus/lzms80
new file mode 100644 (file)
index 0000000..9f537ad
--- /dev/null
@@ -0,0 +1,16 @@
+\ 3PCHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/lzx20 b/tools/libFuzzer/compress/corpus/lzx20
new file mode 100644 (file)
index 0000000..83bcdfd
--- /dev/null
@@ -0,0 +1,16 @@
+\ 2\14CHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/lzx50 b/tools/libFuzzer/compress/corpus/lzx50
new file mode 100644 (file)
index 0000000..fac0534
--- /dev/null
@@ -0,0 +1,16 @@
+\ 22CHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/lzx80 b/tools/libFuzzer/compress/corpus/lzx80
new file mode 100644 (file)
index 0000000..46e720f
--- /dev/null
@@ -0,0 +1,16 @@
+\ 2PCHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/xpress20 b/tools/libFuzzer/compress/corpus/xpress20
new file mode 100644 (file)
index 0000000..e41ed0b
--- /dev/null
@@ -0,0 +1,16 @@
+\ 1\14CHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/xpress50 b/tools/libFuzzer/compress/corpus/xpress50
new file mode 100644 (file)
index 0000000..0a8f87f
--- /dev/null
@@ -0,0 +1,16 @@
+\ 12CHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/corpus/xpress80 b/tools/libFuzzer/compress/corpus/xpress80
new file mode 100644 (file)
index 0000000..22e8761
--- /dev/null
@@ -0,0 +1,16 @@
+\ 1PCHAPTER 1. Loomings.\r
+\r
+Call me Ishmael. Some years ago—never mind how long precisely—having\r
+little or no money in my purse, and nothing particular to interest me\r
+on shore, I thought I would sail about a little and see the watery part\r
+of the world. It is a way I have of driving off the spleen and\r
+regulating the circulation. Whenever I find myself growing grim about\r
+the mouth; whenever it is a damp, drizzly November in my soul; whenever\r
+I find myself involuntarily pausing before coffin warehouses, and\r
+bringing up the rear of every funeral I meet; and especially whenever\r
+my hypos get such an upper hand of me, that it requires a strong moral\r
+principle to prevent me from deliberately stepping into the street, and\r
+methodically knocking people’s hats off—then, I account it high time to\r
+get to sea as soon as I can. This is my substitute for pistol and ball.\r
+With a philosophical flourish Cato throws himself upon his sword; I\r
+quietly take to the ship. There is nothing su
\ No newline at end of file
diff --git a/tools/libFuzzer/compress/fuzz.c b/tools/libFuzzer/compress/fuzz.c
new file mode 100644 (file)
index 0000000..e87bbba
--- /dev/null
@@ -0,0 +1,44 @@
+#include "../fuzzer.h"
+
+/* Fuzz the compression and decompression round trip. */
+int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize)
+{
+       int ctype;
+       int level;
+       struct wimlib_compressor *c;
+       struct wimlib_decompressor *d;
+       size_t csize_avail = insize;
+       uint8_t *cbuf;
+       uint8_t *decompressed;
+       size_t csize;
+       int ret;
+
+       if (insize < 2)
+               return 0;
+       ctype = 1 + ((uint8_t)(in[0] - 1) % 3); /* 1-3 */
+       level = 1 + (in[1] % 100); /* 1-100 */
+       in += 2;
+       insize -= 2;
+
+       cbuf = malloc(csize_avail);
+       decompressed = malloc(insize);
+
+       ret = wimlib_create_compressor(ctype, insize, level, &c);
+       if (ret == 0) {
+               ret = wimlib_create_decompressor(ctype, insize, &d);
+               assert(ret == 0);
+
+               csize = wimlib_compress(in, insize, cbuf, csize_avail, c);
+               if (csize) {
+                       ret = wimlib_decompress(cbuf, csize,
+                                               decompressed, insize, d);
+                       assert(ret == 0);
+                       assert(memcmp(in, decompressed, insize) == 0);
+               }
+               wimlib_free_compressor(c);
+               wimlib_free_decompressor(d);
+       }
+       free(cbuf);
+       free(decompressed);
+       return 0;
+}
diff --git a/tools/libFuzzer/decompress/corpus/lzms b/tools/libFuzzer/decompress/corpus/lzms
new file mode 100644 (file)
index 0000000..739dd49
Binary files /dev/null and b/tools/libFuzzer/decompress/corpus/lzms differ
diff --git a/tools/libFuzzer/decompress/corpus/lzx b/tools/libFuzzer/decompress/corpus/lzx
new file mode 100644 (file)
index 0000000..e555efe
Binary files /dev/null and b/tools/libFuzzer/decompress/corpus/lzx differ
diff --git a/tools/libFuzzer/decompress/corpus/xpress b/tools/libFuzzer/decompress/corpus/xpress
new file mode 100644 (file)
index 0000000..e6c9925
Binary files /dev/null and b/tools/libFuzzer/decompress/corpus/xpress differ
diff --git a/tools/libFuzzer/decompress/fuzz.c b/tools/libFuzzer/decompress/fuzz.c
new file mode 100644 (file)
index 0000000..82782bc
--- /dev/null
@@ -0,0 +1,26 @@
+#include "../fuzzer.h"
+
+/* Fuzz decompression. */
+int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize)
+{
+       int ctype;
+       struct wimlib_decompressor *d;
+       const size_t outsize_avail = 3 * insize;
+       uint8_t *out;
+       int ret;
+
+       if (insize < 1)
+               return 0;
+       ctype = 1 + ((uint8_t)(in[0] - 1) % 3); /* 1-3 */
+       in++;
+       insize--;
+
+       ret = wimlib_create_decompressor(ctype, insize, &d);
+       if (ret == 0) {
+               out = malloc(outsize_avail);
+               wimlib_decompress(in, insize, out, outsize_avail, d);
+               wimlib_free_decompressor(d);
+               free(out);
+       }
+       return 0;
+}
diff --git a/tools/libFuzzer/encoding/corpus/0 b/tools/libFuzzer/encoding/corpus/0
new file mode 100644 (file)
index 0000000..e0d84f4
Binary files /dev/null and b/tools/libFuzzer/encoding/corpus/0 differ
diff --git a/tools/libFuzzer/encoding/corpus/1 b/tools/libFuzzer/encoding/corpus/1
new file mode 100644 (file)
index 0000000..eb05829
Binary files /dev/null and b/tools/libFuzzer/encoding/corpus/1 differ
diff --git a/tools/libFuzzer/encoding/fuzz.c b/tools/libFuzzer/encoding/fuzz.c
new file mode 100644 (file)
index 0000000..16c7d13
--- /dev/null
@@ -0,0 +1,83 @@
+#include "../fuzzer.h"
+
+/*
+ * "UTF-8" (actually "WTF-8") to UTF-16LE (actually "arbitrary sequence of
+ * 16-bit wchars") and back again should be lossless, unless the initial string
+ * isn't valid WTF-8, in which case WIMLIB_ERR_INVALID_UTF8_STRING is expected.
+ */
+static void
+fuzz_utf8_roundtrip(const u8 *in, size_t insize)
+{
+       utf16lechar *utf16;
+       size_t utf16_size;
+       int ret;
+       char *result;
+       size_t result_size;
+
+       ret = wimlib_utf8_to_utf16le((const char *)in, insize,
+                                    &utf16, &utf16_size);
+       if (ret) {
+               assert(ret == WIMLIB_ERR_INVALID_UTF8_STRING);
+               return;
+       }
+       assert(ret == 0);
+       ret = wimlib_utf16le_to_utf8(utf16, utf16_size, &result, &result_size);
+       assert(ret == 0);
+       assert(result_size == insize);
+       assert(memcmp(result, in, insize) == 0);
+       free(result);
+       free(utf16);
+}
+
+/*
+ * "UTF-16LE" (actually "arbitrary sequence of 16-bit wchars") to UTF-8
+ * (actually "WTF-8") and back again should be lossless, unless the initial
+ * length isn't a multiple of 2 bytes, in which case
+ * WIMLIB_ERR_INVALID_UTF16_STRING is expected.
+ */
+static void
+fuzz_utf16_roundtrip(const u8 *in, size_t insize)
+{
+       utf16lechar *in_aligned = malloc(insize);
+       char *utf8;
+       size_t utf8_size;
+       int ret;
+       utf16lechar *result;
+       size_t result_size;
+
+       memcpy(in_aligned, in, insize);
+       ret = wimlib_utf16le_to_utf8(in_aligned, insize, &utf8, &utf8_size);
+       if (insize % 2) {
+               assert(ret == WIMLIB_ERR_INVALID_UTF16_STRING);
+               free(in_aligned);
+               return;
+       }
+       assert(ret == 0);
+       ret = wimlib_utf8_to_utf16le(utf8, utf8_size, &result, &result_size);
+       assert(ret == 0);
+       assert(result_size == insize);
+       assert(memcmp(result, in, insize) == 0);
+       free(result);
+       free(utf8);
+       free(in_aligned);
+}
+
+/* Fuzz character encoding conversion. */
+int LLVMFuzzerTestOneInput(const u8 *in, size_t insize)
+{
+       int which;
+
+       if (insize < 1)
+               return 0;
+       which = *in++;
+       insize--;
+       switch (which) {
+       case 0:
+               fuzz_utf8_roundtrip(in, insize);
+               break;
+       case 1:
+               fuzz_utf16_roundtrip(in, insize);
+               break;
+       }
+       return 0;
+}
diff --git a/tools/libFuzzer/fault-injection.c b/tools/libFuzzer/fault-injection.c
new file mode 100644 (file)
index 0000000..e33c37a
--- /dev/null
@@ -0,0 +1,42 @@
+#include "fuzzer.h"
+
+static int64_t num_allocs_remaining;
+
+static void *
+faultinject_malloc(size_t size)
+{
+       if (__atomic_sub_fetch(&num_allocs_remaining, 1, __ATOMIC_RELAXED) <= 0)
+               return NULL;
+       return malloc(size);
+}
+
+static void
+faultinject_free(void *p)
+{
+       free(p);
+}
+
+static void *
+faultinject_realloc(void *p, size_t size)
+{
+       if (__atomic_sub_fetch(&num_allocs_remaining, 1, __ATOMIC_RELAXED) <= 0)
+               return NULL;
+       return realloc(p, size);
+}
+
+bool
+setup_fault_nth(const uint8_t **in, size_t *insize, uint16_t *fault_nth)
+{
+       uint16_t n;
+
+       if (*insize < 2)
+               return false;
+       memcpy(&n, *in, 2);
+       wimlib_set_memory_allocator(faultinject_malloc, faultinject_free,
+                                   faultinject_realloc);
+       num_allocs_remaining = n ?: INT64_MAX;
+       *in += 2;
+       *insize -= 2;
+       *fault_nth = n;
+       return true;
+}
diff --git a/tools/libFuzzer/fuzz.sh b/tools/libFuzzer/fuzz.sh
new file mode 100755 (executable)
index 0000000..ad7e5e9
--- /dev/null
@@ -0,0 +1,136 @@
+#!/bin/bash
+
+set -e -u -o pipefail
+
+cd "$(dirname "$0")"
+TOPDIR=../..
+SCRIPTDIR=$PWD
+
+read -r -a AVAILABLE_TARGETS < <(echo */fuzz.c | sed 's@/fuzz.c@@g')
+
+usage()
+{
+       cat << EOF
+Usage: $0 [OPTION]... FUZZ_TARGET
+
+Fuzz wimlib with LLVM's libFuzzer.
+
+Options:
+   --asan          Enable AddressSanitizer
+   --input=INPUT   Test a single input file only
+   --max-len=LEN   Maximum length of generated inputs (default: $MAX_LEN)
+   --msan          Enable MemorySanitizer
+   --time=SECONDS  Stop after the given time has passed
+   --ubsan         Enable UndefinedBehaviorSanitizer
+
+Available fuzz targets: ${AVAILABLE_TARGETS[*]}
+EOF
+}
+
+die()
+{
+       echo "$*" 1>&2
+       exit 1
+}
+
+run_cmd()
+{
+       echo "$*"
+       "$@"
+}
+
+EXTRA_SANITIZERS=
+EXTRA_FUZZER_ARGS=()
+INPUT=
+MAX_LEN=32768
+
+longopts_array=(
+asan
+help
+input:
+max-len:
+msan
+time:
+ubsan
+)
+longopts=$(echo "${longopts_array[@]}" | tr ' ' ',')
+
+if ! options=$(getopt -o "" -l "$longopts" -- "$@"); then
+       usage 1>&2
+       exit 1
+fi
+eval set -- "$options"
+while true; do
+       case "$1" in
+       --asan)
+               EXTRA_SANITIZERS+=",address"
+               ;;
+       --help)
+               usage
+               exit 0
+               ;;
+       --input)
+               INPUT=$2
+               shift
+               ;;
+       --max-len)
+               MAX_LEN=$2
+               shift
+               ;;
+       --msan)
+               EXTRA_SANITIZERS+=",memory"
+               ;;
+       --time)
+               EXTRA_FUZZER_ARGS+=("-max_total_time=$2")
+               shift
+               ;;
+       --ubsan)
+               EXTRA_SANITIZERS+=",undefined"
+               ;;
+       --)
+               shift
+               break
+               ;;
+       *)
+               echo 1>&2 "Invalid option '$1'"
+               usage 1>&2
+               exit 1
+       esac
+       shift
+done
+EXTRA_FUZZER_ARGS+=("-max_len=$MAX_LEN")
+
+if (( $# != 1 )); then
+       echo 1>&2 "No fuzz target specified!"
+       usage 1>&2
+       exit 1
+fi
+TARGET=$1
+if [ ! -e "$TARGET/fuzz.c" ]; then
+       echo 1>&2 "'$TARGET' is not a valid fuzz target!"
+       usage 1>&2
+       exit 1
+fi
+cd "$TOPDIR"
+cflags="-g -O1 -Wall -Werror"
+cflags+=" -fsanitize=fuzzer-no-link$EXTRA_SANITIZERS"
+if [ -n "$EXTRA_SANITIZERS" ]; then
+       cflags+=" -fno-sanitize-recover=${EXTRA_SANITIZERS#,}"
+fi
+if ! [ -e config.log ] || ! grep -q -- "'CFLAGS=$cflags'" config.log; then
+       run_cmd ./configure --enable-test-support --without-fuse --without-ntfs-3g \
+               CC=clang CFLAGS="$cflags"
+fi
+run_cmd make "-j$(getconf _NPROCESSORS_ONLN)"
+cd "$SCRIPTDIR"
+if [ -n "$INPUT" ]; then
+       run_cmd clang -g -O1 -fsanitize=fuzzer-no-link$EXTRA_SANITIZERS -Wall -Werror \
+               -I "$TOPDIR/include" "$TARGET/fuzz.c" test-one-input.c fault-injection.c \
+               "$TOPDIR/.libs/libwim.a" -o test-one-input
+       run_cmd ./test-one-input "$INPUT"
+else
+       run_cmd clang -g -O1 -fsanitize=fuzzer$EXTRA_SANITIZERS -Wall -Werror \
+               -I "$TOPDIR/include" "$TARGET/fuzz.c" fault-injection.c \
+               "$TOPDIR/.libs/libwim.a" -o "$TARGET/fuzz"
+       run_cmd "$TARGET/fuzz" "${EXTRA_FUZZER_ARGS[@]}" "$TARGET/corpus"
+fi
diff --git a/tools/libFuzzer/fuzzer.h b/tools/libFuzzer/fuzzer.h
new file mode 100644 (file)
index 0000000..6639060
--- /dev/null
@@ -0,0 +1,13 @@
+#define ENABLE_TEST_SUPPORT 1
+#include <assert.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <wimlib.h>
+#include <wimlib/test_support.h>
+
+bool
+setup_fault_nth(const uint8_t **in, size_t *insize, uint16_t *fault_nth);
diff --git a/tools/libFuzzer/test-one-input.c b/tools/libFuzzer/test-one-input.c
new file mode 100644 (file)
index 0000000..9b201f8
--- /dev/null
@@ -0,0 +1,34 @@
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize);
+
+int main(int argc, char *argv[])
+{
+       int fd;
+       struct stat stbuf;
+       uint8_t *in;
+
+       fd = open(argv[1], O_RDONLY);
+       if (fd < 0) {
+               perror(argv[1]);
+               return 1;
+       }
+       if (fstat(fd, &stbuf) != 0) {
+               perror("fstat");
+               return 1;
+       }
+       in = malloc(stbuf.st_size);
+       if (read(fd, in, stbuf.st_size) != stbuf.st_size) {
+               perror("read");
+               return 1;
+       }
+       LLVMFuzzerTestOneInput(in, stbuf.st_size);
+       close(fd);
+       free(in);
+       return 0;
+}
diff --git a/tools/libFuzzer/wim/corpus/0 b/tools/libFuzzer/wim/corpus/0
new file mode 100644 (file)
index 0000000..a2a3f95
Binary files /dev/null and b/tools/libFuzzer/wim/corpus/0 differ
diff --git a/tools/libFuzzer/wim/fuzz.c b/tools/libFuzzer/wim/fuzz.c
new file mode 100644 (file)
index 0000000..4d21e12
--- /dev/null
@@ -0,0 +1,32 @@
+#include "../fuzzer.h"
+
+/* Fuzz WIM file reading. */
+int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize)
+{
+       uint16_t fault_nth;
+       char tmp_wim[128];
+       char tmp_dir[128];
+       int fd;
+       WIMStruct *wim;
+       int ret;
+
+       if (!setup_fault_nth(&in, &insize, &fault_nth))
+               return 0;
+
+       sprintf(tmp_wim, "/tmp/wim-fuzz-%d.wim", getpid());
+       sprintf(tmp_dir, "/tmp/wim-fuzz-%d", getpid());
+
+       fd = open(tmp_wim, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+       assert(fd >= 0);
+       ret = write(fd, in, insize);
+       assert(ret == insize);
+       close(fd);
+
+       ret = wimlib_open_wim(tmp_wim, 0, &wim);
+       if (ret == 0) {
+               wimlib_extract_image(wim, 1, tmp_dir, 0);
+               wimlib_add_image(wim, tmp_dir, "name", NULL, 0);
+               wimlib_free(wim);
+       }
+       return 0;
+}
diff --git a/tools/libFuzzer/xml_windows/corpus/dll b/tools/libFuzzer/xml_windows/corpus/dll
new file mode 100644 (file)
index 0000000..7a9f1d2
Binary files /dev/null and b/tools/libFuzzer/xml_windows/corpus/dll differ
diff --git a/tools/libFuzzer/xml_windows/corpus/registry b/tools/libFuzzer/xml_windows/corpus/registry
new file mode 100644 (file)
index 0000000..c416b0e
Binary files /dev/null and b/tools/libFuzzer/xml_windows/corpus/registry differ
diff --git a/tools/libFuzzer/xml_windows/fuzz.c b/tools/libFuzzer/xml_windows/fuzz.c
new file mode 100644 (file)
index 0000000..2816378
--- /dev/null
@@ -0,0 +1,43 @@
+#include "../fuzzer.h"
+
+#include <sys/stat.h>
+
+#define TMPDIR "/tmp/fuzz-xml-windows/"
+
+static void
+write_file(const char *path, const void *data, size_t size)
+{
+       int fd;
+       ssize_t res;
+
+       fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0600);
+       assert(fd >= 0);
+       res = write(fd, data, size);
+       assert(res == size);
+       close(fd);
+}
+
+/* Fuzz set_windows_specific_info() in xml_windows.c. */
+int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize)
+{
+       WIMStruct *wim;
+       int ret;
+
+       mkdir(TMPDIR, 0700);
+       mkdir(TMPDIR "Windows", 0700);
+       mkdir(TMPDIR "Windows", 0700);
+       mkdir(TMPDIR "Windows/System32", 0700);
+       mkdir(TMPDIR "Windows/System32/config", 0700);
+       write_file(TMPDIR "Windows/System32/kernel32.dll", in, insize);
+       write_file(TMPDIR "Windows/System32/config/SYSTEM", in, insize);
+       write_file(TMPDIR "Windows/System32/config/SOFTWARE", in, insize);
+
+       ret = wimlib_create_new_wim(WIMLIB_COMPRESSION_TYPE_NONE, &wim);
+       assert(!ret);
+
+       ret = wimlib_add_image(wim, TMPDIR, NULL, NULL, 0);
+       assert(!ret);
+
+       wimlib_free(wim);
+       return 0;
+}
diff --git a/tools/libFuzzer/xmlproc/corpus/0 b/tools/libFuzzer/xmlproc/corpus/0
new file mode 100644 (file)
index 0000000..d63b464
Binary files /dev/null and b/tools/libFuzzer/xmlproc/corpus/0 differ
diff --git a/tools/libFuzzer/xmlproc/fuzz.c b/tools/libFuzzer/xmlproc/fuzz.c
new file mode 100644 (file)
index 0000000..a16dbe9
--- /dev/null
@@ -0,0 +1,38 @@
+#include "../fuzzer.h"
+
+/* Fuzz XML parsing and writing. */
+int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize)
+{
+       uint16_t fault_nth;
+       char *in_str;
+       char *out_str = NULL;
+       int ret;
+
+       if (!setup_fault_nth(&in, &insize, &fault_nth))
+               return 0;
+
+       in_str = malloc(insize + 1);
+       memcpy(in_str, in, insize);
+       in_str[insize] = '\0';
+       ret = wimlib_parse_and_write_xml_doc(in_str, &out_str);
+       if (ret == 0) {
+               char *out2_str = NULL;
+
+               /*
+                * If the first parse+write succeeded, we now should be able to
+                * parse+write the result without changing it further.
+                */
+               ret = wimlib_parse_and_write_xml_doc(out_str, &out2_str);
+               if (ret != 0)
+                       assert(ret == WIMLIB_ERR_NOMEM && fault_nth);
+               else
+                       assert(strcmp(out_str, out2_str) == 0);
+               free(out2_str);
+       } else {
+               assert(ret == WIMLIB_ERR_XML ||
+                      (fault_nth && ret == WIMLIB_ERR_NOMEM));
+       }
+       free(in_str);
+       free(out_str);
+       return 0;
+}
diff --git a/tools/make-releases b/tools/make-releases
deleted file mode 100755 (executable)
index fad2b88..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-set -e
-
-MAKE="make -j$(grep -c processor /proc/cpuinfo)"
-
-export CFLAGS="-O2 -Wall -Werror"
-
-./configure && $MAKE distcheck
-
-# Recompress with 7-Zip
-gzfile=$(ls wimlib-*.tar.gz | tail -1)
-tarfile=${gzfile%.gz}
-gunzip $gzfile
-7z -mx9 a $gzfile $tarfile
-rm -f $tarfile
-
-for arch in i686 x86_64; do
-       ./tools/make-windows-release $arch
-done
diff --git a/tools/make-releases.sh b/tools/make-releases.sh
new file mode 100755 (executable)
index 0000000..eac1712
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+MAKE="make -j$(grep -c processor /proc/cpuinfo)"
+
+export CFLAGS="-O2 -Wall -Werror"
+
+autoreconf -i -f # make sure the version number gets updated
+
+./configure && $MAKE distcheck
+
+# Recompress with libdeflate
+gzfile=$(find . -name 'wimlib-*.tar.gz' | tail -1)
+tarfile=${gzfile%.gz}
+libdeflate-gunzip "$gzfile"
+libdeflate-gzip -12 "$tarfile"
+
+for arch in i686 x86_64; do
+       ./tools/windows-build.sh --arch=$arch --include-docs --zip
+done
diff --git a/tools/make-windows-release b/tools/make-windows-release
deleted file mode 100755 (executable)
index b215511..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/bin/bash
-#
-# This script prepares a Windows binary distribution of wimlib on Linux using
-# MinGW-w64.  The desired architecture must be passed as the first argument.
-
-set -e
-
-if [ ! -e src/wim.c ]; then
-       echo "This script must be run from the toplevel directory" 1>&2
-       exit 1
-fi
-
-if [ $# -lt 1 ]; then
-       echo "Usage: $0 i686|x86_64 [EXTRA_CONFIGURE_ARG]..." 1>&2
-       exit 1
-fi
-
-ARCH="$1"
-shift
-
-case "$ARCH" in
-i686|x86_64)
-       ;;
-*)
-       echo "ERROR: ARCH must be i686 or x86_64" 1>&2
-       exit 1
-       ;;
-esac
-
-VERSION=$(grep 'AC_INIT' configure.ac | \
-         grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+[^]]*')
-DESTDIR=wimlib-${VERSION}-windows-${ARCH}-bin
-ZIPFILE=wimlib-${VERSION}-windows-${ARCH}-bin.zip
-MAKE="make -j $(grep -c processor /proc/cpuinfo)"
-WINDEPDIR=./tools/windeps
-SYSROOT=$WINDEPDIR/sysroot_${ARCH}
-
-# Prepare third party libraries
-
-if [ ! -e $SYSROOT ]; then
-       $MAKE -C $WINDEPDIR sysroot_${ARCH}
-fi
-
-# Compile wimlib
-
-if ! grep -q "./configure --host=${ARCH}-w64-mingw32" config.log || \
-       ! grep -q "configure: exit 0" config.log || \
-       [ $# -gt 0 ]
-then
-       extra_args=
-       if [ $ARCH = x86_64 ]; then
-               extra_args="--enable-ssse3-sha1"
-       fi
-       # Note: putting -static-libgcc in CC is a workaround for libtool
-       # stripping it:
-       # http://www.gnu.org/software/libtool/manual/libtool.html#Stripped-link-flags
-       ./configure --host=${ARCH}-w64-mingw32 --disable-static         \
-               CC="${ARCH}-w64-mingw32-gcc -static-libgcc"             \
-               CPPFLAGS="-I$SYSROOT/include"                           \
-               LDFLAGS="-L$SYSROOT/lib"                                \
-               PKG_CONFIG_PATH="$SYSROOT/lib/pkgconfig"                \
-               --without-libcrypto                                     \
-               $extra_args "$@"
-       $MAKE clean
-fi
-$MAKE
-
-# Create empty destination directory
-
-rm -rf $DESTDIR
-mkdir $DESTDIR
-
-# Install binaries
-
-cp .libs/*.{dll,exe} $DESTDIR
-${ARCH}-w64-mingw32-strip $DESTDIR/*.{dll,exe}
-
-# Install text files
-
-cp NEWS README* COPYING* $DESTDIR
-cp $WINDEPDIR/COPYING* $DESTDIR
-
-sed -n '/^#/q; s/^[\/\* ]*//; p' src/divsufsort.c > $DESTDIR/COPYING.libdivsufsort-lite
-if ! grep -q 'Copyright' $DESTDIR/COPYING.libdivsufsort-lite; then
-       echo "ERROR: failed to extract libdivsufsort-lite license text" 1>&2
-       exit 1
-fi
-(
-       cd $DESTDIR
-       for fil in NEWS README* COPYING*; do
-               sed < $fil > ${fil}.txt -e 's/$/\r/g'
-               rm $fil
-       done
-)
-
-
-# Install man pages
-
-mkdir $DESTDIR/doc
-
-function gen_pdf_from_man_page() {
-       local manbase=$1
-       local pdf=${DESTDIR}/doc/${manbase}.pdf
-
-       echo "Generating $pdf"
-
-       MANPATH="./doc" man -t $manbase | ps2pdf - $pdf
-}
-
-for fil in ./doc/man1/wim*.1; do
-       manbase=`basename $fil`
-       cmd=${manbase%.1}
-       case $cmd in
-       wimlib-imagex|wimmount|wimmountrw|wimunmount)
-               continue
-               ;;
-       esac
-
-       gen_pdf_from_man_page $cmd
-
-       sed 's/$/\r/g' > ${DESTDIR}/${cmd}.cmd <<- EOF
-               @echo off
-               "%~dp0\\wimlib-imagex" ${cmd#wim} %*
-       EOF
-       chmod +x ${DESTDIR}/${cmd}.cmd
-done
-
-gen_pdf_from_man_page wimlib-imagex
-
-# Install development files
-
-mkdir $DESTDIR/devel
-cp .libs/libwim.dll.a $DESTDIR/devel/libwim.lib
-cp include/wimlib.h $DESTDIR/devel/
-
-# Generate ZIP file
-
-rm -f $ZIPFILE
-(
-       dir=$PWD
-       cd $DESTDIR
-       7z -mx9 a "$dir/$ZIPFILE" .
-)
diff --git a/tools/repack-windows-release.sh b/tools/repack-windows-release.sh
new file mode 100755 (executable)
index 0000000..f1101b4
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# This script takes in the path to a windows-CLANGARM64-bin artifact downloaded
+# from GitHub Actions, which lacks the PDF documentation and has the wrong file
+# layout, and packs it up into a releasable zip file.  Assumes that an x86_64
+# zip built from the same commit already exists locally.
+
+set -e -u
+
+usage()
+{
+       echo 1>&2 "Usage: $0 windows-CLANGARM64-bin.zip"
+       exit 1
+}
+
+[ $# -eq 1 ] || usage
+
+tmpdir=$(mktemp -d)
+trap 'rm -rf "$tmpdir"' EXIT
+
+ZIP_FROM_GITHUB=$1
+unzip -q -d "$tmpdir" "$ZIP_FROM_GITHUB"
+DESTDIR=$(basename "$(echo "$tmpdir"/*)")
+rm -rf "$DESTDIR" "$DESTDIR.zip"
+cp -a "$tmpdir/$DESTDIR" "$DESTDIR"
+prefix=$(echo "$DESTDIR" | grep -o 'wimlib.*windows')
+cp -a "${prefix}-x86_64-bin/doc" "$DESTDIR/doc"
+chmod +x "$DESTDIR"/*.{dll,exe,cmd}
+cd "$DESTDIR"
+7z -mx9 a ../"$DESTDIR.zip" . > /dev/null
+cd ..
+echo "Success!  Output is in $DESTDIR.zip"
diff --git a/tools/run-sparse b/tools/run-sparse
deleted file mode 100755 (executable)
index dafad10..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-for fil in src/*.c programs/imagex.c; do
-       sparse $fil -gcc-base-dir `gcc --print-file-name=`              \
-               -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H -D_GNU_SOURCE    \
-               -I. -Iinclude -I/usr/include/libxml2                    \
-               -Wbitwise -Wpointer-subtraction-blows
-done
diff --git a/tools/run-sparse.sh b/tools/run-sparse.sh
new file mode 100755 (executable)
index 0000000..6a7c915
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+for fil in src/*.c programs/imagex.c; do
+       sparse "$fil" -gcc-base-dir "$(gcc --print-file-name=)"         \
+               -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H -D_GNU_SOURCE    \
+               -I. -Iinclude -Wbitwise -Wpointer-subtraction-blows
+done
similarity index 96%
rename from tools/test-examples
rename to tools/test-examples.sh
index 8e4ac66fc7e21d8c3fb46356984d897ebc18c14b..f3a93f932ab9e4b2d63cac2ede02702f06f3cb7a 100755 (executable)
@@ -21,7 +21,7 @@ tmpdir="$(mktemp -d)"
 tmpfile="$(mktemp)"
 tmpfile2="$(mktemp)"
 
-trap "rm -rf \"$tmpdir\" \"$tmpfile\" \"$tmpfile2\"" EXIT
+trap 'rm -rf "$tmpdir" "$tmpfile" "$tmpfile2"' EXIT
 
 do_test() {
        rm -rf "$tmpdir"
similarity index 67%
rename from tools/update-version
rename to tools/update-version.sh
index 6a769a29e7b4193d6694aac5c7211f6809dae5b3..3e9b49bab6715a3ce4b5953367ef525b94cb253c 100755 (executable)
@@ -10,7 +10,7 @@ fi
 oldmonth=$(head -1 doc/man1/wimcapture.1 | cut -d' ' -f4 | tr -d '"')
 oldyear=$(head -1 doc/man1/wimcapture.1 | cut -d' ' -f5 | tr -d '"')
 oldver=$(grep 'the library interface of wimlib' include/wimlib.h \
-        | egrep -o '[0-9]+\.[0-9]+\.[0-9]+')
+        | grep -E -o '[0-9]+\.[0-9]+\.[0-9]+')
 
 newver=$1
 newmajor=$(echo "$newver" | cut -d'.' -f1)
@@ -21,26 +21,15 @@ newyear=$(date +%Y)
 
 newver="${newmajor}.${newminor}.${newpatch}"
 pat='This is wimlib version [^[:space:]]\+ ([^[:space:]]\+ [^[:space:]]\+)'
-sed -i "s/$pat/This is wimlib version $newver ($newmonth $newyear)/" README
+sed -i "s/$pat/This is wimlib version $newver ($newmonth $newyear)/" README.md
 
-sed -i "s/$oldver/$newver/" configure.ac
+sed -i "s/$oldver/$newver/" tools/get-version-number.sh README.WINDOWS.md
 
-sed -i -e 's/\(#define WIMLIB_MAJOR_VERSION[[:space:]]\+\)[[:digit:]]\+/\1'$newmajor'/' \
-       -e 's/\(#define WIMLIB_MINOR_VERSION[[:space:]]\+\)[[:digit:]]\+/\1'$newminor'/' \
-       -e 's/\(#define WIMLIB_PATCH_VERSION[[:space:]]\+\)[[:digit:]]\+/\1'$newpatch'/' \
-       -e 's/\(the library interface of wimlib \)'$oldver'/\1'$newver'/' \
+sed -i -e 's/\(#define WIMLIB_MAJOR_VERSION[[:space:]]\+\)[[:digit:]]\+/\1'"$newmajor"'/' \
+       -e 's/\(#define WIMLIB_MINOR_VERSION[[:space:]]\+\)[[:digit:]]\+/\1'"$newminor"'/' \
+       -e 's/\(#define WIMLIB_PATCH_VERSION[[:space:]]\+\)[[:digit:]]\+/\1'"$newpatch"'/' \
+       -e 's/\(the library interface of wimlib \)'"$oldver"'/\1'"$newver"'/' \
          include/wimlib.h
 
 sed -i -e "1s/$oldmonth $oldyear/$newmonth $newyear/;1s/wimlib $oldver/wimlib $newver/"        \
          doc/man[1-9]/*.[1-9]
-
-sed -i "1i\\
-wimlib ($newver-1) unstable; urgency=low\\
-\\
-  * Update to v$newver\\
-\\
- -- Eric Biggers <ebiggers3@gmail.com>  $(date -R)\\
-" debian/changelog
-
-sed -i 's/\(Version:[[:space:]]*\)[^[:space:]]\+/\1'"$newver"'/' \
-               rpm/*.spec
diff --git a/tools/windeps/Makefile b/tools/windeps/Makefile
deleted file mode 100644 (file)
index 5f25878..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# This Makefile builds the third-party libraries needed to build a standalone
-# libwim.dll for Windows.  We build these third-party libraries ourselves mainly
-# to cut down on bloat.  They are automatically downloaded from the URLs
-# declared below and verified against the checksums given in the 'sha256sums'
-# file.
-#
-# This Makefile requires a GNU toolchain with MinGW-w64 (i686 and x86_64
-# versions).
-#
-
-ARCHITECTURES          := i686 x86_64
-LIBXML2_VERSION                := 2.9.8
-WINPTHREADS_VERSION    := 5.0.3
-
-LIBXML_URL             := ftp://xmlsoft.org/libxml2/libxml2-$(LIBXML2_VERSION).tar.gz
-WINPTHREADS_URL                := http://downloads.sourceforge.net/mingw-w64/mingw-w64/mingw-w64-release/mingw-w64-v$(WINPTHREADS_VERSION).tar.bz2
-
-
-LIBXML_SRCDIR          := libxml2-$(LIBXML2_VERSION)
-LIBXML_DIST            := $(LIBXML_SRCDIR).tar.gz
-SRCDIR_TARGETS         += $(LIBXML_SRCDIR)
-DIST_TARGETS           += $(LIBXML_DIST)
-$(LIBXML_DIST):
-       wget $(LIBXML_URL)
-$(LIBXML_SRCDIR):$(LIBXML_DIST) checksums_verified
-       tar xvf $<
-       cp $@/COPYING COPYING.libxml2
-MAKE_CLEAN_FILES += $(LIBXML_SRCDIR) COPYING.libxml2
-
-WINPTHREADS_DIST       := mingw-w64-v$(WINPTHREADS_VERSION).tar.bz2
-WINPTHREADS_SRCDIR     := winpthreads-$(WINPTHREADS_VERSION)
-SRCDIR_TARGETS         += $(WINPTHREADS_SRCDIR)
-DIST_TARGETS           += $(WINPTHREADS_DIST)
-$(WINPTHREADS_DIST):
-       wget $(WINPTHREADS_URL)
-$(WINPTHREADS_SRCDIR):$(WINPTHREADS_DIST) checksums_verified
-       tar xvf $<
-       cp -aT mingw-w64-v$(WINPTHREADS_VERSION)/mingw-w64-libraries/winpthreads $@
-       cp $@/COPYING COPYING.winpthreads
-MAKE_CLEAN_FILES += $(WINPTHREADS_SRCDIR) mingw-w64-v$(WINPTHREADS_VERSION) COPYING.winpthreads
-
-checksums_verified:$(DIST_TARGETS)
-       sha256sum -c sha256sums
-
-#
-# declare_libxml_target(arch)
-#
-define declare_libxml_target
-libxml_$(1):$(LIBXML_SRCDIR)
-       builddir=build_libxml_$(1);                             \
-       rm -rf $$$$builddir;                                    \
-       mkdir $$$$builddir;                                     \
-       cd $$$$builddir;                                        \
-       ../$(LIBXML_SRCDIR)/configure                           \
-               --host=$(1)-w64-mingw32                         \
-               --enable-static                                 \
-               --disable-shared                                \
-               --prefix=$$$$PWD/../sysroot_$(1)                \
-               CFLAGS=-Os                                      \
-               --with-minimum                                  \
-               --without-lzma                                  \
-               --with-tree                                     \
-               --with-writer;                                  \
-       $(MAKE) install;                                        \
-       rm -f ../sysroot_$(1)/lib/libxml2.la;
-
-$(1)_BUILD_TARGETS += libxml_$(1)
-MAKE_CLEAN_FILES += build_libxml_$(1)
-endef
-
-#
-# declare_winpthreads_target(arch)
-#
-define declare_winpthreads_target
-winpthreads_$(1):$(WINPTHREADS_SRCDIR)
-       builddir=build_winpthreads_$(1);                        \
-       rm -rf $$$$builddir;                                    \
-       cp -r $(WINPTHREADS_SRCDIR) $$$$builddir;               \
-       cd $$$$builddir;                                        \
-       ./configure                                             \
-               --host=$(1)-w64-mingw32                         \
-               --enable-static                                 \
-               --disable-shared                                \
-               --prefix=$$$$PWD/../sysroot_$(1)                \
-               CFLAGS=-O2;                                     \
-       $(MAKE) install;                                        \
-       sed -i -e 's/if defined DLL_EXPORT/if 0/'               \
-              -e 's/pthread_getevent ()/pthread_getevent (void)/'\
-               ../sysroot_$(1)/include/pthread.h;
-
-$(1)_BUILD_TARGETS += winpthreads_$(1)
-MAKE_CLEAN_FILES += build_winpthreads_$(1)
-endef
-
-#
-# declare_arch_targets(arch)
-#
-define declare_arch_targets
-$(eval $(call declare_libxml_target,$(1)))
-$(eval $(call declare_winpthreads_target,$(1)))
-
-sysroot_$(1): $($(1)_BUILD_TARGETS)
-
-ALL_SYSROOTS += sysroot_$(1)
-MAKE_CLEAN_FILES += sysroot_$(1)
-endef
-
-$(foreach arch,$(ARCHITECTURES),$(eval $(call declare_arch_targets,$(arch))))
-
-all: $(ALL_SYSROOTS)
-
-clean:
-       rm -rf $(MAKE_CLEAN_FILES) $(DIST_TARGETS)
-
-.PHONY: all clean $(SRCDIR_TARGETS) checksums_verified
-
-.DEFAULT_GOAL = all
diff --git a/tools/windeps/sha256sums b/tools/windeps/sha256sums
deleted file mode 100644 (file)
index 7e8d1a4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-0b74e51595654f958148759cfef0993114ddccccbb6f31aee018f3558e8e2732  libxml2-2.9.8.tar.gz
-2a601db99ef579b9be69c775218ad956a24a09d7dabc9ff6c5bd60da9ccc9cb4  mingw-w64-v5.0.3.tar.bz2
diff --git a/tools/windows-build.sh b/tools/windows-build.sh
new file mode 100755 (executable)
index 0000000..63bd58b
--- /dev/null
@@ -0,0 +1,345 @@
+#!/bin/bash
+#
+# This script builds wimlib for Windows.  It supports both MSYS2 and Linux.
+
+set -e -u
+
+SCRIPTNAME="$0"
+TOPDIR=$(dirname "$(dirname "$(realpath "$0")")")
+cd "$TOPDIR" # Top-level directory of the git repo
+
+# Global variables, read-only after parse_options has run
+ARCH=
+CC_PKG=
+DESTDIR=
+EXTRA_CONFIGURE_ARGS=
+INCLUDE_DOCS=false
+INSTALL_PREREQUISITES=false
+MAKE="make -j$(getconf _NPROCESSORS_ONLN)"
+MSYSTEM=${MSYSTEM:-}
+SKIP_CONFIGURE=false
+VERSION=$(tools/get-version-number.sh)
+ZIP=false
+ZIPFILE=
+
+PREBUILT_LLVM_MINGW_ENABLED=false
+PREBUILT_LLVM_MINGW_URL=https://github.com/mstorsjo/llvm-mingw/releases/download/20230320/llvm-mingw-20230320-msvcrt-x86_64.zip
+PREBUILT_LLVM_MINGW_ZIP=$(basename "$PREBUILT_LLVM_MINGW_URL")
+PREBUILT_LLVM_MINGW=${PREBUILT_LLVM_MINGW_ZIP%.zip}
+PREBUILT_LLVM_MINGW_BIN="/$PREBUILT_LLVM_MINGW/bin"
+
+usage()
+{
+       cat << EOF
+Usage: $SCRIPTNAME [OPTION]... [EXTRA_CONFIGURE_ARG]...
+Options:
+  --arch=ARCH               Specify the CPU architecture.  This is unnecessary
+                            when using MSYS2.
+
+  --include-docs            Build and install the PDF manual pages.
+
+  --install-prerequisites   Install the prerequisite packages needed to build
+                            wimlib.  This is only supported in MSYS2.  You can
+                            omit this if you have already done this for the same
+                            MSYS2 environment.  This option normally only
+                            installs MSYS2 packages, but for ARM64 cross-builds
+                            it also installs a separate prebuilt toolchain.
+
+  --skip-configure          Skip running the configure script again.  You can
+                            use this to save time in incremental builds if you
+                            are sure you didn't change any options.
+
+  --zip                     Zip the output files up into a zip file.
+EOF
+}
+
+parse_options()
+{
+       case "$MSYSTEM" in
+       "")
+               ARCH=x86_64
+               ;;
+       MINGW32)
+               ARCH=i686
+               CC_PKG=mingw-w64-i686-gcc
+               ;;
+       MINGW64)
+               ARCH=x86_64
+               CC_PKG=mingw-w64-x86_64-gcc
+               ;;
+       CLANG32)
+               ARCH=i686
+               CC_PKG=mingw-w64-clang-i686-clang
+               ;;
+       CLANG64)
+               ARCH=x86_64
+               CC_PKG=mingw-w64-clang-x86_64-clang
+               ;;
+       CLANGARM64)
+               ARCH=aarch64
+               # MSYS2 doesn't yet support cross-compiling for ARM64, so use a
+               # separate prebuilt toolchain for that case.
+               if [ "$(uname -m)" = x86_64 ]; then
+                       PREBUILT_LLVM_MINGW_ENABLED=true
+                       export PATH="$PREBUILT_LLVM_MINGW_BIN:$PATH"
+               else
+                       CC_PKG=mingw-w64-clang-aarch64-clang
+               fi
+               ;;
+       *)
+               echo 1>&2 "Unsupported MSYS2 environment: $MSYSTEM.  This script supports"
+               echo 1>&2 "MINGW32, MINGW64, CLANG32, CLANG64, and CLANGARM64."
+               echo 1>&2 "See https://www.msys2.org/docs/environments/"
+               exit 1
+       esac
+
+       local longopts="help"
+       longopts+=",arch:"
+       longopts+=",include-docs"
+       longopts+=",install-prerequisites"
+       longopts+=",skip-configure"
+       longopts+=",zip"
+
+       local options
+       if ! options=$(getopt -o "" -l "$longopts" -- "$@"); then
+               usage 1>&2
+               exit 1
+       fi
+       eval set -- "$options"
+       while true; do
+               case "$1" in
+               --help)
+                       usage
+                       exit 0
+                       ;;
+               --arch)
+                       ARCH=$2
+                       shift
+                       ;;
+               --include-docs)
+                       INCLUDE_DOCS=true
+                       ;;
+               --install-prerequisites)
+                       if [ -z "$MSYSTEM" ]; then
+                               echo 1>&2 "--install-prerequisites is only supported in MSYS2."
+                               exit 1
+                       fi
+                       INSTALL_PREREQUISITES=true
+                       ;;
+               --skip-configure)
+                       SKIP_CONFIGURE=true
+                       ;;
+               --zip)
+                       ZIP=true
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               *)
+                       echo 1>&2 "Invalid option '$1'"
+                       usage 1>&2
+                       exit 1
+                       ;;
+               esac
+               shift
+       done
+       case "$ARCH" in
+       i686|x86_64|aarch64)
+               ;;
+       *)
+               echo 1>&2 "Unknown ARCH: $ARCH.  Please specify a supported architecture with --arch"
+               exit 1
+               ;;
+       esac
+       DESTDIR=wimlib-${VERSION}-windows-${ARCH}-bin
+       ZIPFILE=$DESTDIR.zip
+       EXTRA_CONFIGURE_ARGS=("$@")
+}
+
+install_prebuilt_llvm_mingw()
+{
+       if [ -e "$PREBUILT_LLVM_MINGW_BIN" ]; then
+               echo "Prebuilt $PREBUILT_LLVM_MINGW is already installed"
+               return
+       fi
+       echo "Downloading $PREBUILT_LLVM_MINGW_ZIP..."
+       wget "$PREBUILT_LLVM_MINGW_URL" -O "/$PREBUILT_LLVM_MINGW_ZIP"
+       echo "Unzipping $PREBUILT_LLVM_MINGW_ZIP..."
+       unzip "/$PREBUILT_LLVM_MINGW_ZIP" -d /
+       if [ ! -e "$PREBUILT_LLVM_MINGW_BIN" ]; then
+               echo 1>&2 "$PREBUILT_LLVM_MINGW_BIN not found after unzip"
+               exit 1
+       fi
+       echo "Done installing prebuilt toolchain $PREBUILT_LLVM_MINGW"
+}
+
+install_prerequisites()
+{
+       echo "Installing the MSYS2 $MSYSTEM packages needed to build wimlib..."
+       local packages=(autoconf automake git libtool make pkgconf)
+       if "$PREBUILT_LLVM_MINGW_ENABLED"; then
+               echo "Will use prebuilt toolchain instead of MSYS2 one"
+               packages+=(wget unzip)
+       else
+               packages+=("$CC_PKG")
+       fi
+       pacman -Syu --noconfirm --needed "${packages[@]}"
+       echo "Done installing the MSYS2 $MSYSTEM packages needed to build wimlib."
+
+       if $PREBUILT_LLVM_MINGW_ENABLED; then
+               install_prebuilt_llvm_mingw
+       fi
+}
+
+bootstrap_repository()
+{
+       echo "Bootstrapping the wimlib repository..."
+       ./bootstrap
+}
+
+configure_wimlib()
+{
+       echo "Configuring wimlib..."
+       local configure_args=("--host=${ARCH}-w64-mingw32")
+       configure_args+=("--disable-static")
+       # -static-libgcc is needed with gcc.  It should go in the CFLAGS, but
+       # libtool strips it, so it must go directly in CC instead.  See
+       # https://www.gnu.org/software/libtool/manual/libtool.html#Stripped-link-flags
+       local cc="${ARCH}-w64-mingw32-cc"
+       if ! type -P "$cc" &>/dev/null; then
+               cc="${ARCH}-w64-mingw32-gcc"
+       fi
+       if ! type -P "$cc" &>/dev/null; then
+               echo 1>&2 "ERROR: $cc not found!"
+               if [ -n "$MSYSTEM" ]; then
+                       echo 1>&2 "Consider using --install-prerequisites"
+               fi
+               exit 1
+       fi
+       if ! "$cc" --version | grep -q -i 'clang'; then
+               configure_args+=("CC=$cc -static-libgcc")
+       fi
+       configure_args+=("${EXTRA_CONFIGURE_ARGS[@]}")
+       ./configure "${configure_args[@]}"
+       $MAKE clean
+}
+
+build_wimlib()
+{
+       echo "Building wimlib..."
+       $MAKE
+}
+
+list_imagex_commands()
+{
+       for cmd in ./doc/man1/wim*.1; do
+               local cmd=${cmd##*/}
+               cmd=${cmd%.1}
+               case "$cmd" in
+               wimlib-imagex|wimmount|wimmountrw|wimunmount)
+                       ;;
+               *)
+                       echo "$cmd"
+                       ;;
+               esac
+       done
+}
+
+install_binaries()
+{
+       echo "Installing binaries..."
+       cp .libs/*.{dll,exe} "$DESTDIR"
+       strip "$DESTDIR"/*.{dll,exe}
+}
+
+install_text_files()
+{
+       echo "Installing NEWS, README, and licenses..."
+       cp NEWS* README* COPYING* "$DESTDIR"
+       sed -n '/^#/q; s/^[\/\* ]*//; p' src/divsufsort.c > "$DESTDIR"/COPYING.libdivsufsort-lite
+       if ! grep -q 'Copyright' "$DESTDIR"/COPYING.libdivsufsort-lite; then
+               echo 1>&2 "ERROR: failed to extract libdivsufsort-lite license text"
+               exit 1
+       fi
+       cd "$DESTDIR"
+       for fil in NEWS* README* COPYING*; do
+               sed < "$fil" > "${fil%.md}".txt -e 's/$/\r/g'
+               rm "$fil"
+       done
+       cd ..
+}
+
+gen_pdf_from_man_page()
+{
+       local cmd=$1
+       local pdf=${DESTDIR}/doc/${cmd}.pdf
+
+       echo "Generating $pdf"
+       MANPATH="./doc" man -t "$cmd" | ps2pdf - "$pdf"
+}
+
+install_pdf_docs()
+{
+       echo "Installing PDF manual pages..."
+       mkdir "$DESTDIR"/doc
+       for cmd in $(list_imagex_commands); do
+               gen_pdf_from_man_page "$cmd"
+       done
+       gen_pdf_from_man_page wimlib-imagex
+}
+
+install_cmd_aliases()
+{
+       echo "Installing wim*.cmd files..."
+       for cmd in $(list_imagex_commands); do
+               sed 's/$/\r/g' > "${DESTDIR}/${cmd}.cmd" <<- EOF
+                       @echo off
+                       "%~dp0\\wimlib-imagex" ${cmd#wim} %*
+               EOF
+               chmod +x "${DESTDIR}/${cmd}.cmd"
+       done
+}
+
+install_development_files()
+{
+       echo "Installing development files..."
+       mkdir "$DESTDIR"/devel
+       cp .libs/libwim.dll.a "$DESTDIR"/devel/libwim.lib
+       cp include/wimlib.h "$DESTDIR"/devel/
+}
+
+create_zip_file()
+{
+       echo "Creating zip file..."
+       cd "$DESTDIR"
+       7z -mx9 a ../"$ZIPFILE" . > /dev/null
+       cd ..
+}
+
+parse_options "$@"
+rm -rf -- "$DESTDIR" "$ZIPFILE"
+mkdir -- "$DESTDIR"
+if $INSTALL_PREREQUISITES; then
+       install_prerequisites
+fi
+if [ ! -e configure ]; then
+       bootstrap_repository
+fi
+if [ ! -e config.log ] || ! $SKIP_CONFIGURE; then
+       configure_wimlib
+fi
+build_wimlib
+install_binaries
+install_text_files
+if $INCLUDE_DOCS; then
+       install_pdf_docs
+fi
+install_cmd_aliases
+install_development_files
+if $ZIP; then
+       create_zip_file
+       echo "Success!  Output is in $ZIPFILE"
+else
+       echo "Success!  Output is in $DESTDIR"
+fi