pax_global_header00006660000000000000000000000064141062751630014517gustar00rootroot0000000000000052 comment=11dfe80e867f199bbf40a3bdcffa77b4aa6429f6 squashfs-tools-ng-1.1.3/000077500000000000000000000000001410627516300151165ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/.github/000077500000000000000000000000001410627516300164565ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/.github/workflows/000077500000000000000000000000001410627516300205135ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/.github/workflows/codeql-analysis.yml000066400000000000000000000011361410627516300243270ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [master, ] pull_request: branches: [master] schedule: - cron: '0 17 * * 5' jobs: analyse: name: Analyse runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 with: fetch-depth: 2 - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} - name: Initialize CodeQL uses: github/codeql-action/init@v1 - name: Autobuild uses: github/codeql-action/autobuild@v1 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 squashfs-tools-ng-1.1.3/.gitignore000066400000000000000000000007471410627516300171160ustar00rootroot00000000000000.deps .dirstamp .gdb_history .libs Makefile Makefile.in aclocal.m4 autom4te.cache compile config.h.in config.log config.status configure depcomp install-sh missing stamp-h1 config.* *.o *.lo *.la *.a *~ *.pc .#* *.exe *.dll *.log *.trs /gensquashfs /rdsquashfs /sqfs2tar /tar2sqfs /sqfsdiff /tar_fuzz /fstree_fuzz test_* test-* libtool ltmain.sh m4/libtool.m4 m4/lt*.m4 Doxyfile tests/*.sh tests/rdsquashfs/*.sh /mknastyfs /mk42sqfs /extract_one /list_files /sqfsbrowse /xattr_benchmark squashfs-tools-ng-1.1.3/.travis.yml000066400000000000000000000043251410627516300172330ustar00rootroot00000000000000language: c addons: apt: packages: - libzstd-dev liblzo2-dev liblz4-dev lzma-dev zlib1g-dev libreadline-dev libbz2-dev homebrew: packages: - zstd lzo lz4 xz zlib bzip2 readline script: - ./autogen.sh - ./configure $CONFIG_OPTS - make - make check || (cat ./test-suite.log; false) matrix: include: # gcc based builds for amd64, arm64, ppc64 - name: ubuntu-gcc-amd64 os: linux arch: amd64 dist: bionic compiler: gcc env: - CONFIG_OPTS="--with-pthread" - name: ubuntu-gcc-arm64 os: linux arch: arm64 dist: bionic compiler: gcc env: - CONFIG_OPTS="--with-pthread" - name: ubuntu-gcc-ppc64le os: linux arch: ppc64le dist: bionic compiler: gcc env: - CONFIG_OPTS="--with-pthread" # clang based builds for amd64, arm64, ppc64 - name: ubuntu-clang-amd64 os: linux arch: amd64 dist: bionic compiler: clang env: - CONFIG_OPTS="--with-pthread" - name: ubuntu-clang-arm64 os: linux arch: arm64 dist: bionic compiler: clang env: - CONFIG_OPTS="--with-pthread" - name: ubuntu-clang-ppc64le os: linux arch: ppc64le dist: bionic compiler: clang env: - CONFIG_OPTS="--with-pthread" # clang based build for macOS - name: macOS os: osx env: - CONFIG_OPTS="--with-pthread" # special configurations # -> build on Xenial to test liblz4 fallback # -> try if building the serial block processor works - name: ubuntu-gcc-amd64-nopthread os: linux arch: amd64 dist: xenial compiler: gcc env: - CONFIG_OPTS="--without-pthread" # Try on an uncommon, big endian system - name: ubuntu-gcc-s390x os: linux arch: s390x dist: bionic compiler: gcc env: - CONFIG_OPTS="--with-pthread" # FreeBSD - name: FreeBSD os: freebsd compiler: clang addons: pkg: packages: - zstd - lzo2 - liblz4 - bzip2 - readline env: - CONFIG_OPTS="--with-pthread" squashfs-tools-ng-1.1.3/CHANGELOG.md000066400000000000000000000515761410627516300167450ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.1.3] - 2021-08-15 ### Added - Overhaul format documentation, convert to ASCIIdoc (#88, #90) - Add an explicit license boiler plate to all the example programs (#89) - Additional example code & documentation ### Fixed - Typos and formating issues in the documentation (#86) - Symlink based path traversal in rdsqaushfs when unpacking to a directory - libsquashfs directory writer size accounting (#85) - tar2sqfs, sqfs2tar: 4 GiB limitation for files in tarballs (#87) ## [1.1.2] - 2021-06-25 ### Added - Test cases for concatenated stream decompression - A more "real-world" test suite for `tar2sqfs` pre-release testing ### Fixed - Replace tabs with spaces in format.txt - Some documentation clarifications and typo fixes - libsquashfs: preserve alignment flag in block processor - libsquashfs: broken block alignment in block write - allow concatenated Bzip2 streams - Use Automake conditional for zstd stream compression support - Use *_MAX from limits.h instead of configure-time type size checks - Additional compiler warnings were turned on and addressed - libfstream: Add printf format specifier attribute - libfstream: guard against potential integer overflows - libfstree: guard against link count and inode number overflow - libfstree: guard against possible overflow in readlink() - libcommon: potentially un-aligned data access in LZO compressor - libsquashfs: potentially unaligned data access in meta data handling - Some format string type/signedness mismatch issues ## [1.1.1] - 2021-05-07 ### Fixed - tar2sqfs: currectly process concatenated xz streams from parallel compression - sqfs2tar: don't report an error if fsync() returns EINVAL - libsquashfs: static linking on Windows - libsquashfs: visibillity of internal mempool functions - libsquashfs: add sqfs_free() function, mainly for Windows portabillity - libsquashfs: block processor: Fix account for manually submitted blocks - Fix build failure if configured with --without-tools ## [1.1.0] - 2021-03-28 ### Added - tar2sqfs: support transparently reading stream compressed archives - sqfs2tar: support creating stream compressed archives - gensquashfs: support file globbing/filtering in the description file - Support bzip2 compression for tar - Implement directory scanning for Windows - a `glob` keyword to `gensquashfs` for `find` like globbing from a directory. ### Changed - Rewrite file I/O in the tools around an I/O stream wrapper which is used to implement the transparent compression. - Internal cleanups & restructuring, trying to improve maintainabillity and testabillity of the code. - Updated benchmarks, including benchmarks for decompression. - Drastic performance improvements of the xattr writer (#68). - Performance improvements in the thread pool block processor. - Bump zstd & xz versions for Windows binary releases. ### Fixed - libsquashfs: Allow shared read access to generated images on Windows (#79) ## [1.0.6] - 2021-08-15 ### Added - Overhaul format documentation, convert to ASCIIdoc (#88, #90) - Add an explicit license boiler plate to all the example programs (#89) - Additional example code & documentation ### Fixed - Typos and formating issues in the documentation (#86) - Fix symlink path traversal in rdsqaushfs - Add a test case for the path traversal bug - Fix libsquashfs directory writer size accounting - tar2sqfs, sqfs2tar: 4 GiB limitation for files in tarballs (#87) ## [1.0.5] - 2021-06-25 ### Fixed - libsquashfs: Allow shared read access to generated images on Windows (#79) - libsquashfs: preserve alignment flag in block processor - libsquashfs: broken block alignment in block write - libsquashfs: add sqfs_free() function, mainly for Windows portabillity - libsquashfs: static linking on Windows - Some documentation clarifications and typo fixes - libfstree: guard against link count and inode number overflow - libcommon: potentially un-aligned data access in LZO compressor - libsquashfs: potentially unaligned data access in meta data handling - More compiler warning flags turned on and partially fixed ## [1.0.4] - 2021-01-23 ### Fixed - typos in documentation - libsquashfs: xattr_writer: fail if allocation fails - remove broken normalization of backslashes (#77, #78) - congestion in block processor when processing lots of tiny fragments - exact byte-for-byte matching during block deduplication - exact byte-for-byte matching during fragment deduplication ### Added - new, extensible block processor create function required for deduplication fixes ## [1.0.3] - 2020-11-05 ### Fixed - tar2sqfs: if --root-becomes is used, also retarget links (#70) - tar2sqfs: when skipping non-prefixed files, also skip contents (#70) - tar2sqfs: null-pointer dereference if a file with a broken path is skipped - rdsquashfs: correctly escape of the input file paths if prefixed ### Added - Packing scripts for a various GNU/Linux distributions (#65, #67) ## [1.0.2] - 2020-09-03 ### Fixed - Fix block processor single block with don't fragment flag bug (#63) - Fix libtar treatment of link targets that fill the header field (#64) - Fix tree node path generation for detached sub trees (#66) - Fix rdsquashfs unpack under Windows if a directory exists - Clarify copyright status of documentation & build system ### Added - Support for GNU tar sparse format 1.0. - A spec file to build RPM packages (#65). - A `--stat` option to rdsquashfs ## [1.0.1] - 2020-08-03 ### Fixed - Xattr reader: re-read the header after seaking to an OOL value (#60) - Documentation: mention the file name limit imposed by the kernel - Documentation: fix wrong magic value and stray tabs in format.txt (#61) - Fix block bounds checking in libsquashfs data reader (#58) - Fix build issue caused by demo code that didn't have (#57, #59) ## [1.0.0] - 2020-06-13 ### Added - Expose more fine grained control values & flags on the XZ and LZMA compressors - A flag for the `libsquashfs` block processor to micro manage block hashing and sparse block detection. - A raw block submission function. - A user pointer that can be forwarded to the block writer. ### Changed - `sqfsdiff` doesn't abort if it fails to read the compressor options - in `libsquashfs`, turn the block writer into an interface with a default implementation and remove the statistics and hooks. - Block processor can function without fragment table and without inode pointer - Strictly enfore min/max dictionary size in XZ & LZMA compressors - Make compression level a generic compression option in `libsquashfs` - More `libsquashfs` API and internal cleanups - Bump the major version number of libsquashfs ### Fixed - Propperly set the last block flag if fragments are disabled - Compilation on GCC4 and below - libtar: size computation of PAX line length (#50) - Semantics of the super block deduplication - Actually set the ZSTD compression level to something greater than 0 - Only add Selinux compile flags if WITH_SELINUX is set. Fixes Mingw cross build on Fedora. - Make `rdsquashfs` describe mode terminate with an error message if an illegal filename is encountered (#52). - Don't include alloca.h on systems that don't provide that header. ## [0.9.1] - 2020-05-03 ### Added - Options to `gensquashfs` for overriding the ownership if packing a directory. ### Changed - Various internal code and build system related cleanups. - Various performance improvements for `gensquashfs` and `tar2sqfs`. - Make `tar2sqfs` compeltely ignore PAX global headers, even if `--no-skip` is set (#45). - Make `gensquashfs` fail if extra arguments are passed, similar to `tar2sqfs`. ### Fixed - Missing assert header inclusion if built with `--without-lzo` (#41) - sqfs2tar: also emit trailing slashes for empty directories (#42) - Illegal implicit cast in public `libsquashfs` headers if used from C++. - If tar2sqfs or gensquashfs fails, delete the output file. - Make sure test cases also work if built with NDEBUG defined. - Add missing `--with-gzip` configure option. - In `tar2sqfs`, actually apply the `--no-tail-packing` if set. - Potential nondeterministic order of extended attributes in `gensquashfs` if packing a directory. - Manpages lagging behind. ## [0.9.0] - 2020-03-30 ### Added - Support parsing [device] block size argument with SI suffix. - Add a write-up on the on-disk format. - A couple demo programs that make use of `libsquashfs`. - Add statistics counters to the block writer and processor. - A compressor interface function to retrieve its configuration. ### Changed - For better compatibility, sqfs2tar appends `/` to directory names. (#37) - Extra data in sqfs_inode_generic_t no longer handled via payload pointers. - Add a currently unsued flag field to the id table create function. - Split off fragment table handling from data reader and writer into dedicated fragment table data type. - Split data writer up into a block writer and a block processor. - All abstract data types inhert from an sqfs_object_t with common functionality. - Lots of performance improvements. - Make block processor sync and flush seperate operations. - Combined the various payload size values in generic inode structure. - Change block processor API to manage creation/resizing of file inodes. - A "do not deduplicate flag" for the block processing pipeline. - Lots of API cleanups. ### Fixed - Include sys/sysmacros.h on any GNU libc platform. - Directory index accounting. - Memory leak in hard link detection code. - Broken iteration over directory children in sqfsdiff. - Data reader returning -1 instead of an error code. - Size accounting for sparse files in tar parsing code. - Stricter verification of the compressor configuration. - Broken builds with older liblz4 and zstd versions (e.g. on Ubuntu Xenial). - Various build issues on MacOS. ### Removed - A number of hook callbacks from the block writer. - sqfs_block_t from the public API. ## [0.8.0] - 2019-12-30 ### Added - Experimental Windows port using MinGW cross compilation toolchain. - Port to BSD systems. - Explicit argument invalid error code in `libsquashfs`. - A `--root-becomes` option to `tar2sqfs` and `sqfs2tar`. - A `--no-tail-packing` option to `tar2sqfs` and `sqfs2tar`. - CHANGELOG.md now references GitHub issue numbers. - Simple integration and regression test suit. - Simple creation of an NFS export table throught `libsquashfs`. - Support for non-directory hard links in `gensquashfs`, `tar2sqfs` and `sqfs2tar`. ### Changed - Return propper error code from `sqfs_get_xattr_prefix_id`. - Return propper error code from `sqfs_compressor_id_from_name`. - Combined error and value return from `sqfs_compressor_id_from_name`. - Return error code from compressor functions if input is larger than 2G. - Lots of cleanups, including the build system. ### Removed - Entirely redundant `sqfs_has_xattr` function from `libsquashfs`. ### Fixed - LZO compressor moved out of libsquashfs to avoid licensing problems. - Make overriding configure variables for LZO library actually work. - Do not follow symlinks when reading xattrs from input files. - Ignore directory entry named `./` in tar2sqfs. (#22, #31) - Reject empty string as directory name in libsquashfs. (#22, #31) - Fix memory leak in tar2sqfs if entries are skipped. - Fix tar_fuzz error check after seek. - Fix the `fstree_init` test to account for defaults from `SOURCE_DATE_EPOCH`. - Honor the no_xattr flag when generating SquashFS images. - Block size check in `sqfs_super_init`. (#29) - Fix pthread block processor interfering with application signal handling. - Added pthread flags to the programs using libsquashfs. - Fix "buffer too small" being treated as fatal error by the zstd compressor. - Fix out of bounds write in the LZO compressor. - Fix queue accounting in the compressor thread pool. (#29) - Fix name of `libsquashfs` pkg-config file. - Reading of binary (i.e. non-textual) xattr values from tar files. (#32) - A bug in parsing the GNU.sparse.name PAX attribute from tar files. - sqfsdiff: recurse into directories that are only in one image. ## [0.7.0] - 2019-10-08 ### Added - LGPLv3 licensed, shared library `libsquashfs.so` containing all the SquashFS related logic. - Sanitized, public headers and pkg-config file for libsquashfs. - Doxygen reference manual for libsquashfs. - Legacy LZMA compression support. (#17) - User configurable queue backlog for tar2sqfs and gensquashfs. ### Changed - Make sqfsdiff continue comparing even if the types are different, but compatible (e.g. extended directory vs basic directory). - Try to determine the number of available CPU cores and use the maximum by default. - Start numbering inodes at 1, instead of 2. - Only store permission bits in inodes, the reader reconstructs them from the inode type. - Make "--keep-time" the default for tar2sqfs and use flag to disable it. ### Fixed - An off-by-one error in the directory packing code. (#18) - Typo in configure fallback path searching for LZO library. - Typo that caused LZMA2 VLI filters to not be used at all. - Possible out-of-bounds access in LZO compressor constructor. - Inverted logic in sqfs2tar extended attributes processing. ### Removed - Comparisong with directory from sqfsdiff. ## [0.6.1] - 2019-08-27 ### Added - Add a change log - Add test programs for fuzzing tar and gensquashfs file format parsers ### Fixed - Harden against integer overflows when parsing squashfs images (#13, #14) - Test against format limits when parsing directory entries (#12) - More thorough bounds checking when reading metadata blocks (#13, #14, #15) ## [0.6.0] - 2019-08-22 ### Added - New utility `sqfsdiff` that can compare squashfs images - rdsquashfs can now dump extended attributes for an inode (#2) - rdsquashfs can now optionally set xattrs on unpacked files (#2) - rdsquashfs can now optionally restore timestamps on unpacked files (#2) - sqfs2tar can now optionally copy xattrs over to the resulting tarball (#2) - gensquashfs can now optionally read xattrs from input files (#2) - gensquashfs now has a --one-file-system option - tar2sqfs and gensquashfs now output some simple statistics - Full fragment and data block deduplication - Support for SOURCE_DATE_EPOCH environment variable - Optimized, faster file unpacking order - Faster, pthread based, parallel block compressor ### Fixed - Return the correct value from data_reader_create - Fix free() of stack pointer in id_table_read error path - Fix missing initialization of file fragment fields - Fix xattr OOL position - Fix super block flags: clear "no xattr" flag when writing xattrs - Fix xattr writer size accounting - Fix explicit NULL dereference in deserialize_fstree failure path - Fix tar header error reporting on 32 bit systems - Make sure file listing generated by rdsquashfs -d is properly escaped - Fix functions with side effect being used inside asserts - Fix zero padding of extracted data blocks - Fix forward seek when unpacking sparse files - Fix wrong argument type for gensquashfs --keep-time - Fix memory leak in dir-scan error code path - Fix chmod of symlinks in restore_fstree - Add proper copyright headers to all source files ### Changed - Various internal data structure and code cleanups - Overhaul README and convert it to markdown ## [0.5.0] - 2019-07-28 ### Added - Support for NFS export - Support for xattr value deduplication - Flag in packers to optionally keep the original time stamps - Largefile support - Implement simple, fork() based parallel unpacking in rdsquashfs ### Fixed - Remove unfriendly words about squashfs-tools from README (#10) - Propper error message for ZSTD compressor - Correct copy-and-paste mistake in the build system - Make sure xattr string table is propperly initialized - More lenient tar checksum verification - Fix xattr unit test - Fix possible leak in tar2sqfs if writing xattrs fails - Fix corner cases in directory list parsing - Fix processing of tar mtime on 32 bit systems (#8) - libfstree: fix signed/unsigned comparisons - Fix fragment reader out of bounds read when loading table - Fix checks of super block block size - Fix potential resource leak in deserialize_tree - Enforce reasonable upper and low bounds on the size of tar headers - Make sure target in fstree_mknode is always set when creating a symlink - Use safer string copy function to fill tar header ## [0.4.2] - 2019-07-17 ### Fixed - Sanity check id table size read from super block - Various bug fixes through Coverity scan - Fix dirindex writing for ext dir inode - fstree: mknode: initialize fragment data, add extra blocksize slot - Fix directory index creation - Support for reading files without fragments - Support for spaces in filenames - Eleminate use of temporary file ## [0.4.1] - 2019-07-07 ### Fixed - read_inode: determine mode bits from inode type - Actually encode/decode directory inode difference as signed - Fix regression in fstree_from_file device node format - Always initialize gensquashfs defaults option ## [0.4.0] - 2019-07-04 ### Added - no-skip option in tar2sqfs and sqfs2tar - Option for sqfs2tar to extract only some subdirectories - Support for xattr extensions in tar parser - Support repacking xattrs from tarball into squashfs filesystem ### Fixed - Null-pointer dereference in restore_unpack - Memory leak in gzip compressor - Stack pointer free() in fstree_from_dir - Use of uninitialized xattr structure - Initialize return status in fstree_relabel_selinux - Make pax header parser bail if parsing a number fails - Double free in GNU tar sparse file parser - Never used overflow error message in fstree_from_file - Unused variable assignment in tar header writer - Make sure fragment and id tables are initialized - Directory index offset calculation - Missing htole32 transformations - Don't blindly strcpy() the tar header name - Typos in README - Composition order of prefix + name for ustar - Actually check return value when writing xattrs - Possible out of bounds read in libcompress.a - Check block_log range before deriving block size from it - tar2sqfs: check for invalid file names first ### Changed - Tar writer: Use more widely supported GNU tar extensions instead of PAX - Simplify deduction logic for squashfs inode type ## [0.3.0] - 2019-06-30 ### Added - Add utility to turn a squashfs image into a POSIX tar archvie - Add utility to turn a POSIX/PAX tar archive into a squashfs image - Add unit tests - Add support for packing sparse files - Add support for unpacking sparse files as sparse files ### Fixed - Actually update permissions in fstree add by path - Always set permissions on symlinks to 0777 - gensquashfs: Fix typo in help text - Fix inode fragment & sparse counter initialization - Ommit fragment table if there really are no fragments ### Changed - Lots of internal cleanups and restructuring ## [0.2.0] - 2019-06-13 ### Fixed - Make empty directories with xattrs work - Flush the last, unfinished fragment block ### Changed - Add pushd/popd utility functions and replace directory traversal code - Lots of internal cleanups - Use abstractions for many operations and move them to support libraries ## [0.1.0] - 2019-06-08 ### Added - Salvage protoype from Pygos project and turn it into generic squashfs packer - Add unpacker ### Changed - Insert abstraction layers and split generic code off into support libraries [1.1.3]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.1.2...v1.1.3 [1.1.2]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.1.1...v1.1.2 [1.1.1]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.2...v1.1.0 [1.0.6]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.5...v1.0.6 [1.0.5]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.4...v1.0.5 [1.0.4]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.3...v1.0.4 [1.0.3]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.2...v1.0.3 [1.0.2]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.9.1...v1.0.0 [0.9.1]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.9...v0.9.1 [0.9.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.8...v0.9 [0.8.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.7...v0.8 [0.7.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.6.1...v0.7 [0.6.1]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.6...v0.6.1 [0.6.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.5...v0.6 [0.5.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.4...v0.5 [0.4.2]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.4.1...v0.4.2 [0.4.1]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.4...v0.4.1 [0.4.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.3...v0.4 [0.3.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.2...v0.3 [0.2.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v0.1...v0.2 [0.1.0]: https://github.com/AgentD/squashfs-tools-ng/releases/tag/v0.1 squashfs-tools-ng-1.1.3/COPYING.md000066400000000000000000000122721410627516300165540ustar00rootroot00000000000000# License of squashfs-tools-ng The `libsquashfs` library is released under the terms and conditions of the **GNU Lesser General Public License version 3 or later**. This applies to all source code in the directories `lib/sqfs`, `lib/util` and `include/sqfs` with the following exceptions: - `lib/util/xxhash.c` contains a modified implementation of the xxhash32 algorithm. See `licenses/xxhash.txt` for copyright and licensing information (2 clause BSD license). - `lib/lz4` contains files extracted from the LZ4 compression library. See `lib/lz4/README` for details and `licenses/LZ4.txt` for copyright and licensing information (2 clause BSD license). - `lib/zlib` contains files that have been extracted from the the zlib compression library and modified. See `lib/zlib/README` for details and `licenses/zlib.txt` for details. - `lib/util/hash_table.c`, `include/hash_table.h` and `lib/util/fast_urem_by_const.h` contain a hash table implementation (MIT license). See `licenses/hash_table.txt` for details. The rest of squashfs-tools-ng is released under the terms and conditions of the **GNU General Public License version 3 or later**, with the following exceptions: - `lib/compat/fnmatch.c` has been copied from Musl libc. - `lib/compat/getopt.c` has been copied from Musl libc. - `lib/compat/getopt_long.c` has been copied from Musl libc. - `lib/compat/getsubopt.c` has been copied from Musl libc. The components copied from Musl libc are subejct to an MIT style license. See `liceneses/musl.txt` for details and only compiled into executable programs if the target system does not provide an implementation. Copies of the LGPLv3 and GPLv3 are included in `licenses/LGPLv3.txt` and `licenses/GPLv3.txt` respectively. The original source code of squashfs-tools-ng has been written by David Oberhollenzer in 2019 and onward. Additional contributions have been added since the initial release which makes some parts of the package subject to the copyright of the respective authors. Appropriate copyright notices and SPDX identifiers are included in the source code files. Although the existing squashfs-tools and the Linux kernel implementation have been used for testing, the source code in this package is neither based on, nor derived from either of them. ## Documentation, examples and the Build System The auto-tools based build system has in large parts been hacked together by copy & pasting from various tutorials and other projects (mostly util-linux, and mtd-utils), overhauled many times since 2015. The m4 macros in the `m4` directory were copied verbatim and have explicit licenses. Please respect those. As for everything else, feel free to copy and paste it as you wish. The `doc` directory contains measurement data, pseudo lab reports and an RFC style write-up of the SquashFS format. You may do with those as you please. If you use those as a basis for writing about SquashFS or this package, please cite your sources and mark verbatim quotations as such. I won't be angry if you don't, but a thesis supervisor, reviewer or fellow Wikipedian might be. The example programs in the `extras` directory are licensed under the **0BSD license**, a copy of which can be found in `licenses/0BSD.txt`. # Binary Packages with 3rd Party Libraries If this file is included in a binary release package, additional 3rd party libraries may be included, which are subject to the copyright of their respective authors and the terms and conditions of their respective licenses. The following may be included: - The LZO compression library. Copyright Markus F.X.J. Oberhumer. This is released under the terms and conditions of the GNU General Public License version 2. A copy of the license is included in `licenses/GPLv2.txt`. - The LZ4 compression library. Copyright Yann Collet. This is released under a 2 clause BSD style license, included in `licenses/LZ4.txt`. This library may be linked directly into `libsquashfs`, built from source code included in the source distribution. - The XZ utils liblzma library is released into the public domain. An excerpt from the `COPYING` file of its source code archive is included in `licenses/xz.txt`. - The zlib compression library. Copyright Jean-loup Gailly and Mark Adler. This is released under the terms and conditions of the zlib license, included in `licenses/zlib.txt`. This library may be linked directly into `libsquashfs`, built from source code included in the source distribution. - The zstd compression library. Copyright Facebook, Inc. All rights reserved. This is released under a BSD style license, included in `licenses/zstd.txt`. - Parts of the Musl C library. Copyright Rich Felker, et al. This is released under an MIT style license, included in `licenses/musl.txt`. Independent of build configurations, the `libsquashfs` library contains the following 3rd party source code, directly linked into the library: - A modified version of the xxhash32 hash function (Copyright Yann Collet). This is released under a 2-Clause BSD License. See `licenses/xxhash.txt` for details. - A hash table implementation liftet from the Mesa3D source code. This is released under the MIT/X11 license. See `licenses/hash_table.txt` for details. squashfs-tools-ng-1.1.3/Doxyfile.in000066400000000000000000000263041410627516300172360ustar00rootroot00000000000000# Doxyfile 1.8.8 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libsquashfs PROJECT_NUMBER = @PACKAGE_VERSION@ PROJECT_BRIEF = "A new set of tools and libraries for working with SquashFS images" PROJECT_LOGO = OUTPUT_DIRECTORY = @DX_DOCDIR@ CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = YES LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = YES SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @top_srcdir@/include/sqfs/ @top_srcdir@/doc INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h *.dox RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = @top_srcdir@/extras EXAMPLE_PATTERNS = *.c EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = YES ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = pdflatex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_SUBDIR = MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = YES UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = NO INCLUDED_BY_GRAPH = NO CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = NO DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = PLANTUML_JAR_PATH = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES squashfs-tools-ng-1.1.3/Makefile.am000066400000000000000000000021061410627516300171510ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include -D_GNU_SOURCE AM_CFLAGS = $(WARN_CFLAGS) if WITH_LZO AM_CPPFLAGS += -DWITH_LZO endif if CUSTOM_ALLOC else AM_CPPFLAGS += -DNO_CUSTOM_ALLOC endif noinst_LTLIBRARIES = noinst_LIBRARIES = noinst_PROGRAMS = bin_PROGRAMS = lib_LTLIBRARIES = dist_man1_MANS = check_PROGRAMS = check_SCRIPTS = pkgconfig_DATA = EXTRA_DIST = autogen.sh README.md CHANGELOG.md COPYING.md mkwinbins.sh licenses EXTRA_DIST += doc TESTS = include lib/zlib/Makemodule.am include lib/lz4/Makemodule.am include lib/sqfs/Makemodule.am include lib/util/Makemodule.am include lib/fstream/Makemodule.am if BUILD_TOOLS include lib/fstree/Makemodule.am include lib/common/Makemodule.am include lib/tar/Makemodule.am include lib/compat/Makemodule.am include bin/gensquashfs/Makemodule.am include bin/rdsquashfs/Makemodule.am include bin/sqfs2tar/Makemodule.am include bin/sqfsdiff/Makemodule.am include bin/tar2sqfs/Makemodule.am endif include extras/Makemodule.am include tests/Makemodule.am if HAVE_DOXYGEN @DX_RULES@ MOSTLYCLEANFILES = $(DX_CLEANFILES) endif squashfs-tools-ng-1.1.3/README.md000066400000000000000000000152341410627516300164020ustar00rootroot00000000000000# About SquashFS is a highly compressed, read only file system often used as a root fs on embedded devices, live systems or simply as a compressed archive format. Think of it as a .tar.gz that you can mount (or XZ, LZO, LZ4, ZSTD). This project originally started out as a fork of squashfs-tools 4.3, after encountering some short comings and realizing that there have been no updates on the SourceForge site or mailing list for a long time. Even before the first public release, the fork was replaced with a complete re-write after growing frustrated with the existing code base. For lack of a better name, and because the original appeared to be unmaintained at the time, the name squashfs-tools-ng was kept, although the published code base technically never had any connection to squashfs-tools. Maintenance of the original squashfs-tools has since resumed, squashfs-tools version 4.4 was released and continues to be maintained in parallel. The utilities provided by squashfs-tools-ng offer alternative tooling and are intentionally named differently, so both packages can be installed side by side. The actual guts of squashfs-tools-ng are encapsulated in a library with a generic API designed to make SquashFS available to other applications as an embeddable, extensible archive format (or to simply read, write or manipulate SquashFS file systems). The utility programs are largely command line wrappers around the library. The following tools are provided: - `gensquashfs` can be used to produce SquashFS images from `gen_init_cpio` like file listings or simply pack an input directory. Can use an SELinux contexts file (see selabel_file(5)) to generate SELinux labels. - `rdsquashfs` can be used to inspect and unpack SquashFS images. - `sqfs2tar` can turn a SquashFS image into a tarball, written to stdout. - `tar2sqfs` can turn a tarball (read from stdin) into a SquashFS image. - `sqfsdiff` can compare the contents of two SquashFS images. The library and the tools that produce SquashFS images are designed to operate deterministically. Same input will produce byte-for-byte identical output. Failure to do so is treated as a critical bug. # Installing A number of Linux distributions already offer squashfs-tools-ng through their package management system. Replogy maintains an up to date list: [![Packaging status](https://repology.org/badge/vertical-allrepos/squashfs-tools-ng.svg)](https://repology.org/project/squashfs-tools-ng/versions) ## Pre-built Windows binary Packages Pre-compiled binary packages for Windows are available here: https://infraroot.at/pub/squashfs/windows Those packages contain the binaries for the tools, the SquashFS library and pre-compiled dependency libraries (zstd, lzo, lzma; others are built in). The binary package does not contain any source code. See below on how to obtain and compile the source for squashfs-tools-ng. The corresponding source code from which the 3rd party libraries have been built is also available for download at the above location. The headers and import libraries to build applications that use libsquashfs are included. For convenience, the pre-compiled, 3rd party dependency libraries also come with headers and import libraries. # Copyright & License In short: libsquashfs is LGPLv3 licensed, the utility programs are GPLv3. Some 3rd party source code is included with more permissive licenses, some of which is actually compiled into libsquashfs. Copyright notices for those must be included when distributing either source or binaries of squashfs-tools-ng. See [COPYING.md](COPYING.md) for more detailed information. # Getting and Building the Source Code Official release tarballs can be obtained here: https://infraroot.at/pub/squashfs The official git tree is available at the following locations: https://github.com/AgentD/squashfs-tools-ng https://git.infraroot.at/squashfs-tools-ng.git Those locations are kept in sync and the former is a GitHub project that also accepts and handles issues & pull requests. If you are working on an official release tarball, you can build the package like every autotools based package: ./configure make make install If you work on the git tree, you need to bootstrap the build system first: ./autogen.sh If Doxygen is available, a reference manual can be built as follows: make doxygen-doc The pre-compiled binary packages for Windows are built using a helper script that uses a MinGW cross toolchain to build squashfs-tools-ng and any of the required dependencies: ./mkwinbins.sh An high-level overview of the source code and architecture [can be found here](doc/architecture.md). ## A Note on LZO Support The SquashFS format supports compression using LZO. The `liblzo2` library itself is released under the GNU GPL, version 2. To make the `libsquashfs` library available as an LGPL library, it *cannot* be linked against `liblzo2`, neither statically nor dynamically. This legal problem has been solved using the following technical measure: - `libsquashfs`, as of right now, does not support LZO compression. - The `libcommon` helper library has an implementation of an `liblzo2` based compressor. This library and the tools that use it are released under the GPL. This way, the tools themselves *do* support LZO compression seamlessly, while the `libsquashfs` library does not. ## Automated Testing and Analysis [![Build Status](https://travis-ci.com/AgentD/squashfs-tools-ng.svg?branch=master)](https://travis-ci.com/AgentD/squashfs-tools-ng) [![Coverity Status](https://scan.coverity.com/projects/18718/badge.svg)](https://scan.coverity.com/projects/squashfs-tools-ng) The GitHub project for squashfs-tools-ng is registered with Travis-CI and Coverity Scan. The [Travis-CI](https://travis-ci.com/github/AgentD/squashfs-tools-ng]) page shows the current build status for various system configurations for the latest commit on master, as well as pull requests on the GitHub project page. The [Coverity Scan](https://scan.coverity.com/projects/squashfs-tools-ng) page shows details for static analysis runs on the code, which are triggered manually and thus run less frequently. ## Further Information A documentation of the SquashFS on-disk format in plain text format can be found in the [documentation directory](doc/format.txt), which is based on an online version that can be found here: https://dr-emann.github.io/squashfs/ The closest thing to an official web site can be found here: https://infraroot.at/projects/squashfs-tools-ng/index.html This location also hosts the Doxygen reference manual for the latest release. There is currently no official mailing list. So far I used the squashfs-tools mailing list on SourceForge for announcments and I will continue to do so until I am booted off. squashfs-tools-ng-1.1.3/autogen.sh000077500000000000000000000000621410627516300171150ustar00rootroot00000000000000#!/bin/sh autoreconf --force --install --symlink squashfs-tools-ng-1.1.3/bin/000077500000000000000000000000001410627516300156665ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/bin/gensquashfs/000077500000000000000000000000001410627516300202155ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/bin/gensquashfs/Makemodule.am000066400000000000000000000012031410627516300226130ustar00rootroot00000000000000gensquashfs_SOURCES = bin/gensquashfs/mkfs.c bin/gensquashfs/mkfs.h gensquashfs_SOURCES += bin/gensquashfs/options.c bin/gensquashfs/selinux.c gensquashfs_SOURCES += bin/gensquashfs/dirscan_xattr.c gensquashfs_LDADD = libcommon.a libsquashfs.la libfstree.a libfstream.a gensquashfs_LDADD += libcompat.a $(LZO_LIBS) $(PTHREAD_LIBS) gensquashfs_CPPFLAGS = $(AM_CPPFLAGS) gensquashfs_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) if WITH_SELINUX gensquashfs_CPPFLAGS += -DWITH_SELINUX gensquashfs_CFLAGS += $(LIBSELINUX_CFLAGS) gensquashfs_LDADD += $(LIBSELINUX_LIBS) endif dist_man1_MANS += bin/gensquashfs/gensquashfs.1 bin_PROGRAMS += gensquashfs squashfs-tools-ng-1.1.3/bin/gensquashfs/dirscan_xattr.c000066400000000000000000000073361410627516300232370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * dirscan_xattr.c * * Copyright (C) 2019 David Oberhollenzer */ #include "mkfs.h" #ifdef HAVE_SYS_XATTR_H static char *get_full_path(const char *prefix, tree_node_t *node) { char *path = NULL, *new = NULL; size_t path_len, prefix_len; int ret; path = fstree_get_path(node); if (path == NULL) goto fail; ret = canonicalize_name(path); assert(ret == 0); path_len = strlen(path); prefix_len = strlen(prefix); while (prefix_len > 0 && prefix[prefix_len - 1] == '/') --prefix_len; if (prefix_len > 0) { new = realloc(path, path_len + prefix_len + 2); if (new == NULL) goto fail; path = new; memmove(path + prefix_len + 1, path, path_len + 1); memcpy(path, prefix, prefix_len); path[prefix_len] = '/'; } return path; fail: perror("getting full path for xattr scan"); free(path); return NULL; } static int xattr_from_path(sqfs_xattr_writer_t *xwr, const char *path) { char *key, *value = NULL, *buffer = NULL; ssize_t buflen, vallen, keylen; int ret; buflen = llistxattr(path, NULL, 0); if (buflen < 0) { fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); return -1; } if (buflen == 0) return 0; buffer = malloc(buflen); if (buffer == NULL) { perror("xattr name buffer"); return -1; } buflen = llistxattr(path, buffer, buflen); if (buflen == -1) { fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); goto fail; } key = buffer; while (buflen > 0) { vallen = lgetxattr(path, key, NULL, 0); if (vallen == -1) { fprintf(stderr, "lgetxattr %s: %s", path, strerror(errno)); goto fail; } if (vallen > 0) { value = calloc(1, vallen); if (value == NULL) { perror("allocating xattr value buffer"); goto fail; } vallen = lgetxattr(path, key, value, vallen); if (vallen == -1) { fprintf(stderr, "lgetxattr %s: %s\n", path, strerror(errno)); goto fail; } ret = sqfs_xattr_writer_add(xwr, key, value, vallen); if (ret) { sqfs_perror(path, "storing xattr key-value pairs", ret); goto fail; } free(value); value = NULL; } keylen = strlen(key) + 1; buflen -= keylen; key += keylen; } free(buffer); return 0; fail: free(value); free(buffer); return -1; } #endif static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, sqfs_xattr_writer_t *xwr, bool scan_xattr, tree_node_t *node) { char *path; int ret; ret = sqfs_xattr_writer_begin(xwr, 0); if (ret) { sqfs_perror(node->name, "recoding xattr key-value pairs\n", ret); return -1; } #ifdef HAVE_SYS_XATTR_H if (scan_xattr) { path = get_full_path(path_prefix, node); if (path == NULL) return -1; ret = xattr_from_path(xwr, path); free(path); if (ret) return -1; } #else (void)path_prefix; #endif if (selinux_handle != NULL) { path = fstree_get_path(node); if (path == NULL) { perror("reconstructing absolute path"); return -1; } ret = selinux_relable_node(selinux_handle, xwr, node, path); free(path); if (ret) return -1; } if (sqfs_xattr_writer_end(xwr, &node->xattr_idx)) { sqfs_perror(node->name, "completing xattr key-value pairs", ret); return -1; } if (S_ISDIR(node->mode)) { node = node->data.dir.children; while (node != NULL) { if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, scan_xattr, node)) { return -1; } node = node->next; } } return 0; } int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, sqfs_xattr_writer_t *xwr, bool scan_xattr) { if (xwr == NULL) return 0; if (selinux_handle == NULL && !scan_xattr) return 0; return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, fs->root); } squashfs-tools-ng-1.1.3/bin/gensquashfs/gensquashfs.1000066400000000000000000000215131410627516300226300ustar00rootroot00000000000000.TH GENSQUASHFS "1" "March 2021" "generate squashfs images" "User Commands" .SH NAME gensquashfs \- generate squashfs images .SH SYNOPSIS .B gensquashfs [\fI\,OPTIONS\/\fR] \/\fR .SH DESCRIPTION Generate a SquashFS image. .SH OPTIONS .TP \fB\-\-pack\-file\fR, \fB\-F\fR Use a \fBgen_init_cpio\fR style description file. The file format is specified below. If \fB\-\-pack\-dir\fR is used, input file paths are relative to the pack directory, otherwise they are relative to the directory the pack file is in. .TP \fB\-\-pack\-dir\fR, \fB\-D\fR If \fB\-\-pack\-file\fR is used, this is the root path relative to which to read files. If no pack file is specified, pack the contents of the given directory into a SquashFS image. The directory becomes the root of the file system. .TP \fB\-\-compressor\fR, \fB\-c\fR Select the compressor to use. Run \fBgensquashfs \-\-help\fR to get a list of all available compressors and the default selection. .TP \fB\-\-comp\-extra\fR, \fB\-X\fR A comma separated list of extra options for the selected compressor. Specify \fBhelp\fR to get a list of available options. .TP \fB\-\-num\-jobs\fR, \fB\-j\fR If libsquashfs was compiled with a built in thread pool based, parallel data compressor, this option can be used to set the number of compressor threads. If not set, the default is the number of available CPU cores. .TP \fB\-\-queue\-backlog\fR, \fB\-Q\fR Maximum number of data blocks in the thread worker queue before the packer starts waiting for the block processors to catch up. Higher values result in higher memory consumption. Defaults to 10 times the number of workers. .TP \fB\-\-block\-size\fR, \fB\-b\fR Block size to use for Squashfs image. Defaults to 131072. .TP \fB\-\-dev\-block\-size\fR, \fB\-B\fR Device block size to padd the image to. Defaults to 4096. .TP \fB\-\-keep\-time\fR, \fB\-k\fR When using \fB\-\-pack\-dir\fR only, use the timestamps from the input files instead of setting defaults on all input paths. The root inode and the modification time on the SquashFS image itself will still be set to defaults. .TP \fB\-\-one\-file\-system\fR, \fB\-o\fR When using \fB\-\-pack\-dir\fR only, stay in the local filesystem and do not cross mount points. .TP \fB\-\-defaults\fR, \fB\-d\fR A comma separated list of default values for implicitly created directories. The following values can be set: .TS tab(;) allbox; l l l l l l l l l l rd. \fBOption\fR;\fBDefault\fR uid=;0 gid=;0 mode=;0755 mtime=;\fB$SOURCE\_DATE\_EPOCH\fR if set, 0 otherwise .TE .TP .TP \fB\-\-set\-uid\fR, \fB\-u\fR Force the owners user ID for ALL inodes to this value, no matter what the pack file or directory entries actually specify. .TP \fB\-\-set\-gid\fR, \fB\-g\fR Force the owners group ID for ALL inodes to this value, no matter what the pack file or directory entries actually specify. .TP \fB\-\-all\-root\fR A short hand for `\-\-set\-uid 0 \-\-set\-gid 0`. .TP \fB\-\-selinux\fR, \fB\-s\fR If built with SELinux support, use the given SELinux label file to add context labels to the elements packed into the SquashFS image. .TP \fB\-\-exportable\fR, \fB\-e\fR Generate an export table for NFS support. .TP \fB\-\-no\-tail\-packing\fR, \fB\-T\fR Do not perform tail end packing on files that are larger than the specified block size. .TP \fB\-\-force\fR, \fB\-f\fR Overwrite the output file if it exists. .TP \fB\-\-quiet\fR, \fB\-q\fR Do not print out progress reports. .TP \fB\-\-help\fR, \fB\-h\fR Print help text and exit. .TP \fB\-\-version\fR, \fB\-V\fR Print version information and exit. .SH INPUT FILE FORMAT The input file contains a simple, newline separated list that describe the files to be included in the squashfs image: .PP .in +4n .nf # a comment file [] dir nod slink link pipe sock glob [OPTIONS...] .fi .in .TS tab(;) allbox; l l l l l l l l l l l l l l l l l l rd. ;T{ Absolute path of the entry in the image. Can be put in quotes if some components contain spaces. T} ;T{ Optional location of the input file. Can be specified relative to either the description file or the pack directory. If omitted, the image path is used as a relative path. T} ;Symlink or hardlink target. ;Mode/permissions of the entry. ;Numeric user id. ;Numeric group id. ;Device type (b=block, c=character). ;Major number of a device special file. ;Minor number of a device special file. .TE .SS File Globbing The \fBglob\fR command requires an \fIinput location\fR which is interpreted relative to the pack directory (or the input file if no directory was specified). This location is scanned recursively and the contents are added to the specified virtual path. The specified \fImode\fR, \fIuid\fR and \fIgid\fR are applied to all new entries added by the glob. They can alternatively be set to the special value \fB*\fR to use the value from the input directory. In front of the source location, several additional options can be specified to control the behavior of the glob command: .TS tab(;) allbox; l l l l l l l l l l l l l l l l rd. \fBOption\fR;\fBDescription\fR \-type;T{ Followed by a single space and a single, lowercase character describing the inode type to accept. Works similar to the \fB\-type\fR option of the \fBfind\fR command. Possible values are \fBb\fR (block devices), \fBc\fR (character devices), \fBd\fR (directories), \fBp\fR (named pipes), \fBf\fR (regular files), \fBl\fR (symlinks) and \fBs\fR (sockets). If \fB\-type\fR is not used, all are accepted. The first use clamps the selection down to a single type and subsequent uses allow additional types. T} \-xdev;Do not cross mount points during a recursive glob. \-mount;An alias for \fB\-xdev\fR \-keeptime;Use the time stamps from the scanned files. \-nonrecursive;T{ Do not descend into directories. Even if the type argument does not include directories, it is still possible to recursively scan a hierarchy. In that case, the scanning will not add \fInew\fR directory nodes, but still recurse into a directory if a coresponding node already exist in the virtual filesystem tree. So a typicall use case might be to first scan only the directories, and then do several narrower globs to fill them. T} \-name ;T{ Only add entries if their name matches a shell glob pattern. If the pattern is supposed to contain spaces, it can be wrapped in quotation marks ("..." or '...'). T} \-path ;T{ Only add entries if their full resulting path in the SquashFS image matches a shell glob pattern. Slashes in the path are only matched against slashes in the pattern and will never match a wild card character or a bracket expression containing a slash. The path is normalized, so it won't have a leading or trailing slash. T} .TE .PP Any other, unknown string starting with \- will be rejected as unknown option. If the input path starts with \-, the sequence \-\- can be used to stop argument parsing, similar to many command line tools. .SS Example .PP .nf # A simple squashfs image dir /dev 0755 0 0 nod /dev/console 0600 0 0 c 5 1 dir /root 0700 0 0 dir /sbin 0755 0 0 # Add a file. Input is relative to pack dir or listing path file /sbin/init 0755 0 0 ../init/sbin/init # Read from ./bin/bash relative to pack dir or listing path # /bin is created implicitly with default attributes. file /bin/bash 0755 0 0 # file name with a space in it and a "special" name file "/opt/my app/\\"special\\"/data" 0600 0 0 # collect the contents of ./lib and put it under /usr/lib # mode and uid/gid are explictly set. First we collect the directory tree, # then all so files, then all symlinks that don't end in ".so" glob /usr/lib 0755 0 0 -type d ./lib glob /usr/lib 0755 0 0 -type f -name "*.so.*" ./lib glob /usr/lib 0777 0 0 -type l -name "*.so.*" ./lib .fi .SH ENVIRONMENT If the command line switch \fB\-\-defaults\fR is not used or no default mtime is specified, the value of the environment variable \fBSOURCE\_DATE\_EPOCH\fR is used for all file and filesystem timestamps. If \fBSOURCE\_DATE\_EPOCH\fR is not set, not a parsable number or it is out of range, the timestamps default to 0. Environment variables are only used if no explicit command line switches are set. Explicit command line switches are always preferred over the environment variables. .SH SEE ALSO rdsquashfs(1), tar2sqfs(1) .SH AUTHOR Written by David Oberhollenzer. .SH COPYRIGHT Copyright \(co 2019 David Oberhollenzer License GPLv3+: GNU GPL version 3 or later . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. squashfs-tools-ng-1.1.3/bin/gensquashfs/mkfs.c000066400000000000000000000075601410627516300213310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mkfs.c * * Copyright (C) 2019 David Oberhollenzer */ #include "mkfs.h" static int pack_files(sqfs_block_processor_t *data, fstree_t *fs, options_t *opt) { sqfs_u64 filesize; sqfs_file_t *file; tree_node_t *node; const char *path; char *node_path; file_info_t *fi; int flags; int ret; if (opt->packdir != NULL && chdir(opt->packdir) != 0) { perror(opt->packdir); return -1; } for (fi = fs->files; fi != NULL; fi = fi->next) { if (fi->input_file == NULL) { node = container_of(fi, tree_node_t, data.file); node_path = fstree_get_path(node); if (node_path == NULL) { perror("reconstructing file path"); return -1; } ret = canonicalize_name(node_path); assert(ret == 0); path = node_path; } else { node_path = NULL; path = fi->input_file; } if (!opt->cfg.quiet) printf("packing %s\n", path); file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); if (file == NULL) { perror(path); free(node_path); return -1; } flags = 0; filesize = file->get_size(file); if (opt->no_tail_packing && filesize > opt->cfg.block_size) flags |= SQFS_BLK_DONT_FRAGMENT; ret = write_data_from_file(path, data, &fi->inode, file, flags); sqfs_destroy(file); free(node_path); if (ret) return -1; } return 0; } static int relabel_tree_dfs(const char *filename, sqfs_xattr_writer_t *xwr, tree_node_t *n, void *selinux_handle) { char *path = fstree_get_path(n); int ret; if (path == NULL) { perror("getting absolute node path for SELinux relabeling"); return -1; } ret = sqfs_xattr_writer_begin(xwr, 0); if (ret) { sqfs_perror(filename, "recording xattr key-value pairs", ret); return -1; } if (selinux_relable_node(selinux_handle, xwr, n, path)) { free(path); return -1; } ret = sqfs_xattr_writer_end(xwr, &n->xattr_idx); if (ret) { sqfs_perror(filename, "flushing completed key-value pairs", ret); return -1; } free(path); if (S_ISDIR(n->mode)) { for (n = n->data.dir.children; n != NULL; n = n->next) { if (relabel_tree_dfs(filename, xwr, n, selinux_handle)) return -1; } } return 0; } static int read_fstree(fstree_t *fs, options_t *opt, sqfs_xattr_writer_t *xwr, void *selinux_handle) { int ret; ret = fstree_from_file(fs, opt->infile, opt->packdir); if (ret == 0 && selinux_handle != NULL) ret = relabel_tree_dfs(opt->cfg.filename, xwr, fs->root, selinux_handle); return ret; } static void override_owner_dfs(const options_t *opt, tree_node_t *n) { if (opt->force_uid) n->uid = opt->force_uid_value; if (opt->force_gid) n->gid = opt->force_gid_value; if (S_ISDIR(n->mode)) { for (n = n->data.dir.children; n != NULL; n = n->next) override_owner_dfs(opt, n); } } int main(int argc, char **argv) { int status = EXIT_FAILURE; void *sehnd = NULL; sqfs_writer_t sqfs; options_t opt; process_command_line(&opt, argc, argv); if (sqfs_writer_init(&sqfs, &opt.cfg)) return EXIT_FAILURE; if (opt.selinux != NULL) { sehnd = selinux_open_context_file(opt.selinux); if (sehnd == NULL) goto out; } if (opt.infile == NULL) { if (fstree_from_dir(&sqfs.fs, sqfs.fs.root, opt.packdir, NULL, NULL, opt.dirscan_flags)) { goto out; } } else { if (read_fstree(&sqfs.fs, &opt, sqfs.xwr, sehnd)) goto out; } if (opt.force_uid || opt.force_gid) override_owner_dfs(&opt, sqfs.fs.root); if (fstree_post_process(&sqfs.fs)) goto out; if (opt.infile == NULL) { if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, sqfs.xwr, opt.scan_xattr)) { goto out; } } if (pack_files(sqfs.data, &sqfs.fs, &opt)) goto out; if (sqfs_writer_finish(&sqfs, &opt.cfg)) goto out; status = EXIT_SUCCESS; out: sqfs_writer_cleanup(&sqfs, status); if (sehnd != NULL) selinux_close_context_file(sehnd); free(opt.packdir); return status; } squashfs-tools-ng-1.1.3/bin/gensquashfs/mkfs.h000066400000000000000000000030421410627516300213250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mkfs.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef MKFS_H #define MKFS_H #include "config.h" #include "common.h" #include "fstree.h" #ifdef HAVE_SYS_XATTR_H #include #if defined(__APPLE__) && defined(__MACH__) #define llistxattr(path, list, size) \ listxattr(path, list, size, XATTR_NOFOLLOW) #define lgetxattr(path, name, value, size) \ getxattr(path, name, value, size, 0, XATTR_NOFOLLOW) #endif #endif #ifdef WITH_SELINUX #include #include #endif #include #include #include #include #include #include #include #include typedef struct { sqfs_writer_cfg_t cfg; unsigned int dirscan_flags; const char *infile; const char *selinux; bool no_tail_packing; /* copied from command line or constructed from infile argument if not specified. Must be free'd. */ char *packdir; unsigned int force_uid_value; unsigned int force_gid_value; bool force_uid; bool force_gid; bool scan_xattr; } options_t; void process_command_line(options_t *opt, int argc, char **argv); int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, sqfs_xattr_writer_t *xwr, bool scan_xattr); void *selinux_open_context_file(const char *filename); int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, tree_node_t *node, const char *path); void selinux_close_context_file(void *sehnd); #endif /* MKFS_H */ squashfs-tools-ng-1.1.3/bin/gensquashfs/options.c000066400000000000000000000264201410627516300220600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * options.c * * Copyright (C) 2019 David Oberhollenzer */ #include "mkfs.h" enum { ALL_ROOT_OPTION = 1, }; static struct option long_opts[] = { { "all-root", no_argument, NULL, ALL_ROOT_OPTION }, { "set-uid", required_argument, NULL, 'u' }, { "set-gid", required_argument, NULL, 'g' }, { "compressor", required_argument, NULL, 'c' }, { "block-size", required_argument, NULL, 'b' }, { "dev-block-size", required_argument, NULL, 'B' }, { "defaults", required_argument, NULL, 'd' }, { "comp-extra", required_argument, NULL, 'X' }, { "pack-file", required_argument, NULL, 'F' }, { "pack-dir", required_argument, NULL, 'D' }, { "num-jobs", required_argument, NULL, 'j' }, { "queue-backlog", required_argument, NULL, 'Q' }, { "keep-time", no_argument, NULL, 'k' }, #ifdef HAVE_SYS_XATTR_H { "keep-xattr", no_argument, NULL, 'x' }, #endif { "one-file-system", no_argument, NULL, 'o' }, { "exportable", no_argument, NULL, 'e' }, { "no-tail-packing", no_argument, NULL, 'T' }, { "force", no_argument, NULL, 'f' }, { "quiet", no_argument, NULL, 'q' }, #ifdef WITH_SELINUX { "selinux", required_argument, NULL, 's' }, #endif { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "F:D:X:c:b:B:d:u:g:j:Q:kxoefqThV" #ifdef WITH_SELINUX "s:" #endif #ifdef HAVE_SYS_XATTR_H "x" #endif ; static const char *help_string = "Usage: gensquashfs [OPTIONS...] \n" "\n" "Possible options:\n" "\n" " --pack-file, -F Use a `gen_init_cpio` style description file.\n" " The file format is specified below.\n" " If --pack-dir is used, input file paths are\n" " relative to the pack directory, otherwise\n" " they are relative to the directory the pack\n" " file is in.\n" " --pack-dir, -D If --pack-file is used, this is the root path\n" " relative to which to read files. If no pack\n" " file is specified, pack the contents of the\n" " given directory into a SquashFS image. The\n" " directory becomes the root of the file\n" " system.\n" "\n" " --compressor, -c Select the compressor to use.\n" " A list of available compressors is below.\n" " --comp-extra, -X A comma separated list of extra options for\n" " the selected compressor. Specify 'help' to\n" " get a list of available options.\n" " --num-jobs, -j Number of compressor jobs to create.\n" " --queue-backlog, -Q Maximum number of data blocks in the thread\n" " worker queue before the packer starts waiting\n" " for the block processors to catch up.\n" " Defaults to 10 times the number of jobs.\n" " --block-size, -b Block size to use for Squashfs image.\n" " Defaults to %u.\n" " --dev-block-size, -B Device block size to padd the image to.\n" " Defaults to %u.\n" " --defaults, -d A comma separated list of default values for\n" " implicitly created directories.\n" "\n" " Possible options:\n" " uid= 0 if not set.\n" " gid= 0 if not set.\n" " mode= 0755 if not set.\n" " mtime= 0 if not set.\n" "\n" " --set-uid, -u Force the owners user ID for ALL inodes to\n" " this value, no matter what the pack file or\n" " directory entries actually specify.\n" " --set-gid, -g Force the owners group ID for ALL inodes to\n" " this value, no matter what the pack file or\n" " directory entries actually specify.\n" " --all-root A short hand for `--set-uid 0 --set-gid 0`.\n" "\n" #ifdef WITH_SELINUX " --selinux, -s Specify an SELinux label file to get context\n" " attributes from.\n" #endif " --keep-time, -k When using --pack-dir only, use the timestamps\n" " from the input files instead of setting\n" " defaults on all input paths.\n" " --keep-xattr, -x When using --pack-dir only, read and pack the\n" " extended attributes from the input files.\n" " --one-file-system, -o When using --pack-dir only, stay in local file\n" " system and do not cross mount points.\n" " --exportable, -e Generate an export table for NFS support.\n" " --no-tail-packing, -T Do not perform tail end packing on files that\n" " are larger than block size.\n" " --force, -f Overwrite the output file if it exists.\n" " --quiet, -q Do not print out progress reports.\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n"; const char *help_details = "When using the pack file option, the given file is expected to contain\n" "newline separated entries that describe the files to be included in the\n" "SquashFS image. The following entry types can be specified:\n" "\n" "# a comment\n" "file []\n" "dir \n" "nod \n" "slink \n" "link \n" "pipe \n" "sock \n" "glob [OPTIONS...] \n" "\n" " Absolute path of the entry in the image. Can be put in quotes\n" " if some components contain spaces.\n" " If given, location of the input file. Either absolute or relative\n" " to the description file. If omitted, the image path is used,\n" " relative to the description file.\n" " Symlink or hardlink target.\n" " Mode/permissions of the entry.\n" " Numeric user id.\n" " Numeric group id.\n" " Device type (b=block, c=character).\n" " Major number of a device special file.\n" " Minor number of a device special file.\n" "\n" "Example:\n" " # A simple squashfs image\n" " dir /dev 0755 0 0\n" " nod /dev/console 0600 0 0 c 5 1\n" " dir /root 0700 0 0\n" " dir /sbin 0755 0 0\n" " \n" " # Add a file. Input is relative to listing or pack dir.\n" " file /sbin/init 0755 0 0 ../init/sbin/init\n" " \n" " # Read bin/bash, relative to listing or pack dir.\n" " # Implicitly create /bin.\n" " file /bin/bash 0755 0 0\n" " \n" " # file name with a space in it.\n" " file \"/opt/my app/\\\"special\\\"/data\" 0600 0 0\n" " \n" " # collect the contents of ./lib and put it under /usr/lib\n" " glob /usr/lib 0755 0 0 -type d ./lib\n" " glob /usr/lib 0755 0 0 -type f -name \"*.so.*\" ./lib\n" " glob /usr/lib 0777 0 0 -type l -name \"*.so.*\" ./lib\n" "\n\n"; void process_command_line(options_t *opt, int argc, char **argv) { bool have_compressor; int i, ret; memset(opt, 0, sizeof(*opt)); sqfs_writer_cfg_init(&opt->cfg); for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case ALL_ROOT_OPTION: opt->force_uid_value = 0; opt->force_gid_value = 0; opt->force_uid = true; opt->force_gid = true; break; case 'u': opt->force_uid_value = strtol(optarg, NULL, 0); opt->force_uid = true; break; case 'g': opt->force_gid_value = strtol(optarg, NULL, 0); opt->force_gid = true; break; case 'T': opt->no_tail_packing = true; break; case 'c': have_compressor = true; ret = sqfs_compressor_id_from_name(optarg); if (ret < 0) { have_compressor = false; #ifdef WITH_LZO if (opt->cfg.comp_id == SQFS_COMP_LZO) have_compressor = true; #endif } if (!have_compressor) { fprintf(stderr, "Unsupported compressor '%s'\n", optarg); exit(EXIT_FAILURE); } opt->cfg.comp_id = ret; break; case 'b': if (parse_size("Block size", &opt->cfg.block_size, optarg, 0)) { exit(EXIT_FAILURE); } break; case 'j': opt->cfg.num_jobs = strtol(optarg, NULL, 0); break; case 'Q': opt->cfg.max_backlog = strtol(optarg, NULL, 0); break; case 'B': if (parse_size("Device block size", &opt->cfg.devblksize, optarg, 0)) { exit(EXIT_FAILURE); } if (opt->cfg.devblksize < 1024) { fputs("Device block size must be at " "least 1024\n", stderr); exit(EXIT_FAILURE); } break; case 'd': opt->cfg.fs_defaults = optarg; break; case 'k': opt->dirscan_flags |= DIR_SCAN_KEEP_TIME; break; #ifdef HAVE_SYS_XATTR_H case 'x': opt->scan_xattr = true; break; #endif case 'o': opt->dirscan_flags |= DIR_SCAN_ONE_FILESYSTEM; break; case 'e': opt->cfg.exportable = true; break; case 'f': opt->cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; break; case 'q': opt->cfg.quiet = true; break; case 'X': opt->cfg.comp_extra = optarg; break; case 'F': opt->infile = optarg; break; case 'D': free(opt->packdir); opt->packdir = strdup(optarg); if (opt->packdir == NULL) { perror(optarg); exit(EXIT_FAILURE); } break; #ifdef WITH_SELINUX case 's': opt->selinux = optarg; break; #endif case 'h': printf(help_string, SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE); fputs(help_details, stdout); compressor_print_available(); exit(EXIT_SUCCESS); case 'V': print_version("gensquashfs"); exit(EXIT_SUCCESS); default: goto fail_arg; } } if (opt->cfg.num_jobs < 1) opt->cfg.num_jobs = 1; if (opt->cfg.max_backlog < 1) opt->cfg.max_backlog = 10 * opt->cfg.num_jobs; if (opt->cfg.comp_extra != NULL && strcmp(opt->cfg.comp_extra, "help") == 0) { compressor_print_help(opt->cfg.comp_id); exit(EXIT_SUCCESS); } if (opt->infile == NULL && opt->packdir == NULL) { fputs("No input file or directory specified.\n", stderr); goto fail_arg; } if (optind >= argc) { fputs("No output file specified.\n", stderr); goto fail_arg; } opt->cfg.filename = argv[optind++]; if (optind < argc) { fputs("Unknown extra arguments specified.\n", stderr); goto fail_arg; } /* construct packdir if not specified */ if (opt->packdir == NULL && opt->infile != NULL) { const char *split = strrchr(opt->infile, '/'); if (split != NULL) { opt->packdir = strndup(opt->infile, split - opt->infile); if (opt->packdir == NULL) { perror("constructing input directory path"); exit(EXIT_FAILURE); } } } return; fail_arg: fputs("Try `gensquashfs --help' for more information.\n", stderr); free(opt->packdir); exit(EXIT_FAILURE); } squashfs-tools-ng-1.1.3/bin/gensquashfs/selinux.c000066400000000000000000000031701410627516300220510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * selinux.c * * Copyright (C) 2019 David Oberhollenzer */ #include "mkfs.h" #define XATTR_NAME_SELINUX "security.selinux" #define XATTR_VALUE_SELINUX "system_u:object_r:unlabeled_t:s0" #ifdef WITH_SELINUX int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, tree_node_t *node, const char *path) { char *context = NULL; int ret; if (selabel_lookup(sehnd, &context, path, node->mode) < 0) { context = strdup(XATTR_VALUE_SELINUX); if (context == NULL) goto fail; } ret = sqfs_xattr_writer_add(xwr, XATTR_NAME_SELINUX, context, strlen(context)); free(context); if (ret) sqfs_perror(node->name, "storing SELinux xattr", ret); return ret; fail: perror("relabeling files"); return -1; } void *selinux_open_context_file(const char *filename) { struct selabel_handle *sehnd; struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, filename }, }; sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (sehnd == NULL) perror(filename); return sehnd; } void selinux_close_context_file(void *sehnd) { selabel_close(sehnd); } #else int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, tree_node_t *node, const char *path) { (void)sehnd; (void)xwr; (void)node; (void)path; fputs("Built without SELinux support, cannot add SELinux labels\n", stderr); return -1; } void *selinux_open_context_file(const char *filename) { (void)filename; fputs("Built without SELinux support, cannot open contexts file\n", stderr); return NULL; } void selinux_close_context_file(void *sehnd) { (void)sehnd; } #endif squashfs-tools-ng-1.1.3/bin/rdsquashfs/000077500000000000000000000000001410627516300200515ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/bin/rdsquashfs/Makemodule.am000066400000000000000000000011431410627516300224520ustar00rootroot00000000000000rdsquashfs_SOURCES = bin/rdsquashfs/rdsquashfs.c bin/rdsquashfs/rdsquashfs.h rdsquashfs_SOURCES += bin/rdsquashfs/list_files.c bin/rdsquashfs/options.c rdsquashfs_SOURCES += bin/rdsquashfs/restore_fstree.c bin/rdsquashfs/describe.c rdsquashfs_SOURCES += bin/rdsquashfs/fill_files.c bin/rdsquashfs/dump_xattrs.c rdsquashfs_SOURCES += bin/rdsquashfs/stat.c rdsquashfs_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) rdsquashfs_LDADD = libcommon.a libfstream.a libcompat.a libsquashfs.la rdsquashfs_LDADD += libfstree.a $(LZO_LIBS) $(PTHREAD_LIBS) dist_man1_MANS += bin/rdsquashfs/rdsquashfs.1 bin_PROGRAMS += rdsquashfs squashfs-tools-ng-1.1.3/bin/rdsquashfs/describe.c000066400000000000000000000055001410627516300217750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * describe.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" static int print_name(const sqfs_tree_node_t *n, bool dont_escape) { char *start, *ptr, *name = sqfs_tree_node_get_path(n); if (name == NULL) { perror("Recovering file path of tree node"); return -1; } if (canonicalize_name(name) != 0) { fprintf(stderr, "Error sanitizing file path '%s'\n", name); free(name); return -1; } if (dont_escape || (strchr(name, ' ') == NULL && strchr(name, '"') == NULL)) { fputs(name, stdout); } else { fputc('"', stdout); ptr = strchr(name, '"'); if (ptr != NULL) { start = name; do { fwrite(start, 1, ptr - start, stdout); fputs("\\\"", stdout); start = ptr + 1; ptr = strchr(start, '"'); } while (ptr != NULL); fputs(start, stdout); } else { fputs(name, stdout); } fputc('"', stdout); } free(name); return 0; } static void print_perm(const sqfs_tree_node_t *n) { printf(" 0%o %u %u", (unsigned int)n->inode->base.mode & (~S_IFMT), n->uid, n->gid); } static int print_simple(const char *type, const sqfs_tree_node_t *n, const char *extra) { printf("%s ", type); if (print_name(n, false)) return -1; print_perm(n); if (extra != NULL) printf(" %s", extra); fputc('\n', stdout); return 0; } int describe_tree(const sqfs_tree_node_t *root, const char *unpack_root) { const sqfs_tree_node_t *n; if (!is_filename_sane((const char *)root->name, false)) { fprintf(stderr, "Encountered illegal file name '%s'\n", root->name); return -1; } switch (root->inode->base.mode & S_IFMT) { case S_IFSOCK: return print_simple("sock", root, NULL); case S_IFLNK: return print_simple("slink", root, (const char *)root->inode->extra); case S_IFIFO: return print_simple("pipe", root, NULL); case S_IFREG: if (unpack_root == NULL) return print_simple("file", root, NULL); fputs("file ", stdout); if (print_name(root, false)) return -1; print_perm(root); printf(" %s/", unpack_root); if (print_name(root, true)) return -1; fputc('\n', stdout); break; case S_IFCHR: case S_IFBLK: { char buffer[32]; sqfs_u32 devno; if (root->inode->base.type == SQFS_INODE_EXT_BDEV || root->inode->base.type == SQFS_INODE_EXT_CDEV) { devno = root->inode->data.dev_ext.devno; } else { devno = root->inode->data.dev.devno; } sprintf(buffer, "%c %u %u", S_ISCHR(root->inode->base.mode) ? 'c' : 'b', major(devno), minor(devno)); return print_simple("nod", root, buffer); } case S_IFDIR: if (root->name[0] != '\0') { if (print_simple("dir", root, NULL)) return -1; } for (n = root->children; n != NULL; n = n->next) { if (describe_tree(n, unpack_root)) return -1; } break; default: break; } return 0; } squashfs-tools-ng-1.1.3/bin/rdsquashfs/dump_xattrs.c000066400000000000000000000043101410627516300225650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * dump_xattrs.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" static void print_hex(const sqfs_u8 *value, size_t len) { printf("0x"); while (len--) printf("%02X", *(value++)); } static bool is_printable(const sqfs_u8 *value, size_t len) { size_t utf8_cont = 0; sqfs_u8 x; while (len--) { x = *(value++); if (utf8_cont > 0) { if ((x & 0xC0) != 0x80) return false; --utf8_cont; } else { if (x < 0x80) { if (x < 0x20) { if (x >= 0x07 && x <= 0x0D) continue; if (x == 0x00) continue; return false; } if (x == 0x7F) return false; } if ((x & 0xE0) == 0xC0) { utf8_cont = 1; } else if ((x & 0xF0) == 0xE0) { utf8_cont = 2; } else if ((x & 0xF8) == 0xF0) { utf8_cont = 3; } else if ((x & 0xFC) == 0xF8) { utf8_cont = 4; } else if ((x & 0xFE) == 0xFC) { utf8_cont = 5; } if (utf8_cont > 0 && len < utf8_cont) return false; } } return true; } int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode) { sqfs_xattr_value_t *value; sqfs_xattr_entry_t *key; sqfs_xattr_id_t desc; sqfs_u32 index; size_t i; if (xattr == NULL) return 0; sqfs_inode_get_xattr_index(inode, &index); if (index == 0xFFFFFFFF) return 0; if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { fputs("Error resolving xattr index\n", stderr); return -1; } if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { fputs("Error locating xattr key-value pairs\n", stderr); return -1; } for (i = 0; i < desc.count; ++i) { if (sqfs_xattr_reader_read_key(xattr, &key)) { fputs("Error reading xattr key\n", stderr); return -1; } if (sqfs_xattr_reader_read_value(xattr, key, &value)) { fputs("Error reading xattr value\n", stderr); sqfs_free(key); return -1; } if (is_printable(key->key, key->size)) { printf("%s=", key->key); } else { print_hex(key->key, key->size); } if (is_printable(value->value, value->size)) { printf("%s\n", value->value); } else { print_hex(value->value, value->size); printf("\n"); } sqfs_free(key); sqfs_free(value); } return 0; } squashfs-tools-ng-1.1.3/bin/rdsquashfs/fill_files.c000066400000000000000000000076451410627516300223410ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fill_files.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "rdsquashfs.h" static struct file_ent { char *path; const sqfs_inode_generic_t *inode; } *files = NULL; static size_t num_files = 0, max_files = 0; static size_t block_size = 0; static int compare_files(const void *l, const void *r) { sqfs_u32 lhs_frag_idx, lhs_frag_off, rhs_frag_idx, rhs_frag_off; sqfs_u64 lhs_size, rhs_size, lhs_start, rhs_start; const struct file_ent *lhs = l, *rhs = r; sqfs_inode_get_frag_location(lhs->inode, &lhs_frag_idx, &lhs_frag_off); sqfs_inode_get_file_block_start(lhs->inode, &lhs_start); sqfs_inode_get_file_size(lhs->inode, &lhs_size); sqfs_inode_get_frag_location(rhs->inode, &rhs_frag_idx, &rhs_frag_off); sqfs_inode_get_file_block_start(rhs->inode, &rhs_start); sqfs_inode_get_file_size(rhs->inode, &rhs_size); /* Files with fragments come first, ordered by ID. In case of tie, files without data blocks come first, and the others are ordered by start block. */ if ((lhs_size % block_size) && (lhs_frag_off < block_size) && (lhs_frag_idx != 0xFFFFFFFF)) { if ((rhs_size % block_size) && (rhs_frag_off < block_size) && (rhs_frag_idx != 0xFFFFFFFF)) return -1; if (lhs_frag_idx < rhs_frag_idx) return -1; if (lhs_frag_idx > rhs_frag_idx) return 1; if (lhs_size < block_size) return (rhs_size < block_size) ? 0 : -1; if (rhs_size < block_size) return 1; goto order_by_start; } if ((rhs_size % block_size) && (rhs_frag_off < block_size) && (rhs_frag_idx != 0xFFFFFFFF)) return 1; /* order the rest by start block */ order_by_start: return lhs_start < rhs_start ? -1 : lhs_start > rhs_start ? 1 : 0; } static int add_file(const sqfs_tree_node_t *node) { struct file_ent *new; size_t new_sz; char *path; if (num_files == max_files) { new_sz = max_files ? max_files * 2 : 256; new = realloc(files, sizeof(files[0]) * new_sz); if (new == NULL) { perror("expanding file list"); return -1; } files = new; max_files = new_sz; } path = sqfs_tree_node_get_path(node); if (path == NULL) { perror("assembling file path"); return -1; } if (canonicalize_name(path)) { fprintf(stderr, "Invalid file path '%s'\n", path); free(path); return -1; } files[num_files].path = path; files[num_files].inode = node->inode; num_files++; return 0; } static void clear_file_list(void) { size_t i; for (i = 0; i < num_files; ++i) free(files[i].path); free(files); files = NULL; num_files = 0; max_files = 0; } static int gen_file_list_dfs(const sqfs_tree_node_t *n) { if (!is_filename_sane((const char *)n->name, true)) { fprintf(stderr, "Found an entry named '%s', skipping.\n", n->name); return 0; } if (S_ISREG(n->inode->base.mode)) return add_file(n); if (S_ISDIR(n->inode->base.mode)) { for (n = n->children; n != NULL; n = n->next) { if (gen_file_list_dfs(n)) return -1; } } return 0; } static int fill_files(sqfs_data_reader_t *data, int flags) { int ret, openflags; ostream_t *fp; size_t i; openflags = OSTREAM_OPEN_OVERWRITE; if (flags & UNPACK_NO_SPARSE) openflags |= OSTREAM_OPEN_SPARSE; for (i = 0; i < num_files; ++i) { fp = ostream_open_file(files[i].path, openflags); if (fp == NULL) return -1; if (!(flags & UNPACK_QUIET)) printf("unpacking %s\n", files[i].path); ret = sqfs_data_reader_dump(files[i].path, data, files[i].inode, fp, block_size); if (ret == 0) ret = ostream_flush(fp); sqfs_destroy(fp); if (ret) return -1; } return 0; } int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, sqfs_data_reader_t *data, int flags) { int status; block_size = blk_sz; if (gen_file_list_dfs(root)) { clear_file_list(); return -1; } qsort(files, num_files, sizeof(files[0]), compare_files); status = fill_files(data, flags); clear_file_list(); return status; } squashfs-tools-ng-1.1.3/bin/rdsquashfs/list_files.c000066400000000000000000000075431410627516300223630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * list_files.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" static void mode_to_str(sqfs_u16 mode, char *p) { switch (mode & S_IFMT) { case S_IFDIR: *(p++) = 'd'; break; case S_IFCHR: *(p++) = 'c'; break; case S_IFBLK: *(p++) = 'b'; break; case S_IFREG: *(p++) = '-'; break; case S_IFLNK: *(p++) = 'l'; break; case S_IFSOCK: *(p++) = 's'; break; case S_IFIFO: *(p++) = 'p'; break; default: *(p++) = '?'; break; } *(p++) = (mode & S_IRUSR) ? 'r' : '-'; *(p++) = (mode & S_IWUSR) ? 'w' : '-'; switch (mode & (S_IXUSR | S_ISUID)) { case S_IXUSR | S_ISUID: *(p++) = 's'; break; case S_IXUSR: *(p++) = 'x'; break; case S_ISUID: *(p++) = 'S'; break; default: *(p++) = '-'; break; } *(p++) = (mode & S_IRGRP) ? 'r' : '-'; *(p++) = (mode & S_IWGRP) ? 'w' : '-'; switch (mode & (S_IXGRP | S_ISGID)) { case S_IXGRP | S_ISGID: *(p++) = 's'; break; case S_IXGRP: *(p++) = 'x'; break; case S_ISGID: *(p++) = 'S'; break; case 0: *(p++) = '-'; break; default: break; } *(p++) = (mode & S_IROTH) ? 'r' : '-'; *(p++) = (mode & S_IWOTH) ? 'w' : '-'; switch (mode & (S_IXOTH | S_ISVTX)) { case S_IXOTH | S_ISVTX: *(p++) = 't'; break; case S_IXOTH: *(p++) = 'x'; break; case S_ISVTX: *(p++) = 'T'; break; case 0: *(p++) = '-'; break; default: break; } *p = '\0'; } static int count_int_chars(unsigned int i) { int count = 1; while (i > 10) { ++count; i /= 10; } return count; } static void print_node_size(const sqfs_tree_node_t *n, char *buffer) { switch (n->inode->base.mode & S_IFMT) { case S_IFLNK: print_size(strlen((const char *)n->inode->extra), buffer, true); break; case S_IFREG: { sqfs_u64 size; sqfs_inode_get_file_size(n->inode, &size); print_size(size, buffer, true); break; } case S_IFDIR: if (n->inode->base.type == SQFS_INODE_EXT_DIR) { print_size(n->inode->data.dir_ext.size, buffer, true); } else { print_size(n->inode->data.dir.size, buffer, true); } break; case S_IFBLK: case S_IFCHR: { sqfs_u32 devno; if (n->inode->base.type == SQFS_INODE_EXT_BDEV || n->inode->base.type == SQFS_INODE_EXT_CDEV) { devno = n->inode->data.dev_ext.devno; } else { devno = n->inode->data.dev.devno; } sprintf(buffer, "%u:%u", major(devno), minor(devno)); break; } default: buffer[0] = '0'; buffer[1] = '\0'; break; } } void list_files(const sqfs_tree_node_t *node) { int i, max_uid_chars = 0, max_gid_chars = 0, max_sz_chars = 0; char modestr[12], sizestr[32]; const sqfs_tree_node_t *n; if (S_ISDIR(node->inode->base.mode)) { for (n = node->children; n != NULL; n = n->next) { i = count_int_chars(n->uid); max_uid_chars = i > max_uid_chars ? i : max_uid_chars; i = count_int_chars(n->gid); max_gid_chars = i > max_gid_chars ? i : max_gid_chars; print_node_size(n, sizestr); i = strlen(sizestr); max_sz_chars = i > max_sz_chars ? i : max_sz_chars; } for (n = node->children; n != NULL; n = n->next) { mode_to_str(n->inode->base.mode, modestr); print_node_size(n, sizestr); printf("%s %*u/%-*u %*s %s", modestr, max_uid_chars, n->uid, max_gid_chars, n->gid, max_sz_chars, sizestr, n->name); if (S_ISLNK(n->inode->base.mode)) { printf(" -> %s\n", (const char *)n->inode->extra); } else { fputc('\n', stdout); } } } else { mode_to_str(node->inode->base.mode, modestr); print_node_size(node, sizestr); printf("%s %u/%u %s %s", modestr, node->uid, node->gid, sizestr, node->name); if (S_ISLNK(node->inode->base.mode)) { printf(" -> %s\n", (const char *)node->inode->extra); } else { fputc('\n', stdout); } } } squashfs-tools-ng-1.1.3/bin/rdsquashfs/options.c000066400000000000000000000146101410627516300217120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * options.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" static struct option long_opts[] = { { "list", required_argument, NULL, 'l' }, { "cat", required_argument, NULL, 'c' }, { "xattr", required_argument, NULL, 'x' }, { "stat", required_argument, NULL, 's' }, { "unpack-root", required_argument, NULL, 'p' }, { "unpack-path", required_argument, NULL, 'u' }, { "no-dev", no_argument, NULL, 'D' }, { "no-sock", no_argument, NULL, 'S' }, { "no-fifo", no_argument, NULL, 'F' }, { "no-slink", no_argument, NULL, 'L' }, { "no-empty-dir", no_argument, NULL, 'E' }, { "no-sparse", no_argument, NULL, 'Z' }, #ifdef HAVE_SYS_XATTR_H { "set-xattr", no_argument, NULL, 'X' }, #endif { "set-times", no_argument, NULL, 'T' }, { "describe", no_argument, NULL, 'd' }, { "chmod", no_argument, NULL, 'C' }, { "chown", no_argument, NULL, 'O' }, { "quiet", no_argument, NULL, 'q' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "l:c:u:p:x:s:DSFLCOEZTj:dqhV" #ifdef HAVE_SYS_XATTR_H "X" #endif ; static const char *help_string = "Usage: rdsquashfs [OPTIONS] \n" "\n" "View or extract the contents of a squashfs image.\n" "\n" "Possible options:\n" "\n" " --list, -l Produce a directory listing for a given path in\n" " the squashfs image.\n" " --cat, -c If the specified path is a regular file in the,\n" " image, dump its contents to stdout.\n" " --xattr, -x Enumerate extended attributes associated with\n" " an inode that the given path resolves to.\n" " --unpack-path, -u Unpack this sub directory from the image. To\n" " unpack everything, simply specify /.\n" " --stat, -s Dump all information that can be extracted from\n" " the inode coresponding to a path, including\n" " SquashFS specific internals.\n" " --describe, -d Produce a file listing from the image.\n" "\n" " --unpack-root, -p If used with --unpack-path, this is where the\n" " data unpacked to. If used with --describe, this\n" " is used as a prefix for the input path of\n" " regular files.\n" "\n" " --no-dev, -D Do not unpack device special files.\n" " --no-sock, -S Do not unpack socket files.\n" " --no-fifo, -F Do not unpack named pipes.\n" " --no-slink, -L Do not unpack symbolic links.\n" " --no-empty-dir, -E Do not unpack directories that would end up\n" " empty after applying the above rules.\n" " --no-sparse, -Z Do not create sparse files, always write zero\n" " blocks to disk.\n" #ifdef HAVE_SYS_XATTR_H " --set-xattr, -X When unpacking files to disk, set the extended\n" " attributes from the squashfs image.\n" #endif " --set-times, -T When unpacking files to disk, set the create\n" " and modify timestamps from the squashfs image.\n" " --chmod, -C Change permission flags of unpacked files to\n" " those store in the squashfs image.\n" " --chown, -O Change ownership of unpacked files to the\n" " UID/GID set in the squashfs image.\n" " --quiet, -q Do not print out progress while unpacking.\n" "\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n"; static char *get_path(char *old, const char *arg) { char *path; free(old); path = strdup(arg); if (path == NULL) { perror("processing arguments"); exit(EXIT_FAILURE); } if (canonicalize_name(path)) { fprintf(stderr, "Invalid path: %s\n", arg); free(path); exit(EXIT_FAILURE); } return path; } void process_command_line(options_t *opt, int argc, char **argv) { int i; opt->op = OP_NONE; opt->rdtree_flags = 0; opt->flags = 0; opt->cmdpath = NULL; opt->unpack_root = NULL; opt->image_name = NULL; for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case 'D': opt->rdtree_flags |= SQFS_TREE_NO_DEVICES; break; case 'S': opt->rdtree_flags |= SQFS_TREE_NO_SOCKETS; break; case 'F': opt->rdtree_flags |= SQFS_TREE_NO_FIFO; break; case 'L': opt->rdtree_flags |= SQFS_TREE_NO_SLINKS; break; case 'E': opt->rdtree_flags |= SQFS_TREE_NO_EMPTY; break; case 'C': opt->flags |= UNPACK_CHMOD; break; case 'O': opt->flags |= UNPACK_CHOWN; break; case 'Z': opt->flags |= UNPACK_NO_SPARSE; break; #ifdef HAVE_SYS_XATTR_H case 'X': opt->flags |= UNPACK_SET_XATTR; break; #endif case 'T': opt->flags |= UNPACK_SET_TIMES; break; case 'c': opt->op = OP_CAT; opt->cmdpath = get_path(opt->cmdpath, optarg); break; case 'd': opt->op = OP_DESCRIBE; free(opt->cmdpath); opt->cmdpath = NULL; break; case 'x': opt->op = OP_RDATTR; opt->cmdpath = get_path(opt->cmdpath, optarg); break; case 's': opt->op = OP_STAT; opt->cmdpath = get_path(opt->cmdpath, optarg); break; case 'l': opt->op = OP_LS; opt->cmdpath = get_path(opt->cmdpath, optarg); break; case 'p': opt->unpack_root = optarg; break; case 'u': opt->op = OP_UNPACK; opt->cmdpath = get_path(opt->cmdpath, optarg); break; case 'q': opt->flags |= UNPACK_QUIET; break; case 'h': fputs(help_string, stdout); free(opt->cmdpath); exit(EXIT_SUCCESS); case 'V': print_version("rdsquashfs"); free(opt->cmdpath); exit(EXIT_SUCCESS); default: goto fail_arg; } } if (opt->op == OP_NONE) { fputs("No operation specified\n", stderr); goto fail_arg; } if (opt->op == OP_LS || opt->op == OP_CAT || opt->op == OP_RDATTR || opt->op == OP_STAT) { opt->rdtree_flags |= SQFS_TREE_NO_RECURSE; } if (optind >= argc) { fputs("Missing image argument\n", stderr); goto fail_arg; } opt->image_name = argv[optind++]; return; fail_arg: fputs("Try `rdsquashfs --help' for more information.\n", stderr); free(opt->cmdpath); exit(EXIT_FAILURE); } squashfs-tools-ng-1.1.3/bin/rdsquashfs/rdsquashfs.1000066400000000000000000000064011410627516300223170ustar00rootroot00000000000000.TH RDSQUASHFS "1" "May 2019" "inspect SquashFS filesystems" "User Commands" .SH NAME rdsquashfs \- tool to examine or uncompress SquashFS filesystems .SH SYNOPSIS .B rdsquashfs [\fI\,OPTIONS\/\fR] \fI\,\/\fR .SH DESCRIPTION View or extract the contents of a squashfs image. .PP The following options can be used to specify what operation to perform. One of those has to be present: .TP \fB\-\-list\fR, \fB\-l\fR Produce a directory listing similar to \fBls \-l\fR for a given path in the SquashFS image. .TP \fB\-\-cat\fR, \fB\-c\fR If the specified path is a regular file in the image, extract it and dump its contents to stdout. .TP \fB\-\-xattr\fR, \fB\-x\fR If the inode that the specified path resolves to has extended attributes, dump them as key value pairs to stdout. .TP \fB\-\-unpack\-path\fR, \fB\-u\fR Unpack the specified sub directory from the image. To unpack everything, simply specify /. .TP \fB\-\-describe\fR, \fB\-d\fR Produce a file listing from the image compatible with the format consumed by gensquashfs. .TP \fB\-\-stat\fR, \fB\-s\fR Dump all available information about the inode that the path refers to, including SquashFS specific internals such as the on-disk layout of a file or the fast lookup index stored in an extended directory inode. .PP The following options can be used to control the behaviour of the specified operation: .TP \fB\-\-unpack\-root\fR, \fB\-p\fR If used with \fB\-\-unpack\-path\fR, this is where the data is unpacked to. If used with \fB\-\-describe\fR, this is used as a prefix for the input path of regular files. .TP \fB\-\-no\-dev\fR, \fB\-D\fR Skip device special files when parsing the filesystem tree. .TP \fB\-\-no\-sock\fR, \fB\-S\fR Skip socket files when parsing the filesystem tree. .TP \fB\-\-no\-fifo\fR, \fB\-F\fR Skip named pipes when parsing the filesystem tree. .TP \fB\-\-no\-slink\fR, \fB\-L\fR Skip symbolic links when parsing the filesystem tree. .TP \fB\-\-no\-empty\-dir\fR, \fB\-E\fR Skip empty directories, including ones that are empty after applying the above rules. .PP The following options are specific to unpacking files from a SquashFS image to disk: .TP \fB\-\-no\-sparse\fR, \fB\-Z\fR Do not create sparse files. Always unpack sparse files by writing blocks of zeros to disk. .TP \fB\-\-set\-xattr\fR, \fB\-X\fR Set the extended attributes from the SquashFS image. .TP \fB\-\-set\-times\fR, \fB\-T\fR Set the create and modify timestamps of the file to the mtime from the SquashFS image. .TP \fB\-\-chmod\fR, \fB\-C\fR Change permission flags of unpacked files to those stored in the SquashFS image. .TP \fB\-\-chown\fR, \fB\-O\fR Change ownership of unpacked files to the UID/GID set in the SquashFS image. .TP \fB\-\-quiet\fR, \fB\-q\fR Do not print out progress while unpacking. .PP Other options: .TP \fB\-\-help\fR, \fB\-h\fR Print help text and exit. .TP \fB\-\-version\fR, \fB\-V\fR Print version information and exit. .SH SEE ALSO gensquashfs(1), sqfs2tar(1), sqfsdiff(1) .SH AUTHOR Written by David Oberhollenzer. .SH COPYRIGHT Copyright \(co 2019 David Oberhollenzer License GPLv3+: GNU GPL version 3 or later . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. squashfs-tools-ng-1.1.3/bin/rdsquashfs/rdsquashfs.c000066400000000000000000000127731410627516300224120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * rdsquashfs.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" static sqfs_tree_node_t *list_merge(sqfs_tree_node_t *lhs, sqfs_tree_node_t *rhs) { sqfs_tree_node_t *it, *head = NULL, **next_ptr = &head; while (lhs != NULL && rhs != NULL) { if (strcmp((const char *)lhs->name, (const char *)rhs->name) <= 0) { it = lhs; lhs = lhs->next; } else { it = rhs; rhs = rhs->next; } *next_ptr = it; next_ptr = &it->next; } it = (lhs != NULL ? lhs : rhs); *next_ptr = it; return head; } static sqfs_tree_node_t *list_sort(sqfs_tree_node_t *head) { sqfs_tree_node_t *it, *half, *prev; it = half = prev = head; while (it != NULL) { prev = half; half = half->next; it = it->next; if (it != NULL) it = it->next; } if (half == NULL) return head; prev->next = NULL; return list_merge(list_sort(head), list_sort(half)); } static int tree_sort(sqfs_tree_node_t *root) { sqfs_tree_node_t *it; if (root->children == NULL) return 0; root->children = list_sort(root->children); /* XXX: not only an inconvenience but a security issue: e.g. we unpack a SquashFS image that has a symlink pointing somewhere, and then a sub-directory or file with the same name, the unpacker can be tricked to follow the symlink and write anything, anywhere on the filesystem. */ for (it = root->children; it->next != NULL; it = it->next) { if (strcmp((const char *)it->name, (const char *)it->next->name) == 0) { char *path = sqfs_tree_node_get_path(it); fprintf(stderr, "Entry '%s' found more than once!\n", path); sqfs_free(path); return -1; } } for (it = root->children; it != NULL; it = it->next) { if (tree_sort(it)) return -1; } return 0; } int main(int argc, char **argv) { sqfs_xattr_reader_t *xattr = NULL; sqfs_compressor_config_t cfg; int status = EXIT_FAILURE; sqfs_data_reader_t *data; sqfs_dir_reader_t *dirrd; sqfs_compressor_t *cmp; sqfs_id_table_t *idtbl; sqfs_tree_node_t *n; sqfs_super_t super; sqfs_file_t *file; options_t opt; int ret; process_command_line(&opt, argc, argv); file = sqfs_open_file(opt.image_name, SQFS_FILE_OPEN_READ_ONLY); if (file == NULL) { perror(opt.image_name); goto out_cmd; } ret = sqfs_super_read(&super, file); if (ret) { sqfs_perror(opt.image_name, "reading super block", ret); goto out_file; } sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&cfg, &cmp); #ifdef WITH_LZO if (super.compression_id == SQFS_COMP_LZO && ret != 0) ret = lzo_compressor_create(&cfg, &cmp); #endif if (ret != 0) { sqfs_perror(opt.image_name, "creating compressor", ret); goto out_file; } if (!(super.flags & SQFS_FLAG_NO_XATTRS)) { xattr = sqfs_xattr_reader_create(0); if (xattr == NULL) { sqfs_perror(opt.image_name, "creating xattr reader", SQFS_ERROR_ALLOC); goto out_cmp; } ret = sqfs_xattr_reader_load(xattr, &super, file, cmp); if (ret) { sqfs_perror(opt.image_name, "loading xattr table", ret); goto out_xr; } } idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { sqfs_perror(opt.image_name, "creating ID table", SQFS_ERROR_ALLOC); goto out_xr; } ret = sqfs_id_table_read(idtbl, file, &super, cmp); if (ret) { sqfs_perror(opt.image_name, "loading ID table", ret); goto out_id; } dirrd = sqfs_dir_reader_create(&super, cmp, file, 0); if (dirrd == NULL) { sqfs_perror(opt.image_name, "creating dir reader", SQFS_ERROR_ALLOC); goto out_id; } data = sqfs_data_reader_create(file, super.block_size, cmp, 0); if (data == NULL) { sqfs_perror(opt.image_name, "creating data reader", SQFS_ERROR_ALLOC); goto out_dr; } ret = sqfs_data_reader_load_fragment_table(data, &super); if (ret) { sqfs_perror(opt.image_name, "loading fragment table", ret); goto out_data; } ret = sqfs_dir_reader_get_full_hierarchy(dirrd, idtbl, opt.cmdpath, opt.rdtree_flags, &n); if (ret) { sqfs_perror(opt.image_name, "reading filesystem tree", ret); goto out_data; } switch (opt.op) { case OP_LS: list_files(n); break; case OP_STAT: if (stat_file(n)) goto out; break; case OP_CAT: { ostream_t *fp; if (!S_ISREG(n->inode->base.mode)) { fprintf(stderr, "/%s: not a regular file\n", opt.cmdpath); goto out; } fp = ostream_open_stdout(); if (fp == NULL) goto out; if (sqfs_data_reader_dump(opt.cmdpath, data, n->inode, fp, super.block_size)) { sqfs_destroy(fp); goto out; } sqfs_destroy(fp); break; } case OP_UNPACK: if (tree_sort(n)) goto out; if (opt.unpack_root != NULL) { if (mkdir_p(opt.unpack_root)) goto out; if (chdir(opt.unpack_root)) { perror(opt.unpack_root); goto out; } } if (restore_fstree(n, opt.flags)) goto out; if (fill_unpacked_files(super.block_size, n, data, opt.flags)) goto out; if (update_tree_attribs(xattr, n, opt.flags)) goto out; break; case OP_DESCRIBE: if (describe_tree(n, opt.unpack_root)) goto out; break; case OP_RDATTR: if (dump_xattrs(xattr, n->inode)) goto out; break; default: break; } status = EXIT_SUCCESS; out: sqfs_dir_tree_destroy(n); out_data: sqfs_destroy(data); out_dr: sqfs_destroy(dirrd); out_id: sqfs_destroy(idtbl); out_xr: if (xattr != NULL) sqfs_destroy(xattr); out_cmp: sqfs_destroy(cmp); out_file: sqfs_destroy(file); out_cmd: free(opt.cmdpath); return status; } squashfs-tools-ng-1.1.3/bin/rdsquashfs/rdsquashfs.h000066400000000000000000000032351410627516300224100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * rdsquashfs.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef RDSQUASHFS_H #define RDSQUASHFS_H #include "config.h" #include "common.h" #include "fstree.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #ifdef HAVE_SYS_XATTR_H #include #if defined(__APPLE__) && defined(__MACH__) #define lsetxattr(path, name, value, size, flags) \ setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW) #endif #endif #include #include #include #include #include #include #include #include enum UNPACK_FLAGS { UNPACK_CHMOD = 0x01, UNPACK_CHOWN = 0x02, UNPACK_QUIET = 0x04, UNPACK_NO_SPARSE = 0x08, UNPACK_SET_XATTR = 0x10, UNPACK_SET_TIMES = 0x20, }; enum { OP_NONE = 0, OP_LS, OP_CAT, OP_UNPACK, OP_DESCRIBE, OP_RDATTR, OP_STAT, }; typedef struct { int op; int rdtree_flags; int flags; char *cmdpath; const char *unpack_root; const char *image_name; } options_t; void list_files(const sqfs_tree_node_t *node); int stat_file(const sqfs_tree_node_t *node); int restore_fstree(sqfs_tree_node_t *root, int flags); int update_tree_attribs(sqfs_xattr_reader_t *xattr, const sqfs_tree_node_t *root, int flags); int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, sqfs_data_reader_t *data, int flags); int describe_tree(const sqfs_tree_node_t *root, const char *unpack_root); int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode); void process_command_line(options_t *opt, int argc, char **argv); #endif /* RDSQUASHFS_H */ squashfs-tools-ng-1.1.3/bin/rdsquashfs/restore_fstree.c000066400000000000000000000151271410627516300232560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * restore_fstree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" #ifdef _WIN32 static int create_node(const sqfs_tree_node_t *n, const char *name, int flags) { WCHAR *wpath; HANDLE fh; (void)flags; wpath = path_to_windows(name); if (wpath == NULL) return -1; switch (n->inode->base.mode & S_IFMT) { case S_IFDIR: if (!CreateDirectoryW(wpath, NULL)) { if (GetLastError() != ERROR_ALREADY_EXISTS) goto fail; } break; case S_IFREG: fh = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, 0, NULL); if (fh == INVALID_HANDLE_VALUE) goto fail; CloseHandle(fh); break; default: break; } free(wpath); return 0; fail: fprintf(stderr, "Creating %s: %ld\n", name, GetLastError()); free(wpath); return -1; } #else static int create_node(const sqfs_tree_node_t *n, const char *name, int flags) { sqfs_u32 devno; int fd, mode; switch (n->inode->base.mode & S_IFMT) { case S_IFDIR: if (mkdir(name, 0755) && errno != EEXIST) { fprintf(stderr, "mkdir %s: %s\n", name, strerror(errno)); return -1; } break; case S_IFLNK: if (symlink((const char *)n->inode->extra, name)) { fprintf(stderr, "ln -s %s %s: %s\n", (const char *)n->inode->extra, name, strerror(errno)); return -1; } break; case S_IFSOCK: case S_IFIFO: if (mknod(name, (n->inode->base.mode & S_IFMT) | 0700, 0)) { fprintf(stderr, "creating %s: %s\n", name, strerror(errno)); return -1; } break; case S_IFBLK: case S_IFCHR: if (n->inode->base.type == SQFS_INODE_EXT_BDEV || n->inode->base.type == SQFS_INODE_EXT_CDEV) { devno = n->inode->data.dev_ext.devno; } else { devno = n->inode->data.dev.devno; } if (mknod(name, n->inode->base.mode & S_IFMT, devno)) { fprintf(stderr, "creating device %s: %s\n", name, strerror(errno)); return -1; } break; case S_IFREG: if (flags & UNPACK_CHMOD) { mode = (n->inode->base.mode & ~S_IFMT) | 0200; } else { mode = 0644; } fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode); if (fd < 0) { fprintf(stderr, "creating %s: %s\n", name, strerror(errno)); return -1; } close(fd); break; default: break; } return 0; } #endif static int create_node_dfs(const sqfs_tree_node_t *n, int flags) { const sqfs_tree_node_t *c; char *name; int ret; if (!is_filename_sane((const char *)n->name, true)) { fprintf(stderr, "Found an entry named '%s', skipping.\n", n->name); return 0; } name = sqfs_tree_node_get_path(n); if (name == NULL) { fprintf(stderr, "Constructing full path for '%s': %s\n", (const char *)n->name, strerror(errno)); return -1; } ret = canonicalize_name(name); assert(ret == 0); if (!(flags & UNPACK_QUIET)) printf("creating %s\n", name); ret = create_node(n, name, flags); free(name); if (ret) return -1; if (S_ISDIR(n->inode->base.mode)) { for (c = n->children; c != NULL; c = c->next) { if (create_node_dfs(c, flags)) return -1; } } return 0; } #ifdef HAVE_SYS_XATTR_H static int set_xattr(const char *path, sqfs_xattr_reader_t *xattr, const sqfs_tree_node_t *n) { sqfs_xattr_value_t *value; sqfs_xattr_entry_t *key; sqfs_xattr_id_t desc; sqfs_u32 index; size_t i; int ret; sqfs_inode_get_xattr_index(n->inode, &index); if (index == 0xFFFFFFFF) return 0; if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { fputs("Error resolving xattr index\n", stderr); return -1; } if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { fputs("Error locating xattr key-value pairs\n", stderr); return -1; } for (i = 0; i < desc.count; ++i) { if (sqfs_xattr_reader_read_key(xattr, &key)) { fputs("Error reading xattr key\n", stderr); return -1; } if (sqfs_xattr_reader_read_value(xattr, key, &value)) { fputs("Error reading xattr value\n", stderr); sqfs_free(key); return -1; } ret = lsetxattr(path, (const char *)key->key, value->value, value->size, 0); if (ret) { fprintf(stderr, "setting xattr '%s' on %s: %s\n", key->key, path, strerror(errno)); } sqfs_free(key); sqfs_free(value); if (ret) return -1; } return 0; } #endif static int set_attribs(sqfs_xattr_reader_t *xattr, const sqfs_tree_node_t *n, int flags) { const sqfs_tree_node_t *c; char *path; int ret; if (!is_filename_sane((const char *)n->name, true)) return 0; if (S_ISDIR(n->inode->base.mode)) { for (c = n->children; c != NULL; c = c->next) { if (set_attribs(xattr, c, flags)) return -1; } } path = sqfs_tree_node_get_path(n); if (path == NULL) { fprintf(stderr, "Reconstructing full path: %s\n", strerror(errno)); return -1; } ret = canonicalize_name(path); assert(ret == 0); #ifdef HAVE_SYS_XATTR_H if ((flags & UNPACK_SET_XATTR) && xattr != NULL) { if (set_xattr(path, xattr, n)) goto fail; } #endif #ifndef _WIN32 if (flags & UNPACK_SET_TIMES) { struct timespec times[2]; memset(times, 0, sizeof(times)); times[0].tv_sec = n->inode->base.mod_time; times[1].tv_sec = n->inode->base.mod_time; if (utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW)) { fprintf(stderr, "setting timestamp on %s: %s\n", path, strerror(errno)); goto fail; } } #endif if (flags & UNPACK_CHOWN) { if (fchownat(AT_FDCWD, path, n->uid, n->gid, AT_SYMLINK_NOFOLLOW)) { fprintf(stderr, "chown %s: %s\n", path, strerror(errno)); goto fail; } } if (flags & UNPACK_CHMOD && !S_ISLNK(n->inode->base.mode)) { if (fchmodat(AT_FDCWD, path, n->inode->base.mode & ~S_IFMT, 0)) { fprintf(stderr, "chmod %s: %s\n", path, strerror(errno)); goto fail; } } free(path); return 0; fail: free(path); return -1; } int restore_fstree(sqfs_tree_node_t *root, int flags) { sqfs_tree_node_t *n, *old_parent; /* make sure fstree_get_path() stops at this node */ old_parent = root->parent; root->parent = NULL; if (S_ISDIR(root->inode->base.mode)) { for (n = root->children; n != NULL; n = n->next) { if (create_node_dfs(n, flags)) return -1; } } else { if (create_node_dfs(root, flags)) return -1; } root->parent = old_parent; return 0; } int update_tree_attribs(sqfs_xattr_reader_t *xattr, const sqfs_tree_node_t *root, int flags) { const sqfs_tree_node_t *n; if ((flags & (UNPACK_CHOWN | UNPACK_CHMOD | UNPACK_SET_TIMES | UNPACK_SET_XATTR)) == 0) { return 0; } if (S_ISDIR(root->inode->base.mode)) { for (n = root->children; n != NULL; n = n->next) { if (set_attribs(xattr, n, flags)) return -1; } } else { if (set_attribs(xattr, root, flags)) return -1; } return 0; } squashfs-tools-ng-1.1.3/bin/rdsquashfs/stat.c000066400000000000000000000126071410627516300211760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * stat.c * * Copyright (C) 2020 David Oberhollenzer */ #include "rdsquashfs.h" static const char *inode_types[] = { [SQFS_INODE_DIR] = "directory", [SQFS_INODE_FILE] = "file", [SQFS_INODE_SLINK] = "symbolic link", [SQFS_INODE_BDEV] = "block device", [SQFS_INODE_CDEV] = "character device", [SQFS_INODE_FIFO] = "named pipe", [SQFS_INODE_SOCKET] = "socket", [SQFS_INODE_EXT_DIR] = "extended directory", [SQFS_INODE_EXT_FILE] = "extended file", [SQFS_INODE_EXT_SLINK] = "extended symbolic link", [SQFS_INODE_EXT_BDEV] = "extended block device", [SQFS_INODE_EXT_CDEV] = "extended character device", [SQFS_INODE_EXT_FIFO] = "extended named pipe", [SQFS_INODE_EXT_SOCKET] = "extended socket", }; int stat_file(const sqfs_tree_node_t *node) { sqfs_u32 xattr_idx = 0xFFFFFFFF, devno = 0, link_size = 0; const sqfs_inode_generic_t *inode = node->inode; const char *type = NULL, *link_target = NULL; sqfs_u32 frag_idx, frag_offset; bool have_devno = false; sqfs_u64 location, size; unsigned int nlinks = 0; sqfs_dir_index_t *idx; char buffer[64]; time_t timeval; struct tm *tm; size_t i; int ret; /* decode */ if ((size_t)inode->base.type < sizeof(inode_types) / sizeof(inode_types[0])) { type = inode_types[inode->base.type]; } sqfs_inode_get_xattr_index(inode, &xattr_idx); switch (inode->base.type) { case SQFS_INODE_DIR: nlinks = inode->data.dir.nlink; break; case SQFS_INODE_SLINK: nlinks = inode->data.slink.nlink; link_target = (const char *)inode->extra; link_size = inode->data.slink.target_size; break; case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: nlinks = inode->data.dev.nlink; devno = inode->data.dev.devno; have_devno = true; break; case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: nlinks = inode->data.ipc.nlink; break; case SQFS_INODE_EXT_DIR: nlinks = inode->data.dir_ext.nlink; break; case SQFS_INODE_EXT_FILE: nlinks = inode->data.file_ext.nlink; break; case SQFS_INODE_EXT_SLINK: nlinks = inode->data.slink_ext.nlink; link_target = (const char *)inode->extra; link_size = inode->data.slink_ext.target_size; break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: nlinks = inode->data.dev_ext.nlink; devno = inode->data.dev_ext.devno; have_devno = true; break; case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: nlinks = inode->data.ipc_ext.nlink; break; default: break; } timeval = inode->base.mod_time; tm = gmtime(&timeval); strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", tm); /* info dump */ printf("Name: %s\n", (const char *)node->name); printf("Inode type: %s\n", type == NULL ? "UNKNOWN" : type); printf("Inode number: %u\n", inode->base.inode_number); printf("Access: 0%o\n", (unsigned int)inode->base.mode & ~SQFS_INODE_MODE_MASK); printf("UID: %u (index = %u)\n", node->uid, inode->base.uid_idx); printf("GID: %u (index = %u)\n", node->gid, inode->base.gid_idx); printf("Last modified: %s (%u)\n", buffer, inode->base.mod_time); if (type != NULL && inode->base.type != SQFS_INODE_FILE) printf("Hard link count: %u\n", nlinks); if (type != NULL && inode->base.type >= SQFS_INODE_EXT_DIR) printf("Xattr index: 0x%X\n", xattr_idx); if (link_target != NULL) printf("Link target: %.*s\n", (int)link_size, link_target); if (have_devno) { printf("Device number: %u:%u (%u)\n", major(devno), minor(devno), devno); } switch (inode->base.type) { case SQFS_INODE_FILE: case SQFS_INODE_EXT_FILE: sqfs_inode_get_file_block_start(inode, &location); sqfs_inode_get_file_size(inode, &size); sqfs_inode_get_frag_location(inode, &frag_idx, &frag_offset); printf("Fragment index: 0x%X\n", frag_idx); printf("Fragment offset: %u\n", frag_offset); printf("File size: %lu\n", (unsigned long)size); if (inode->base.type == SQFS_INODE_EXT_FILE) printf("Sparse: %lu\n", inode->data.file_ext.sparse); printf("Blocks start: %lu\n", (unsigned long)location); printf("Block count: %lu\n", (unsigned long)sqfs_inode_get_file_block_count(inode)); for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { printf("\tBlock #%lu size: %u (%s)\n", (unsigned long)i, SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]), SQFS_IS_BLOCK_COMPRESSED(inode->extra[i]) ? "compressed" : "uncompressed"); } break; case SQFS_INODE_DIR: printf("Start block: %u\n", inode->data.dir.start_block); printf("Offset: %u\n", inode->data.dir.offset); printf("Listing size: %u\n", inode->data.dir.size); printf("Parent inode: %u\n", inode->data.dir.parent_inode); break; case SQFS_INODE_EXT_DIR: printf("Start block: %u\n", inode->data.dir_ext.start_block); printf("Offset: %u\n", inode->data.dir_ext.offset); printf("Listing size: %u\n", inode->data.dir_ext.size); printf("Parent inode: %u\n", inode->data.dir_ext.parent_inode); printf("Directory index entries: %u\n", inode->data.dir_ext.inodex_count); if (inode->data.dir_ext.size == 0) break; for (i = 0; ; ++i) { ret = sqfs_inode_unpack_dir_index_entry(inode, &idx, i); if (ret == SQFS_ERROR_OUT_OF_BOUNDS) break; if (ret < 0) { sqfs_perror(NULL, "reading directory index", ret); return -1; } printf("\t'%.*s' -> block %u, header offset %u\n", (int)(idx->size + 1), idx->name, idx->start_block, idx->index); sqfs_free(idx); } break; default: break; } return 0; } squashfs-tools-ng-1.1.3/bin/sqfs2tar/000077500000000000000000000000001410627516300174335ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/bin/sqfs2tar/Makemodule.am000066400000000000000000000011011410627516300220260ustar00rootroot00000000000000sqfs2tar_SOURCES = bin/sqfs2tar/sqfs2tar.c bin/sqfs2tar/sqfs2tar.h sqfs2tar_SOURCES += bin/sqfs2tar/options.c bin/sqfs2tar/write_tree.c sqfs2tar_SOURCES += bin/sqfs2tar/xattr.c sqfs2tar_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfs2tar_LDADD = libcommon.a libutil.a libsquashfs.la libtar.a sqfs2tar_LDADD += libfstream.a libcompat.a libfstree.a sqfs2tar_LDADD += $(ZLIB_LIBS) $(XZ_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(BZIP2_LIBS) sqfs2tar_LDADD += $(PTHREAD_LIBS) if WITH_OWN_ZLIB sqfs2tar_LDADD += libz.la endif dist_man1_MANS += bin/sqfs2tar/sqfs2tar.1 bin_PROGRAMS += sqfs2tar squashfs-tools-ng-1.1.3/bin/sqfs2tar/options.c000066400000000000000000000132311410627516300212720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * options.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfs2tar.h" static struct option long_opts[] = { { "compressor", required_argument, NULL, 'c' }, { "subdir", required_argument, NULL, 'd' }, { "keep-as-dir", no_argument, NULL, 'k' }, { "root-becomes", required_argument, NULL, 'r' }, { "no-skip", no_argument, NULL, 's' }, { "no-xattr", no_argument, NULL, 'X' }, { "no-hard-links", no_argument, NULL, 'L' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "c:d:kr:sXLhV"; static const char *usagestr = "Usage: sqfs2tar [OPTIONS...] \n" "\n" "Read an input squashfs archive and turn it into a tar archive, written\n" "to stdout.\n" "\n" "Possible options:\n" "\n" " --compressor, -c If set, stream compress the resulting tarball.\n" " By default, the tarball is uncompressed.\n" "\n" " --subdir, -d Unpack the given sub directory instead of the\n" " filesystem root. Can be specified more than\n" " once to select multiple directories. If only\n" " one is specified, it becomes the new root of\n" " node of the archive file system tree.\n" "\n" " --root-becomes, -r Turn the root inode into a directory with the\n" " specified name. Everything else will be stored\n" " inside this directory. The special value '.' is\n" " allowed to prefix all tar paths with './' and\n" " add an entry named '.' for the root inode.\n" " If this option isn't used, all meta data stored\n" " in the root inode IS LOST!\n" "\n" " --keep-as-dir, -k If --subdir is used only once, don't make the\n" " subdir the archive root, instead keep it as\n" " prefix for all unpacked files.\n" " Using --subdir more than once implies\n" " --keep-as-dir.\n" " --no-xattr, -X Do not copy extended attributes.\n" " --no-hard-links, -L Do not generate hard links. Produce duplicate\n" " entries instead.\n" "\n" " --no-skip, -s Abort if a file cannot be stored in a tar\n" " archive. By default, it is simply skipped\n" " and a warning is written to stderr.\n" "\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n" "Supported tar compression formats:\n"; bool dont_skip = false; bool keep_as_dir = false; bool no_xattr = false; bool no_links = false; char *root_becomes = NULL; char **subdirs = NULL; size_t num_subdirs = 0; static size_t max_subdirs = 0; int compressor = 0; const char *filename = NULL; void process_args(int argc, char **argv) { size_t idx, new_count; const char *name; int i, ret; char **new; for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case 'c': compressor = fstream_compressor_id_from_name(optarg); if (compressor <= 0) { fprintf(stderr, "unknown compressor '%s'.\n", optarg); goto fail; } if (!fstream_compressor_exists(compressor)) { fprintf(stderr, "%s compressor is not supported.\n", optarg); goto fail; } break; case 'd': if (num_subdirs == max_subdirs) { new_count = max_subdirs ? max_subdirs * 2 : 16; new = realloc(subdirs, new_count * sizeof(subdirs[0])); if (new == NULL) goto fail_errno; max_subdirs = new_count; subdirs = new; } subdirs[num_subdirs] = strdup(optarg); if (subdirs[num_subdirs] == NULL) goto fail_errno; if (canonicalize_name(subdirs[num_subdirs])) { perror(optarg); goto fail; } ++num_subdirs; break; case 'r': free(root_becomes); root_becomes = strdup(optarg); if (root_becomes == NULL) goto fail_errno; if (strcmp(root_becomes, "./") == 0) root_becomes[1] = '\0'; if (strcmp(root_becomes, ".") == 0) break; if (canonicalize_name(root_becomes) != 0 || strlen(root_becomes) == 0) { fprintf(stderr, "Invalid root directory '%s'.\n", optarg); goto fail_arg; } break; case 'k': keep_as_dir = true; break; case 's': dont_skip = true; break; case 'X': no_xattr = true; break; case 'L': no_links = true; break; case 'h': fputs(usagestr, stdout); i = FSTREAM_COMPRESSOR_MIN; while (i <= FSTREAM_COMPRESSOR_MAX) { name = fstream_compressor_name_from_id(i); if (fstream_compressor_exists(i)) printf("\t%s\n", name); ++i; } fputc('\n', stdout); goto out_success; case 'V': print_version("sqfs2tar"); goto out_success; default: goto fail_arg; } } if (optind >= argc) { fputs("Missing argument: squashfs image\n", stderr); goto fail_arg; } filename = argv[optind++]; if (optind < argc) { fputs("Unknown extra arguments\n", stderr); goto fail_arg; } if (num_subdirs > 1) keep_as_dir = true; return; fail_errno: perror("parsing options"); goto fail; fail_arg: fputs("Try `sqfs2tar --help' for more information.\n", stderr); goto fail; fail: ret = EXIT_FAILURE; goto out_exit; out_success: ret = EXIT_SUCCESS; goto out_exit; out_exit: for (idx = 0; idx < num_subdirs; ++idx) free(subdirs[idx]); free(root_becomes); free(subdirs); exit(ret); } squashfs-tools-ng-1.1.3/bin/sqfs2tar/sqfs2tar.1000066400000000000000000000105311410627516300212620ustar00rootroot00000000000000.TH SQFS2TAR "1" "June 2019" "sqfs2tar" "User Commands" .SH NAME sqfs2tar \- turn a SquashFS image into a tar archive .SH SYNOPSIS .B sqfs2tar [\fI\,OPTIONS\/\fR...] \fI\,\/\fR .SH DESCRIPTION Quickly and painlessly turn a SquashFS filesystem image into a tar archive that can then be examined and processed by any tool that can work on tar archives. The resulting archive is written to stdout. .PP Possible options: .TP \fB\-\-compressor\fR, \fB\-c\fR By default the result is a raw, uncompressed tar ball. Using this option it is possible to select a stream compression format (such as \fBgzip\fR, \fBxz\fR, \fBzstd\fR or \fBbzip2\fR) to use for the output archive. Run \fBsqfs2tar \-\-help\fR to get a list of all available compressors. .TP \fB\-\-root\-becomes\fR, \fB\-r\fR Prefix all paths in the tarball with the given directory name and add an entry for this directory that receives all meta data (permissions, ownership, extended attributes, et cetera) of the root inode. The special value \fB.\fR can be used since many tar archivers themselves pack the attributes of the root directory that way and naturally support this. If this option is not used, all meta data from the root inode IS LOST! .TP \fB\-\-subdir\fR, \fB\-d\fR Unpack the given sub directory instead of the filesystem root. Can be specified more than once to select multiple directories. If only one is specified, it becomes the new root of the archive filesystem tree. .TP \fB\-\-keep\-as\-dir\fR, \fB\-k\fR If \fB\-\-subdir\fR is used only once, don't make the subdir the archive root, instead keep it as prefix for all unpacked files. Using \fB\-\-subdir\fR more than once implies \fB\-\-keep\-as\-dir\fR. .TP \fB\-\-no\-xattr\fR, \fB\-X\fR Discard extended attributes from the SquashFS image. The default behavior is to copy all xattrs attached to SquashFS inodes into the resulting tar archive. .TP \fB\-\-no\-hard\-links\fR, \fB\-L\fR Normally, sqfs2tar runs hard link detection and generates hard links for entries that refer to the same inode. If this flag is set, hard link detection is not performed and duplicate data records are generated instead. .TP \fB\-\-no\-skip\fR, \fB\-s\fR Abort if a file cannot be stored in a tar archive. For instance, the tar format does not support socket files, but SquashFS does. The default behaviour of \fBsqfs2tar\fR is to emit a warning to stderr and skip the entry. If this flag is set, processing is aborted and \fBsqfs2tar\fR exits with an error status. .TP \fB\-\-help\fR, \fB\-h\fR Print help text and exit. .TP \fB\-\-version\fR, \fB\-V\fR Print version information and exit. .SH COMPATIBILITY The output format is pre-POSIX ustar using GNU extensions where necessary. Experimentation determined that this is most widely supported by activeley used tar implementations (besides GNU tar), even more than the newer POSIX format with PAX extensions. If any file or directory is encountered that cannot be converted, it is skipped and a warning is written to stderr. Unless the \fB\-\-no\-skip\fR option is set, which aborts processing if a file cannot be converted. This mainly affects socket files which are supported by SquashFS but not by POSIX tar, GNU tar or PAX. Since the tar format contains a sequence of files with absolute names, it has no direct concept of a tree or an unnamed root node. Consequently, meta data from the SquashFS root inode is lost, unless the \fB\-\-root\-becomes\fR option is used. The output archive can optionally be compressed. Default settings are used for the supported compressors and there is currently no intention to expose finer grained control over them. To set custom compressor flags, create an uncompressed archive and pipe it into a dedicated compressor process. .SH EXAMPLES Turn a SquashFS image into a tar archive: .IP sqfs2tar rootfs.sqfs > rootfs.tar .TP Creating a compressed archive with gzip headers: .IP sqfs2tar --compressor gzip rootfs.sqfs > rootfs.tar.gz .TP Compressing the output archive, but using custom compressor flags: .IP sqfs2tar rootfs.sqfs | xz -9e > rootfs.tar.xz .SH SEE ALSO rdsquashfs(1), tar2sqfs(1) .SH AUTHOR Written by David Oberhollenzer. .SH COPYRIGHT Copyright \(co 2019 David Oberhollenzer License GPLv3+: GNU GPL version 3 or later . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. squashfs-tools-ng-1.1.3/bin/sqfs2tar/sqfs2tar.c000066400000000000000000000125621410627516300213520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sqfs2tar.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfs2tar.h" sqfs_xattr_reader_t *xr; sqfs_data_reader_t *data; sqfs_super_t super; ostream_t *out_file = NULL; static sqfs_file_t *file; char *assemble_tar_path(char *name, bool is_dir) { size_t len, new_len; char *temp; (void)is_dir; if (root_becomes == NULL && !is_dir) return name; new_len = strlen(name); if (root_becomes != NULL) new_len += strlen(root_becomes) + 1; if (is_dir) new_len += 1; temp = realloc(name, new_len + 1); if (temp == NULL) { perror("assembling tar entry filename"); free(name); return NULL; } name = temp; if (root_becomes != NULL) { len = strlen(root_becomes); memmove(name + len + 1, name, strlen(name) + 1); memcpy(name, root_becomes, len); name[len] = '/'; } if (is_dir) { len = strlen(name); if (len == 0 || name[len - 1] != '/') { name[len++] = '/'; name[len] = '\0'; } } return name; } static int terminate_archive(void) { char buffer[1024]; memset(buffer, '\0', sizeof(buffer)); return ostream_append(out_file, buffer, sizeof(buffer)); } static sqfs_tree_node_t *tree_merge(sqfs_tree_node_t *lhs, sqfs_tree_node_t *rhs) { sqfs_tree_node_t *head = NULL, **next_ptr = &head; sqfs_tree_node_t *it, *l, *r; int diff; while (lhs->children != NULL && rhs->children != NULL) { diff = strcmp((const char *)lhs->children->name, (const char *)rhs->children->name); if (diff < 0) { it = lhs->children; lhs->children = lhs->children->next; } else if (diff > 0) { it = rhs->children; rhs->children = rhs->children->next; } else { l = lhs->children; lhs->children = lhs->children->next; r = rhs->children; rhs->children = rhs->children->next; it = tree_merge(l, r); } *next_ptr = it; next_ptr = &it->next; } it = (lhs->children != NULL ? lhs->children : rhs->children); *next_ptr = it; sqfs_dir_tree_destroy(rhs); lhs->children = head; return lhs; } int main(int argc, char **argv) { sqfs_tree_node_t *root = NULL, *subtree; int flags, ret, status = EXIT_FAILURE; sqfs_compressor_config_t cfg; sqfs_compressor_t *cmp; sqfs_id_table_t *idtbl; sqfs_dir_reader_t *dr; size_t i; process_args(argc, argv); out_file = ostream_open_stdout(); if (out_file == NULL) { perror("changing stdout to binary mode"); goto out_dirs; } if (compressor > 0) { out_file = ostream_compressor_create(out_file, compressor); if (out_file == NULL) goto out_dirs; } file = sqfs_open_file(filename, SQFS_FILE_OPEN_READ_ONLY); if (file == NULL) { perror(filename); goto out_ostrm; } ret = sqfs_super_read(&super, file); if (ret) { sqfs_perror(filename, "reading super block", ret); goto out_fd; } sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&cfg, &cmp); #ifdef WITH_LZO if (super.compression_id == SQFS_COMP_LZO && ret != 0) ret = lzo_compressor_create(&cfg, &cmp); #endif if (ret != 0) { sqfs_perror(filename, "creating compressor", ret); goto out_fd; } idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { perror("creating ID table"); goto out_cmp; } ret = sqfs_id_table_read(idtbl, file, &super, cmp); if (ret) { sqfs_perror(filename, "loading ID table", ret); goto out_id; } data = sqfs_data_reader_create(file, super.block_size, cmp, 0); if (data == NULL) { sqfs_perror(filename, "creating data reader", SQFS_ERROR_ALLOC); goto out_id; } ret = sqfs_data_reader_load_fragment_table(data, &super); if (ret) { sqfs_perror(filename, "loading fragment table", ret); goto out_data; } dr = sqfs_dir_reader_create(&super, cmp, file, 0); if (dr == NULL) { sqfs_perror(filename, "creating dir reader", SQFS_ERROR_ALLOC); goto out_data; } if (!no_xattr && !(super.flags & SQFS_FLAG_NO_XATTRS)) { xr = sqfs_xattr_reader_create(0); if (xr == NULL) { sqfs_perror(filename, "creating xattr reader", SQFS_ERROR_ALLOC); goto out_dr; } ret = sqfs_xattr_reader_load(xr, &super, file, cmp); if (ret) { sqfs_perror(filename, "loading xattr table", ret); goto out_xr; } } if (num_subdirs == 0) { ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, NULL, 0, &root); if (ret) { sqfs_perror(filename, "loading filesystem tree", ret); goto out; } } else { flags = 0; if (keep_as_dir || num_subdirs > 1) flags = SQFS_TREE_STORE_PARENTS; for (i = 0; i < num_subdirs; ++i) { ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, subdirs[i], flags, &subtree); if (ret) { sqfs_perror(subdirs[i], "loading filesystem " "tree", ret); goto out; } if (root == NULL) { root = subtree; } else { root = tree_merge(root, subtree); } } } if (write_tree(root)) goto out; if (terminate_archive()) goto out; if (ostream_flush(out_file)) goto out; status = EXIT_SUCCESS; out: if (root != NULL) sqfs_dir_tree_destroy(root); out_xr: if (xr != NULL) sqfs_destroy(xr); out_dr: sqfs_destroy(dr); out_data: sqfs_destroy(data); out_id: sqfs_destroy(idtbl); out_cmp: sqfs_destroy(cmp); out_fd: sqfs_destroy(file); out_ostrm: sqfs_destroy(out_file); out_dirs: for (i = 0; i < num_subdirs; ++i) free(subdirs[i]); free(subdirs); free(root_becomes); return status; } squashfs-tools-ng-1.1.3/bin/sqfs2tar/sqfs2tar.h000066400000000000000000000020221410627516300213450ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sqfs2tar.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef SQFS2TAR_H #define SQFS2TAR_H #include "config.h" #include "common.h" #include "tar.h" #include #include #include #include #include #include #include /* options.c */ extern bool dont_skip; extern bool keep_as_dir; extern bool no_xattr; extern bool no_links; extern char *root_becomes; extern char **subdirs; extern size_t num_subdirs; extern int compressor; extern const char *filename; void process_args(int argc, char **argv); /* tar2sqfs.c */ extern sqfs_xattr_reader_t *xr; extern sqfs_data_reader_t *data; extern sqfs_super_t super; extern ostream_t *out_file; char *assemble_tar_path(char *name, bool is_dir); /* xattr.c */ int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, tar_xattr_t **out); /* write_tree.c */ int write_tree(const sqfs_tree_node_t *n); #endif /* SQFS2TAR_H */ squashfs-tools-ng-1.1.3/bin/sqfs2tar/write_tree.c000066400000000000000000000060711410627516300217540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * write_tree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfs2tar.h" static sqfs_hard_link_t *links = NULL; static unsigned int record_counter; static sqfs_hard_link_t *find_hard_link(const char *name, sqfs_u32 inum) { sqfs_hard_link_t *lnk = NULL; for (lnk = links; lnk != NULL; lnk = lnk->next) { if (lnk->inode_number == inum) { if (strcmp(name, lnk->target) == 0) lnk = NULL; break; } } return lnk; } static int write_tree_dfs(const sqfs_tree_node_t *n) { sqfs_hard_link_t *lnk = NULL; tar_xattr_t *xattr = NULL; char *name, *target; struct stat sb; size_t len; int ret; inode_stat(n, &sb); if (n->parent == NULL) { if (root_becomes == NULL) goto skip_hdr; len = strlen(root_becomes); name = malloc(len + 2); if (name == NULL) { perror("creating root directory"); return -1; } memcpy(name, root_becomes, len); name[len] = '/'; name[len + 1] = '\0'; } else { if (!is_filename_sane((const char *)n->name, false)) { fprintf(stderr, "Found a file named '%s', skipping.\n", n->name); if (dont_skip) { fputs("Not allowed to skip files, aborting!\n", stderr); return -1; } return 0; } name = sqfs_tree_node_get_path(n); if (name == NULL) { perror("resolving tree node path"); return -1; } if (canonicalize_name(name)) goto out_skip; name = assemble_tar_path(name, S_ISDIR(sb.st_mode)); if (name == NULL) return -1; lnk = find_hard_link(name, n->inode->base.inode_number); if (lnk != NULL) { ret = write_hard_link(out_file, &sb, name, lnk->target, record_counter++); free(name); return ret; } } if (!no_xattr) { if (get_xattrs(name, n->inode, &xattr)) { free(name); return -1; } } target = S_ISLNK(sb.st_mode) ? (char *)n->inode->extra : NULL; ret = write_tar_header(out_file, &sb, name, target, xattr, record_counter++); free_xattr_list(xattr); if (ret > 0) goto out_skip; if (ret < 0) { free(name); return -1; } if (S_ISREG(sb.st_mode)) { if (sqfs_data_reader_dump(name, data, n->inode, out_file, super.block_size)) { free(name); return -1; } if (padd_file(out_file, sb.st_size)) { free(name); return -1; } } free(name); skip_hdr: for (n = n->children; n != NULL; n = n->next) { if (write_tree_dfs(n)) return -1; } return 0; out_skip: if (dont_skip) { fputs("Not allowed to skip files, aborting!\n", stderr); ret = -1; } else { fprintf(stderr, "Skipping %s\n", name); ret = 0; } free(name); return ret; } int write_tree(const sqfs_tree_node_t *n) { sqfs_hard_link_t *lnk; int status = -1; if (!no_links) { if (sqfs_tree_find_hard_links(n, &links)) return -1; for (lnk = links; lnk != NULL; lnk = lnk->next) { lnk->target = assemble_tar_path(lnk->target, false); if (lnk->target == NULL) goto out_links; } } status = write_tree_dfs(n); out_links: while (links != NULL) { lnk = links; links = links->next; free(lnk->target); free(lnk); } return status; } squashfs-tools-ng-1.1.3/bin/sqfs2tar/xattr.c000066400000000000000000000034511410627516300207440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xattr.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfs2tar.h" static tar_xattr_t *mkxattr(const sqfs_xattr_entry_t *key, const sqfs_xattr_value_t *value) { tar_xattr_t *ent; ent = calloc(1, sizeof(*ent) + strlen((const char *)key->key) + value->size + 2); if (ent == NULL) { perror("creating xattr entry"); return NULL; } ent->key = ent->data; ent->value = (sqfs_u8 *)ent->key + strlen((const char *)key->key) + 1; ent->value_len = value->size; strcpy(ent->key, (const char *)key->key); memcpy(ent->value, value->value, value->size + 1); return ent; } int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, tar_xattr_t **out) { tar_xattr_t *list = NULL, *ent; sqfs_xattr_value_t *value; sqfs_xattr_entry_t *key; sqfs_xattr_id_t desc; sqfs_u32 index; size_t i; int ret; if (xr == NULL) return 0; sqfs_inode_get_xattr_index(inode, &index); if (index == 0xFFFFFFFF) return 0; ret = sqfs_xattr_reader_get_desc(xr, index, &desc); if (ret) { sqfs_perror(name, "resolving xattr index", ret); return -1; } ret = sqfs_xattr_reader_seek_kv(xr, &desc); if (ret) { sqfs_perror(name, "locating xattr key-value pairs", ret); return -1; } for (i = 0; i < desc.count; ++i) { ret = sqfs_xattr_reader_read_key(xr, &key); if (ret) { sqfs_perror(name, "reading xattr key", ret); goto fail; } ret = sqfs_xattr_reader_read_value(xr, key, &value); if (ret) { sqfs_perror(name, "reading xattr value", ret); sqfs_free(key); goto fail; } ent = mkxattr(key, value); sqfs_free(key); sqfs_free(value); if (ent == NULL) goto fail; ent->next = list; list = ent; } *out = list; return 0; fail: free_xattr_list(list); return -1; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/000077500000000000000000000000001410627516300174735ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/bin/sqfsdiff/Makemodule.am000066400000000000000000000010621410627516300220740ustar00rootroot00000000000000sqfsdiff_SOURCES = bin/sqfsdiff/sqfsdiff.c bin/sqfsdiff/sqfsdiff.h sqfsdiff_SOURCES += bin/sqfsdiff/util.c bin/sqfsdiff/options.c sqfsdiff_SOURCES += bin/sqfsdiff/compare_dir.c bin/sqfsdiff/node_compare.c sqfsdiff_SOURCES += bin/sqfsdiff/compare_files.c bin/sqfsdiff/super.c sqfsdiff_SOURCES += bin/sqfsdiff/extract.c sqfsdiff_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfsdiff_LDADD = libcommon.a libsquashfs.la libfstream.a libcompat.a sqfsdiff_LDADD += $(LZO_LIBS) libfstree.a $(PTHREAD_LIBS) dist_man1_MANS += bin/sqfsdiff/sqfsdiff.1 bin_PROGRAMS += sqfsdiff squashfs-tools-ng-1.1.3/bin/sqfsdiff/compare_dir.c000066400000000000000000000036761410627516300221370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compare_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" static int print_omitted(sqfsdiff_t *sd, bool is_old, sqfs_tree_node_t *n) { char *path = node_path(n); if (path == NULL) return -1; fprintf(stdout, "%c %s\n", is_old ? '<' : '>', path); if ((sd->compare_flags & COMPARE_EXTRACT_FILES) && S_ISREG(n->inode->base.mode)) { if (extract_files(sd, is_old ? n->inode : NULL, is_old ? NULL : n->inode, path)) { free(path); return -1; } } free(path); for (n = n->children; n != NULL; n = n->next) { if (print_omitted(sd, is_old, n)) return -1; } return 0; } int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, sqfs_tree_node_t *new) { sqfs_tree_node_t *old_it = old->children, *old_prev = NULL; sqfs_tree_node_t *new_it = new->children, *new_prev = NULL; int ret, result = 0; while (old_it != NULL || new_it != NULL) { if (old_it != NULL && new_it != NULL) { ret = strcmp((const char *)old_it->name, (const char *)new_it->name); } else if (old_it == NULL) { ret = 1; } else { ret = -1; } if (ret < 0) { result = 1; if (print_omitted(sd, true, old_it)) return -1; if (old_prev == NULL) { old->children = old_it->next; sqfs_dir_tree_destroy(old_it); old_it = old->children; } else { old_prev->next = old_it->next; sqfs_dir_tree_destroy(old_it); old_it = old_prev->next; } } else if (ret > 0) { result = 1; if (print_omitted(sd, false, new_it)) return -1; if (new_prev == NULL) { new->children = new_it->next; sqfs_dir_tree_destroy(new_it); new_it = new->children; } else { new_prev->next = new_it->next; sqfs_dir_tree_destroy(new_it); new_it = new_prev->next; } } else { old_prev = old_it; old_it = old_it->next; new_prev = new_it; new_it = new_it->next; } } return result; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/compare_files.c000066400000000000000000000031431410627516300224500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compare_files.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" static unsigned char old_buf[MAX_WINDOW_SIZE]; static unsigned char new_buf[MAX_WINDOW_SIZE]; static int read_blob(const char *prefix, const char *path, sqfs_data_reader_t *rd, const sqfs_inode_generic_t *inode, void *buffer, sqfs_u64 offset, size_t size) { ssize_t ret; ret = sqfs_data_reader_read(rd, inode, offset, buffer, size); ret = (ret < 0 || (size_t)ret < size) ? -1 : 0; if (ret) { fprintf(stderr, "Failed to read %s from %s\n", path, prefix); return -1; } return 0; } int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, const sqfs_inode_generic_t *new, const char *path) { sqfs_u64 offset, diff, oldsz, newsz; int status = 0, ret; sqfs_inode_get_file_size(old, &oldsz); sqfs_inode_get_file_size(new, &newsz); if (oldsz != newsz) goto out_different; if (sd->compare_flags & COMPARE_NO_CONTENTS) return 0; for (offset = 0; offset < oldsz; offset += diff) { diff = oldsz - offset; if (diff > MAX_WINDOW_SIZE) diff = MAX_WINDOW_SIZE; ret = read_blob(sd->old_path, path, sd->sqfs_old.data, old, old_buf, offset, diff); if (ret) return -1; ret = read_blob(sd->new_path, path, sd->sqfs_new.data, new, new_buf, offset, diff); if (ret) return -1; if (memcmp(old_buf, new_buf, diff) != 0) goto out_different; } return status; out_different: if (sd->compare_flags & COMPARE_EXTRACT_FILES) { if (extract_files(sd, old, new, path)) return -1; } return 1; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/extract.c000066400000000000000000000022711410627516300213130ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * extract.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" static int extract(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, const char *prefix, const char *path, size_t block_size) { char *ptr, *temp; ostream_t *fp; temp = alloca(strlen(prefix) + strlen(path) + 2); sprintf(temp, "%s/%s", prefix, path); ptr = strrchr(temp, '/'); *ptr = '\0'; if (mkdir_p(temp)) return -1; *ptr = '/'; fp = ostream_open_file(temp, OSTREAM_OPEN_OVERWRITE | OSTREAM_OPEN_SPARSE); if (fp == NULL) { perror(temp); return -1; } if (sqfs_data_reader_dump(path, data, inode, fp, block_size)) { sqfs_destroy(fp); return -1; } ostream_flush(fp); sqfs_destroy(fp); return 0; } int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, const sqfs_inode_generic_t *new, const char *path) { if (old != NULL) { if (extract(sd->sqfs_old.data, old, "old", path, sd->sqfs_old.super.block_size)) return -1; } if (new != NULL) { if (extract(sd->sqfs_new.data, new, "new", path, sd->sqfs_new.super.block_size)) return -1; } return 0; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/node_compare.c000066400000000000000000000111521410627516300222720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * node_compare.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b) { char *path = sqfs_tree_node_get_path(a); sqfs_tree_node_t *ait, *bit; bool promoted, demoted; int ret, status = 0; if (path == NULL) { perror("constructing absolute file path"); return -1; } if (a->inode->base.type != b->inode->base.type) { promoted = demoted = false; switch (a->inode->base.type) { case SQFS_INODE_DIR: if (b->inode->base.type == SQFS_INODE_EXT_DIR) promoted = true; break; case SQFS_INODE_FILE: if (b->inode->base.type == SQFS_INODE_EXT_FILE) promoted = true; break; case SQFS_INODE_SLINK: if (b->inode->base.type == SQFS_INODE_EXT_SLINK) promoted = true; break; case SQFS_INODE_BDEV: if (b->inode->base.type == SQFS_INODE_EXT_BDEV) promoted = true; break; case SQFS_INODE_CDEV: if (b->inode->base.type == SQFS_INODE_EXT_CDEV) promoted = true; break; case SQFS_INODE_FIFO: if (b->inode->base.type == SQFS_INODE_EXT_FIFO) promoted = true; break; case SQFS_INODE_SOCKET: if (b->inode->base.type == SQFS_INODE_EXT_SOCKET) promoted = true; break; case SQFS_INODE_EXT_DIR: if (b->inode->base.type == SQFS_INODE_DIR) demoted = true; break; case SQFS_INODE_EXT_FILE: if (b->inode->base.type == SQFS_INODE_FILE) demoted = true; break; case SQFS_INODE_EXT_SLINK: if (b->inode->base.type == SQFS_INODE_SLINK) demoted = true; break; case SQFS_INODE_EXT_BDEV: if (b->inode->base.type == SQFS_INODE_BDEV) demoted = true; break; case SQFS_INODE_EXT_CDEV: if (b->inode->base.type == SQFS_INODE_CDEV) demoted = true; break; case SQFS_INODE_EXT_FIFO: if (b->inode->base.type == SQFS_INODE_FIFO) demoted = true; break; case SQFS_INODE_EXT_SOCKET: if (b->inode->base.type == SQFS_INODE_SOCKET) demoted = true; break; default: break; } if (promoted) { fprintf(stdout, "%s has an extended type\n", path); status = 1; } else if (demoted) { fprintf(stdout, "%s has a basic type\n", path); status = 1; } else { fprintf(stdout, "%s has a different type\n", path); free(path); return 1; } } if (!(sd->compare_flags & COMPARE_NO_PERM)) { if ((a->inode->base.mode & ~S_IFMT) != (b->inode->base.mode & ~S_IFMT)) { fprintf(stdout, "%s has different permissions\n", path); status = 1; } } if (!(sd->compare_flags & COMPARE_NO_OWNER)) { if (a->uid != b->uid || a->gid != b->gid) { fprintf(stdout, "%s has different ownership\n", path); status = 1; } } if (sd->compare_flags & COMPARE_TIMESTAMP) { if (a->inode->base.mod_time != b->inode->base.mod_time) { fprintf(stdout, "%s has a different timestamp\n", path); status = 1; } } if (sd->compare_flags & COMPARE_INODE_NUM) { if (a->inode->base.inode_number != b->inode->base.inode_number) { fprintf(stdout, "%s has a different inode number\n", path); status = 1; } } switch (a->inode->base.type) { case SQFS_INODE_SOCKET: case SQFS_INODE_EXT_SOCKET: case SQFS_INODE_FIFO: case SQFS_INODE_EXT_FIFO: break; case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: if (a->inode->data.dev.devno != b->inode->data.dev.devno) { fprintf(stdout, "%s has different device number\n", path); status = 1; } break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: if (a->inode->data.dev_ext.devno != b->inode->data.dev_ext.devno) { fprintf(stdout, "%s has different device number\n", path); status = 1; } break; case SQFS_INODE_SLINK: case SQFS_INODE_EXT_SLINK: if (strcmp((const char *)a->inode->extra, (const char *)b->inode->extra)) { fprintf(stdout, "%s has a different link target\n", path); } break; case SQFS_INODE_DIR: case SQFS_INODE_EXT_DIR: ret = compare_dir_entries(sd, a, b); if (ret < 0) { status = -1; break; } if (ret > 0) status = 1; free(path); path = NULL; ait = a->children; bit = b->children; while (ait != NULL && bit != NULL) { ret = node_compare(sd, ait, bit); if (ret < 0) return -1; if (ret > 0) status = 1; ait = ait->next; bit = bit->next; } break; case SQFS_INODE_FILE: case SQFS_INODE_EXT_FILE: ret = compare_files(sd, a->inode, b->inode, path); if (ret < 0) { status = -1; } else if (ret > 0) { fprintf(stdout, "regular file %s differs\n", path); status = 1; } break; default: fprintf(stdout, "%s has unknown type, ignoring\n", path); break; } free(path); return status; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/options.c000066400000000000000000000074631410627516300213440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sqfsdiff.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" static struct option long_opts[] = { { "old", required_argument, NULL, 'a' }, { "new", required_argument, NULL, 'b' }, { "no-owner", no_argument, NULL, 'O' }, { "no-permissions", no_argument, NULL, 'P' }, { "no-contents", no_argument, NULL, 'C' }, { "timestamps", no_argument, NULL, 'T' }, { "inode-num", no_argument, NULL, 'I' }, { "super", no_argument, NULL, 'S' }, { "extract", required_argument, NULL, 'e' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "a:b:OPCTISe:hV"; static const char *usagestr = "Usage: sqfsdiff [OPTIONS...] --old,-a --new,-b \n" "\n" "Compare two squashfs images. In contrast to doing a direct diff of the\n" "images, this actually parses the filesystems and generates a more\n" "meaningful difference report.\n" "\n" "If only contents are compared, any differences in packed file layout,\n" "ordering, compression, inode meta data and so on is ignored and the two\n" "images are considered equal if each directory contains the same entries,\n" "symlink with the same paths have the same targets, device nodes the same\n" "device number and files the same size and contents.\n" "\n" "A report of any difference is printed to stdout. The exit status is similar\n" "that of diff(1): 0 means equal, 1 means different, 2 means problem.\n" "\n" "Possible options:\n" "\n" " --old, -a The first of the two filesystems to compare.\n" " --new, -b The second of the two filesystems to compare.\n" "\n" " --no-contents, -C Do not compare file contents.\n" " --no-owner, -O Do not compare file owners.\n" " --no-permissions, -P Do not compare permission bits.\n" "\n" " --timestamps, -T Compare file timestamps.\n" " --inode-num, -I Compare inode numbers of all files.\n" " --super, -S Also compare meta data in super blocks.\n" "\n" " --extract, -e Extract files that differ to the specified\n" " directory. Contents of the first filesystem\n" " end up in a subdirectory 'old' and of the\n" " second filesystem in a subdirectory 'new'.\n" "\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n"; void process_options(sqfsdiff_t *sd, int argc, char **argv) { int i; for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case 'a': sd->old_path = optarg; break; case 'b': sd->new_path = optarg; break; case 'O': sd->compare_flags |= COMPARE_NO_OWNER; break; case 'P': sd->compare_flags |= COMPARE_NO_PERM; break; case 'C': sd->compare_flags |= COMPARE_NO_CONTENTS; break; case 'T': sd->compare_flags |= COMPARE_TIMESTAMP; break; case 'I': sd->compare_flags |= COMPARE_INODE_NUM; break; case 'S': sd->compare_super = true; break; case 'e': sd->compare_flags |= COMPARE_EXTRACT_FILES; sd->extract_dir = optarg; break; case 'h': fputs(usagestr, stdout); exit(0); case 'V': print_version("sqfsdiff"); exit(0); default: goto fail_arg; } } if (sd->old_path == NULL) { fputs("Missing arguments: first filesystem\n", stderr); goto fail_arg; } if (sd->new_path == NULL) { fputs("Missing arguments: second filesystem\n", stderr); goto fail_arg; } if (optind < argc) { fputs("Unknown extra arguments\n", stderr); goto fail_arg; } return; fail_arg: fprintf(stderr, "Try `sqfsdiff --help' for more information.\n"); exit(2); } squashfs-tools-ng-1.1.3/bin/sqfsdiff/sqfsdiff.1000066400000000000000000000046331410627516300213700ustar00rootroot00000000000000.TH SQFSDIFF "1" "August 2019" "sqfsdiff" "User Commands" .SH NAME sqfsdiff \- compare two squashfs images by contents and metadata .SH SYNOPSIS .B sqfsdiff [\fI\,OPTIONS\/\fR...] \-\-old \fI\,\fR \-\-new \fI\,\/\fR .SH DESCRIPTION Compare two squashfs images. In contrast to doing a direct diff of the images, this actually parses the filesystems and generates a more meaningful difference report. .PP If only contents are compared, any differences in packed file layout, ordering, compression, inode meta data and so on is ignored and the two images are considered equal if each directory contains the same entries, symlink with the same paths have the same targets, device nodes the same device number and files the same size and contents. .PP A report of any difference is printed to stdout. The exit status is similar that of diff(1): 0 means equal, 1 means different, 2 means problem. .PP Possible options: .TP \fB\-\-old\fR, \fB\-a\fR Specify the first filesystem image or source directory, relativ to which the changes are evaluated. .TP \fB\-\-new\fR, \fB\-b\fR Specify the second filesystem image to source directory to compare to the first one. .TP \fB\-\-no\-contents\fR, \fB\-C\fR Do not compare file contents. .TP \fB\-\-no\-owner\fR, \fB\-O\fR Do not compare file owners. .TP \fB\-\-no\-permissions\fR, \fB\-P\fR Do not compare permission bits. .TP \fB\-\-timestamps\fR, \fB\-T\fR Compare file timestamps. .TP \fB\-\-inode\-num\fR, \fB\-I\fR Compare inode numbers of all files. .TP \fB\-\-super\fR, \fB\-S\fR Also compare meta data in super blocks. .TP \fB\-\-extract\fR, \fB\-e\fR Extract files that exist in both images but have different contents to the specified directory. Contents of the first image end up in a sub directory named \fBold\fR and the contents of the second image in a sub directory named \fBnew\fR. .TP \fB\-\-help\fR, \fB\-h\fR Print help text and exit. .TP \fB\-\-version\fR, \fB\-V\fR Print version information and exit. .SH EXIT STATUS The exit status is similar that of diff(1): 0 means equal, 1 means different, 2 means problem. .SH SEE ALSO rdsquashfs(1), sqfs2tar(1) .SH AUTHOR Written by David Oberhollenzer. .SH COPYRIGHT Copyright \(co 2019 David Oberhollenzer et al License GPLv3+: GNU GPL version 3 or later . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. squashfs-tools-ng-1.1.3/bin/sqfsdiff/sqfsdiff.c000066400000000000000000000073101410627516300214450ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sqfsdiff.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" static int open_sfqs(sqfs_state_t *state, const char *path) { int ret; state->file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); if (state->file == NULL) { perror(path); return -1; } ret = sqfs_super_read(&state->super, state->file); if (ret) { sqfs_perror(path, "reading super block", ret); goto fail_file; } sqfs_compressor_config_init(&state->cfg, state->super.compression_id, state->super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&state->cfg, &state->cmp); #ifdef WITH_LZO if (state->super.compression_id == SQFS_COMP_LZO && ret != 0) ret = lzo_compressor_create(&state->cfg, &state->cmp); #endif if (ret != 0) { sqfs_perror(path, "creating compressor", ret); goto fail_file; } if (state->super.flags & SQFS_FLAG_COMPRESSOR_OPTIONS) { ret = state->cmp->read_options(state->cmp, state->file); if (ret == 0) { state->cmp->get_configuration(state->cmp, &state->options); state->have_options = true; } else { sqfs_perror(path, "reading compressor options", ret); state->have_options = false; } } else { state->have_options = false; } state->idtbl = sqfs_id_table_create(0); if (state->idtbl == NULL) { sqfs_perror(path, "creating ID table", SQFS_ERROR_ALLOC); goto fail_cmp; } ret = sqfs_id_table_read(state->idtbl, state->file, &state->super, state->cmp); if (ret) { sqfs_perror(path, "loading ID table", ret); goto fail_id; } state->dr = sqfs_dir_reader_create(&state->super, state->cmp, state->file, 0); if (state->dr == NULL) { sqfs_perror(path, "creating directory reader", SQFS_ERROR_ALLOC); goto fail_id; } ret = sqfs_dir_reader_get_full_hierarchy(state->dr, state->idtbl, NULL, 0, &state->root); if (ret) { sqfs_perror(path, "loading filesystem tree", ret); goto fail_dr; } state->data = sqfs_data_reader_create(state->file, state->super.block_size, state->cmp, 0); if (state->data == NULL) { sqfs_perror(path, "creating data reader", SQFS_ERROR_ALLOC); goto fail_tree; } ret = sqfs_data_reader_load_fragment_table(state->data, &state->super); if (ret) { sqfs_perror(path, "loading fragment table", ret); goto fail_data; } return 0; fail_data: sqfs_destroy(state->data); fail_tree: sqfs_dir_tree_destroy(state->root); fail_dr: sqfs_destroy(state->dr); fail_id: sqfs_destroy(state->idtbl); fail_cmp: sqfs_destroy(state->cmp); fail_file: sqfs_destroy(state->file); return -1; } static void close_sfqs(sqfs_state_t *state) { sqfs_destroy(state->data); sqfs_dir_tree_destroy(state->root); sqfs_destroy(state->dr); sqfs_destroy(state->idtbl); sqfs_destroy(state->cmp); sqfs_destroy(state->file); } int main(int argc, char **argv) { int status, ret = 0; sqfsdiff_t sd; memset(&sd, 0, sizeof(sd)); process_options(&sd, argc, argv); if (sd.extract_dir != NULL) { if (mkdir_p(sd.extract_dir)) return 2; } if (open_sfqs(&sd.sqfs_old, sd.old_path)) return 2; if (open_sfqs(&sd.sqfs_new, sd.new_path)) { status = 2; goto out_sqfs_old; } if (sd.extract_dir != NULL) { if (chdir(sd.extract_dir)) { perror(sd.extract_dir); ret = -1; goto out; } } ret = node_compare(&sd, sd.sqfs_old.root, sd.sqfs_new.root); if (ret != 0) goto out; if (sd.compare_super) { ret = compare_super_blocks(&sd.sqfs_old.super, &sd.sqfs_new.super); if (ret != 0) goto out; } out: if (ret < 0) { status = 2; } else if (ret > 0) { status = 1; } else { status = 0; } close_sfqs(&sd.sqfs_new); out_sqfs_old: close_sfqs(&sd.sqfs_old); return status; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/sqfsdiff.h000066400000000000000000000031511410627516300214510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sqfsdiff.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef DIFFTOOL_H #define DIFFTOOL_H #include "config.h" #include "common.h" #include "fstree.h" #include #include #include #include #define MAX_WINDOW_SIZE (1024 * 1024 * 4) typedef struct { sqfs_compressor_config_t cfg; sqfs_compressor_t *cmp; sqfs_super_t super; sqfs_file_t *file; sqfs_id_table_t *idtbl; sqfs_dir_reader_t *dr; sqfs_tree_node_t *root; sqfs_data_reader_t *data; sqfs_compressor_config_t options; bool have_options; } sqfs_state_t; typedef struct { const char *old_path; const char *new_path; int compare_flags; sqfs_state_t sqfs_old; sqfs_state_t sqfs_new; bool compare_super; const char *extract_dir; } sqfsdiff_t; enum { COMPARE_NO_PERM = 0x01, COMPARE_NO_OWNER = 0x02, COMPARE_NO_CONTENTS = 0x04, COMPARE_TIMESTAMP = 0x08, COMPARE_INODE_NUM = 0x10, COMPARE_EXTRACT_FILES = 0x20, }; int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, sqfs_tree_node_t *new); char *node_path(const sqfs_tree_node_t *n); int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, const sqfs_inode_generic_t *new, const char *path); int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b); int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b); int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, const sqfs_inode_generic_t *new, const char *path); void process_options(sqfsdiff_t *sd, int argc, char **argv); #endif /* DIFFTOOL_H */ squashfs-tools-ng-1.1.3/bin/sqfsdiff/super.c000066400000000000000000000067531410627516300210100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * super.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" static const struct { sqfs_u16 mask; const char *name; } sqfs_flags[] = { { SQFS_FLAG_UNCOMPRESSED_INODES, "uncompressed inodes" }, { SQFS_FLAG_UNCOMPRESSED_DATA, "uncompressed data" }, { SQFS_FLAG_UNCOMPRESSED_FRAGMENTS, "uncompressed fragments" }, { SQFS_FLAG_NO_FRAGMENTS, "no fragments" }, { SQFS_FLAG_ALWAYS_FRAGMENTS, "always fragments" }, { SQFS_FLAG_NO_DUPLICATES, "no duplicates" }, { SQFS_FLAG_EXPORTABLE, "exportable" }, { SQFS_FLAG_UNCOMPRESSED_XATTRS, "uncompressed xattrs" }, { SQFS_FLAG_NO_XATTRS, "no xattrs" }, { SQFS_FLAG_COMPRESSOR_OPTIONS, "compressor options" }, { SQFS_FLAG_UNCOMPRESSED_IDS, "uncompressed ids" }, }; static void print_value_difference(const char *name, sqfs_u64 a, sqfs_u64 b) { sqfs_u64 diff; char c; if (a != b) { if (a < b) { c = '+'; diff = b - a; } else { c = '-'; diff = a - b; } fprintf(stdout, "%s: %c%lu\n", name, c, (unsigned long)diff); } } static void print_offset_diff(const char *name, sqfs_u64 a, sqfs_u64 b) { if (a != b) fprintf(stdout, "Location of %s differs\n", name); } static void print_flag_diff(sqfs_u16 a, sqfs_u16 b) { sqfs_u16 diff = a ^ b, mask; size_t i; char c; if (diff == 0) return; fputs("flags:\n", stdout); for (i = 0; i < sizeof(sqfs_flags) / sizeof(sqfs_flags[0]); ++i) { if (diff & sqfs_flags[i].mask) { c = a & sqfs_flags[i].mask ? '<' : '>'; fprintf(stdout, "\t%c%s\n", c, sqfs_flags[i].name); } a &= ~sqfs_flags[i].mask; b &= ~sqfs_flags[i].mask; diff &= ~sqfs_flags[i].mask; } for (i = 0, mask = 0x01; i < 16; ++i, mask <<= 1) { if (diff & mask) { fprintf(stdout, "\t%c additional unknown\n", a & mask ? '<' : '>'); } } } int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b) { if (memcmp(a, b, sizeof(*a)) == 0) return 0; fputs("======== super blocks are different ========\n", stdout); /* TODO: if a new magic number or squashfs version is introduced, compare them. */ print_value_difference("inode count", a->inode_count, b->inode_count); print_value_difference("modification time", a->modification_time, b->modification_time); print_value_difference("block size", a->block_size, b->block_size); print_value_difference("block log", a->block_log, b->block_log); print_value_difference("fragment table entries", a->fragment_entry_count, b->fragment_entry_count); print_value_difference("ID table entries", a->id_count, b->id_count); if (a->compression_id != b->compression_id) { fprintf(stdout, "compressor: %s vs %s\n", sqfs_compressor_name_from_id(a->compression_id), sqfs_compressor_name_from_id(b->compression_id)); } print_flag_diff(a->flags, b->flags); print_value_difference("total bytes used", a->bytes_used, b->bytes_used); print_offset_diff("root inode", a->root_inode_ref, b->root_inode_ref); print_offset_diff("ID table", a->id_table_start, b->id_table_start); print_offset_diff("xattr ID table", a->xattr_id_table_start, b->xattr_id_table_start); print_offset_diff("inode table", a->inode_table_start, b->inode_table_start); print_offset_diff("directory table", a->directory_table_start, b->directory_table_start); print_offset_diff("fragment table", a->fragment_table_start, b->fragment_table_start); print_offset_diff("export table", a->export_table_start, b->export_table_start); return 1; } squashfs-tools-ng-1.1.3/bin/sqfsdiff/util.c000066400000000000000000000007011410627516300206120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * util.c * * Copyright (C) 2019 David Oberhollenzer */ #include "sqfsdiff.h" char *node_path(const sqfs_tree_node_t *n) { char *path = sqfs_tree_node_get_path(n); if (path == NULL) { perror("get path"); return NULL; } if (canonicalize_name(path)) { fprintf(stderr, "failed to canonicalization '%s'\n", path); free(path); return NULL; } return path; } squashfs-tools-ng-1.1.3/bin/tar2sqfs/000077500000000000000000000000001410627516300174335ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/bin/tar2sqfs/Makemodule.am000066400000000000000000000010371410627516300220360ustar00rootroot00000000000000tar2sqfs_SOURCES = bin/tar2sqfs/tar2sqfs.c bin/tar2sqfs/tar2sqfs.h tar2sqfs_SOURCES += bin/tar2sqfs/options.c bin/tar2sqfs/process_tarball.c tar2sqfs_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) tar2sqfs_LDADD = libcommon.a libsquashfs.la libtar.a libfstream.a tar2sqfs_LDADD += libfstree.a libcompat.a libfstree.a $(LZO_LIBS) tar2sqfs_LDADD += $(ZLIB_LIBS) $(XZ_LIBS) $(ZSTD_LIBS) $(BZIP2_LIBS) tar2sqfs_LDADD += $(PTHREAD_LIBS) if WITH_OWN_ZLIB tar2sqfs_LDADD += libz.la endif dist_man1_MANS += bin/tar2sqfs/tar2sqfs.1 bin_PROGRAMS += tar2sqfs squashfs-tools-ng-1.1.3/bin/tar2sqfs/options.c000066400000000000000000000171571410627516300213050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * options.c * * Copyright (C) 2019 David Oberhollenzer */ #include "tar2sqfs.h" static struct option long_opts[] = { { "root-becomes", required_argument, NULL, 'r' }, { "compressor", required_argument, NULL, 'c' }, { "block-size", required_argument, NULL, 'b' }, { "dev-block-size", required_argument, NULL, 'B' }, { "defaults", required_argument, NULL, 'd' }, { "num-jobs", required_argument, NULL, 'j' }, { "queue-backlog", required_argument, NULL, 'Q' }, { "comp-extra", required_argument, NULL, 'X' }, { "no-skip", no_argument, NULL, 's' }, { "no-xattr", no_argument, NULL, 'x' }, { "no-keep-time", no_argument, NULL, 'k' }, { "exportable", no_argument, NULL, 'e' }, { "no-symlink-retarget", no_argument, NULL, 'S' }, { "no-tail-packing", no_argument, NULL, 'T' }, { "force", no_argument, NULL, 'f' }, { "quiet", no_argument, NULL, 'q' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "r:c:b:B:d:X:j:Q:sxekfqSThV"; static const char *usagestr = "Usage: tar2sqfs [OPTIONS...] \n" "\n" "Read a tar archive from stdin and turn it into a squashfs filesystem image.\n" "\n" "Possible options:\n" "\n" " --root-becomes, -r The specified directory becomes the root.\n" " Only its children are packed into the image\n" " and its attributes (ownership, permissions,\n" " xattrs, ...) are stored in the root inode.\n" " If not set and a tarbal has an entry for './'\n" " or '/', it becomes the root instead.\n" " --no-symlink-retarget, -S If --root-becomes is used, link targets are\n" " adjusted if they are prefixed by the root\n" " path. If this flag is set, symlinks are left\n" " untouched and only hard links are changed.\n" "\n" " --compressor, -c Select the compressor to use.\n" " A list of available compressors is below.\n" " --comp-extra, -X A comma separated list of extra options for\n" " the selected compressor. Specify 'help' to\n" " get a list of available options.\n" " --num-jobs, -j Number of compressor jobs to create.\n" " --queue-backlog, -Q Maximum number of data blocks in the thread\n" " worker queue before the packer starts waiting\n" " for the block processors to catch up.\n" " Defaults to 10 times the number of jobs.\n" " --block-size, -b Block size to use for Squashfs image.\n" " Defaults to %u.\n" " --dev-block-size, -B Device block size to padd the image to.\n" " Defaults to %u.\n" " --defaults, -d A comma separated list of default values for\n" " implicitly created directories.\n" "\n" " Possible options:\n" " uid= 0 if not set.\n" " gid= 0 if not set.\n" " mode= 0755 if not set.\n" " mtime= 0 if not set.\n" "\n" " --no-skip, -s Abort if a tar record cannot be read instead\n" " of skipping it.\n" " --no-xattr, -x Do not copy extended attributes from archive.\n" " --no-keep-time, -k Do not keep the time stamps stored in the\n" " archive. Instead, set defaults on all files.\n" " --exportable, -e Generate an export table for NFS support.\n" " --no-tail-packing, -T Do not perform tail end packing on files that\n" " are larger than block size.\n" " --force, -f Overwrite the output file if it exists.\n" " --quiet, -q Do not print out progress reports.\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n"; bool dont_skip = false; bool keep_time = true; bool no_tail_pack = false; bool no_symlink_retarget = false; sqfs_writer_cfg_t cfg; char *root_becomes = NULL; static void input_compressor_print_available(void) { int i = FSTREAM_COMPRESSOR_MIN; const char *name; fputs("\nSupported tar compression formats:\n", stdout); while (i <= FSTREAM_COMPRESSOR_MAX) { name = fstream_compressor_name_from_id(i); if (fstream_compressor_exists(i)) printf("\t%s\n", name); ++i; } fputs("\tuncompressed\n", stdout); fputc('\n', stdout); } void process_args(int argc, char **argv) { bool have_compressor; int i, ret; sqfs_writer_cfg_init(&cfg); for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case 'S': no_symlink_retarget = true; break; case 'T': no_tail_pack = true; break; case 'b': if (parse_size("Block size", &cfg.block_size, optarg, 0)) { exit(EXIT_FAILURE); } break; case 'B': if (parse_size("Device block size", &cfg.devblksize, optarg, 0)) { exit(EXIT_FAILURE); } if (cfg.devblksize < 1024) { fputs("Device block size must be at " "least 1024\n", stderr); exit(EXIT_FAILURE); } break; case 'c': have_compressor = true; ret = sqfs_compressor_id_from_name(optarg); if (ret < 0) { have_compressor = false; #ifdef WITH_LZO if (cfg.comp_id == SQFS_COMP_LZO) have_compressor = true; #endif } if (!have_compressor) { fprintf(stderr, "Unsupported compressor '%s'\n", optarg); exit(EXIT_FAILURE); } cfg.comp_id = ret; break; case 'j': cfg.num_jobs = strtol(optarg, NULL, 0); break; case 'Q': cfg.max_backlog = strtol(optarg, NULL, 0); break; case 'X': cfg.comp_extra = optarg; break; case 'd': cfg.fs_defaults = optarg; break; case 'x': cfg.no_xattr = true; break; case 'k': keep_time = false; break; case 'r': free(root_becomes); root_becomes = strdup(optarg); if (root_becomes == NULL) { perror("copying root directory name"); exit(EXIT_FAILURE); } if (canonicalize_name(root_becomes) != 0 || strlen(root_becomes) == 0) { fprintf(stderr, "Invalid root directory '%s'.\n", optarg); goto fail_arg; } break; case 's': dont_skip = true; break; case 'e': cfg.exportable = true; break; case 'f': cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; break; case 'q': cfg.quiet = true; break; case 'h': printf(usagestr, SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE); compressor_print_available(); input_compressor_print_available(); exit(EXIT_SUCCESS); case 'V': print_version("tar2sqfs"); exit(EXIT_SUCCESS); default: goto fail_arg; } } if (cfg.num_jobs < 1) cfg.num_jobs = 1; if (cfg.max_backlog < 1) cfg.max_backlog = 10 * cfg.num_jobs; if (cfg.comp_extra != NULL && strcmp(cfg.comp_extra, "help") == 0) { compressor_print_help(cfg.comp_id); exit(EXIT_SUCCESS); } if (optind >= argc) { fputs("Missing argument: squashfs image\n", stderr); goto fail_arg; } cfg.filename = argv[optind++]; if (optind < argc) { fputs("Unknown extra arguments specified.\n", stderr); goto fail_arg; } return; fail_arg: fputs("Try `tar2sqfs --help' for more information.\n", stderr); exit(EXIT_FAILURE); } squashfs-tools-ng-1.1.3/bin/tar2sqfs/process_tarball.c000066400000000000000000000155341410627516300227660ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * process_tarball.c * * Copyright (C) 2019 David Oberhollenzer */ #include "tar2sqfs.h" static int write_file(istream_t *input_file, sqfs_writer_t *sqfs, const tar_header_decoded_t *hdr, file_info_t *fi, sqfs_u64 filesize) { const sparse_map_t *list; int flags = 0, ret = 0; sqfs_u64 offset, diff; bool sparse_region; ostream_t *out; if (no_tail_pack && filesize > cfg.block_size) flags |= SQFS_BLK_DONT_FRAGMENT; out = data_writer_ostream_create(hdr->name, sqfs->data, &fi->inode, flags); if (out == NULL) return -1; list = hdr->sparse; for (offset = 0; offset < filesize; offset += diff) { if (hdr->sparse != NULL) { if (list == NULL) { sparse_region = true; diff = filesize - offset; } else if (offset < list->offset) { sparse_region = true; diff = list->offset - offset; } else if (offset - list->offset >= list->count) { list = list->next; diff = 0; continue; } else { sparse_region = false; diff = list->count - (offset - list->offset); } } else { sparse_region = false; diff = filesize - offset; } if (diff > 0x7FFFFFFFUL) diff = 0x7FFFFFFFUL; if (sparse_region) { ret = ostream_append_sparse(out, diff); } else { ret = ostream_append_from_istream(out, input_file, diff); if (ret == 0) { fprintf(stderr, "%s: unexpected end-of-file\n", hdr->name); ret = -1; } else if (ret > 0) { diff = ret; ret = 0; } } if (ret < 0) break; } ostream_flush(out); sqfs_destroy(out); if (ret) return -1; return skip_padding(input_file, hdr->sparse == NULL ? filesize : hdr->record_size); } static int copy_xattr(sqfs_writer_t *sqfs, tree_node_t *node, const tar_header_decoded_t *hdr) { tar_xattr_t *xattr; int ret; ret = sqfs_xattr_writer_begin(sqfs->xwr, 0); if (ret) { sqfs_perror(hdr->name, "beginning xattr block", ret); return -1; } for (xattr = hdr->xattr; xattr != NULL; xattr = xattr->next) { if (sqfs_get_xattr_prefix_id(xattr->key) < 0) { fprintf(stderr, "%s: squashfs does not " "support xattr prefix of %s\n", dont_skip ? "ERROR" : "WARNING", xattr->key); if (dont_skip) return -1; continue; } ret = sqfs_xattr_writer_add(sqfs->xwr, xattr->key, xattr->value, xattr->value_len); if (ret) { sqfs_perror(hdr->name, "storing xattr key-value pair", ret); return -1; } } ret = sqfs_xattr_writer_end(sqfs->xwr, &node->xattr_idx); if (ret) { sqfs_perror(hdr->name, "completing xattr block", ret); return -1; } return 0; } static int create_node_and_repack_data(istream_t *input_file, sqfs_writer_t *sqfs, tar_header_decoded_t *hdr) { tree_node_t *node; if (hdr->is_hard_link) { node = fstree_add_hard_link(&sqfs->fs, hdr->name, hdr->link_target); if (node == NULL) goto fail_errno; if (!cfg.quiet) { printf("Hard link %s -> %s\n", hdr->name, hdr->link_target); } return 0; } if (!keep_time) { hdr->sb.st_mtime = sqfs->fs.defaults.st_mtime; } node = fstree_add_generic(&sqfs->fs, hdr->name, &hdr->sb, hdr->link_target); if (node == NULL) goto fail_errno; if (!cfg.quiet) printf("Packing %s\n", hdr->name); if (!cfg.no_xattr) { if (copy_xattr(sqfs, node, hdr)) return -1; } if (S_ISREG(hdr->sb.st_mode)) { if (write_file(input_file, sqfs, hdr, &node->data.file, hdr->sb.st_size)) { return -1; } } return 0; fail_errno: perror(hdr->name); return -1; } static int set_root_attribs(sqfs_writer_t *sqfs, const tar_header_decoded_t *hdr) { if (hdr->is_hard_link || !S_ISDIR(hdr->sb.st_mode)) { fprintf(stderr, "'%s' is not a directory!\n", hdr->name); return -1; } sqfs->fs.root->uid = hdr->sb.st_uid; sqfs->fs.root->gid = hdr->sb.st_gid; sqfs->fs.root->mode = hdr->sb.st_mode; if (keep_time) sqfs->fs.root->mod_time = hdr->sb.st_mtime; if (!cfg.no_xattr) { if (copy_xattr(sqfs, sqfs->fs.root, hdr)) return -1; } return 0; } int process_tarball(istream_t *input_file, sqfs_writer_t *sqfs) { bool skip, is_root, is_prefixed; tar_header_decoded_t hdr; sqfs_u64 offset, count; sparse_map_t *m; size_t rootlen; char *target; int ret; rootlen = root_becomes == NULL ? 0 : strlen(root_becomes); for (;;) { ret = read_header(input_file, &hdr); if (ret > 0) break; if (ret < 0) return -1; if (hdr.mtime < 0) hdr.mtime = 0; if ((sqfs_u64)hdr.mtime > 0x0FFFFFFFFUL) hdr.mtime = 0x0FFFFFFFFUL; hdr.sb.st_mtime = hdr.mtime; skip = false; is_root = false; is_prefixed = true; if (hdr.name == NULL || canonicalize_name(hdr.name) != 0) { fprintf(stderr, "skipping '%s' (invalid name)\n", hdr.name); skip = true; } else if (root_becomes != NULL) { if (strncmp(hdr.name, root_becomes, rootlen) == 0) { if (hdr.name[rootlen] == '\0') { is_root = true; } else if (hdr.name[rootlen] != '/') { is_prefixed = false; } } else { is_prefixed = false; } if (is_prefixed && !is_root) { memmove(hdr.name, hdr.name + rootlen + 1, strlen(hdr.name + rootlen + 1) + 1); } if (is_prefixed && hdr.name[0] == '\0') { fputs("skipping entry with empty name\n", stderr); skip = true; } if (hdr.link_target != NULL && (hdr.is_hard_link || !no_symlink_retarget)) { target = strdup(hdr.link_target); if (target == NULL) { fprintf(stderr, "packing '%s': %s\n", hdr.name, strerror(errno)); goto fail; } if (canonicalize_name(target) == 0 && !strncmp(target, root_becomes, rootlen) && target[rootlen] == '/') { memmove(hdr.link_target, target + rootlen, strlen(target + rootlen) + 1); } free(target); } } else if (hdr.name[0] == '\0') { is_root = true; } if (!is_prefixed) { if (skip_entry(input_file, hdr.sb.st_size)) goto fail; clear_header(&hdr); continue; } if (is_root) { if (set_root_attribs(sqfs, &hdr)) goto fail; clear_header(&hdr); continue; } if (!skip && hdr.unknown_record) { fprintf(stderr, "%s: unknown entry type\n", hdr.name); skip = true; } if (!skip && hdr.sparse != NULL) { offset = hdr.sparse->offset; count = 0; for (m = hdr.sparse; m != NULL; m = m->next) { if (m->offset < offset) { skip = true; break; } offset = m->offset + m->count; count += m->count; } if (count != hdr.record_size) skip = true; if (skip) { fprintf(stderr, "%s: broken sparse " "file layout\n", hdr.name); } } if (skip) { if (dont_skip) goto fail; if (skip_entry(input_file, hdr.sb.st_size)) goto fail; clear_header(&hdr); continue; } if (create_node_and_repack_data(input_file, sqfs, &hdr)) goto fail; clear_header(&hdr); } return 0; fail: clear_header(&hdr); return -1; } squashfs-tools-ng-1.1.3/bin/tar2sqfs/tar2sqfs.1000066400000000000000000000140611410627516300212640ustar00rootroot00000000000000.TH TAR2SQFS "1" "June 2019" "tar2sqfs" "User Commands" .SH NAME tar2sqfs \- create a SquashFS image from a tar archive .SH SYNOPSIS .B tar2sqfs [\fI\,OPTIONS\/\fR...] \fI\,\/\fR .SH DESCRIPTION Quickly and painlessly turn a tar ball into a SquashFS filesystem image. .PP By default, the program reads the archive from standard input. Compressed archives are supported. .PP Possible options: .TP \fB\-\-root\-becomes\fR, \fB\-r\fR If set, only pack entries that are underneath the specified directory. The prefix is stripped and the meta data for the directory itself is copied to the root inode (i.e. the ownership, permissions, extended attributes, modification time). If this option is not set, tar2sqfs implicitly treats \fB./\fR or absolute paths this way, i.e. if the archive contains an entry for \fB./\fR, it becomes the root node and the prefix is stripped from all paths (and similar for absolute paths and \fB/\fR). .TP \fB\-\-no\-symlink\-retarget\fR, \fB\-S\fR If \-\-root\-becomes is used, link targets are adjusted if they are prefixed by the root path. By default, this is also done on symbolic links, that have a target that is prefixed by the root path and they are converted to aboluste paths with the prefix removed. However, because symlinks can point across mount points, this may actually be intended for some use cases. This flag allows changing the default behaviour, so only hard links are retargeted. .TP \fB\-\-compressor\fR, \fB\-c\fR Select the compressor to use. Run \fBtar2sqfs \-\-help\fR to get a list of all available compressors and the default selection. .TP \fB\-\-comp\-extra\fR, \fB\-X\fR A comma separated list of extra options for the selected compressor. Specify \fBhelp\fR to get a list of available options. .TP \fB\-\-num\-jobs\fR, \fB\-j\fR If libsquashfs was compiled with a thread pool based, parallel data compressor, this option can be used to set the number of compressor threads. If not set, the default is the number of available CPU cores. .TP \fB\-\-queue\-backlog\fR, \fB\-Q\fR Maximum number of data blocks in the thread worker queue before the packer starts waiting for the block processors to catch up. Higher values result in higher memory consumption. Defaults to 10 times the number of workers. .TP \fB\-\-block\-size\fR, \fB\-b\fR Block size to use for SquashFS image. Defaults to 131072. .TP \fB\-\-dev\-block\-size\fR, \fB\-B\fR Device block size to padd the image to. Defaults to 4096. .TP \fB\-\-defaults\fR, \fB\-d\fR A comma separated list of default values for implicitly created directories. The following values can be set: .TS tab(;) allbox; l l l l l l l l l l rd. \fBOption\fR;\fBDefault\fR uid=;0 gid=;0 mode=;0755 mtime=;\fB$SOURCE\_DATE\_EPOCH\fR if set, 0 otherwise .TE .TP .TP \fB\-\-no\-keep\-time\fR, \fB\-k\fR Replace the time stamps from the tar archive with default time stamps for all entries. The default behavior is to preserve the time stamps from the archive to the extent possible (SquashFS has second resolution and 32 bit time stamps; tar can use extensions to specify much larger timestamps with arbitrary precision). The root inode (unless \fB\-\-root\-becomes\fR is used) and the modification time on the SquashFS image itself will still be set to defaults. .TP \fB\-\-no\-xattr\fR, \fB\-x\fR Do not copy extended attributes from archive. Default behaviour is to copy all extended attributes and skip the ones that cannot be encoded in SquashFS. .TP \fB\-\-no\-skip\fR, \fB\-s\fR Abort if a tar record cannot be read instead of skipping it. .TP \fB\-\-exportable\fR, \fB\-e\fR Generate an export table for NFS support. .TP \fB\-\-no\-tail\-packing\fR, \fB\-T\fR Do not perform tail end packing on files that are larger than the specified block size. .TP \fB\-\-force\fR, \fB\-f\fR Overwrite the output file if it exists. .TP \fB\-\-quiet\fR, \fB\-q\fR Do not print out progress reports. .TP \fB\-\-help\fR, \fB\-h\fR Print help text and exit. .TP \fB\-\-version\fR, \fB\-V\fR Print version information and exit. .SH COMPATIBILITY Currently the program can process v7 format, pre-POSIX ustar, POSIX tar and GNU tar archives. PAX extension headers are also supported. Global PAX headers are ignored. The support for GNU tar is limited to a commonly used subset (i.e. some legacy extensions that GNU tar itself no longer generates are not supported; neither are multi volume archives). The input tar file can either be uncompressed, or stream compressed using \fBgzip\fR, \fBxz\fR, \fBzstd\fR or \fBbzip2\fR. The program transparently auto-detects and unpacks any stream compressed archive. The exact list of supported compressors depends on the compile configuration. Extended attributes are supported through the \fBSCHILY.xattr\fR extension (favoured by GNU tar and star) or through the \fBLIBARCHIVE.xattr\fR extension. If any unsupported section or extended attribute key is encountered in an archive, a warning message is written to stderr. If the \fB\-\-no\-skip\fR option is set, processing aborts. By default, unknown sections and unsupported extended attributes are simply skipped after issuing a warning. .SH ENVIRONMENT If the command line switch \fB\-\-defaults\fR is not used or no default mtime is specified, the value of the environment variable \fBSOURCE\_DATE\_EPOCH\fR is used for all file and filesystem timestamps. If \fBSOURCE\_DATE\_EPOCH\fR is not set, not a parsable number or it is out of range, the timestamps default to 0. Environment variables are only used if no explicit command line switches are set. Explicit command line switches are always preferred over the environment variables. .SH EXAMPLES .TP Turn an uncompressed tar archive into a SquashFS image: .IP tar2sqfs rootfs.sqfs < rootfs.tar.gz .SH SEE ALSO gensquashfs(1), rdsquashfs(1), sqfs2tar(1) .SH AUTHOR Written by David Oberhollenzer. .SH COPYRIGHT Copyright \(co 2019 David Oberhollenzer License GPLv3+: GNU GPL version 3 or later . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. squashfs-tools-ng-1.1.3/bin/tar2sqfs/tar2sqfs.c000066400000000000000000000031131410627516300213420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar2sqfs.c * * Copyright (C) 2019 David Oberhollenzer */ #include "tar2sqfs.h" static int tar_probe(const sqfs_u8 *data, size_t size) { size_t i, offset; if (size >= TAR_RECORD_SIZE) { for (i = 0; i < TAR_RECORD_SIZE; ++i) { if (data[i] != 0x00) break; } if (i == TAR_RECORD_SIZE) { data += TAR_RECORD_SIZE; size -= TAR_RECORD_SIZE; } } offset = offsetof(tar_header_t, magic); if (offset + 5 <= size) { if (memcmp(data + offset, "ustar", 5) == 0) return 1; } return 0; } int main(int argc, char **argv) { int status = EXIT_FAILURE; istream_t *input_file = NULL; sqfs_writer_t sqfs; int ret; process_args(argc, argv); input_file = istream_open_stdin(); if (input_file == NULL) return EXIT_FAILURE; ret = istream_detect_compressor(input_file, tar_probe); if (ret < 0) goto out_if; if (ret > 0) { if (!fstream_compressor_exists(ret)) { fprintf(stderr, "%s: %s compression is not supported.\n", istream_get_filename(input_file), fstream_compressor_name_from_id(ret)); goto out_if; } input_file = istream_compressor_create(input_file, ret); if (input_file == NULL) return EXIT_FAILURE; } memset(&sqfs, 0, sizeof(sqfs)); if (sqfs_writer_init(&sqfs, &cfg)) goto out_if; if (process_tarball(input_file, &sqfs)) goto out; if (fstree_post_process(&sqfs.fs)) goto out; if (sqfs_writer_finish(&sqfs, &cfg)) goto out; status = EXIT_SUCCESS; out: sqfs_writer_cleanup(&sqfs, status); out_if: sqfs_destroy(input_file); return status; } squashfs-tools-ng-1.1.3/bin/tar2sqfs/tar2sqfs.h000066400000000000000000000012611410627516300213510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar2sqfs.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef TAR2SQFS_H #define TAR2SQFS_H #include "config.h" #include "common.h" #include "compat.h" #include "tar.h" #include #include #include #include #include /* options.c */ extern bool dont_skip; extern bool keep_time; extern bool no_tail_pack; extern bool no_symlink_retarget; extern sqfs_writer_cfg_t cfg; extern char *root_becomes; void process_args(int argc, char **argv); /* process_tarball.c */ int process_tarball(istream_t *input_file, sqfs_writer_t *sqfs); #endif /* TAR2SQFS_H */ squashfs-tools-ng-1.1.3/configure.ac000066400000000000000000000243131410627516300174070ustar00rootroot00000000000000AC_PREREQ([2.60]) AC_INIT([squashfs-tools-ng], [1.1.3], [goliath@infraroot.at], squashfs-tools-ng) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign dist-xz subdir-objects]) AM_SILENT_RULES([yes]) AC_PROG_CC([cc gcc clang]) LT_INIT([win32-dll]) AC_PROG_CC_C99 AC_PROG_INSTALL AC_PROG_SED AC_SYS_LARGEFILE AC_CANONICAL_HOST AC_SUBST([LIBSQUASHFS_SO_VERSION], [3:2:2]) m4_ifndef([PKG_PROG_PKG_CONFIG], [m4_fatal([Could not locate the pkg-config autoconf macros. These are usually located in /usr/share/aclocal/pkg.m4. If your macros are in a different location, try setting the environment variable AL_OPTS="-I/other/macro/dir" before running ./autogen.sh or autoreconf again. Make sure pkg-config is installed.])]) PKG_PROG_PKG_CONFIG PKG_INSTALLDIR UL_WARN_ADD([-Wall]) UL_WARN_ADD([-Wextra]) UL_WARN_ADD([-Wunused]) UL_WARN_ADD([-Wmissing-prototypes]) UL_WARN_ADD([-Wmissing-declarations]) UL_WARN_ADD([-Wwrite-strings]) UL_WARN_ADD([-Wjump-misses-init]) UL_WARN_ADD([-Wuninitialized]) UL_WARN_ADD([-Winit-self]) UL_WARN_ADD([-Wlogical-op]) UL_WARN_ADD([-Wunused-but-set-parameter]) UL_WARN_ADD([-Wunused-but-set-variable]) UL_WARN_ADD([-Wunused-parameter]) UL_WARN_ADD([-Wunused-result]) UL_WARN_ADD([-Wunused-variable]) UL_WARN_ADD([-Wduplicated-cond]) UL_WARN_ADD([-Wduplicated-branches]) UL_WARN_ADD([-Wrestrict]) UL_WARN_ADD([-Wnull-dereference]) UL_WARN_ADD([-pedantic]) UL_WARN_ADD([-Wpedantic]) UL_WARN_ADD([-Walloc-zero]) UL_WARN_ADD([-Wcast-align]) UL_WARN_ADD([-Wcast-align=strict]) UL_WARN_ADD([-Wcast-qual]) UL_WARN_ADD([-Wdangling-else]) UL_WARN_ADD([-Wdate-time]) UL_WARN_ADD([-Wdouble-promotion]) UL_WARN_ADD([-Wformat-nonliteral]) UL_WARN_ADD([-Wformat-security]) UL_WARN_ADD([-Wformat-signedness]) UL_WARN_ADD([-Wmissing-format-attribute]) UL_WARN_ADD([-Wmissing-noreturn]) UL_WARN_ADD([-Wpacked]) UL_WARN_ADD([-Wpacked-not-aligned]) UL_WARN_ADD([-Wredundant-decls]) UL_WARN_ADD([-Wswitch-default]) UL_WARN_ADD([-Wswitch-enum]) UL_WARN_ADD([-Wtrampolines]) UL_WARN_ADD([-Wundef]) UL_WARN_ADD([-Wunused-const-variable]) UL_WARN_ADD([-Wvla]) AC_SUBST([WARN_CFLAGS]) ##### target detection & target specific stuff ##### build_windows="no" case "${host_os}" in cygwin*|mingw*) build_windows=yes ;; esac AM_CONDITIONAL([WINDOWS], [test "x$build_windows" = "xyes"]) ##### config options ##### AC_ARG_WITH([bzip2], [AS_HELP_STRING([--with-bzip2], [Build with bzip2 compression support])], [], [with_bzip2="check"]) AC_ARG_WITH([xz], [AS_HELP_STRING([--with-xz], [Build with xz compression support])], [], [with_xz="check"]) AC_ARG_WITH([lzo], [AS_HELP_STRING([--with-lzo], [Build with lzo compression support])], [], [with_lzo="check"]) AC_ARG_WITH([lz4], [AS_HELP_STRING([--with-lz4], [Build with lz4 compression support])], [], [with_lz4="check"]) AC_ARG_WITH([builtin-lz4], [AS_HELP_STRING([--with-builtin-lz4], [Use a custom, static liblz4])], [], [with_builtin_lz4="no"]) AC_ARG_WITH([zstd], [AS_HELP_STRING([--with-zstd], [Build with zstd compression support])], [], [with_zstd="check"]) AC_ARG_WITH([gzip], [AS_HELP_STRING([--with-gzip], [Build with zlib compression support])], [], [with_gzip="check"]) AC_ARG_WITH([builtin-zlib], [AS_HELP_STRING([--with-builtin-zlib], [Use a custom, static zlib])], [], [with_builtin_zlib="no"]) AC_ARG_WITH([selinux], [AS_HELP_STRING([--with-selinux], [Build with SELinux label file support])], [], [with_selinux="check"]) AC_ARG_WITH([pthread], [AS_HELP_STRING([--without-pthread], [Build without pthread based block compressor])], [], [with_pthread="yes"]) AC_ARG_WITH([tools], [AS_HELP_STRING([--without-tools], [Only build libsquashfs, do not build the tools.])], [], [with_tools="yes"]) AC_ARG_ENABLE([custom-alloc], [AS_HELP_STRING([--disable-custom-alloc], [Do not used any custom allocators.])], [], [enable_custom_alloc="yes"]) AC_ARG_ENABLE([corpora-tests], [AS_HELP_STRING([--enable-corpora-tests], [Perform corpora based reproducability tests.])], [], [enable_corpora_tests="no"]) AM_CONDITIONAL([CORPORA_TESTS], [test "x$enable_corpora_tests" = "xyes"]) AS_IF([test "x$build_windows" = "xyes"], [with_pthread="no"], []) AS_IF([test "x$with_tools" != "xyes"], [with_selinux="no"], []) AM_CONDITIONAL([BUILD_TOOLS], [test "x$with_tools" = "xyes"]) ##### Doxygen reference manual ##### AC_CHECK_PROGS([DOXYGEN], [doxygen]) AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) if test -z "$DOXYGEN"; then with_doxygen="no" else with_doxygen="yes" DX_DOXYGEN_FEATURE(ON) DX_DOT_FEATURE(ON) DX_HTML_FEATURE(ON) DX_CHM_FEATURE(OFF) DX_CHI_FEATURE(OFF) DX_MAN_FEATURE(OFF) DX_RTF_FEATURE(OFF) DX_XML_FEATURE(OFF) DX_PDF_FEATURE(OFF) DX_PS_FEATURE(OFF) DX_INIT_DOXYGEN(libsquashfs, Doxyfile) fi ##### search for dependencies ##### AC_ARG_VAR([BZIP2_CFLAGS], [C compiler flags for lib bzip2]) AC_ARG_VAR([BZIP2_LIBS], [linker flags for lib bzip2]) AS_IF([test -z "$BZIP2_LIBS" -a "x$with_bzip2" != "xno"], [ AC_CHECK_LIB([bz2], [BZ2_bzCompress], [AC_CHECK_HEADERS([bzlib.h], [BZIP2_LIBS="-lbz2"], [])], []) ], []) AS_IF([test -z "$BZIP2_LIBS"], [AS_IF([test "x$with_bzip2" = "xyes"], [AC_MSG_ERROR([cannot find Bzip2 libs])], [with_bzip2="no"])], [with_bzip2="yes"]) AS_IF([test "x$with_gzip" != "xno" -a "x$with_builtin_zlib" != "xyes"], [ PKG_CHECK_MODULES(ZLIB, [zlib], [with_gzip="yes"], [AS_IF([test "x$with_gzip" != "xcheck"], [AC_MSG_ERROR([cannot find zlib])], [with_gzip="no"])]) ], []) AS_IF([test "x$with_builtin_zlib" != "xno"], [with_gzip="yes"], []) AS_IF([test "x$with_xz" != "xno"], [ PKG_CHECK_MODULES(XZ, [liblzma >= 5.0.0], [with_xz="yes"], [AS_IF([test "x$with_xz" != "xcheck"], [AC_MSG_ERROR([cannot XZ sdk])], [with_xz="no"])]) ], []) AS_IF([test "x$with_lz4" != "xno" -a "x$with_builtin_lz4" != "xyes"], [ PKG_CHECK_MODULES(LZ4, [liblz4], [with_lz4="yes"], [AS_IF([test "x$with_lz4" = "xyes"], [AC_MSG_ERROR([cannot find liblz4])], [with_lz4="no"])]) ], []) AS_IF([test "x$with_builtin_lz4" != "xno"], [with_lz4="yes"], []) AS_IF([test "x$with_zstd" != "xno"], [ PKG_CHECK_MODULES(ZSTD, [libzstd >= 1.3.1], [with_zstd="yes"], [AS_IF([test "x$with_zstd" = "xyes"], [AC_MSG_ERROR([cannot find zstd])], [with_zstd="no"])]) ], []) AC_TEST_ZSTD_STREAM AS_IF([test "x$with_selinux" != "xno"], [ have_selinux="yes" PKG_CHECK_MODULES(LIBSELINUX, [libselinux], [], [have_selinux="no"]) AC_CHECK_HEADERS([selinux/selinux.h], [], [have_selinux="no"]) AC_CHECK_HEADERS([selinux/label.h], [], [have_selinux="no"]) AS_IF([test "x$with_selinux" != "xcheck" -a "x$have_selinux" = "xno"], [AC_MSG_ERROR([cannot find SELinux libraries])], [with_selinux="$have_selinux"]) ], []) AC_ARG_VAR([LZO_CFLAGS], [C compiler flags for lzo]) AC_ARG_VAR([LZO_LIBS], [linker flags for lzo]) AS_IF([test -z "$LZO_LIBS" -a "x$with_lzo" != "xno"], [ AC_CHECK_LIB([lzo2], [lzo1x_1_15_compress], [LZO_LIBS="-llzo2"], [AC_CHECK_LIB([lzo],[lzo1x_1_15_compress], [LZO_LIBS="-llzo"], [] )] ) ], []) AS_IF([test -z "$LZO_LIBS"], [AS_IF([test "x$with_lzo" = "xyes"], [AC_MSG_ERROR([cannot find liblzo])], [with_lzo="no"])], [with_lzo="yes"]) AS_IF([test "x$with_pthread" != "xno"], [ AX_PTHREAD([with_pthread="yes"], [AS_IF([test "x$with_pthread" = "xyes"], [AC_MSG_ERROR([cannot find pthread])], [with_pthread="no"])]) ], []) AM_CONDITIONAL([WITH_BZIP2], [test "x$with_bzip2" = "xyes"]) AM_CONDITIONAL([WITH_GZIP], [test "x$with_gzip" = "xyes"]) AM_CONDITIONAL([WITH_XZ], [test "x$with_xz" = "xyes"]) AM_CONDITIONAL([WITH_LZ4], [test "x$with_lz4" = "xyes"]) AM_CONDITIONAL([WITH_ZSTD], [test "x$with_zstd" = "xyes"]) AM_CONDITIONAL([WITH_LZO], [test "x$with_lzo" = "xyes"]) AM_CONDITIONAL([WITH_SELINUX], [test "x$with_selinux" = "xyes"]) AM_CONDITIONAL([HAVE_PTHREAD], [test "x$with_pthread" = "xyes"]) AM_CONDITIONAL([WITH_OWN_LZ4], [test "x$with_builtin_lz4" = "xyes"]) AM_CONDITIONAL([WITH_OWN_ZLIB], [test "x$with_builtin_zlib" = "xyes"]) libsqfs_dep_mod="" AS_IF([test "x$with_lz4" = "xyes" -a "x$with_builtin_lz4" != "xyes"], [libsqfs_dep_mod="$libsqfs_dep_mod liblz4"], []) AS_IF([test "x$with_gzip" = "xyes" -a "x$with_builtin_zlib" != "xyes"], [libsqfs_dep_mod="$libsqfs_dep_mod zlib"], []) AM_COND_IF([WITH_XZ], [libsqfs_dep_mod="$libsqfs_dep_mod liblzma >= 5.0.0"], []) AM_COND_IF([WITH_ZSTD], [libsqfs_dep_mod="$libsqfs_dep_mod libzstd"], []) AC_SUBST([LIBSQFS_DEP_MOD], ["$libsqfs_dep_mod"]) PKG_CHECK_MODULES(READLINE, [readline], [have_readline="yes"], [have_readline="no"]) AM_CONDITIONAL([WITH_READLINE], [test "x$have_readline" = "xyes"]) ##### additional checks ##### AC_CHECK_HEADERS([sys/xattr.h], [], []) AC_CHECK_HEADERS([sys/sysinfo.h], [], []) AC_CHECK_HEADERS([alloca.h], [], []) AC_CHECK_FUNCS([strndup getopt getopt_long getsubopt fnmatch]) ##### generate output ##### AM_CONDITIONAL([CUSTOM_ALLOC], [test "x$enable_custom_alloc" = "xyes"]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([lib/sqfs/libsquashfs1.pc]) AC_CONFIG_FILES([Doxyfile]) AC_CONFIG_FILES([tests/cantrbry.sh], [chmod +x tests/cantrbry.sh]) AC_CONFIG_FILES([tests/test_tar_sqfs.sh], [chmod +x tests/test_tar_sqfs.sh]) AC_CONFIG_FILES([tests/pack_dir_root.sh], [chmod +x tests/pack_dir_root.sh]) AC_CONFIG_FILES([tests/tarcompress.sh], [chmod +x tests/tarcompress.sh]) AC_CONFIG_FILES([tests/rdsquashfs/pathtraversal.sh], [chmod +x tests/rdsquashfs/pathtraversal.sh]) AC_OUTPUT([Makefile]) AC_MSG_RESULT([ ${PACKAGE} ${VERSION} prefix: ${prefix} exec prefix: ${exec_prefix} runstatedir: ${runstatedir} bindir: ${bindir} sbindir: ${sbindir} libdir: ${libdir} includedir: ${includedir} compiler: ${CC} cflags: ${CFLAGS} ldflags: ${LDFLAGS} GZIP support: ${with_gzip} XZ/LZMA support: ${with_xz} LZO support: ${with_lzo} LZ4 support: ${with_lz4} ZSTD support: ${with_zstd} BZIP2 support: ${with_bzip2} SELinux support: ${with_selinux} Using pthreads: ${with_pthread} Custom allocators: ${enable_custom_alloc} Building tools: ${with_tools} Doxygen found: ${with_doxygen} warnings: ${WARN_CFLAGS} Type 'make' or 'make ' to compile. ]) squashfs-tools-ng-1.1.3/coverity.sh000077500000000000000000000015121410627516300173200ustar00rootroot00000000000000#!/bin/sh COVERITY_PATH="$HOME/Downloads/cov-analysis-linux64-2020.09" TOKEN=$(cat "$HOME/.coverity/squashfs-tools-ng") EMAIL=$(git log --follow --pretty=format:"%ae" -- coverity.sh | head -n 1) DESCRIPTION=$(git describe --always --tags --dirty) VERSION=$(grep AC_INIT configure.ac | grep -o \\[[0-9.]*\\] | tr -d []) export PATH="$COVERITY_PATH/bin:$PATH" ./autogen.sh ./configure make clean cov-build --dir cov-int make -j tar czvf squashfs-tools-ng.tgz cov-int echo "Uploading tarball with the following settings:" echo "Email: $EMAIL" echo "Version: $VERSION" echo "Description: $DESCRIPTION" curl --form token="$TOKEN" \ --form email="$EMAIL" \ --form file=@squashfs-tools-ng.tgz \ --form version="$VERSION" \ --form description="$DESCRIPTION" \ https://scan.coverity.com/builds?project=squashfs-tools-ng squashfs-tools-ng-1.1.3/doc/000077500000000000000000000000001410627516300156635ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/doc/architecture.md000066400000000000000000000171541410627516300206770ustar00rootroot00000000000000# Squashfs-tools-ng Software Architecture Generally speaking, the package tries to somewhat imitate a typical Unix filesystem structure. The source code for an executable program is located in `bin//`, while the source code for a library is located in `lib//`, without the typical `lib` prefix. Shared header files for the libraries are in `include/`. So far, a header sub-directory is only used for `libsquashfs`, since those headers are somewhat more numerous and are installed on the system in the same sub-directory. If a binray program comes with a man page, the man page is located at the same location as the program source (i.e. `bin//.1`). Extra documentation (like this file) is located in the `doc` directory, and source code for example programs which are not installed is in `extras`. Unit tests for the libraries are in `tests/`, with a `lib` prefix and tests for programs are in `tests/`. ## Library Stacking To achieve loose coupling, core functionality is implemented by libraries in a reasonably generic way, and may in-turn make use of other libraries to implement their functionality. To the extent possible, the actual application programs are merely frontends for the underlying libraries. The following diagram tries to illustrate how the libraries are stacked: _______________________________________ | | | Application Programs | |_______________________________________| ____________________________ | | | libcommon | __________|____________________________| | | | | | libtar | libfstree | | |__________|_______ | libsqfs | | | | | | libfstream | | | |__________________|______|_____________| | | | | libcompat | libutil | |_________________________|_____________| At the bottom, `libutil` contains common helper functions and container data structures (dynamic array, hash table, rb-tree, et cetera) used by both `libsqfs` and the application programs. The `libcompat` library contains fallback implementations for OS library functions that might not be available everywhere (e.g. POSIX stuff missing on Windows or GNU stuff missing on BSD). The `libfstream` library implements stream based I/O abstraction, i.e. it has an abstract data structure for a non-seek-able read-only input streams and write-only output streams. It has concrete implementations wrapping the underlying OS functionality, as well as stream-compressor based implementations that wrap an existing interface instance. On top of `libfstream`, the `libtar` library is implemented. It supports simple reading & decoding of tar headers from an input stream, as well as generating and writing tar headers to an output stream and supports various extensions (e.g. GNU, SCHILY). Thanks to the `libfstream` compressor wrappers, it supports transparent compression/decompression of tar headers and data. The `libfstree` library contains functionality related to managing a and manipulating a hierarchical filesystem tree. It can directly parse the description format for `gensquashfs` or scan a directory. The `libsqfs` (actually `libsquashfs`) library implements the bulk of the squashfs reading/writing functionality. It is built as a shared library and is installed on the target system along with the application programs and a bunch of public headers. Finally, `libcommon` contains miscellaneous stuff shared between the application programs, such as common CLI handling code, some higher level utilities, higher level wrappers around `libsqfs` for some tool specific tasks. ### Licensing Implications The application programs and the static libraries are GPL licensed, while `libsquashfs` is licensed under the LGPL. Because the code of `libutil` is compiled into `libsquashfs`, it also needs to be under the LGPL and only contain 3rd party code under a compatible license. Furthermore, since the LZO compressor library is GPL licensed, `libsquashfs` cannot use it directly and thus does not support LZO compression. Instead, the `libcommon` library contains an implementation of the `libsquashfs` compressor interface that uses the LZO library, so the application programs *do support* LZO, but the library doesn't. ### Managing Symbols and Visiblity All symbols exported from `libsquashfs` must start with a `sqfs_` prefix. Likewise, all data structures and typedefs in the public header use this prefix and macros use an `SQFS_` prefix, in order to prevent namespace pollution. The `sqfs/predef.h` header contains a macro called `SQFS_API` for marking exported symbols. Whether the symbols are imported or exported, depends on the presence of the `SQFS_BUILDING_DLL` macro. To mark symbols as explicitly not exported (required on some platforms), the macro `SQFS_INTERNAL` is used (e.g. on all `libutil` functions to keep the internal). An additional `SQFS_INLINE` macro is provided for inline functions declared in headers. The public headers of `libsquashfs` also must not include any headers of the other libraries, as they are not installed on the target system. However, somewhat contradictory to the diagtram, a number of the libraries outlined above need declarations, typedefs and macros from `sqfs/predef.h` and simply include thta header. ## Object Oriented Design Anybody who has done C programming to a reasonable degree should be familiar with the technique. An interface is basically a `struct` with function pointers where the first argument is a pointer to the instance (`this` pointer). Single inheritance basically means making the base struct the first member of the extended struct. A pointer to the extended object can be down cast to a pointer to the base struct and used as such. To the extent possible, concrete implementations are made completely opaque and only have a factory function to instantiate them, for a more loose coupling. The `sqfs/predef.h` defines and typedefs a `sqfs_object_t` structure, which is at the bottom of the inheritance hierarchy. It contains two function pointers `delete` and `copy`. The former destroys and frees the object itself, the later creates an exact copy of the object. The `copy` callbacks may be `NULL`, if creating copies is not applicable for a particular type of object. For convenience, two inline helpers `sqfs_destroy` and `sqfs_copy` are provided that cast a `void` pointer into an object pointer and call the respecive callbacks. The later also checks if the callback is `NULL`. ## The libsquashfs malloc/free Issue While most code in `libsquashfs` works with objects that have a `destroy` hook, some functions return pointers to data blobs or dumb structures that have been allocated with `malloc` and expect the caller to free them again. This turned out to be a design issue, since the shared library could in theory end up being linked against a different C runtime then an application using it. On Unix like systems this would require a rather freakish circumstances, but on Windows this actually happens fairly easily. As a result, a `sqfs_free` function was added to `libsquashfs` to expose access to the `free` function of the libraries run-time. All new code using `libsquashfs` should use that function, but to maintain backward compatibility with existing code, the library has to continue using regular malloc at those places, so programs that currently work with a simple `free` also continue to work. squashfs-tools-ng-1.1.3/doc/benchmark.ods000066400000000000000000003124651410627516300203370ustar00rootroot00000000000000PK|k|Rl9..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK|k|RObject 2/meta.xmlN0 4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 2/content.xml][sD~_2&{lN(8-*7Œ-啔_=ӊ+'K 7#%\|}>ew:%t=/l|7"3ob߬u͋u >:.(Mzs>t2-Jf͠IGUz;vz˛OGD>* p᫴rcQ]|rWѢ8G7~ߎn90<0+.@l5a_]^7ճvKսD&AgݷUn|lհ6:]^yyNPN x5[eoiYgiu*?`uNı5|amNu6EՍU&)(Q^:"n5-ҙ?עobF>:vӻ~մ=1gPVtW`yw?v"~vhh <͠YWWEd'MFYh9lEW8L:JW1Oi72[^m&Q|V;_obS7y򨩒}WiUʋe *SR 2&𦲄* l٫.ay hU=Ue3vqSsyt_`ī/8#JŶ:ۧX&gjݔD W&.t%>$S&v:Hb$lTдIU,Өt|s$$% %}ūb/y3?PUΏ˲vpFGތco6oRۄyCD/?Z/7mqN at;Ktut.ސ Nj>?wȗW+2v̩|;Kt `UYzGm͛hv~YEg.㨏[.4ϯtn2=Se}`:͛svv;|Pg$HHSNe{}s09^\M&7ٿb yzyLə;H] 5SK06Ռ6Ot7wx>_vs$#a׶iTk($)Sgİ-澃VJC l:ڳʱ]}y&SfӇtBj*5\]Q5t ҭk+A $#~ ANְ4t{J;ޔ*.~(yG5t7Ք3)eC!v!SԫX9aYQ^1H|"f̒:eZ;<~?䧽v'S!}˩a*yb*-zMci>MZEOwGoŌpkܐ 7CB"[ۧ*";qi3]kƣ{z_dǭD֣/އ"}8n'{>Yq}<>z[Ӊ'^l[YD֓/ǭ>nJ>aVג("1)PRp3 &_sbVLh)PF#| &_ Jc5P9l4CX| [ASOqi6@#@'/*! %a)L>BSkH>BBWr|/ iI$#!c\mCLkZe(?B Dt NF^T C1}&R0DBTQjc$|n OL" &_3i-1J5$!ᲱJ?Gş"@JHj@H(0R1 Wܧt2)PQ&/+]`ҹ*$!a)|oBC$|Qf(T=pĝ|@1(< @tLJY#(35=x`  tuj& H0Tjh8!R} % a6鏑P򍴄S !/#H@PJZ&$b)l 1 EZ[wǧt2 j9| Q Wz PYs 5|ҕ<-} &_( B EpuO n12>4(0ᓁXJ рM ؤV ɵ0DH8kJ!BSF0SC"kl8DBрL吒j`$֌A׭2?F6=%JVJe%q2 *.$D!k >hѶH(R@ωR|8 ru,h*(b}?hct4࿶, 9WB P#Pf3wk^xoM8Sg!C$*M uNZ돑P4Pʂ^qFa$ԆWL`tܢt?N w6v{Ce X#$ԆorB{K&mx/8ؗ*e* C#/JȀ0\OtD࿰, {"Fɇ8*  t;gT^Cw `4,d@o'H4U L&J Fjr{($ \W+C0BC;Q>H4t#f0JaHisLG 0H (F*/f ׮:1!  6DgmP 9(#$(H(^c SV?NO FZN8!rz@I$*1(R1~@#7H TF+2BBɇ\(na4H@^\Co$t)BH("J~$55 E~>07DBRk6FsD0RKihp_ۿ/~/PK fe%/xPK|k|RConfigurations2/progressbar/PK|k|RConfigurations2/toolpanel/PK|k|RConfigurations2/menubar/PK|k|RConfigurations2/popupmenu/PK|k|RConfigurations2/floater/PK|k|RConfigurations2/images/Bitmaps/PK|k|RConfigurations2/accelerator/PK|k|RConfigurations2/statusbar/PK|k|RConfigurations2/toolbar/PK|k|RObject 3/meta.xmlN0 4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 3/content.xml]YsF~_ȺZn3E kPK, wY9Xuu>,?Mfs=nr%vФy8}пu汤qOͬ84gY6i-wxoVYCez4y{i8-nHl\'n5]ΏJgo{JaIsUm#rVM_6 ŏa~^p Ŋ<-6υ]?8XRU_&|׿o{"Ou,#&;{O;]kX (/ۮ i4<܏+ޚ|02:J7r;]c2Y!}1g{w94/_>XK=h"F #{c\m7H ҇=u;0s]ꎄ+Veހlt2tHWy/\eY{xFwU/LUre+Z^6\^sɦjS=~lڃ륧. Άs-Jp HwH _N51г{ា vm׳ Ҋ(tϤȋPrJB*^yuWIIvqEx\rg['sWpiQ)?@"ʥMV湟p7,M?ʗMY>zo&^9,7E+rB2vz*!Unީh%dV.Ez%d"&ge`^C5ԝ>@y9~8~@W? q>l_etҢc55mTPI=˖.|.߶Bem\g``G>Ӫ*|\$gc#kDZ6C"?ԟ2Z;B=d-#H]pdgbq[G/Xn8FT@f~[G4\.<{h߳l? ѼD]-pWfϻ/L7[Oo>d:wgν{Ǟi]owvap*}IXI{kG߬/Mr^{_.=f}=oz!7kM~ěy&r^{\H{~}~cZu)⢀T(=yWAf6(JHC4*wi*_'?5ƺ~qb/Ov`WVmW+ }Z\ٽv[ԍdtm3,oo|z~.il7ͬ_❤/~_dSLu|#sx#}L 1W?2]jP8Hc: !zu3y󞶍튏݃clfd163Y>l_(x%Q@-FX_4X1o} WPX.b(^)ԈS&q@$ƥ6R:P, 7 岰#J h:*t)Hd(!p%(aE'ȋcDpA'c[ "7se)PSY9GM-8<h5S8wF(sEdXo0߰@ E# = *,A]<*Z& Ĥ?;\AR 3IaLFC3b>@HƦ$!jMKb&P"j/a\As Q )D9"J@j0H0(Tj @b()υ"5ՔY2dTP4PoдDVhYlB](A7>::s $H0/J P%I qNw"(-p{TIHGp7y|p ~H4RД )HaVV4wTJYH4̠qt (DiBV%@P#(; *MǰQ$glF D Mp#HAAlfJ]\/NC/B GC *hGrJ DA*-1+lBhΖ~h~Uj|Qpj0h!.G h hd@1,0&>$\B4D`(bPi:MF8yėk!`(-HkFz%Z_ VBF ¿&ɴEq1.G h!!-Zj hA|P ھd?6WDIǚri֘1-%@?Qm@hжV} @RF?G`/J/jk߭|~r- PKBJ*!yPK|k|Rnb  Thumbnails/thumbnail.pngPNG  IHDRJPLTE2>'''7772A=U+dl3Dm9y_S&Y7.~!|+!~&KCPMZ'We6nGal:GGG[SKWWWGeHoCQuCNwLnhsGgggwwwD(a5i3sQwFxWY#_4\*ms/ewa)k6x!rGR"Eybl}"˲$@p"i%lo·"W' YDxtnq1Dr (ɵn/iRJ,@2kۙ<+Y$mr137P-#{j#zZt04pL.8(Qbɬlg2HH$0T"1PU=A@$"W8H c`4m'=dY`T<$'(sMCA/},\zj*(oʜ-E`쎯,(JIl Hg1\n 8sXfPn6gNHZ8Z܁<@A cqegDN+wAe-ۖo";n[8FL!-ҜYMAOJ E,)W^fBF‘j=2shE\$Ab$Ab$Ab$A~^Ado%jGqŘٗN3%.ps "#0sZPLmP@?8'lhPK)zXU|'QmC6w8u };cA#:] >(6X{dGI"gҶa_ .QP.XAPaWA@glm$H&P;[C$s1jb p)°NہɸCZsuTe$d`께#eA\iaN\I@P\jhK؅ri\E²LMq f,0'`=f7Kx-P'b$`s>pp+jHyճ!J  1BAGlDfժ!+MU\1ڑHvV\ҼVܻ_iPwQ;rCFFv 6Q9$w'v ]WI\ѷw*4;X7^QjYd>T1?h:ow DNK1vLta/OsU^ٍ=,ϣ2/Z{Me&0OWF~bCnaf:OΠ #[ *4-uQrWL%@`_4흻 *s Q u,MNr 1U1ئSK N2vWAx8/eUp*[ܵ7 a|vc͘-[>fL:=ӗ^8RJ$ƋƯ3dEvD"q=}14Nf>Y?xȍ\KFZЄ'5:ӡn0s~`7ulV 񂠹ksKS\7r$s< H#uU+~セ~1] Bcۛgũ7mxy2^,J4hijO?[o]W[,|LORoԼ܉-;q }峂HL%2u -OU$t 㑩\1-HUwɇ~'%`wfK0v{6}j/^~'V_ezYJdtGGL|YNVp8ygKo E.R/ Q\~pdcc wU HkohhX8|o 6\S&Go;4{{PֆHj]9)Kڵkux-u}* oʀ|[{eZ_{uJfRD`a偐v(#~+8W]y+>tt5 ` LuN_[Hd^^c`W7Hvp;,δQװs89hrmUӁpM8;>ɏ^{XaY9ٌG'B+# ?xЁ@dӪ0C݌:$|J5fmwg^8$L<&|..P6=gP~HvT"}rHcDfZ|RpUJ_mXHVg'H`FqjP:3niЌS4=#a*ff6ν;Tӱo"_^?kܸ?tgӈ^wjhMYRnP(ٱ)#߮J5W0$cٖbpmۖ54L?C1A6-&775.SwݵpOr'Zr{SH#2RrǗX"7]ڂ]Н^Vwx"55w7-uzcss3ȯ~< 󩱥Ti[s2!#Ot66ob jOr0+z~M;Ȯͪkl;mv󿼥1/qMhE]@ԅ3qcsAccwg-eSAܝK+ln?TDp\#卻A"C۶+@G AyZF};Daal)u[='gwJ1be_!ͽwb0HHUĞO*q'<DA >$;li* E#>,&Z%LLtҁ Ñ=>s}Vuy8\3q"}Ü>vܴftUL`2 ՚CQWyׂpq NEH34 ኉@ą5wˣ2R5W IC*u`/D2róCC=? {tGUؤWV~AbA!$jd#VPhjDn·Ù{|:q6vDe~$Ab$9cD"Mđ3&A>6?>K1Xv&`)Lz\yhK2̡<%Y6H:\!8n $˽h<,(] \j !{aD6g=|Ab$Ab$Ab$Ab$Ab$Ab9~ b6ȪFp!T =q[ezwJOK#2ly0sD眍iɘ4J ي+?RPRFe !t:ɘ3x9#7#ƍeXg2Riob%cq=m+T6B 5 0[Z"u\ZY¬%=d@$Gp3qZUA6О1w?hT1d{Q eĀW Lbn.٣U9/ b9~!i>LQ-.$@ijDU$kdwZc`4M!E@?ES,%JII 4ǘi !{Ɣe4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 6/content.xmlZߏ6~_a8CAdz](FK % $wHdZ&-& g|3.T#悰aϼ)H?L_'av;UCK餬 .*#.cӽuYKex=s"xd'ZgӞ{^n$r >;N{Y˥Vuxqzx{U>Uca!ju1ґ} r笾{\ !$*SlwJW,mZ-K]L:pDO56j6rxU FQ!IdZQvEەn{/2l覈 Xyx4\|8 ǪXnc_[I/$cwR8Xހ|7k: [v|r{wy( a߼)T`̕էS޲I?+PՄr ٸΜB\oۿwSHa~CˎETSls׈:~ nf:b6S׍YP{ϛ^)Mꯂ:9HN$͟cs6Fo{Z/PAY-Yn9p $XLg_@Jݶ< _mgNUs*&QΤ.j]:mkǀ0 a@=j3P/BMʔ2II1%2P˒c$\Tl-Rp hg ׋z봈>C.$r Ux8VB8NMjuY7T3P]0N9;…2Cxznt";{* jMn9 qWxYkvQnv[2& TFS>8Vhq6Q0Gՠ<'CO6(y0Wo~j I~4 n8ņ3qHB9\*(JѕEy;FUmsvʕQ7jP]&ϗVݛSHc '.Gx# t}~kiZ/2iG8HQ-./8BMwIeڜlr/q+$wA﯃KM"\^HK?n@hùL;@%ի!_,Aanλ@UWby pC_ ކ77EIdݕ;dD00[F3~ݧyۯϱPKJ"PK|k|RObject 8/meta.xmlN0 4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 8/content.xmlZ[~\h|c'N3)sh@_Kqԕ-CR&C8NƙEwi-R>#E v1cG"$;5!y1/V| Yb3+;ṂN`w. 9vJK-;|5JVC@59Foي[kc6=M])9|Y8T6Ï5ZuEv|"_^(#rIT#Fl9l9 ^,[KʷWdG26ByB]P煮 o㇓F fpFa;~= TVTb)]vhc:v/1)ak tkE5n׺ݽ@F`gnXҀU'FF=Wqj(4֗=Җc%J o7`+m5qUsg x-h3>F;g"FsɃxo*0eHv̙֫{9Łb=3$+#zxNE^TS& IYE`A^ֆi(t@)e! )07H/єDJ.&v@tW䛟LTN~$ E߮jS0CE!9{Ze uv`}~z\ YtλvͦubڊCu~8gUa7hkN_Pd~ 8bnCR}Eѹ"蹈pq!r.u $$,qr&0yf2.aC&{K)ߜ [зpi8pz,;Yy^_J .&pvA^7_\\ $S,j3퉴_;4Xpf,.G0ĸDSo: m5lTp:)35?+ ;/δAl\V.jGefP-'QHxM ӥzH3j pQr?Pwr_v/6A cP9gDfHa&D&WQ0Wn䅑.#eIypxЙh1k%vd{W({$݋c<{"sω(.;IJ36ED4psco; ^O3=]}}|zdGEdK>'A:SuM}ɧh]jf鞿DݳWj7PK'!PK|k|R styles.xmlZs6޿BLoڙHJvlzm$Q;߼JT}Q +# 5f[5$vDdd_81E.F W#̴AR@U \1/!oYqϐ$&?~ڧ$Ɔm㈓bt54ԍʱYLPӉ.ks& vE؋qD$@5<1JOIt$iF`:384D+z/9J!qٿG5%rߠӯcfi(&oɇ7~6>q89+bvIEҩ#/G)G+ 0z03 ^LDF)=ͧ쉺(0s=APN/hB'u3Vxsݖ6Xo;#)ܒCԏY^U~ t!҇2_-N^DmP)f$J4;´9Գe[$n<w1!1fbEGQu $ކcO"fT-੆IcUB QzSH.]V G})K/n!y JmBK{/ԈdD,C` w)rt&R,a>ak=mul}_q쫅n~oMBY+ V*057"4ȴֶnT#}H{b)@EPL/}5=?`'UW ^sT9` h?3?c15t Ѹ{@v6~ y Ixm`?~m>JVGZ6(rubz>Q"JY $LWsJ*:3ȸ`ēW?kN\Oˉ:;dyB0.g(wzBTv2䰚L?#v4:bzHv$V3SX?H$KT=cQZxAuG=}Tƥy.Plr={(8FES+҇ʟʰo9g+sy$E`ܬ!8`A;&, M=l=G q'GEa[ ¶xnbo{,oNyںRu?x`:$܄++ewEN;ڗ ]=gwKv,i:B]Vm}l<]s͙!ʼnirK0)Ջ`,O߯U%4Tf>3g'=C-K /.[p:'P?D5 \i׈?+hǷ[DD%ivPI®gDG3/ Wl16뫫 pЎ _y[~3xlJmsgz NTAA~ny Yq"* _I"i<bcHgӁ1Vmzkq fE Z <7?׿A`PNWl/΂3JVFwaӾM1Π_:{XPKe߾+PK|k|RObject 4/meta.xmlN0 4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 4/content.xml]YsF~_Zێ ;ǼAHa \jIx@ٽPYyԗ|U~9-XЈLH~45MXd:)t]Oźuu=˺(\f&]DE1Bɬ4*^o?>>F6ӧz֏^I,x9Kt_A.raPw˾^ǫN6i9(i,(_׍:Uv`t/?s~b<*7a9+M_2`x%faue󼌃x>ݨH/[]kmh8<;k1xâ;{3 H8XnY'!}1'{>4_l>~6ccXymFa;)&f-<|rWC]|ڄ}u{nnߴW-Va+ ]uVٺKeVdv{33M;K{:.,U?KoYVeՕ_, :mzc5|amNun*CSuQz騋մKg\ *R/O!4!б{>u+U q3iG/Kt=yjU:9¿\i\Oզ~gs:]ƛ~L-6~iV;_obS79x娩4U6L@He:oMMe U:f|K 3^Ԯ*zz8c|`)< #^}5i(l_~uj:ѱbMhKۯQ)&x@;_q,q!}8s"dL{OPA&ڗ2w4ViL#!_#PrY'Y*ɷB0^!ߌ_qYӤx\KO8##ߌc_oRۄyCF/?6qN at;kt::Oߐ Nj>?ww_;28S>'w-ȳG/==+7^hvyYMg..صpQ32]|DQ;U<@stRv16WSk9mn??7iWW(Y7fIE2]'W@#]wGQoa,L4t7Ox> _vs4q{m8ciLJ#l5D(LJD%RCj@+z 晀Fl##0T$ mE =B\C.¶"H0r(.*#͝iݹdwv^y5nm)U\>O{JvhYw.J"<ޠVgDD j __bD(a ˢtrF1󝋙2Kd] |sO|O{N"!=+#!drb*-Mci>CZM_ؿv=2h6+fSlNٺ>_٤ىKPZs0%g[i}tj]aou;c^ B45鋩[lpe2ڮYjɮ_r9jX޸8Iy7|7^;>sy# _m޼NXo^Z#xG&7d ~wLxc׏.5ܻ/I1Ľz <Ͷyt DH*FJ_?'9WjRÔB+B"5 $ WPƼp}_h#1 ٩[9%=t_@Un$L+^9vLD~ceF mcPha$|) 0IH>BBɷKm$cҾ|O$BBhSPH,H%`XO.=6G/1FQ(uf9*?$ݚxP 6UYk9$|?R!|eLr"H(Zj.J/#'(O++ na3GBVh(*LKTH"b$$x$AD uH0Qi\3&_ PSz"c$|iHC<<BI -}" }p.VkMB`xt#,vr E PJ:ZS%B# t (BhHeqr :D { Lj՜P۟!f6hcbc@(" )B+ L'EFV@!0DBɧp*Hq6l7PP7Ja5XžɡD\ 3%}!FB1ȴXWC$|Ak^YxHi钽?FUH(mDUBB/PPa$ ps~K, x1(T94.0lO4(|fh6CA8DBW\sb,Ts_>FB8iH#LJ~M}8UPD# j0r1 h)P1 &M+-G>H0FQƉ+FpGnZj %X\DC/#-S|LP%\u#W\(EB=sP!OQ&Hm 6Jr6@H(cW6=DBuE e(𭴨CH$FINOx]|v 9B!CsgP$ݻF$S3֔JT `$ hy4Bhhm,-Fmw |Fbv嶻R1BZrI  %_3j{՝8 _>FȷK5Qʣ!r܉ԧҝw?m(>ݣ&@ >Fm{B.G^DJCO!Zm5H(P7_{ H(-jʘ1(_ U, )Iw筂4`D@\rHv.G@=QF`Pc4Ռ@18C^A!8C ~FBtWp`<#Z JX*-"j0QE0.G[ˁ2 jȽ &_5$P^#*Ź%!@#5{#FD`HwRJ#@"``rD@!b%цRj !CN+yL1濨1D13wb$I.1^Hhr:L BBрqГ4`4 0JzC _Tߏ4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 9/content.xmlZYo6~0h>Ȓ,E}@_i٥Dc;CJ%Yv )& f84v 3 Y1͒_} xL1L.-s~lsG\$|09p/]1R u8>||Pm}Xмw#,io# ;2~ tVv@;gr, bwPwZLr"s&Tm㭖^ղz NA)eGg {$(߂9$Eh(gĨint]YՕwsI;bL(Kt5I_guYL{` \UmKD9Gx>kl̍ c bpA%iV"#莸 -V.:YxQ:?ЯS3ttZttMi  T qnFꉻ|vC TS Z;TTCgGeX@qI߭C5LwOgx{}N_X%Z/bkC|~ 4/Vx v8Չ-'fH _/ =Y+U:ǣ5bQ]2F 4|0 |4#0R9(x3Kf&g ɝ!3ŻR'!jcAwbC| aK8]<xn{ȹBn,n0 viHo3|LP秂Z ReH*k%4GY[C4GpYJ.exouHUOC9Q ,kȺIX!M*1l%\}u$b?9}=02;Y&N\\0IqњbVb;"ݧ둈?@LS 9i5 \#:=0aNyMn[mGmnM\TlҊMq'zd-e o`%mکЀ)+IXN7c{guŧӇ>^3Fs'0@{Dƫj0XK*ybH9 Gi0'd6>xN{=+x*q4MFg=-#B<`3?Z񝿖A{"WLt$ϣv^*j9q8Ѩ&-4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 7/content.xmlZߏ6~p݃~[@8l.)P+2JHzmeYf)KMf8\tñgaܳ )H?+ۚm$ūJY).*!.cqXzx:ZinvVp8؇@~dGg]7pr'CY3;;Y $qԨn/=/WcJT` s#b 6X,ZsVV:roAns~}1 !QbWvbei\9jY`a^= D4d񚔮:]49TE$yk$0Պu+2Lݮt{H`Nhڂ''U=WQWN8FMt/8FI6NZ[v# V[Mual:a-izrww/0:5Ӂ>-T`cqV/?% U_4FL꾢ۉC:U^? 8<|j, XaR21I28WGks/],W.";Jc|  -X?s 0k2< &!E2D{ K9|ߗg9' Wnq!Αi8jEڋ6߻UsxEh8ee6^o43ψ}y"uVa+x ʭb63smDi AMX/.FV`ĥ^T1̠jcCO+=mC+ӵ Iѥp\սԡff_v6A SPM9/e0wP 9/P-L/1^n 4 a($PwwM_j }*INmuoǑ.=;OǾNֿ j@; b0 }%wT O/ŢSO=ɷEp{,\h/$vzCr)[8˾Xa%~d//f'K<̝ws7_CePKƿaF"PK|k|RObject 5/meta.xmlN0 4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 5/content.xml][s۸~h};@qiIvtF.%$=EAt-#JYw͟V'[VY~7S2yfɿLtb:-+yWp'e]Ż}]ogbc-Ӣ\d wTeF3Bl9K:>ezA\=n~RGz,3p~صXcy;tcyhQ0߷ocb9 O; O$w ޽~Xr<_b> Tĝ-.5mh/qx4 SdBw&̀_R#aɱ3VOniqRC3`0ی?T2ی&v4ژ:76kUAQ'}<9GW7mtq^jCInLAWݷUnll nof{漽i'r=t*˟%Wk,dso趬3[]97`]m`c˱5߆9i.UV7ZSu(Q^:"Nrl8ע>@7Vrs/5]^d+(++ |^7pϤjPrL{B*^yuWQI8l=Rgij9"+;9Ur]ڤjS?36deǔn7'[l=Vr>Mg4Jj'Qo)/ cl,Ri筊:,(6{3,̠zYYԮ*p.ʹ)"Gpp|⌏lz/s- SuݔF W!)=fɔL]CvyXs|v~~ev+ߏ9P3r|F>:Ʀ{cfI{P~ɛәE%Mn9ԨLPa'iZZ?$ ÒWnM)LJB%l9>c_} T@l$cߓTjI[,ڄ.`lJAKGKZKȩnjpwe;<;nu)UR>GO-%EQG~/JFy^VSΆ~mDk#b1• ò(3|fe{=>X%-ʆv9nvz>dz'S!1+!!|r\oʖM|J[E/O>h]f 9(z54T)u}ʲܩKSյ?PHʩd21g F Ċ2apD % SV! :X` /p;*%Db#@!GˑE`uȅ[L \@F6ܽxPLsF#k8&ƙA%0F'<8OzP0ZP ZP0?ȏSc_R`ͺ„x.G BBQ}aP BBȇJ(ۛLPʩe1 >݀C> (Ċ5-3q`\SE5r) ;Эr;F3-d  wE9"h G P=m[2;BP0ZXkTGj 扮&M](P@jWL - $CByspZ+@^@PR-rHiChAA -`(-Ho#(HC(LdтTԧP-xhaAj`ј7<(e!)q AhAR~ |!(-)?čLP"{hChyALiXa#*ݙI=셤_/bot_2PKQԬ_yPK|k|R settings.xml[s:~E;])uӺXUWy j!&A EwNNNrg>>A%祓/JzLKq}x@.y }H1B&Hv'Rd>5hɺ[eZ"lygyDDY-/Q yb Vս L^vRΡ) Fg)#=-vFmoCM.tR=v_4гE>Ru&9Pz;b3d.Hȕ0:٦5 W'JVܜ)e{8;dEohP@3LṚf_s[ÚieO|A~6~_1p5QDj0q}=k4j1J1T,q1ׇsq' w&`"lGAԑ@N2䠝!9!9C²CeI!juRj`ov{GZHV(AO^gS)7BboK߁^̘0KaHS@KRiOsGbPQSOѢ@)Ģ@X,gʝVp5|( iawؘ\܏;3Nk>Z kgd,lvw'ƹP 6hG[حa&޵{Ǝl'u{}{>i2*up_eOb]+ɘz7nHIkkGj[{lcF[)Zgpi Ɗ9\΍ui\Dƴ&tmdk0 o]l[^=4.@E y4WĹe߷nmY{U$ c)+c07 q r#KfK_BtQVPI! Xz(h`7@Ɣ=oQ3؟CRmE:"e G{2Q+ aӋzzj'42kѥOHS^D.אd_ +B5AYW-鼅6u[Ku0CJ-PKbe1PK|k|RObject 1/meta.xmlN0 4[@oU5ښ$J"#/ϏvZ"WVWE%5 /ReF:ͨuLAJx4viV4FTp/ -7XXɱUݺ24R^@Bcǵɒj!;8l $ 4>o;>/N4#p~;yvTA'uF9| SFtѦ=n_b]w:C5U^:%15a1zLbƖ)D`[@K8`r%4[OY#HYo`sz% :Ycid(jUY %iuYwf&.!;#Z:^)Bt,4d׼1uq_5YeŮm^࿡Wyj7 :jVRhZ'Qpmv_-:m-IA>۞y!C&5VƃWvJJ`Q~̗^f'HU)*L ]CtPK(PK|k|RObject 1/content.xml]Ys6~_Rj6Kq*&yHU$$qCZ2%ҖGj@4Mڹv]\ЪYnfkvA˔ey|7h.bttecl/e=OWI0VM&R=BX4˶Lh=Qn/vdʥj9٭^VYV v,5f4qwf=DZ)g VoͶU9{^&kZV8) - _|\"#g?~[Q7Ij͊n,oU!eI *[6{$I׭lM*T:4r|)7&iM*ME0q>2=gI6w,X|jwծʹt״Ik5+-f=m] vD'ݛ˦~2n}N37n0nYKr=]r1|ܮi57<`1]p;*ĽW>.ضxKⱹ=4N+/6"c\nUvGCFDsZd"83aO;߽'F|RWmZab*Lj&ذ>"cE( UW$ۆqԐf__]*iF^({7Ś,hu^J],sY~6{f^]{^]M3HWX$뼸CYVfOJUB/ߪb JVRp89NRʩ9c꼑6ՖvTb$R.KFMtZ0no|n0ݰM#)M^vRO& \/]L^Eѭ4&jtpr0@GZ߭Ya4wIv6/bQcg-wERtv7M7]cAj,MʗFof$z*0 *MErM ]-^yzOEE< U4U59 *{5?^W"`kQ@6Vm^Y'sB^MO=8?LF_S-Ԋ2C jۤS<E?]̭cAbb0[{͏+-QZ^b[/,O֬ގ ϱ̛QTەoIXfތǞWѾƙiyZ>r|^-kQtH-˱μFE? xCz4Y,μF=?}azZ%+!%3Q#W`͊<{c?}Ḍ]vew=!tuP-OZtvm,w3B:ZL 5wI]˾W+E;B7IΑ[JNp' ۵Ki{8|^\-|M/nl/MX%- H-[ӑn.8CBǖT@0$z VvbĦ`BURj:ɲ \=,Asm(01vW|ŭt#JjEkV{M58|>4!M_ R۱5^rLJ| 1+}wM[v+7UN;vXO~;SM}ӼELeB:M jdW*"`oɿ]l1S~%D yx#󣰘^eg"`U2VUsZMwj;A-v;} ~gMSVfjZ2D&I-+QBt}ec8: d ڰl7):>VhޏB,ޏBeφ(t΂(t^:l>B,>Besφ(t΂(t^:l>B,>Beφ(t_΂x1.)a}1(xM? _wx%Tw[' +iC}P2V4h%uYsjP0zycBcht{v|-;?&KF|9/ds_Rӫ'(.~KcDg/G~RUy|O*lpg>_}I&{'WӋO?)M=6$PZ SB<&LXFfZ_);( 7Kc%cx^,E{t Х @Y>#'ṂK.7^*Eܳ=%>?J6.[3r۔)T$yA\.4A+IS+EpcZ4/?H4/ }W>\MꋓHz qC^q~: m4lhF$$˵FP40T4xp8\~9@PI,CxT41HW2ȃ!q,_IX2{]3 HNXh$%FqA=h+ ,ڼ\[hW^Hcd (Sp;LwW$c \8&i$^O`.F2^:ZwFHBG*F!N4LG@38uUXEEKX~jT4// зߴ -nqvC- ^@A-vڊ@F%\^%ˉ6NRc$n5  x!x'4H-hٶ]KX<$j(JXh-xT$UrE$j.A#K,n$Eh$>@!I,g ۂJAi坤֪tE C-eK$_DVK`& X?pdoPuhR$M3xm&.5I(u=FT7ZY4^!>h#Xj <$v3IbeG`"C@_v5AK&i$5ZKh7h1D>?rj B.h0?Q ^zˊ؅nBw719l`Ahmٰ"$l dX&:AkE {{[L8j28Fbk]%lFj*TgNh?rc[! !>& b򐲵?GhM%I"a] Gn]pև7bavPKʿ rPK|k|Rmeta.xmlSM WX^1FWaVj*900^9썙yo޼D[ltJۦDp{zr; mE2%Y}0J9iC5whau W>A+ v/X15J^Ν7 $qBVhdIdP8N1]ml_S^ &9^br,=a@a{P1( ݞqde1OyͿ-߲i4ftգ>z2Y'iLm.Dw@BNW/'چa6hMP `:Jќ`̚Kdɺ(sD"r;TPK]TLPK|k|RObjectReplacements/Object 9\]L\E> Bhm ARMlW"e4H-@ib@CjP#>&I㻵}FM|0b|Yϙý\8Ė璹g|s;͝;3;7<&ZA>sX<0^&dM9n[\ax c HrV xasV e $ q`gtH7PVst= ILi>8e0ŏE>7 ;!mx΂7MX`m">a[JW,jۮ?|kָx)V`辜Ȁu< RД":pXrNo.OKUxhՠ1rmHr\&$/&$X,&$@zJVNc"WzJVNL)Ymr8IWk9d&1rxDX69$F1]%=%M7#r&9엞&9KOjMb&OկZp9<"ZskMr,>!9o;IC.!9]n#XOWtѺIC!9IC%Dl6YpfwқmK]ҕfYpm +5d 0tepZTgjLte0%LTNn`,ضHWvk ,Y\~X"]ٵ hc #H|47EnJǻ[x8 ?ql|vl>13|7u1]NA2 "XUV-Y&?{d<!]U HwâRWs$ǪQt0*!?X9!|\tI =k/ʖ'QvOW){.o/;u߃v i` TP7cYt⣚b1c:D*d)iSQZQ\~"z~ɜ9uoa ^29KFk˼v]LOe|LgƱL-'pRj8 o0B0|s ?f]liF2x(xhePC,XT+-]{{`4Crт)@#HZPGNr[v{=^vI+%adOㅶ읛idO`x3Wv0l5,n2} $G0(.&a x \Dsb! MЋyOI"ay@eSO#[zued +KN`2=8ïy=.Ev|RSNlK >[ U|p%W⬫ K -aSWzcMZUtpy(C3[j[ "8}ge)BF g!A|p-g JOr ruCGq+Hgəq/đDEyU U{ cR;wz 4<?@ld0sd[l{Og4ytGXkϣӛ=媞K3j^GS ^W+huȁ p u Qߕ*"˵%-Ivq<,#eoLT얣FFޘ$-2RH%n9H#$h)7&G*Ivy19RIWOb+e'rDu2RX9"?dr'rDV){crI㑛e'rD~H'rDTeT?Izt?#@r'rHY$Va 9X9$~+G0Obʨ~Sr(c1Jc̥+h7yީ%Qxnwd_l<+4&Éo\6ek<<+4?d嵞'Q+=\,/$ʕ[Ep|d/>9&/~..ĮӉxdpt|JVus[*C9E:,{y>75Q&4>=ҹ!9 ;GM=C9ZW5 !j)=[GsLnt*?%.r%GaGdbG؛,Js50>!'D"i'D&do?.eBZ>s;di^"sk)=u\ |r%Q<"#6&;&_/f`ÿ 0mhj< GU5Z_5Ӵ_S>B<=tʿI+(8[C\۴ 8 v-:&-tZwʵts.^!kD\ۜu-m/6l\;Ur-Z^l\vw̵-Ybs-v>NC]he]vSh&q>$"Izl~M QWJ~j?\g~pmCLՀ pV}~b%w~(&eDDD1"V}3Xq%0-WCEO 0dDofֈ v]uE J=TPXR=TPCUzZC5z PV;Cz(PZhD5f=衵zhjCmz]ߗ mBHuYbP.:(v ;&ܝd݉;/؛7ƝltքSwyXaci}6"fXdLkWޒ7 [_@- PKwRPPK|k|RObjectReplacements/Object 6]LU-lY>(vkJ6+ Z %"в iL(iH(!&ćRfR_JCc|Ęhb|hCk|;D`݁rs3h{1uG $USrDWkI?hCob-hc[Xۉ5Z^oa^f圵v- ւBt|XbExth/ٖz%+ [I:}(t6vwJmIim=$^5nƈn'ȳmBzֶ\YҶ]22RIqokUIQ"ȎE(ϫ8;DⵌUEw /)8&3ek GHn(LÑ.8"%2Sp6ʰ̔1"cwHftƱ/(3ek GH /h"h*5)p 2Sp8E<.1S)3ek S| 3ek SwLp:)[c8"ਊLpߙLս#8"Pwdp.)p%"0)p㏬"0)p_hpx8\;"Pp¡'iG Zf3dKI++>ͮZz~VN^ٵ)\+#{ҶTA^ٵ)$.^ٵ)tT>fYʮMaoV~\2Ոmte!. Ql<11 zRɞTogsk{hsg>rE&8چƇFGb]}#d>܀b]GW>|@\ޒ޵˯wn+w|@ڤwZWag 3/ig\7/a쾊4sb/XFZ(`:JʧĬ ^ڈtĺzX& ?b19S;H4X,ϔOw ?U0Mx#mY!~]hH33}gR3Qi3 -13?ug02g\==ß'fpcMy6e||l D6Lpa[\c<ŲRy?ڝ.> Wyqڌpg*8GS>NwZy4{ا6C&vUo`{سR޸ F{6'ِ (s2l&åA9N 6d] a9']>EȕockO:y&Q?!||Q ul?Ԯ[^onB, *Dtٺ-L.$)")W}I1Ѯ|1:KG9^xA9eUg}|G{U7EzW"wmѻUPJ]ezW^KJD k޵MԻj[98x%NPKI`7NPK|k|RObjectReplacements/Object 7]]oE~_p-B%Mib =@ET (QDBE0)&~ .?@D7q!;;viOw9s9<̻;a[sSjE+H|,!gi>O DZ|6YB*vntK$Pj,$ƏA(vssxllVM:F)g D}y\Kr[ IV~eγvʩ=;̖OH>qg~{hg_`U5|YΡv5ao#^1DUO0td{srnh2\qg^U$ᕣLxV~QZ#+W%1rDDeOO{I'S3^#GD~T/#"rT{/#"r7{93V9$J}x)'1rDHh$Fɡ!99"$ ~#GоOb䈐ڗIC?#Brh_ '1rDHk$F!'%?̥6'YdKkϓNa Qw-=|icx}2צP3Kg%A͜/mz ׊V]kr1^.jYjXΗ6=BQv"8_ډ 8QT'}T{mo=AxR!uwwp ;pX_%&h,8㜒Eo:g1?px ®ቸD0=8׃4D㯏APwseUֈ] {BD,쬽33a#!5t k/w#n9%sKWqfL"eA~Ie|4 oN-3^´mc׆rbD[fi-ã+E|yOɖ ٯw n/3 3/;{~ql}HnTd~{:>9hȽayUHoqɀͣM&҅jH-iR&2)Ԛf o)J4)xHH/6!WL 96v/: ]3'̓4"zqy0db(_ZР}f5昌qc͌!kDX O=@0X:s UYD{pT3؊Sڇ!a6sR( "}y[5x.*4?H}F`.Q.Qwuݯ#ED 2H;"".'|ѹJs{H#PMSXdCsG:ZOFy?^kkKbOkL"_ yjP b5TJP*WkjR]JWCUjZ ըZ5Tv^ %z5WsDZ"5Ԥ%RC!'9~<:2Bzž}ayq䀽p.w%=̲8[%g o"mv1Wb\2E.hL/ؼ]]sS PKnDtqmPK|k|RObjectReplacements/Object 5] Tՙ>3003Ww#qQc8 dfP [.@WJX*e*+lH.wDR[XwY-fe~Os96{w> )BdO s kxVnNc K>J mK ,k,F|^LjB̜ {c/j?vcO9sR%/, DHFR19T-Ji@`:8Y,p~'xAՕtxkf ?HZf ?Hf ?Hq"BdRt3Ftf ?H )D6: Kǣ`.&a./L!)]:vML!)]:\BdRtсBdRtL!)]:vc6; M)"^Ҧ{͘)D6: M IL2)m:D3FL!)m:k1SltxAJ1SltxAJe@d S Kdx2Q&t1p^2|BȔݔDt `B$L2e eB)(:~L9Q&t Ȕ}~$L()t/HDGбfs$LI)d(/HDGӵ Sa^2fAS eBLLgzA":ʄODt 02eW(:V: K¬c?HDG> QFtIDtS eD?HDGQFtgDt{ eDj'?HDGСFdXpӱO L'8.Dz&ـk,kΛR g>g]!&q~ӯX8gt$8N0M/s9K⠔,6o;<*wʣb!s?&1:@vH}^fZyvaU=D92+P>#G]fU692+P_fqlILq#1:@v&2 (FU<@R2U4d7g=QCC Sp?CeWFU?&1:@vz!Y@!}U}lsd nW7.ݫnTfC Spn~ސoSCNcM$l'l)9eoSIN!!6s=kI^{RkӸK3B~/ks0@3}OьC Sp SRO@z: !Ty`5C2Fܮ@K>iOʯ6&1:@vO>p 4/UD~692+l&kӛR[@h&,MATIk̋sd nW.K;&1:@vjk>Aޗ*<"@=`c-y_䁽&1:@v)z/U!y`sd nWJ)?&R3 mC Spur?Mޗ*#@<.[1-}#1:@v+AǷR%2Fk;u>;u8y)lZ=рc#׀Mg1nspy*{xtpNy']7@ !6s=8tڀU=ZT!tv> GAT/C]!k.E*^U=ZT!tvOAzNPV^U=ZT!tvOf/ ' U=ZT!tv[qF(z!B@ {WS7a.%l}]Kآ \®vEDع7ڑ0ؽS\:e+C\oE{9i7#ӓS^"F< /]y9-bszdy19Ki7(#'{)d>o;/>/^|y ' e):N Hk`T@ SgUi^@}VE9.=ߩ7]n_@{c*#G~Z짝чp^[^ð30ngqi/a9? o˸[^sōb nbP'n։5reS.j{ZxuLCGcJu"]9Ve|om[5|=1^)u +/60&cqݟo1Kf CmzGn,TUiLᦱ 6,[Si\M 7n/P)V)^)Q)YM 75*RpSpS`smsUȷmഷ%[F_ς "cru~KᙺSb|nTkj#^L]S (pnj:lv%B@pW' Y J>^bD݈P=l/_Mk۩鯏6`WzTI!zAKIQf7HeUoDt0ӡ5<~']f!-B@pK'~)(DmF\d덈/QĻ2_f'Tܚ.1t%?npKIQ2I$ʙ!ѡe j~}cەBˍMe6ꟙT bۇ@D:MIdDC /D_n.ZbK/(ӌmA ʪi):J[CNNT>%Ĭ:gr3ʩq4Sn_NyƓS(a9ʤc͒u֬2jrLvHvN BٯcRۈ_h//'s'`FA Ey?e.B-o~M^3(( n:g>H\ %1]*ӭrDNm!Rc8 ǪW+{rG 1-[/nyJƫ+~,4'R>+J,Lؗ]g@n{}kr";1*n!G]͋b5|`WW/wʎ=͘ƃvrFKKUPKj[PK|k|RObjectReplacements/Object 4] py>ezҕcd3# !Pe^l`Jh4L{9,ȇq|?#z ?5ǕK|2)Q&; 0 iEnM:ws(bu QM-X!$J d>#$J d>%$Jd>'$J$d>*$Jt-d>7,$J5d>U.$Jd>d>s0$J\d>2$Jps T(\:ΚL-I":gtTꓰ AR:9 )UDB*CB*CB*C~B*]sQMǝ^i:*k(toE_͢ ͗~fNO1:o&viyM-4-4W\雚&r4J#/iu "ώiME*b珳womFSV fmGWա,ݡ0/~Ϸ@Oߍ-G?m|v9į94=QG ot | /v6 qt:yk V!{o䈧lV7#䍮!/xXow7+mqt:yk VlFیaYGW#7@aZelWAgYGW#7@aZi+?g/,GW#7@aZalu6e'7 䍮!/xXi l`]}Zyk Ve'|l6KNG]C _-=՘m;mv}GW@1m4Q|f3?QG ot | Է m[mv _&7@a16yqt:yk V}ƼXoxfs8zD5׶1)[Nczv)NY7;eSfrng(;egSy /_ȍҶm/iaB;{_P?/@-=d˕b e5+F>^q{_|5{` _r5+q@ޗ+O 䍮!/xX7,i`˕b e5+y|Sf˕fl@g 䍮!/xXV9)Me˕gl&]C _\4ޗ+?X&]C _8\ͽBޗ+OfVK.7@aZΗ׸̾/W= _r5+2i!˕6\@h)Ha`j/W=\ ot | #1s\ {`g 䍮!/xXxCr _r5+PcK}> 䍮!/xX0{_|=aM.7@o)kcqvֳS6| ~_ .>o&:=oKtLyfuWCԉM.a(m/Q"jgϫ=v/ב~tGa}4=7*57^BI3uekpw_r!DT !$аBek>{Ze 6^r!DT !$аBzvwBDB +<{f7]Q=\UC 4Tdaw'"hX!)նҹ;9ղT+8ɅQ5@ h3n!o eW#!VxDǚjfPv:BBHaTDuQ5@ ϧf4'BDB +Hʮ^GUC 4p6ӈxPv:BBHaWv I[O^ʮ^GUC 4d.Nc eW#!Vx/ѠIɛA!j!jO>$sOa;5Nvjaevj>NQvj윫x_4G tP(-U~6!6J~#OdH짒IRB{yGGٓyD,"hX!ཬb/ǃuɬ|BBHarc|/ؓ9&-\UC 4^oz=\UC 4^na/6dn|BBHaq괁 'A !j!b%?^L gL.!Vx/|뒛wA!j!<̗_dOaM.!Vx/OK1{2 >`r!DT !$аB{YD^"9uQ5@ Fb䑱 &W#!Vx/4 M!j!ɋA;3L^GUC 4^^A^ ";uQ5@9 ;5/p`Mm fgR-CWJ3t'CNS^зdG'OcNWwC1S%짐m?E^~fy0%of=o G#%J&:JjK e#{0kٛy#"hX!,e'T)"hX!|0?dW#!Vx/wus[|#z!DT !$аB{By0ϐ7SW#!Vx/{;ȃLz!DT !$аB{`^ʐ7rW#!Vx/2t7ΚDT !$аB{"7 ]ʯįTT]BN~MP395okl"[tO&{bhKOf[-A uDSIԤBS)P`i!Id>`r!DT !$аBl^vd>ƞtn\UC 4C|s{2أ0\UC 4k^̴ &W#!V^̿p|o T)"hX!V9 9~o $\UC 4+$^_[ !j!b_Jq+pSr!DT !$аB,Px1+ԄQ5@ l2m1nCK.!V</9KCjBBHa0x1xg&BDB +DE‹w?j4z!DT !$аBX05M74\"hX!`<9>`r:BBHSٽ&K~ZԜ%,95\%,95dɩl?̒S,95%<%h]S%짐m?E>j[aNO-A772|Bp?rwy'~MYeɫ=]@^c]q<Qv:yk V"/Ok]C _ya6tnv7oȊc)ewY{%w]|U;wtݴ۾#?fj]ÈZv{ManjD#`Wo9qC9F5-t~~`g"J&즺E-J MRFv]SL&֘%-I M1a&Θ3 ;#%l4aۻc, ̔ф]rbL–O&,NCa^ӃoPvC JQm9 mwLN˼(|Ni p>}crZńgsxLN˼uz>K -LN˼М"9KY9!lwW:XagwcZeVߖєE?6#_W؊?O/Ǜ]uxsz ~aqy1O+%fi17v&se:[ɶ6u&>۔ZɔuvҢjW^[s aߞna5FcP1c޶˱Vc~/fƩ L*4xSm)śoV鄢M[7 o^iFM ś75oj.śZ7oj+Ui`fMś2E05znϝѤÔ3d7Rd3lw9Izx¨9<}ch߻Rt2/ΰGRbuf]›cs==dXSy`u򫧋(5DFbF˕74\z:qnMԕTF) 5ʥ3i YM$Rk3"g҈Ixj ˽-e:,/~ay/Ae7CJ"!B-&Wx5i,x ={S|~ b~9L~MyD6O˒IeWH+d]evOq/.q , ?qk7zgˉIegIٳˋ2e.߷Q~kg$Ρ 2"ʮ]f(ot|'P8P}WYΖVY ed5Doa9gNOkrj&Գ$:?q62WXޯ2qv5R&F2 YZI%@ɮM_COۈ@8G;v-9Pm)w~;>nve8]C5{ )ivskʌVf)7;y&+}ў3]bei.?nFc`S,ڳvYd̳HerTc].;._.\.T\[/qľ30|30ǽ}%A`2R0&Q0`4DWS~U"(!DS֮ k MRĐ억٭5l0?t7Z:{~܏>gy9;sz}s~&5OO T[KR_-!j Ţpƨe4cMD}ݽFdkRB0|֭h+ \xŊ\C&ݧQ!27% 9 BlSd*&Ar:TܲNt40Ցh:0OhHt4rLw$Ar:@A] 9 B& S?I󓐩d$HNGЁZT|nX$A5d*>U. t`229h:0OLt4L&Ar:3Er0$]@tI/h :ħ藃t4+Ar: 9 DzrUQ"M]m:/Yc=pXZ[qXE?K_7f.9˻iې״fCs: J/,Wrn+XFh 4? ouWT]휑pߝ$xEBy~!h:]lS1IzzX=,pM{61QF gt \ X7 'c^kMmR9K WS>#'ߵ#.!+_>UOYq429K W X뢳B?TsԀpo@LVyjᮧs~mAyz9:4%/䧪n'eSԱ>ޱ2=3++j㱺N't!`>IMVT !l~j'^"eS/#UB_!`&>M''zlDaEW'(z!B@ st$CMVT !l~ w$DaEW}xX .^$3eS/#UB_!`滢dleJa+̻?{(z!B@ n9 Š*! ԯ0.す2P62BXQ%V6 SeS/#UB_!`2P62BXQ%&^~ eS/#UB_!`=^i(z!B@ SY6ɞ4CaE* _~o+4/;ily N/Ω/q/[OI1>g<]+vYjᮧsҾ`A/V27&ީceSQ}4Pb0IUT/#UB_!` vI6ȦG^F+6PB{L4{KE2BXQ%2inaEWb dmgaEWbo${MzTT/#UB_!`&".WYv62VT !l~)vdiɦ^F+6PB{H&|{lAE2BXQ%ء"dx|ूeJa+L#E2Ɏ *Š*! ԯ0d!f2BXQ%"|x*dfYVT !l~)6M2DalVT !l~)MxgeKńJa+L$WO]fbBXQ%J34O} ܿи [vhԬPAD wL=%`."+V5bD]L"Km_0_vN 9Hd]["(ZèceSQ}4Pb0n D\ML+6PB{( sf8NMVT !l~)̱KdjeJa+L/d+iO@S/#UB_!`! Dd ٙz!B@ S Y ";faEWb/MVT !l~)X@f;22BXQ%أc{JdaEWbvC5ݫ<>`eJa+L1dŠ*! ԯ0ž9f32BXQ%@DvC,%੬y&<GM}^R ^R2/o^R&/e/z/X;' GJv;uL`*j㰺/*}BLb'wCdR]E2BXQ%b1`馸KE2BXQ%إP͗9;?\R2BXQ%X?Ti5, aEW |) (@?⡢M&N"A\= 1aVI؆1aVIXk[LX_a}9axjL؂2UvKgL2UvvOLآ2Uv9a=zJLؒ2–U6vNaK~T" ÿA& fGOKr:*ü)896;gg&90o-NzNw?邪t$ r:'9]TaÜԜ"cZ)5u" `bpnHڨdfpuM'gu Rh_>Ҵom'о(}if2Ѿ$%9#E;.'>+o<BjA谜$wͨTZ6(3ߪ mKh?6:BÞw匞uUu!V \9+dJkBJMH0,."h%Lyd;:gՅ t}x7PWB_RkD|DY:DFlꝈ󆬨Ygu@ei%/n.[ j0~a.v ٮz'3TԬ:qL e;; fտ>(I~MyDe|o5kL& ަLrK'u~=FY@_sөzZQ˴@Os3˻ f՗2 9z.n eDM̺)gtuS'Ǻ).RNYԥ̪/N8"gU îgRONݪ+פchܮ8!t24ר2Hz^ȕLTqDlQ6G ter{hh@DpQ-}CI[6]]C_{PE~:!٬4ğ2ʜ7r.~CFݤߨݢ>qDp\͟ik~;Lt~JQ0Vu8wiM1$lS .4`ħ4r'pxWsf~|xxJ}v389Lt.oթ1_Wd'k oחMNxZGщS'GQD)jW;bz. ϬfgyWPKV(4aPK|k|RObjectReplacements/Object 3] Tՙ>cLCQHm$( $H!P1 >nbSPI5UJ&GBtKUCv7U*eOߙ\9{{;^rQPL)u|Uq JՋu;dkw&hW՟ХV}- Kkb953}"]0Wo^bEc(_GErHiH?SPWӅzłv%]뺻na%-C|դJMmS?W#ԛڏ}Q:#]A2W;T*Ca4tCEײ͒tse{zk0e隑x8UB#x8L!ё RtL!ё RtL!ё Rt؈B#xx3(FG&Hq 3(FG&H1)D1:2A_L!ё Rt<03(FG&Hұ\ Kǝ1Sbtd/7OL!ё RtlL!ё RtP#&<ޑ RtlmL!)n:L!)n:>Kb7 c)D1: M=)D1: MiA27LTg DtAS$Dc 2w %B)(:6L= HDG.2d*nYA":J0!Sq?Q"tT|BC$D) HDG3P S.A&@ %BOL'CA":J Dt0{ 2*(:`d*>s0Q"t9td/W8 J3A":Ji %DLC|v&HDG !>'=$ QBt_ Q"t*s&:2Aݥų{29C8OGU8#vd5}Y^L_̕opµqtg%wMEI~Q\VC+kXW^ՙ܇JW ܻ3pC)0]}^{zhաvP{wMJ!a/)\j{Gg4K NrSM=D%w+3آWh!d.!-[;{uwUNDS@قhXz?z2YKd VEЧ[grEM~4bYKd Vy:)?bYKd Vd}tv_GS@قh>{WgOelM=D%w+(}z !}z4Ώ@قnUYA>GSQF kt l TqR, zM+bYKd VIJ8NmMyI)%w+G_Rj:GSWL kt l tjR, {Ïu1%w+h@|]gw|M[bYKd .mS9e)AN)CN)HNY#9e) 9e)$Nm筲b?ofLx`0_Cnم?pf~z[B/S</@قh/`#˔{q d.!-[9%˔ l3^ kt l ~a2rv d.!-[ѡue bYKd VytL Ǧ ˔#lc^]B [pM8ieɘJd.!-[&Yy_y`w)@قh^i}>"@ق=`c5y_|y`Fܭ@4 SϑeJWYKd V шa#y_F>1%w+x\Pm}?@ق<+72cAYF kt l &2?!˔OIL kt l7[LN_+q4qyZ F:)[Ygz>ãW8>8vݏлOn[e JI޺0ӗ=0۫;;:8oX԰_v;@w/u×+낲Eu+xV[i?*le`Q%@ #}zWP62BBpnr%<*(z!XT !8PB :y((z!XT !8PBV ^M,[!;O钲tIxƐ/*!V31le`Q%@ ^PNWeS/#*!V|CEu+x;h@8z?P62BBpngIeS/#*!VjhHz9P62BBpnO% M,[!{L&ЛE~J~ZCÖ#B4hs 'NNjxP-k:ޣUO'?SrO*[O.VJ~=bҫrf7ݑBOyQnwم|;Brct d JKONfh1,[!{9M^i!Ji1!XT !8PB5tizd0C`Q%@ e&pfR- SնTz!XT !8PB4cUJJAIb\\2BBpny R-ƞE/PԢSsc#:5݄NͧT R:4+V\beVv_`EO^~G=nfm޽g+.^^7WQwaV!z?U2ޤJ]5׵ |ze̻֩wyӴ4wӊ`"@\1vO ¦G%lW'lFa3"vG'lfa3#vSOج fE%F͎K9͉KXwO F%1>agv~Dؙœc0XCMd@ٶ2(yYipp Y9-`szdbY9-as 3rZŠ/9>=9OP=r:;+ jN!Õ:ІԙC} af¦x?C6V/j9$g}rO<oUDnƧ}vnѮ[iT"~R9b c?c1 _8hRo5_S jڦ6[vե7kkqUmuZo/։-ԨGiIׯJ@*q~um U)jWlƕ:ߌZA2Wn94wSyanidΦU:+r7UnT))))&wSmnԐ17VΦM͹Zr7r6 D] 2\ryuD*_Ǩ^Xgό z{yY@_]5Mx3μ.Ēo_ۏv__ tr_!Zq˔T݊P =l._mk鯏$+=}.wR_ % l@y )DѠlꭈ.6,jN+P~~y~` v%"e*3SJo~E3]YE]w|  UNsB&i;~mT׆5c@O3cUqL(e'B'uX㷍[AJ,,m8޺A֯OiN" T=_1}_k/osRXUEDE 9ծ?{s Ǟ&3AwFKWMPK6>ydHRPK|k|RObjectReplacements/Object 2] Tՙ>c`/fgh 0(  A\.`J- V\-UTY Zf!=ݷӗ9Ν9;=}ιwmʫNeMB^Ԋ*藗dnT~(74W|mjdu4Ir*KG>c}"FXtH]o1ۇr5<&&B>O>$䕩/DZzҁkV0зf¾%x]\͛%J^Ǘ.?BَWvF*U@JQ,h*)EfK :sWe{z5i,᧫+'HXSbtd/^5(FG&Hb KXSbtd/?nƚB#xak QL6)D1:2A?5(FG&Hqx KS`M!ё Rt<>k QLS)^:y: K?ĚB#x逡hHBLcFxG&Hqӱ35(FG82k Q Hqz)D1: M)D1: M*7f)^:?(:,A (:pA|B)(:6{A\de(:T;A@M'4Ab:Jr5(:` T|K$D 9PS?A'AM'CAb:J5(:`T|\$D耹PSA1#x鸪+(!:ħftⳎ3Ab:JIؙ 1%DLC|~&HLG !b!$D0WUtdHqO!?OZ|{ߧNǒ8Ozvgiƹ',q>ӿ4̕OЍCqw-:LU^5~^CVi]΢bܫY}w sZVŗuEGѪAߠn07z{Q¯(\xhP7N Nӵɇ(#5@nZ#-ʋO~cEM>D%w+P/ם7ٺ6Kh!d.!-[_շ /&?|2YKd V%ܩkq͂?|2YKd VEW}N "]+[h!d.!-[苅ug -|h!d.!-[髸źGXL kt l 4W_^֗׵>ߏ&@قu- ұB?21%w+PM2Qfh!d.!-[fNUI} ۺ6SC]B [pMd] uɟ,&5@n:SUʋ\GQF kt l 4KJūum&uC]B [piv:{uSvu)SBN);NNy!9eb?o<06T$ =[x0 4He}U} d.!-[Pc}SAZF kt l O9t}21_t%w+r_$ˤFeFܭ@KN>CޗIC/@قh¹i'y_&CS] kt l .}i]B [pͣ8[HޗI/%'] kt l 4.2L!ZFܭ@=4u}} d.!-[i@ ֒eבv d.!-[fH!~/y` d.!-[. 垮FˤG d.!-[:o&ˤo%cE]B [pM!̏}NcYKd .mu6C䔕SSM86rfrʞ yc5xzjRvS}ʦ l׏G~]nyla)if/ݐvoH)g̀BBpnZSM,[! iAJP0ཤȋͱ<FEu+x/ȋ9@K'O,,[!{9M^iZ25LfVEEu+x/ȋGK>O>.*!V^$hFDd]UBԭ`t!XT !8PB0y1e#<х`Q%@ QbҴce|BBpn$y1'i^ɜd|BBpnet5z1idJe`Q%@ ˲*bJΗEu+x/k+ыYKcUt,[!{)@/ǍW|!XT !8Piiwȩye  THi62YKd V v)x4DHr%w+x>SLlC/!meFܭ@K'ӆ!Bڔd.!-[#:^ђ6 Ҧ\F kt l 5)Vi62YKd V K &HC)]B [pts =Ѓ4DHr%w+xP0q[zSiS.#5@nozTг4DHr%w+xVл ,HC)]B [pzZゴԪMeFܭ@qm /i62YKd >:}7^p`}>QWz͗:rˍwlܵevF3c| >GCOkU̶7r>N+iSWs\A5[՛!hex`NȦ"@\1vGOج fńe?3ueM: ºc² 'lvac² 'lNasb² 醰 zb² Cإ]Me,b$ &, ;L(U)/zBF&9i#hY99-asvK9iW#\tvNN @;+霜x 39Oҥ_oǓϙ̓ϡFa "lZs8P 0QEËƄ /*/^4>h]xQoExQexQUxQuxQMxQmxQ]xQ}.655cN/j/j /J _7=`7ALmSy硬R20JϢ~˯/èk5ߤ+':c.~ۣ pR?ui cuOn!V.vY5ؔ7 o^ t{꒷B-"MELF˭^УB\vv6uؔ,zU^ իKS@+Pz &ߊ¢FmnmGj4`y_hxpm]Ae(j@ hST:_,jv9{yv 2S7vqo%لۄtɒH2/jvq'9{Ivv>~oAQ q*/s45jw4-R>MzޗoA Q~ׂ<@ Ҧ\f5[p:5sX ү' L(y}^}rqȮn]v"wD#iw)$B{f,%-/@E@FF=v ;L [!*u:Pu e e e&PbT(12J&P"T(2J4QƴhQiȴ}lYJP2_muSdy';WuIMw~(s˷m]&󒻘x^ry %ןStߑ#r<,gn'o>gG?PK"APK|k|R content.xml}ksG+w?Yv䍱cI~OĄ7D4Iǯ dHuV1 km9h 8Uux1=^,'CYÃzv2OfgOˣp_OO''ꢞNгEVC'r|2]''z>|>j8;cѪ:ޖ>[ӻJ+!x>?-N17=/.>nشRz1OsǧGhnrrWywo99/Fj4;M{"񢾜/V—WiSl|r\Ok$MO+4W}W}XLp/Ո]Xec_7ƾ_q9=: r_ץQxZt㖵_bS6zvu^<Ϯ+8uQo,}qvOWڇˋM۝)̶t^@zo=Pt-|RO4>?Xm=םx:wǻGud1$;uV{ꦎ׿><ش-jSF8:Gd/p &~`L?==|IGczdlN>ZL>6?׿f3-)H!i/j4^viA6v磫:yε!4?[u9/po <ӫyãKb5'_GkXx ~&c\qYS[|DN ENT!DG1 UStr6;Zί8=|>|Q_֣4h%GgyZ7}zӋl2;֧hYo >l f)uq_߇n'l49noNajttztt>_L>qn}'Y"jd}zXv(j%8gs[/ޣ]4:Y]yz8%,s}dSty>TUYgi-d1pẄ́<.nx`o{kc46]h<\{d4;[}?LVlN@Zö߽m6难4җZ*߂s=|5[M.j6;@tm0L>gr}hAՊfw_Ѡ_xyr9p o3~j<۬/h+0"HWwZ/qxj:铧];eԝe%<0p:('N0ߏTsяH)B#e!&X퇆X3[]ICMўCW6lKCZQY+YF|ﳓl~P{wGFMq 74; ?#.#Ɗ82&R=;U7e~0qhĢ=9`i.)hi.ڗuTMFM3X$XF;AsA(iּ*^zf qԆV&TRW1Җ2W"X'UH) -uf.h/e]YDeh$`O~')*c|1e !©gE](* D$qAN;Ms&g)0ހd;E\K@_pI=M,훒= ^TĕuѾԐ蛛=uѾ@#XrK4Ej Ⲵ]ad?m/L|#YF_4XzX#O9(/6w=h5cmIG|),.qJJuў:bhOkV5I,ٛqKUP5IrU+uXyFSpׁ{d9t]hx>8hK+|]pYwīR*PRO6i}SpD퉆uѾζJQ`ўhxE]m:\^tuah_-A#&-y(!qB]gnbhim:XAc%ib䌕15csJJA_XI(E>԰.zo].q4E{a\EdoKcoA*!uўv+Ad8$"irz:)ٗC"\a_ "=zh-xhOPU%{XlJvB9f; Mɞ^u+wk`wuz:ZM~ 2#tT6 |Τ|b%`g /3_g[-cL2i3ܕv=Mzl=Sz/^)kz/ޫ5W{JGG)l;>gN|_;WN|K/@j5xRʩ RH$olN@ `8d(Аy a┗䥉:xhBMjc8guoB 01{)Ls;TF -& w NٚG^( 'yR`v@- 9)>%4"'xDoaڹ( qq# +`rvxDsi7.jhJ .@j+aDFxDwi'NI+-r6GѷRIvP&{Ie3Z]U܇c&TH@="FBW y&PU)3]KA=Rl#A!U!c NٞiDG))sȣ6jEͳLAlͼ !hp.$A D)VHJIS)SWBt`kĤa)S0k%.uxl qJL0\?hi 1 S 9qDg`o'KA=:Nb8NA=4Xh@%N>BH [9-1GUjg)vP<ҽ+esFGrhx{Y5'B V At%;̑h48*Qgs0]*U bym%qK051Xb5h 8ko{{\CVM vϵ#̀`91<͈㼗jD vPTb=nUfB\*;3DW{ؙx{aAYTㆼ2A;y5YP<꽴0`V;-~/M( q+8B؎Bh5RQeQ0x sQC l;#p|y":X=.Z V^A;d.HPRNB#hKA=QGu 82X0N byvoW+뜧z*sð&NWzlfmۀfu B P+q^(^V Ћxz!(pB)#*[oTJP< "[7` Ę;(^V0l 6lłUqE+$NgRda LXeRX%{{LVA{ VɞނZ8S7b*s[^ JДx 8ȃU 5ԇKe6g^/e|# +PwP<"|#* <Gv|x$|}QIxb̜Rd0F955/vׇQۨD^\M*C٤8S0@# +`ī"eFY)ȷ (_XqBEg/0gr|G`NwݝZ#* 2*UƷa{'پ9sJ+TB{al3 Vk ̘& +J:<ilk"̃Q.= VTx[T#0uJ"L%|4A">/3ѻQYmp"~H.&lR܅3zFxD|aT˵xmf#*Sxǃ@ GYǨ #* mNHLș  _F߆NDUes*oک"JIxZ=$gvU)r2cJ0E&NA>{0H/XE|{)ل mXE|XЂ1 m 1o:/הvP<"vkQHxd|a'"h1 sle*-sc*J|w2rFd  DXe|&{߽N8bԖ*sK\J1}'q> E ,*ھEZh~B @HA2>Qì .f Vρfx#vwP!Td*3dPxA y-/t`2WڨQ/)Lj@xd|Y0 .q6GƗU;b8"d xd|a3bCjM`eF)"ODڰJ`Yu\x0+V7|wh'qL Lᴅ N^JG-ByL1;=q.#-/c ɽ2A \^@;(_ȗC/ ,Jc^zko܋ezqt2^]̖G1}xaZLfgw7wKส~eɋ8D1~ϙԳO_ _ uA$W=Aor5 gYng[-cq6g\9lM`sЄDSq6eٵ!< LNbYumȿ"/חR)NObYvml&?ĶoXV]-^x6;OA, יu(mp+ùf7tVs {.']n{KAjÎqJx. r*HG56SgV&Tk,)S瑏x!LR\ )3Ka NAR<30  2B3*cN6S72$,S`B|@-& ^ ĈP;(^X:r6GNQ9!GVf;v}jAtOP>px OR4hm y^-w5o#K+.xM^xD{Y`,1(A7Ҙ U/dlxD{Y|xF>71r%=u #csO"o֍9#Ŝ,¹m2L>q ӖzRK Lma$A !? 1YQFh `2auG|$Ah>+I綃Qe(ТǨ7N3$ùQ:OY'cs~F9Ī3xZ1E X{{_iq2@TygUSzc/J]o)UWU$} bUyvo|lRy[DX{{Z  șӜ0&Gh4)ig~/Q`0h"/5x{ 5qQyx{Y40!|6A7 c Ĕ;(^V #@X M-оw)hc`9du )@J pĪs؃7ݝ'\)U0vyb,Ī Nei32zl.H/ V^T18'9x5ԋKc6g^?e|ţ +PwP<}ţ* |Gvee ?O\bz*EN+_\ hIԥUgǨ F`!>y0,t?WljSkR9HX%|{Uy Քdy,6 T5Y b뙉- ^ckGڝCji6 M^:%xzix&Xeb=A xoaҪPz=HB okK}AH `qT/z/6u|*AH &nVX ۻ(3ĕW΂g5ToXz{80/ ͱS\a+UG XzPa!'(oLAr=/uŀ1?X- ϳzhDq[ToX%|f@ׇv`^0A '0 yM4iҷ+K0Di&ْ s G—U[J XxD|a W{/*lPE|# fCx3Jr.#*=1?1|z OsF39IB%4f0o, V ^E6,a@])U簏 ObJA>#xm1wrRKG O4mJA*>D2H̡18AI/jQ=L0*UƷA2>=(roXe|kY0)UfZ$ASF 'l&AE/lX;(_Zip_sJd #*SYxd|Y0Q/K9y # BJY;ڄxd|Y`&4%GFmxZ_`%w-eNXhRGlSd# N^ʁ hRcC\p)⳸Jj\$6Sgt(hH)chDzD%nCpi7mf 1 ӨGENXD|q`ĸԑ|"%A xea4izEVHKP;(_X2xYl*(_lyHP" 69:Q6,7OZ5Tn-/.rY~|PVe/ÓGٸ=\pg +ptW?o=0Λ$.1ly=9;_abe~iQO<^fS9&泣hUOY|z?6XX'_s_3L*q:>}zX}.ily>x_NVWP10h 074btV?חI}QVK.2k6˭!,y>a's5q}:oY/SAs3+bSty/kpI3˻w|smtb2;m~w涝>r- 7tC1^%{k:MGڦj2cBCѢ>w?{+p-~4>\[[qL泃_.zobVB!AosmZZx{;0%ѻa`~Yg'JtSˊmq%*b›Dь%R` PYL]:&D-*Y/FYVt#)S=X7YLԲIZYL| k6ϓ,>R0V\g13]df=m}doF}fyBf]SJ)bf̬QF fF}f62QfeY'=hiU]g2[be֜ <ڠ0t٘9Lk)Sn+12+]F{ځF af=I*yVJ;=i{S+=hަO:uU9/raAJ&גd/;y˒,Lk[,IwZKs^;-)%= ώzxu÷?w[V~OAk_ֺ`Wb]a҆u|1:Zٞp{9#}ףX@ =[V笁j#pʘ)(Rg{{jcӎ|M {yN-M8C^v˝ռ^gvGX;5lR/ڨ?uCo䅊a]MydHRObjectReplacements/Object 3PK|k|Rî/P%4ObjectReplacements/Object 2PK|k|Rh Lmanifest.rdfPK|k|R"AMMETA-INF/manifest.xmlPK|k|Rzj96j APcontent.xmlPK55 Wsquashfs-tools-ng-1.1.3/doc/benchmark.txt000066400000000000000000000321471410627516300203650ustar00rootroot00000000000000 1) Test Setup ************* The tests were performed an a system with the following specifications: AMD Ryzen 7 3700X 32GiB DDR4 RAM Fedora 33 The following gcc versions of GCC and Linux were used: gcc (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9) Linux 5.11.9-200.fc33.x86_64 The following squashfs-tools-ng commit was tested: 7d2b3b077d7e204e64a1c57845524250c5b4a142 An optimized build of squashfs-tools-ng was compiled and installed to a tmpfs: $ mkdir /dev/shm/temp $ ln -s /dev/shm/temp out $ ./autogen.sh $ ./configure CFLAGS="-O3 -Ofast -march=native -mtune=native" \ LDFLAGS="-O3 -Ofast" --prefix=$(pwd)/out $ make -j install-strip $ cd out Working in a tmpfs was done to eliminate any influence of I/O performance and I/O caching side effects to the extend possible and only measure the actual processing time. For all benchmark tests, a Debian image extracted from the Debian 10.2 LiveDVD for AMD64 with XFCE was used. The Debian image is expected to contain realistic input data for a Linux file system and also provide enough data for an interesting benchmark. For all performed benchmarks, graphical representations of the results and derived values can be seen in "benchmark.ods". 1) Parallel Compression Benchmark ********************************* 1.1) What was measured? The Debian image was first converted to a tarball: $ ./bin/sqfs2tar debian.sqfs > test.tar The tarball was then repacked and time was measured as follows: $ time -p ./bin/tar2sqfs -j -c -f test.sqfs < test.tar The repacking was repeated 4 times and the worst wall-clock time ("real") was used for comparison. The was varied from 1 to 16 and for , all available compressors were used. All possible combinations and were measured. In addition, a serial reference version was compiled by running configure with the additional option --without-pthread and re-running the tests for all compressors without the option. In addition to the existing compressors, the LZO compressor in libcommon.a was briefly patched to not perform any compression at all. This way, a baseline comparison was established for a completely uncompressed SquashFS image. 1.2) What was computed from the results? The relative and absolute speedup were determined as follows: runtime_parallel(compressor, num_cpu) spedup_rel(compressor, num_cpu) = ------------------------------------- runtime_parallel(compressor, 1) runtime_parallel(compressor, num_cpu) spedup_abs(compressor, num_cpu) = ------------------------------------- runtime_serial(compressor) In addition, relative and absolute efficiency of the parallel implementation were determined: speedup_rel(compressor, num_cpu) efficiency_rel(compressor, num_cpu) = -------------------------------- num_cpu speedup_abs(compressor, num_cpu) efficiency_abs(compressor, num_cpu) = -------------------------------- num_cpu Furthermore, although not relevant for this specific benchmark, having the converted tarballs available, the compression ratio was computed as follows: size(tarball) max_throughput(compressor) = -------------------------- min(runtime(compressor)) 1.4) Results The raw timing results are as follows: Jobs XZ lzma gzip LZO LZ4 zstd none serial 1108.39s 995.43s 609.79s 753.14s 13.58s 550.59s 5.86s 1 1116.06s 990.33s 598.85s 753.53s 11.25s 550.37s 4.23s 2 591.21s 536.61s 312.14s 394.21s 6.41s 294.12s 4.13s 3 415.90s 370.48s 215.92s 273.14s 4.84s 205.14s 4.58s 4 320.02s 288.35s 165.50s 210.32s 4.29s 159.71s 4.62s 5 263.94s 235.69s 136.28s 172.33s 4.19s 132.27s 4.94s 6 224.23s 200.63s 116.44s 146.80s 4.28s 112.79s 5.08s 7 196.78s 176.35s 100.66s 128.61s 4.24s 99.26s 5.43s 8 175.04s 157.82s 89.79s 113.47s 4.46s 88.22s 5.68s 9 166.52s 148.88s 83.01s 106.14s 4.64s 84.97s 5.76s 10 159.35s 141.08s 77.04s 99.92s 4.84s 81.61s 5.94s 11 151.08s 136.27s 71.52s 94.23s 5.00s 77.51s 6.14s 12 144.72s 128.91s 67.21s 89.33s 5.28s 74.10s 6.39s 13 137.91s 122.67s 63.43s 84.39s 5.41s 71.83s 6.51s 14 132.94s 117.79s 59.45s 80.87s 5.71s 68.86s 6.68s 15 126.76s 113.51s 56.37s 76.68s 5.74s 65.78s 6.91s 16 119.06s 107.15s 52.56s 71.49s 6.37s 62.52s 7.10s 1.5) Discussion Most obviously, the results indicate that LZ4, unlike the other compressors, is clearly I/O bound and not CPU bound and doesn't benefit from parallelization beyond 2-4 worker threads and even that benefit is marginal with efficiency plummeting immediately. The other compressors are clearly CPU bound. Speedup increases linearly until about 8 cores, but with a slope < 1, as evident by efficiency linearly decreasing and reaching 80% for 8 cores. A reason for this sub-linear scaling may be the choke point introduced by the creation of fragment blocks, that *requires* a synchronization. To test this theory, a second benchmark should be performed with fragment block generation completely disabled. This requires a new flag to be added to tar2sqfs (and also gensquashfs). Using more than 8 jobs causes a much slower increase in speedup and efficiency declines even faster. This is probably due to the fact that the test system only has 8 physical cores and beyond that, SMT has to be used. It should also be noted that for most of the compressors, as well as the uncompressed version, the thread pool compressor with only a single thread turns out to be *slightly* faster than the serial reference implementation. A possible explanation for this might be that the fragment blocks are actually assembled in the main thread, in parallel to the worker that can still continue with other data blocks. Because of this decoupling there is in fact some degree of parallelism, even if only one worker thread is used. For the uncompressed version, the work still done in the thread pool is the hashing of blocks and fragments for de-duplication. Also of interest are the changes from the previous version of the benchmark, performed on v0.9 of squashfs-tools-ng. Since then, the thread pool design has been overhauled to spend a lot less time in the critical regions, but to also perform byte-for-byte equivalence checks before considering blocks or fragments to be identical. This may require a read-back and decompression step in the main thread in order to access already written fragment blocks. While the overall behavior has stayed the same, performance for XZ & LZMA has decreased slightly, whereas performance for the gzip, LZ4 & ZSTD has improved slightly. As the decompression benchmark shows, the first two are a lot slower at decompression, which needs to be done when reading back a fragment block from disk, and due to the higher data density also have a higher chance of actually having to decompress a block, so as a net result, the performance penalty from exact fragment matching eats all gains from the new thread pool design. For the more I/O bound compressors like LZ4 & ZSTD, decompressing a block is done much faster and due to the low data density for LZ4, the chance of actually having to decompress a block is lowered. As a result, the gains from the new thread pool design apparently outweigh the read-back penalty. Also noteworthy, due to the inclusion of an uncompressed reference, is that the LZ4 compressor is actually very close in performance to the uncompressed version, in some cases even outperforming it. This might be due to the fact that LZ4 actually does compress blocks, so in many cases where the uncompressed version needs to read back a full block during deduplication, the LZ4 version only needs to read a considerably smaller amount of data, reducing the penalty of having to read back blocks. 2) Reference Decompression Benchmark ************************************ 2.1) What was measured? A SquashFS image was generated for each supported compressor: $ ./bin/sqfs2tar debian.sqfs | ./bin/tar2sqfs -c test.sqfs And then, for each compressor, the unpacking time was measured: $ time -p ./bin/sqfs2tar test.sqfs > /dev/null The unpacking step was repeated 4 times and the worst wall-clock time ("real") was used for comparison. 2.2) What was computed from the results? The throughput was established by dividing the size of the resulting tarball by the time taken to produce it from the image. For better comparison, this was also normalized to the throughput of the uncompressed SquashFS image. 2.3) Results xz 120.53s lzma 118.91s gzip 20.57s lzo 10.65s zstd 7.74s lz4 2.59s uncompressed 1.42s 2.4) Discussion From the measurement, it becomes obvious that LZ4 and zstd are the two fastest decompressors, both being very close to the uncompressed version. Zstd is particularly noteworthy here, because it is not far behind LZ4 in speed, but also achieves a substantially better compression ratio that is between gzip and lzma. LZ4, despite being the fastest in decompression and beating the others in compression speed by orders of magnitudes, has by far the worst compression ratio. It should be noted that the number of actually compressed blocks has not been determined. A worse compression ratio can lead to more blocks being stored uncompressed, reducing the workload and thus affecting decompression time. However, since zstd has a better compression ratio than gzip, takes only 30% of the time to decompress, and in the serial compression benchmark only takes 2% of the time to compress, we can safely say that in this benchmark, zstd beats gzip by every metric. Furthermore, while XZ stands out as the compressor with the best compression ratio, zstd only takes ~6% of the time to decompress the entire image, while being ~17% bigger than XZ. Shaving off 17% is definitely significant, especially considering that in absolute numbers it is in the 100MB range, but it clearly comes at a substantial performance cost. Also interesting are the results for the LZO compressor. Its compression speed is between gzip and LZMA, decompression speed is about 50% of gzip, and only a little bit worse than zstd, but its compression ratio is the second worst only after LZ4, which beats it by a factor of 5 in decompression speed and by ~60 in compression speed. Concluding, for applications where a good compression ratio is most important, XZ is obviously the best choice, but if speed is favored, zstd is probably a very good option to go with. LZ4 is much faster, but has a lot worse compression ratio. It is probably best suited as transparent compression for a read/write file system or network protocols. Finally, it should be noted, that this serial decompression benchmark is not representative of a real-life workload where only a small set of files are accessed in a random access fashion. In that case, a caching layer can largely mitigate the decompression cost, translating it into an initial or only occasionally occurring cache miss latency. But this benchmark should in theory give an approximate idea how those cache miss latencies are expected to compare between the different compressors. 3) Compression Size and Overhead Benchmark ****************************************** 3.1) What was measured? For each compressor, a SquashFS image was created in the way outlined in the parallel compression benchmark and the resulting file size was recorded. In addition, the raw tarball size was recorded for comparison. 3.2) What was computed from the results? The compression ratio was established as follows: size(compressor) ratio(compressor) = -------------------- size(uncompressed) 3.3) Results SquashFS tar Uncompressed ~6.1GiB (6,542,389,248) ~6.5GiB (7,008,118,272) LZ4 ~3.1GiB (3,381,751,808) LZO ~2.5GiB (2,732,015,616) gzip ~2.3GiB (2,471,276,544) zstd ~2.1GiB (2,295,078,912) lzma ~2.0GiB (2,102,169,600) XZ ~2.0GiB (2,098,466,816) 3.4) Discussion Obviously XZ and lzma achieve the highest data density, shrinking the SquashFS image down to less than a third of the input size. Noteworthy is also Zstd achieving higher data density than gzip while being faster in compression as well as decompression. Interestingly, even the uncompressed SquashFS image is still smaller than the uncompressed tarball. Obviously SquashFS packs data and meta data more efficiently than the tar format, shaving off ~7% in size. squashfs-tools-ng-1.1.3/doc/format.adoc000066400000000000000000001217751410627516300200200ustar00rootroot00000000000000= Squashfs Binary Format :toc: left :toclevels: 4 :sectnums: == About SquashFS is a compressed, read-only filesystem for Linux that can also be used as a flexible, general purpose, compressed archive format, optimized for fast random access with support for Unix permissions, sparse files and extended attributes. SquashFS supports data and metadata compression through zlib, lz4, lzo, lzma, xz or zstd. For fast random access, compressed files are split up in fixed size blocks that are compressed separately. The block size can be set between 4k and 1M (default for squashfs-tools and squashfs-tools-ng is 128K). This document attempts to specify the on-disk format in detail. It is based on a previous on-line version that was originally written by Zachary Dremann and subsequently expanded by David Oberhollenzer during reverse engineering attempts and available here: https://dr-emann.github.io/squashfs/. == Overview SquashFS always stores integers in little endian format. The data blocks that make up the SquashFS archive are byte aligned, i.e. they typically do not care for alignment. The implementation in the Linux kernel requires the archive itself to be a multiple of either 1k or 4k in size (called the device block size) and user space tools typically use 4k to be compatible with both. A SquashFS archive consists of a maximum of nine parts: [%nowrap] ---- _______________ | | Important information about the archive, including | Superblock | locations of other sections. |_______________| | | If non-default compression options have been used, | Compression | they can optionally be stored here, to facilitate | options | later, offline editing of the archive. |_______________| | | | Data blocks | The contents of the files in the archive, | & fragments | split into separately compressed blocks. |_______________| | | Metadata (ownership, permissions, etc) for | Inode table | items in the archive. |_______________| | | | Directory | Directory listings, including file names, and | table | references to inodes. |_______________| | | | Fragment | Description of fragment locations within the | table | Datablocks & Fragments section. |_______________| | | A mapping from inode numbers to disk locations, | Export table | required for NFS export. |_______________| | | | UID/GID | A list of unique UID/GIDs. Inodes use an index into | lookup table | this table to save memory. |_______________| | | | Xattr | Extended attributes for items in the archive. | table | |_______________| ---- Although the super block details the exact positions of each section, most implementations, including the one in the Linux kernel, insist on this exact order. === Packing File Data The file data is packed into the archive after the super block (and optional compressor options). Files are divided into fixed size blocks that are separately compressed and stored in order. SquashFS supports optional tail-end-packing of files that are not an exact multiple of the block size. The remaining ends can either be treated as a short block, or can be packed together with the tail ends of other files in a single "fragment block". Files that are less than block size are treated the same way. If the size of a data or fragment block would exceed the input size after compression, the original, uncompressed data is stored, so that the size of a block after compression never exceeds the input block size. === Packing Metadata Metadata (e.g. inodes, directory listings, etc...) is treated as a continuous stream of records that is chopped up into 8KiB blocks that are separately compressed into special metadata blocks. The input size of 8KiB is fixed and independent of the data block size. Similar to data blocks, if the compressed size would exceed 8KiB, the uncompressed block is stored instead, so the on-disk size of a metadata block never exceeds 8KiB. Individual entries are allowed to cross the block boundary, so e.g. an inode may be located at the end of a metadata block with some part of it located at the start of the next block. Both have to be read and decompressed when reading this inode. If an entry is written across block boundaries, there *MUST NOT* be any gap between the compressed metadata blocks on-disk. In contrast to data blocks, every metadata block is preceded by a single, 16 bit unsigned integer. This integer holds the on-disk size of the block that follows. The MSB is set if the block is stored uncompressed. Whenever a metadata block is referenced, the position of this integer is given. To read a metadata block, seek to the indicated position and read the 16 bit header. Sanity check that the lower 15 bit are less than 8KiB and proceed to read that many bytes. If the highest bit of the header is cleared, uncompress the data into an 8KiB buffer that *MUST NOT* overflow. In the SquashFS archive format, metadata entries (e.g. inodes) are often referenced using a 64 bit integer. The lower 16 bit hold an offset into the uncompressed block and the upper 48 bit point to the on-disk location of the block. The on-disk location is relative to the type of metadata entry, e.g. for inodes it is relative to the start of the inode table given by the super block. === Storing Lookup Tables Lookup tables are arrays (i.e. sequences of identical sized records) that are addressed by an index. Such tables are stored in the SquashFS format as metadata blocks, i.e. by dividing the table data into 8KiB chunks that are separately compressed and stored in sequence. To allow constant time lookup, a list of 64 bit unsigned integers is stored, holding the on-disk locations of each metadata block. This list itself is stored uncompressed and not preceded by a header. When referring to a lookup table, the superblock gives the number of table entries and points to this location list. Since the table entry size is a known, fixed value, the required number of metadata blocks can be computed: block_count = ceil(table_count * entry_size / 8192) Which is also the number of 64 bit integers in the location list. When resolving a lookup table index, first work out the index of the metadata block: meta_index = floor(index * entry_size / 8192) Using this index on the location list yields the on-disk location of the metadata block containing the entry. After reading this metadata block, the byte offset into the block can be computed to get the entry: offset = index * entry_size % 8192 The location list can be cached in memory. Resolving an index requires at worst a single metadata block read (at most 8194 bytes fetched from an unaligned on-disk location). === Supported Compressors The SquashFS format supports the following compressors: * zlib deflate (referred to as "gzip" but only uses raw zlib streams) * lzo * lzma 1 (considered deprecated) * lzma 2 (referred to as "xz") * lz4 * zstd The archive can only specify one compressor in the super block and has to use it for both file data and metadata compression. Using one compressor for data and switching to a different compressor for e.g. inodes is not supported. While it is technically not possible to pick a "null" compressor in the super block, an implementation can still deliberately write only uncompressed blocks to a SquashFS archive, or choose to store certain metadata blocks without compression. The lzma 2 aka xz compressor *MUST* use `CRC32` checksums only. Using `SHA-256` is not supported. == The Superblock The superblock is the first section of a SquashFS archive. It is always 96 bytes in size and contains important information about the archive, including the locations of other sections. [cols="1,3,13a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | magic | Must be set to `0x73717368` ("hsqs" on disk). | u32 | inode count | The number of inodes stored in the archive. | u32 | mod time | Last modification time of the archive. Count seconds since 00:00, Jan 1st 1970 UTC (not counting leap seconds). This is unsigned, so it expires in the year 2106 (as opposed to 2038). | u32 | block size | The size of a data block in bytes. Must be a power of two between 4096 (4k) and 1048576 (1 MiB). | u32 | frag count | The number of entries in the fragment table. | u16 | compressor | An ID designating the compressor used for both data and meta data blocks. [cols=">1,2,8",frame="none",grid="none",options="header"] !=== ! Value ! Name ! Comment ! 1 ! GZIP ! just zlib streams (no gzip headers\!) ! 2 ! LZO ! ! 3 ! LZMA ! LZMA version 1 ! 4 ! XZ ! LZMA version 2 as used by xz-utils ! 5 ! LZ4 ! ! 6 ! ZSTD ! !=== | u16 | block log | The log~2~ of the block size. If the two fields do not agree, the archive is considered corrupted. | u16 | flags | Bit wise *OR* of the flag bits below. [cols=">1m,10",frame="none",grid="none",options="header"] !=== ! Value ! Meaning ! 0x0001 ! Inodes are stored uncompressed. ! 0x0002 ! Data blocks are stored uncompressed. ! 0x0004 ! Unused, should always be unset. ! 0x0008 ! Fragments are stored uncompressed. ! 0x0010 ! Fragments are not used. ! 0x0020 ! Fragments are always generated. ! 0x0040 ! Data has been deduplicated. ! 0x0080 ! NFS export table exists. ! 0x0100 ! Xattrs are stored uncompressed. ! 0x0200 ! There are no Xattrs in the archive. ! 0x0400 ! Compressor options are present. ! 0x0800 ! The ID table is uncompressed. !=== | u16 | id count | The number of entries in the ID lookup table. | u16 | version major | Major version of the format. Must be set to 4. | u16 | version minor | Minor version of the format. Must be set to 0. | u64 | root inode | A reference to the inode of the root directory. | u64 | bytes used | The number of bytes used by the archive. Because SquashFS archives must be padded to a multiple of the underlying device block size, this can be less than the actual file size. | u64 | ID table | The byte offset at which the id table starts. | u64 | Xattr table | The byte offset at which the xattr id table starts. | u64 | Inode table | The byte offset at which the inode table starts. | u64 | Dir. table | The byte offset at which the directory table starts. | u64 | Frag table | The byte offset at which the fragment table starts. | u64 | Export table | The byte offset at which the export table starts. |=== The Xattr table, fragment table and export table are optional. If they are omitted from the archive, the respective fields indicating their position must be set to `0xFFFFFFFFFFFFFFFF` (i.e. all bits set). Most of the flags only serve an informational purpose and are only useful when editing the archive to convey the original packer settings. The only flag that actually carries information is the "Compressor options are present" flag. In fact, this is the only flag that the Linux kernel implementation actually tests for. The compressor options, however, are also only there for informal purpose, as most compression libraries understand their own stream format irregardless of the options used to compress and in fact don't provide any options for the decompressor. In the Linux kernel, the XZ decompressor is currently the only one that processes those options to pre-allocate the LZMA dictionary if a non-default size was used. === Compression Options If the compressor options flag is set in the superblock, the superblock is immediately followed by a single metadata block, which is always uncompressed. The data stored in this block is compressor dependent. There are two special cases: * For LZ4, the compressor options always have to be present. * The LZMA compressor does not support compressor options, so this section must never be present. For the compressors currently implemented, a 4 to 8 byte payload follows. The following sub sections outline the contents for each compressor that supports options. The default values if the options are missing are outlined as well. ==== GZIP [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | compression level | In the range 1 to 9 (inclusive). Defaults to 9. | u16 | window size | In the rage 8 to 15 (inclusive). Defaults to 15. | u16 | strategies | A bit field describing the enabled strategies. If no flags are set, the default strategy is implicitly used. Please consult the ZLIB manual for details on specific strategies. [cols=">1m,10",frame="none",grid="none",options="header"] !=== ! Value ! Comment ! 0x0001 ! Default strategy. ! 0x0002 ! Filtered. ! 0x0004 ! Huffman Only. ! 0x0008 ! Run Length Encoded. ! 0x0010 ! Fixed. !=== |=== NOTE: The SquashFS writer typically tries all selected strategies (including not setting any and letting zlib work with defaults) and stores the result with the smallest size. ==== XZ [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | dictionary size | *SHOULD* be >= 8KiB, and must be either a power of 2, or the sum of two consecutive powers of 2. | u32 | Filters | A bit field describing the additional enabled filters attempted to better compress executable code. [cols=">1m,10",frame="none",grid="none",options="header"] !=== ! Value ! Comment ! 0x0001 ! x86 ! 0x0002 ! PowerPC ! 0x0004 ! IA64 ! 0x0008 ! ARM ! 0x0010 ! ARM thumb ! 0x0020 ! SPARC !=== |=== NOTE: A SquashFS writer typically tries all selected VLI filters (including not setting any and letting libxz work with defaults) and stores the resulting block that has the smallest size. Also note that further options, such as XZ presets, are not included. The compressor typically uses the libxz defaults, i.e. level 6 and not using the extreme flag. Likewise for `lc`, `lp` and `pb` (defaults are 3, 0 and 2 respectively). If the encoder chooses to change those values, the decoder will still be able to read the data, but there is currently no way to convey that those values were changed. This is specifically problematic for the compression level, since increasing the level can result in drastically increasing the decoders memory consumption. ==== LZ4 [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | Version | *MUST* be set to 1. | u32 | Flags | A bit field describing the enabled LZ4 flags. There is currently only one possible flag: [cols=">1m,10",frame="none",grid="none",options="header"] !=== ! Value ! Comment ! 0x0001 ! Use LZ4 High Compression(HC) mode. !=== |=== ==== ZSTD [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | compression level | Should be in range 1 to 22 (inclusive). The real maximum is the zstd defined ZSTD_maxCLevel(). + + The default value is 15. |=== ==== LZO [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | algorithm | Which variant of LZO to use. [cols=">1m,10",frame="none",grid="none",options="header"] !=== ! Value ! Comment ! 0 ! lzo1x_1 ! 1 ! lzo1x_1_11 ! 2 ! lzo1x_1_12 ! 3 ! lzo1x_1_15 ! 4 ! lzo1x_999 (default) !=== | u32 | compression level | For lzo1x_999, this can be a value between 0 and 9 inclusive (defaults to 8). *MUST* be 0 for all other algorithms. |=== == Data and Fragment Blocks As outlined in 2.1, file data is packed by dividing the input files into fixed size chunks (the block size from the super block) that are stored in sequence. The picture below tries to illustrate this concept: .Packing of File Data [%nowrap] .... _____ _____ _____ _ _____ _____ _ _ File A: |__A__|__A__|__A__|A| File B: |__B__|__B__|B| File C: |C| | | | | | | | | | +---+ | | | | | | | | +------+ | | | | | | | | | | | | | | | | +------|---------------+ | | | | | | | +--|---------------------+ | | | | | | | | | | | | | | | +-----------------------+ | +------------+ | | | | | | | | V V V V V V V V __ _ ___ ___ ___ __ Fragment block: |A|B|C| Output: |_A|A|_A_|_B_|_B_|_F| | __V__ A |__F__| | | +------------------------+ .... In the above diagram, file A consists of 3 blocks and a single tail end, file B has 2 blocks and one tail end while file C is smaller than block size. For each file, the blocks are individually compressed and stored on disk in order. The tail ends of A and B, together with the entire contents of C are packed together into a fragment block F, that is compressed and stored on disk once it is full. This tail-end-packing is completely optional. The tail ends (or in case of C the entire file) can also be treated as truncated blocks that expand to less than block size when uncompressed. There are no headers in front of data or fragment blocks and there *MUST NOT* be any gaps between data blocks from a single file, but a SquashFS packer is free to leave gaps between two different files or fragment blocks. The packer is also free to decide how to arrange fragments within a fragment block and what fragments to pack together. To locate file data, the file inodes store the following information: * The uncompressed size of the file. From this, the number of blocks can be computed: block_count = floor(file_size / block_size) # if tail end packing is used block_count = ceil(file_size / block_size) # otherwise * The exact location of the first block, if one exists. * For each consecutive block, the on-disk size. + A 32 bit integer is used with bit 24 (i.e. `1 << 24`) set if the block is stored uncompressed. * If tail-end-packing was done, the location of the fragment block and a byte offset into the uncompressed fragment block. The size of the tail end can be computed easily: tail_end_size = file_size % block_size Since a fragment block will likely be referred to by multiple files, inodes don't store its on-disk location and size directly, but instead use a 32 bit index into a fragment block lookup table (see the <>). If a data block other than the last one unpacks to less than block size, the rest of the buffer is filled with 0 bytes. This way, sparse files are implemented. Specifically if a block has an on-disk size of 0 this translates to an entire block filled with 0 bytes without having to retrieve any data from disk. The on-disk locations of file blocks *MAY* overlap and different file inodes are free to refer to the same fragment. Typical SquashFS packers would explicitly use this to for files that are duplicates of others. Doing so is NOT counted as a hard link. If an inode references on-disk locations outside the data area, the result is undefined. == Inode Table Inodes are packed into metadata blocks and are not aligned, i.e. they can span the boundary between metadata blocks. To save space, there are different inodes for each type (regular file, directory, device, etc.) of varying contents and size. To further save more space, inodes come in two flavors: simple inode types optimized for a simple, standard use case, and extended inode types where extra information has to be stored. SquashFS more or less supports 32 bit UIDs and GIDs. As an optimization, those IDs are stored in a lookup table (see <>) and the inodes themselves hold a 16 bit index into this table. This allows to 32 bit UIDs/GIDs, but only among 2^16^ unique values. The location of the first metadata block is indicated by the inode table start in the superblock. The inode table ends at the start of the directory table. === Common Inode Header All Inodes share a common header, which contains some common information, as well as describing the type of Inode which follows. This header has the following structure: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u16 | type | The type of item described by the inode which follows this header [cols=">1,10",frame="none",grid="none",options="header"] !=== ! Value ! Comment ! 1 ! Basic Directory ! 2 ! Basic File ! 3 ! Basic Symlink ! 4 ! Basic Block Device ! 5 ! Basic Character Device ! 6 ! Basic Named Pipe (FIFO) ! 7 ! Basic Socked ! 8 ! Extended Directory ! 9 ! Extended File ! 10 ! Extended Symlink ! 11 ! Extended Block Device ! 12 ! Extended Character Device ! 13 ! Extended Named Pipe (FIFO) ! 14 ! Extended Socked !=== | u16 | permissions | A bit mask representing Unix file system permissions for the inode. This only stores permissions, not the type. The type is reconstructed from the field above. | u16 | uid | An index into the <>, giving the user ID of the owner. | u16 | gid | An index into the <>, giving the group ID of the owner. | u32 | mtime | The unsigned number of seconds (not counting leap seconds) since 00:00, Jan 1st, 1970 UTC when the item described by the inode was last modified. | u32 | inode number | Unique node number. Must be at least 1 and at most the inode count from the super block. |=== === Directory Inodes Directory inodes mainly contain a reference into the directory table where the listing of entries is stored. A basic directory has an entry listing of at most 64k (uncompressed) and no extended attributes. The layout of the inode data is as follows: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | block index | The location of the metadata block in the directory table where the entry information starts. This is relative to the directory table location. | u32 | link count | The number of hard links to this directory. | u16 | file size | Total (uncompressed) size in bytes of the entry listing in the directory table, including headers. + + This value is 3 bytes larger than the real listing. The Linux kernel creates "." and ".." entries for offsets 0 and 1, and only after 3 looks into the listing, subtracting 3 from the size. | u16 | block offset | The (uncompressed) offset within the metadata block in the directory table where the directory listing starts. | u32 | parent inode | The inode number of the parent of this directory. If this is the root directory, this *SHOULD* be 0. |=== NOTE: For historical reasons, the hard link count of a directory includes the number of entries in the directory and is initialized to 2 for an empty directory. I.e. a directory with N entries has at least N + 2 link count. If the "file size" is set to a value < 4, the directory is empty and there is no corresponding listing in the directory table. An extended directory can have a listing that is at most 4GiB in size, may have extended attributes and can have an optional index for faster lookup: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | Same as above. | u32 | file size | Same as above. | u32 | block index | Same as above. | u32 | parent inode | Same as above. | u16 | index count | The number of directory index entries following the inode structure. | u16 | block offset | Same as above. | u32 | xattr index | An index into the <> or `0xFFFFFFFF` if the inode has no extended attributes. |=== The index follows directly after the inode. See <> for details on how the directory index is structured. === File Inodes Basic files can be at most 4 GiB in size (uncompressed), must be located within the first 4 GiB of the SquashFS image, cannot have any extended attributes and don't support hard-link or sparse file accounting: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | blocks start | The offset from the start of the archive to the first data block. | u32 | frag index | An index into the <> which describes the fragment block that the tail end of this file is stored in. If not used, this is set to `0xFFFFFFFF`. | u32 | block offset | The (uncompressed) offset within the fragment block where the tail end of this file is. See <> for details. | u32 | file size | The (uncompressed) size of this file. | u32[] | block sizes | An array of consecutive block sizes. See <> for details. |=== If 'frag index' is set to `0xFFFFFFFF`, the number of blocks is computed as ceil(file_size / block_size) otherwise, if 'frag index' is a valid fragment index, the block count is computed as floor(file_size / block_size) and the size of the tail end is file_size % block_size To access a data block, first compute the block index as index = floor(offset / block_size) then compute the on-disk location of the block by summing up the sizes of the blocks that come before it: location = block_start for i = 0; i < index; i++ location += block_sizes[i] & 0x00FFFFFF The tail end, if present, is accessed by resolving the fragment index through the fragment lookup table (see the <>), loading the fragment block and using the given 'block offset' into the fragment block. Extended files have a 64 bit location and size, have additional counters for sparse file accounting and hard links, and can have extended attributes: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u64 | blocks start | Same as above (but larger). | u64 | file size | Same as above (but larger). | u64 | sparse | The number of bytes saved by omitting zero bytes. Used in the kernel for sparse file accounting. | u32 | link count | The number of hard links to this node. | u32 | frag index | Same as above. | u32 | block offset | Same as above. | u32 | xattr index | An index into the <> or `0xFFFFFFFF` if the inode has no extended attributes. | u32[] | block sizes | Same as above. |=== === Symbolic Links Symbolic links mainly have a target path stored directly after the inode header, as well as a hard-link counter (yes, you can have hard links to symlinks): [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | The number of hard links to this symlink. | u32 | target size | The size in bytes of the target path this symlink points to. | u8[] | target path | An array of bytes holding the target path this symlink points to. The path is 'target size' bytes long and NOT null-terminated. |=== The extended symlink type adds an additional extended attribute index: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | Same as above. | u32 | target size | Same as above. | u8[] | target path | Same as above. | u32 | xattr index | An index into the <> |=== === Device Special Files Basic device special files only store a hard-link counter and a device number. The layout is identical for both character and block devices: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | The number of hard links to this entry. | u32 | device number | The system specific device number. + + On Linux, this consists of major and minor device numbers that can be extracted as follows: major = (dev & 0xFFF00) >> 8. minor = (dev & 0x000FF) | ((dev >> 12) & 0xFFF00) |=== The extended device file inode adds an additional extended attribute index: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | Same as above. | u32 | device number | Same as above. | u32 | xattr index | An index into the <> |=== === IPC Inodes (FIFO or Socket) Named pipe (FIFO) and socket special files only add a hard-link counter after the inode header: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | The number of hard links to this entry. |=== The extended versions add an additional extended attribute index: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | link count | Same as above. | u32 | xattr index | An index into the <> |=== == Directory Table For each directory inode, the directory table stores a linear list of all entries, with references back to the inodes that describe those entries. The entry list itself is sorted ASCIIbetically by entry name and split into multiple runs, each preceded by a short header. The directory inodes store the total, uncompressed size of the entire listing, including headers. Using this size, a SquashFS reader can determine if another header with further entries should be following once it reaches the end of a run. To save space, the header indicates a metadata block and a reference inode number. The entries that follow simply store a difference to that inode number and an offset into the specified metadata block. Every time, the inode block changes or the difference of the inode number to the reference in the header cannot be encoded in 16 bits anymore, a new header is emitted. A header must be followed by *AT MOST* 256 entries. If there are more entries, a new header *MUST* be emitted. Typically, inode allocation strategies would sort the children of a directory and then allocate inode numbers incrementally, to optimize directory entry listings. Since hard links might be further further away than ±32k of the reference number, they might require a new header to be emitted. Inode number allocation and picking of the reference could of course be optimized to prevent this. The directory header has the following structure: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | count | Number of entries following the header. | u32 | start | The location of the metadata block in the inode table where the inodes are stored. This is relative to the inode table start from the super block. | s32 | inode number | An arbitrary inode number. The entries that follow store their inode number as a difference to this. |=== The counter is stored off-by-one, i.e. a value of 0 indicates 1 entry follows. This also makes it impossible to encode a size of 0, which wouldn't make any sense. Empty directories simply have their size set to 0 in the inode instead, so no extra dummy header has to be stored or looked up. The header is followed by multiple entries that each have this structure: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u16 | offset | An offset into the uncompressed inode metadata block. | s16 | inode offset | The difference of this inode's number to the reference stored in the header. | u16 | type | The inode type. For extended inodes, the basic type is stored here instead. | u16 | name size | One less than the size of the entry name. | u8[] | name | The file name of the entry without a trailing null byte. Has `name size` + 1 bytes. |=== In the entry structure itself, the file names are stored without trailing null bytes. Since a zero length name makes no sense, the name length is stored off-by-one, i.e. the value 0 cannot be encoded. The inode type is stored in the entry, but always as the corresponding basic type. While the field is technically 16 bits, the kernel implementation currently imposes an arbitrary limit of 255 on the name size field. Since the field is off-by-one, this means that a file name in SquashFS can be at most 256 characters long. === Directory Index To speed up lookups on directories with lots of entries, the extended directory inode can store an index, holding the locations of all directory headers and the name of the first entry after the header. When searching for an entry, the reader can then iterate over the index to find a range of metadata blocks that should contain a given entry and then only scan over the given range. To allow for even faster lookups, a new header should be emitted every time the entry list crosses a metadata block boundary. This narrows the boundary down to a single metadata block lookup in most cases. The index entries have the following structure: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | index | This stores a byte offset from the first directory header to the current header, as if the uncompressed directory metadata blocks were laid out in memory consecutively. | u32 | start | Start offset of a directory table metadata block, relative to the directory table start. | u32 | name size | One less than the size of the entry name. | u8[] | name | The name of the first entry following the header without a trailing null byte. |=== == Fragment Table Tail-ends and smaller than block size files can be combined into fragment blocks that are at most 'block size' bytes long. The fragment table describes the location and size of the fragment blocks (not the tail-ends within them). This is a lookup table which stores entries of the following shape: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u64 | start | The offset within the archive where the fragment block starts | u32 | size | The on-disk size of the fragment block. If the block is uncompressed, bit 24 (i.e. `1 << 24`) is set. | u32 | unused | *SHOULD* be set to 0. |=== The table is stored on-disk as described in <>. The fragment table location in the superblock points to an array of 64 bit integers that store the on-disk locations of the metadata blocks containing the lookup table. Each metadata block can store up to 512 entries (`8129 / 16`). The "unused" field is there for alignment and *SHOULD* be set to 0, however the Linux kernel currently ignores this field completely, making it impossible for Linux to ever re-purpose this field. == Export Table To support NFS exports, SquashFS needs a fast way to resolve an inode number to an inode structure. For this purpose, a SquashFS archive can optionally contain an export table, which is basically a flat array of 64 bit inode references, with the inode number being used as an index into the array. Because the inode number 0 is not used (reserved as a sentinel value), the array actually starts at inode number 1 and the index is thus inode_number - 1. The array itself is stored in a series of metadata blocks, as outlined in <>. Since each block can store 1024 references (`8192 / 8`), there will be `ceil(inode_count / 1024)` metadata blocks for the entire array. == ID Table As outlined in <>, SquashFS supports 32 bit user and group IDs. To compact the inode table, the unique UID/GID values are collected in a lookup table and a 16 bit table index is stored in the inode instead. This lookup table is stored as outlined in <>. Each metadata block can store up to 2048 IDs (`8192 / 4`). [[_xattr_table,Xattr Table]] == Extended Attribute Table Extended attributes are arbitrary key value pairs attached to inodes. The key names use dots as separators to create a hierarchy of name spaces. The key value pairs of all inodes are stored consecutively in a series of metadata blocks. The values can either be stored inline, i.e. a key entry is directly followed by a value, or out-of-line to deduplicate identical values and use a reference instead. Typically, the first occurrence of a value is stored in line and every consecutive use of the same value uses an out-of-line reference back to the first one. The keys are stored using the following data structure: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u16 | type | A prefix ID for the key name. If the value that follows is stored out-of-line, the flag `0x0100` is **OR**ed to the type ID. [cols=">1,10",frame="none",grid="none",options="header"] !=== ! Value ! Comment ! 0 ! Prefix the name with `"user."` ! 1 ! Prefix the name with `"trusted."` ! 2 ! Prefix the name with `"security."` !=== | u16 | name size | The number of key bytes the follows. | u8[] | name | The remainder of the key without the prefix and without a trailing null byte. |=== Following the key, this structure is used to store the value: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u32 | value size | The size of the value string. If the value is stored out of line, this is always 8, i.e. the size of an unsigned 64 bit integer. | u8[] | value | This is 'value size' bytes of arbitrary binary data. If the value is stored out-of-line, this is a 64 bit reference, i.e. a location of a metadata block, shifted left by 16 and **OR**ed with an offset into the uncompressed block, giving the location of another value structure. |=== The metadata block location given by an out-of-line reference is relative to the location of the first block. To actually address a block of key value pairs associated with an inode, a lookup table is used that specifies the start and size of a sequence of key value pairs. All an inode needs to store is a 32 bit index into this table. If two inodes have an identical attribute sets, the key/value sequence is only written once, there is only one lookup table entry and both inodes have the same index. Each lookup table entry has the following structure: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u64 | xattr ref | A reference to the start of the key value block, i.e. the metadata block location shifted left by 16, **OR**ed with an offset into the uncompressed block. | u32 | count | The number of key value pairs. | u32 | size | The exact, uncompressed size in bytes of the entire block of key value pairs, counting what has been written to disk and including the key/value entry structures. |=== This lookup table is stored as outlined in <> Each metadata block can hold 512 (`8192 / 16`) entries. However, in contrast to <>, additional data is given before the list of metadata block locations, to locate the key-value pairs, as well as the actual number of lookup table entries that are not specified in the super block. The 'Xattr table' entry in the superblock gives the absolute location of the following data structure which is stored on-disk as is, uncompressed: [cols="1,4,20a",frame="none",grid="none",options="header"] |=== | Type | Name | Description | u64 | kv start | The absolute position of the first metadata block holding the key/value pairs. | u32 | count | The number of entries in the lookup table. | u32 | unused | *SHOULD* be set to 0, however Linux currently ignores this field completely and squashfs-tools used to leak stack data here, making it impossible for Linux to ever re-purpose this field. | u64[] | locations | An array holding the absolute on-disk location of each metadata block of the lookup table. |=== If an inode has a a valid xattr index (i.e. not `0xFFFFFFFF`), the metadata block index is computed as block_idx = floor(index / 512) which is then used to retrieve the metadata block index from the locations array. Once the block has been read from disk and uncompressed, the byte offset into the metadata block can be computed as offset = (index * 16) % 8192 From this position, the structure can be read that holds a reference to the metadata block that contains the key/value pairs (and byte offset into the uncompressed block where the pairs start), as well as the number of key/value pairs and their total, uncompressed size. squashfs-tools-ng-1.1.3/doc/mainpage.dox000066400000000000000000000026041410627516300201620ustar00rootroot00000000000000/** * @mainpage libsquashfs API reference * * @section intro Introduction * * The libsquashfs library attempts to encapsulate the actual core of the * SquashFS reading and writing logic of the squashfs-tools-ng package, * while trying to offer a generic API that should cover a broad variety of * applications that might want to make use SquashFS. * * All disk I/O is abstracted away through the \ref sqfs_file_t interface. A * reference implementation that uses native file I/O can be instatiated * using @ref sqfs_open_file. Providing a custom implementation allows reading * or writing SquashFS images to something other than regular files, embedding * SquashFS in a custom container format or applying custom transformations on * the raw byte level. * * If want to get started with reading SquashFS images, a good starting point * would be the @ref sqfs_data_reader_t and @ref sqfs_dir_reader_t that * provide an interface for browsing a SquashFS directory tree and reading * data contained within. * * A few helper structures are need for that tough, specifically the SquashFS * super block (see @ref sqfs_super_t) that can be read from a * @ref sqfs_file_t using @ref sqfs_super_read. * * A decompressor (see @ref sqfs_compressor_t) is also needed, which can be * created using @ref sqfs_compressor_config_init and * @ref sqfs_compressor_create. * * @example list_files.c */ squashfs-tools-ng-1.1.3/doc/parallelism.txt000066400000000000000000000112721410627516300207340ustar00rootroot00000000000000 Parallelizing SquashFS Data Packing *********************************** 0) Overview *********** On a high level, data blocks are processed as follows: The "block processor" has a simple begin/append/end interface for submitting file data. Internally it chops the file data up into fixed size blocks that are each [optionally] compressed and hashed. If the "end" function is called and there is still left over data, a fragment is created. Fragments are only hashed. If another fragment exists with the same size and hash, it is discarded and the existing fragment is referenced. Fragments are collected in a fragment block that, once it overflows, is processed like a normal block. The final compressed & hashed data blocks & fragment blocks are passed on to the "block writer". The block writer simply writes blocks to the output file. Flags are used to communicate what the first and last block of a file are. Entire files are deduplicated by trying to find a sequence of identical size/hash pairs in the already written blocks. 0.1) Implementation The implementation of the block processor is in lib/sqfs/block_processor. The file common.c contains the frontend for file data submission and common functions for processing a single block, handling a completed block and handling a completed fragment. A reference serial implementation is provided in the file serial.c 1) Thread Pool Based Block Processor ************************************ 1.1) Goals and Challanges In addition to the goal of boosting performance, the thread pool based block processor must meet the following requirements: - Output MUST be deterministic and reproducible. I.e. feeding byte-for-byte the same input MUST ALWAYS produce byte-for-byte the same output. - Blocks the belong to a single file MUST be written in the order that they were submitted. - Output MUST be byte-for-byte equivalent to the serial reference implementation. Changing the serial reference implementation to achieve this is OK. - I/O cannot be done in worker threads. The underlying back-end must be assumed to not be thread safe and may get very confused by suddenly running in a different thread, even if only one thread at a time uses it. 1.2) The Current Approach The current implementation is in winpthread.c (based on pthread or Windows native threads, depending on whats available). It keeps track of blocks in 3 different FIFO queues: - A "work queue" that freshly submitted blocks go to. Worker threads take blocks from this queue for processing. - A "done queue" that worker threads submit blocks to, once completed. - An "I/O queue" that contains blocks ready to be written to disk. When the main thread submits a block, it gives it an incremental "processing" sequence number and appends it to the "work queue". Thread pool workers take the first best block of the queue, process it and add it to the "done" queue, sorted by its processing sequence number. The main thread dequeues blocks from the done queue sorted by their processing sequence number, using a second counter to make sure blocks are dequeued in the exact same order as they were added to the work queue. Regular data blocks from the "done queue" are given an incremental I/O sequence number and added to the "I/O queue", sorted by this number. Fragments are deduplicated and consolidated into a fragment block. If this block overflows, it is appended to the "work queue" the exact same way as regular blocks, but it is **already given an I/O sequence number**. If a block dequeued from the "done queue" turns out to be a fragment block, it is added to the "I/O queue", sorted by its I/O sequence number **that it already has**, i.e. no new sequence number is allocated. The I/O queue is dequeued in the same fashion as the "done queue", using a second counter to enforce ordering. The actual implementation interleaves enqueueing and dequeueing in the block submission function. It dequeues blocks if the queues reach a pre-set maximum backlog. In that case, it tries to dequeue from the I/O queue first and if that fails, tries to dequeue from the "done queue". If that also fails, it uses signal/await to be woken up by a worker thread once it adds a block to the "done queue". Fragment post-processing and re-queueing of blocks is done inside the critical region, but the actual I/O is done outside (for obvious reasons). Profiling on small filesystems using perf shows that the outlined approach seems to perform quite well for CPU bound compressors like XZ, but doesn't add a lot for I/O bound compressors like zstd. If you have a better idea how to do this, please let me know. squashfs-tools-ng-1.1.3/extras/000077500000000000000000000000001410627516300164245ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/extras/Makemodule.am000066400000000000000000000010521410627516300210240ustar00rootroot00000000000000mknastyfs_SOURCES = extras/mknastyfs.c mknastyfs_LDADD = libsquashfs.la mk42sqfs_SOURCES = extras/mk42sqfs.c mk42sqfs_LDADD = libsquashfs.la list_files_SOURCES = extras/list_files.c list_files_LDADD = libsquashfs.la extract_one_SOURCES = extras/extract_one.c extract_one_LDADD = libsquashfs.la if WITH_READLINE sqfsbrowse_SOURCES = extras/browse.c sqfsbrowse_CFLAGS = $(AM_CFLAGS) $(READLINE_CFLAGS) sqfsbrowse_LDADD = libsquashfs.la $(READLINE_LIBS) noinst_PROGRAMS += sqfsbrowse endif noinst_PROGRAMS += mknastyfs mk42sqfs list_files extract_one squashfs-tools-ng-1.1.3/extras/browse.c000066400000000000000000000405431410627516300200770ustar00rootroot00000000000000/* SPDX-License-Identifier: 0BSD */ /* * browse.c * * Copyright (C) 2020 David Oberhollenzer */ #include "sqfs/data_reader.h" #include "sqfs/compressor.h" #include "sqfs/dir_reader.h" #include "sqfs/id_table.h" #include "sqfs/inode.h" #include "sqfs/super.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/dir.h" #include "sqfs/io.h" #include #include #include #include #include #include #include static sqfs_dir_reader_t *dr; static sqfs_super_t super; static sqfs_inode_generic_t *working_dir; static sqfs_id_table_t *idtbl; static sqfs_data_reader_t *data; /*****************************************************************************/ static void change_directory(const char *dirname) { sqfs_inode_generic_t *inode; int ret; if (dirname == NULL || *dirname == '/') { free(working_dir); working_dir = NULL; sqfs_dir_reader_get_root_inode(dr, &working_dir); } if (dirname != NULL) { ret = sqfs_dir_reader_find_by_path(dr, working_dir, dirname, &inode); if (ret != 0) { printf("Error resolving '%s', error code %d\n", dirname, ret); return; } free(working_dir); working_dir = inode; } } /*****************************************************************************/ static void list_directory(const char *dirname) { sqfs_inode_generic_t *root, *inode; size_t i, max_len, len, col_count; sqfs_dir_entry_t *ent; int ret; /* Get the directory inode we want to dump and open the directory */ if (dirname == NULL) { ret = sqfs_dir_reader_open_dir(dr, working_dir, 0); if (ret) goto fail_open; } else if (*dirname == '/') { sqfs_dir_reader_get_root_inode(dr, &root); ret = sqfs_dir_reader_find_by_path(dr, root, dirname, &inode); sqfs_free(root); if (ret) goto fail_resolve; ret = sqfs_dir_reader_open_dir(dr, inode, 0); sqfs_free(inode); if (ret) goto fail_open; } else { ret = sqfs_dir_reader_find_by_path(dr, working_dir, dirname, &inode); if (ret) goto fail_resolve; ret = sqfs_dir_reader_open_dir(dr, inode, 0); sqfs_free(inode); if (ret) goto fail_open; } /* first pass over the directory to figure out column count/length */ for (max_len = 0; ; max_len = len > max_len ? len : max_len) { ret = sqfs_dir_reader_read(dr, &ent); if (ret > 0) break; if (ret < 0) { fputs("Error while reading directory list\n", stderr); break; } len = ent->size + 1; sqfs_free(ent); } col_count = 79 / (max_len + 1); col_count = col_count < 1 ? 1 : col_count; i = 0; /* second pass for printing directory contents */ sqfs_dir_reader_rewind(dr); for (;;) { ret = sqfs_dir_reader_read(dr, &ent); if (ret > 0) break; if (ret < 0) { fputs("Error while reading directory list\n", stderr); break; } /* entries always use basic types only */ switch (ent->type) { case SQFS_INODE_DIR: fputs("\033[01;34m", stdout); break; case SQFS_INODE_FILE: break; case SQFS_INODE_SLINK: fputs("\033[01;36m", stdout); break; case SQFS_INODE_BDEV: fputs("\033[22;33m", stdout); break; case SQFS_INODE_CDEV: fputs("\033[01;33m", stdout); break; case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: fputs("\033[01;35m", stdout); break; default: break; } len = ent->size + 1; printf("%.*s", ent->size + 1, ent->name); fputs("\033[0m", stdout); sqfs_free(ent); ++i; if (i == col_count) { i = 0; fputc('\n', stdout); } else { while (len++ < max_len) fputc(' ', stdout); fputc(' ', stdout); } } if (i != 0) fputc('\n', stdout); return; fail_open: printf("Error opening '%s', error code %d\n", dirname, ret); return; fail_resolve: printf("Error resolving '%s', error code %d\n", dirname, ret); return; } /*****************************************************************************/ static void mode_to_str(sqfs_u16 mode, char *p) { *(p++) = (mode & SQFS_INODE_OWNER_R) ? 'r' : '-'; *(p++) = (mode & SQFS_INODE_OWNER_W) ? 'w' : '-'; switch (mode & (SQFS_INODE_OWNER_X | SQFS_INODE_SET_UID)) { case SQFS_INODE_OWNER_X | SQFS_INODE_SET_UID: *(p++) = 's'; break; case SQFS_INODE_OWNER_X: *(p++) = 'x'; break; case SQFS_INODE_SET_UID: *(p++) = 'S'; break; default: *(p++) = '-'; break; } *(p++) = (mode & SQFS_INODE_GROUP_R) ? 'r' : '-'; *(p++) = (mode & SQFS_INODE_GROUP_W) ? 'w' : '-'; switch (mode & (SQFS_INODE_GROUP_X | SQFS_INODE_SET_GID)) { case SQFS_INODE_GROUP_X | SQFS_INODE_SET_GID: *(p++) = 's'; break; case SQFS_INODE_GROUP_X: *(p++) = 'x'; break; case SQFS_INODE_SET_GID: *(p++) = 'S'; break; default: *(p++) = '-'; break; } *(p++) = (mode & SQFS_INODE_OTHERS_R) ? 'r' : '-'; *(p++) = (mode & SQFS_INODE_OTHERS_W) ? 'w' : '-'; switch (mode & (SQFS_INODE_OTHERS_X | SQFS_INODE_STICKY)) { case SQFS_INODE_OTHERS_X | SQFS_INODE_STICKY: *(p++) = 't'; break; case SQFS_INODE_OTHERS_X: *(p++) = 'x'; break; case SQFS_INODE_STICKY: *(p++) = 'T'; break; default: *(p++) = '-'; break; } *p = '\0'; } static void stat_cmd(const char *filename) { sqfs_inode_generic_t *inode, *root; sqfs_dir_index_t *idx; sqfs_u32 uid, gid; const char *type; char buffer[64]; time_t timeval; struct tm *tm; size_t i; int ret; /* get the inode we are interested in */ if (filename == NULL) { printf("Missing argument: file name\n"); return; } if (*filename == '/') { sqfs_dir_reader_get_root_inode(dr, &root); ret = sqfs_dir_reader_find_by_path(dr, root, filename, &inode); sqfs_free(root); if (ret) goto fail_resolve; } else { ret = sqfs_dir_reader_find_by_path(dr, working_dir, filename, &inode); if (ret) goto fail_resolve; } /* some basic information */ switch (inode->base.type) { case SQFS_INODE_DIR: type = "directory"; break; case SQFS_INODE_FILE: type = "file"; break; case SQFS_INODE_SLINK: type = "symbolic link"; break; case SQFS_INODE_BDEV: type = "block device"; break; case SQFS_INODE_CDEV: type = "character device"; break; case SQFS_INODE_FIFO: type = "named pipe"; break; case SQFS_INODE_SOCKET: type = "socket"; break; case SQFS_INODE_EXT_DIR: type = "extended directory"; break; case SQFS_INODE_EXT_FILE: type = "extended file"; break; case SQFS_INODE_EXT_SLINK: type = "extended symbolic link"; break; case SQFS_INODE_EXT_BDEV: type = "extended block device"; break; case SQFS_INODE_EXT_CDEV: type = "extended character device"; break; case SQFS_INODE_EXT_FIFO: type = "extended named pipe"; break; case SQFS_INODE_EXT_SOCKET: type = "extended socket"; break; default: type = "UNKNOWN"; break; } printf("Stat: %s\n", filename); printf("Type: %s\n", type); printf("Inode number: %u\n", inode->base.inode_number); mode_to_str(inode->base.mode & ~SQFS_INODE_MODE_MASK, buffer); printf("Access: 0%o/%s\n", (unsigned int)inode->base.mode & ~SQFS_INODE_MODE_MASK, buffer); /* resolve and print UID/GID */ if (sqfs_id_table_index_to_id(idtbl, inode->base.uid_idx, &uid)) { strcpy(buffer, "-- error --"); } else { sprintf(buffer, "%u", uid); } printf("UID: %s (index = %u)\n", buffer, inode->base.uid_idx); if (sqfs_id_table_index_to_id(idtbl, inode->base.gid_idx, &gid)) { strcpy(buffer, "-- error --"); } else { sprintf(buffer, "%u", gid); } printf("GID: %s (index = %u)\n", buffer, inode->base.gid_idx); /* last modification time stamp */ timeval = inode->base.mod_time; tm = gmtime(&timeval); strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", tm); printf("Last modified: %s (%u)\n", buffer, inode->base.mod_time); /* inode type specific data */ switch (inode->base.type) { case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: printf("Hard link count: %u\n", inode->data.dev.nlink); printf("Device number: %u\n", inode->data.dev.devno); break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: printf("Hard link count: %u\n", inode->data.dev_ext.nlink); printf("Xattr index: 0x%X\n", inode->data.dev_ext.xattr_idx); printf("Device number: %u\n", inode->data.dev_ext.devno); break; case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: printf("Hard link count: %u\n", inode->data.ipc.nlink); break; case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: printf("Hard link count: %u\n", inode->data.ipc_ext.nlink); printf("Xattr index: 0x%X\n", inode->data.ipc_ext.xattr_idx); break; case SQFS_INODE_SLINK: printf("Hard link count: %u\n", inode->data.slink.nlink); printf("Link target: %.*s\n", (int)inode->data.slink.target_size, (const char *)inode->extra); break; case SQFS_INODE_EXT_SLINK: printf("Hard link count: %u\n", inode->data.slink_ext.nlink); printf("Xattr index: 0x%X\n", inode->data.slink_ext.xattr_idx); printf("Link target: %.*s\n", (int)inode->data.slink_ext.target_size, (const char *)inode->extra); break; case SQFS_INODE_FILE: printf("Blocks start: %u\n", inode->data.file.blocks_start); printf("Block count: %lu\n", (unsigned long)sqfs_inode_get_file_block_count(inode)); printf("Fragment index: 0x%X\n", inode->data.file.fragment_index); printf("Fragment offset: %u\n", inode->data.file.fragment_offset); printf("File size: %u\n", inode->data.file.file_size); for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { printf("\tBlock #%lu size: %u (%s)\n", (unsigned long)i, SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]), SQFS_IS_BLOCK_COMPRESSED(inode->extra[i]) ? "compressed" : "uncompressed"); } break; case SQFS_INODE_EXT_FILE: printf("Blocks start: %lu\n", inode->data.file_ext.blocks_start); printf("Block count: %lu\n", (unsigned long)sqfs_inode_get_file_block_count(inode)); printf("Fragment index: 0x%X\n", inode->data.file_ext.fragment_idx); printf("Fragment offset: %u\n", inode->data.file_ext.fragment_offset); printf("File size: %lu\n", inode->data.file_ext.file_size); printf("Sparse: %lu\n", inode->data.file_ext.sparse); printf("Hard link count: %u\n", inode->data.file_ext.nlink); printf("Xattr index: 0x%X\n", inode->data.file_ext.xattr_idx); for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { printf("\tBlock #%lu size: %u (%s)\n", (unsigned long)i, SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]), SQFS_IS_BLOCK_COMPRESSED(inode->extra[i]) ? "compressed" : "uncompressed"); } break; case SQFS_INODE_DIR: printf("Start block: %u\n", inode->data.dir.start_block); printf("Offset: %u\n", inode->data.dir.offset); printf("Hard link count: %u\n", inode->data.dir.nlink); printf("Size: %u\n", inode->data.dir.size); printf("Parent inode: %u\n", inode->data.dir.parent_inode); break; case SQFS_INODE_EXT_DIR: printf("Start block: %u\n", inode->data.dir_ext.start_block); printf("Offset: %u\n", inode->data.dir_ext.offset); printf("Hard link count: %u\n", inode->data.dir_ext.nlink); printf("Size: %u\n", inode->data.dir_ext.size); printf("Parent inode: %u\n", inode->data.dir_ext.parent_inode); printf("Xattr index: 0x%X\n", inode->data.dir_ext.xattr_idx); printf("Directory index entries: %u\n", inode->data.dir_ext.inodex_count); if (inode->data.dir_ext.size == 0) break; /* dump the extended directories fast-lookup index */ for (i = 0; ; ++i) { ret = sqfs_inode_unpack_dir_index_entry(inode, &idx, i); if (ret == SQFS_ERROR_OUT_OF_BOUNDS) break; if (ret < 0) { printf("Error reading index, error code %d\n", ret); break; } printf("\tIndex: %u\n", idx->index); printf("\tStart block: %u\n", idx->start_block); printf("\tSize: %u\n", idx->size + 1); printf("\tEntry: %.*s\n\n", (int)(idx->size + 1), idx->name); sqfs_free(idx); } break; default: break; } sqfs_free(inode); return; fail_resolve: printf("Error resolving '%s', error code %d\n", filename, ret); return; } /*****************************************************************************/ static void cat_cmd(const char *filename) { sqfs_inode_generic_t *inode, *root; sqfs_u64 offset = 0; char buffer[512]; sqfs_s32 diff; int ret; /* get the inode of the file */ if (filename == NULL) { printf("Missing argument: file name\n"); return; } if (*filename == '/') { sqfs_dir_reader_get_root_inode(dr, &root); ret = sqfs_dir_reader_find_by_path(dr, root, filename, &inode); sqfs_free(root); } else { ret = sqfs_dir_reader_find_by_path(dr, working_dir, filename, &inode); } if (ret) { printf("Error resolving '%s', error code %d\n", filename, ret); return; } /* Use the high level read-like function that uses the data-reader * internal cache. There are also other functions for direct block * or fragment access. */ for (;;) { diff = sqfs_data_reader_read(data, inode, offset, buffer, sizeof(buffer)); if (diff == 0) break; if (diff < 0) { printf("Error reading from file '%s', error code %d\n", filename, diff); break; } fwrite(buffer, 1, diff, stdout); offset += diff; } sqfs_free(inode); } /*****************************************************************************/ static const struct { const char *cmd; void (*handler)(const char *arg); } commands[] = { { "ls", list_directory }, { "cd", change_directory }, { "stat", stat_cmd }, { "cat", cat_cmd }, }; int main(int argc, char **argv) { char *cmd, *arg, *buffer = NULL; int ret, status = EXIT_FAILURE; sqfs_compressor_config_t cfg; sqfs_compressor_t *cmp; sqfs_file_t *file; size_t i; /* open the SquashFS file we want to read */ if (argc != 2) { fputs("Usage: sqfsbrowse \n", stderr); return EXIT_FAILURE; } file = sqfs_open_file(argv[1], SQFS_FILE_OPEN_READ_ONLY); if (file == NULL) { perror(argv[1]); return EXIT_FAILURE; } /* read the super block, create a compressor and process the compressor options */ if (sqfs_super_read(&super, file)) { fprintf(stderr, "%s: error reading super block.\n", argv[1]); goto out_fd; } sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&cfg, &cmp); if (ret != 0) { fprintf(stderr, "%s: error creating compressor: %d.\n", argv[1], ret); goto out_fd; } /* Create and read the UID/GID mapping table */ idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fputs("Error creating ID table.\n", stderr); goto out_cmp; } if (sqfs_id_table_read(idtbl, file, &super, cmp)) { fprintf(stderr, "%s: error loading ID table.\n", argv[1]); goto out_id; } /* create a directory reader and get the root inode */ dr = sqfs_dir_reader_create(&super, cmp, file, 0); if (dr == NULL) { fprintf(stderr, "%s: error creating directory reader.\n", argv[1]); goto out_id; } if (sqfs_dir_reader_get_root_inode(dr, &working_dir)) { fprintf(stderr, "%s: error reading root inode.\n", argv[1]); goto out_dir; } /* create a data reader */ data = sqfs_data_reader_create(file, super.block_size, cmp, 0); if (data == NULL) { fprintf(stderr, "%s: error creating data reader.\n", argv[1]); goto out_dir; } if (sqfs_data_reader_load_fragment_table(data, &super)) { fprintf(stderr, "%s: error loading fragment table.\n", argv[1]); goto out; } /* main readline loop */ for (;;) { free(buffer); buffer = readline("$ "); if (buffer == NULL) goto out; for (cmd = buffer; isspace(*cmd); ++cmd) ; if (*cmd == '\0') continue; add_history(cmd); for (arg = cmd; *arg != '\0' && !isspace(*arg); ++arg) ; if (isspace(*arg)) { *(arg++) = '\0'; while (isspace(*arg)) ++arg; if (*arg == '\0') arg = NULL; } else { arg = NULL; } for (i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) { if (strcmp(commands[i].cmd, cmd) == 0) { commands[i].handler(arg); break; } } } /* cleanup */ status = EXIT_SUCCESS; free(buffer); out: sqfs_destroy(data); out_dir: if (working_dir != NULL) free(working_dir); sqfs_destroy(dr); out_id: sqfs_destroy(idtbl); out_cmp: sqfs_destroy(cmp); out_fd: sqfs_destroy(file); return status; } squashfs-tools-ng-1.1.3/extras/extract_one.c000066400000000000000000000104751410627516300211120ustar00rootroot00000000000000/* SPDX-License-Identifier: 0BSD */ /* * extract_one.c * * Copyright (C) 2021 Luca Boccassi */ #include "sqfs/compressor.h" #include "sqfs/data_reader.h" #include "sqfs/dir_reader.h" #include "sqfs/error.h" #include "sqfs/id_table.h" #include "sqfs/io.h" #include "sqfs/inode.h" #include "sqfs/super.h" #include "sqfs/xattr_reader.h" #include #include #include #include #include #include #include int main(int argc, char **argv) { sqfs_xattr_reader_t *xattr = NULL; sqfs_data_reader_t *data = NULL; sqfs_dir_reader_t *dirrd = NULL; sqfs_compressor_t *cmp = NULL; sqfs_id_table_t *idtbl = NULL; sqfs_tree_node_t *n = NULL; sqfs_file_t *file = NULL; sqfs_u8 *p, *output = NULL; sqfs_compressor_config_t cfg; sqfs_super_t super; sqfs_u64 file_size; int status = EXIT_FAILURE, ret; if (argc != 3) { fputs("Usage: extract_one \n", stderr); return EXIT_FAILURE; } file = sqfs_open_file(argv[1], SQFS_FILE_OPEN_READ_ONLY); if (file == NULL) { perror(argv[1]); return EXIT_FAILURE; } ret = sqfs_super_read(&super, file); if (ret) { fprintf(stderr, "%s: error reading super block.\n", argv[1]); goto out_file; } sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&cfg, &cmp); if (ret != 0) { fprintf(stderr, "%s: error creating compressor: %d.\n", argv[1], ret); goto out_file; } if (!(super.flags & SQFS_FLAG_NO_XATTRS)) { xattr = sqfs_xattr_reader_create(0); if (xattr == NULL) { fprintf(stderr, "%s: error creating xattr reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); goto out_cmp; } ret = sqfs_xattr_reader_load(xattr, &super, file, cmp); if (ret) { fprintf(stderr, "%s: error loading xattr reader: %d.\n", argv[1], ret); goto out_xattr; } } idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fprintf(stderr, "%s: error creating ID table: %d.\n", argv[1], ret); goto out_xattr; } ret = sqfs_id_table_read(idtbl, file, &super, cmp); if (ret) { fprintf(stderr, "%s: error loading ID table: %d.\n", argv[1], ret); goto out_idtbl; } dirrd = sqfs_dir_reader_create(&super, cmp, file, 0); if (dirrd == NULL) { fprintf(stderr, "%s: error creating dir reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); goto out_idtbl; } data = sqfs_data_reader_create(file, super.block_size, cmp, 0); if (data == NULL) { fprintf(stderr, "%s: error creating data reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); goto out_dirrd; } ret = sqfs_data_reader_load_fragment_table(data, &super); if (ret) { fprintf(stderr, "%s: error loading fragment table: %d.\n", argv[1], ret); goto out_data; } ret = sqfs_dir_reader_get_full_hierarchy(dirrd, idtbl, argv[2], 0, &n); if (ret) { fprintf(stderr, "%s: error reading filesystem hierarchy: %d.\n", argv[1], ret); goto out_data; } if (!S_ISREG(n->inode->base.mode)) { fprintf(stderr, "/%s is not a file\n", argv[2]); goto out_tree; } sqfs_inode_get_file_size(n->inode, &file_size); output = p = malloc(file_size); if (output == NULL) { fprintf(stderr, "malloc failed: %d\n", errno); goto out_tree; } for (size_t i = 0; i < sqfs_inode_get_file_block_count(n->inode); ++i) { size_t chunk_size, read = (file_size < super.block_size) ? file_size : super.block_size; sqfs_u8 *chunk; ret = sqfs_data_reader_get_block(data, n->inode, i, &chunk_size, &chunk); if (ret) { fprintf(stderr, "reading data block: %d\n", ret); goto out_tree; } memcpy(p, chunk, chunk_size); p += chunk_size; free(chunk); file_size -= read; } if (file_size > 0) { size_t chunk_size; sqfs_u8 *chunk; ret = sqfs_data_reader_get_fragment(data, n->inode, &chunk_size, &chunk); if (ret) { fprintf(stderr, "reading fragment block: %d\n", ret); goto out_tree; } memcpy(p, chunk, chunk_size); free(chunk); } /* for example simplicity, assume this is a text file */ fprintf(stdout, "%s\n", (char *)output); status = EXIT_SUCCESS; out_tree: sqfs_dir_tree_destroy(n); out_data: sqfs_destroy(data); out_dirrd: sqfs_destroy(dirrd); out_idtbl: sqfs_destroy(idtbl); out_xattr: if (xattr != NULL) sqfs_destroy(xattr); out_cmp: sqfs_destroy(cmp); out_file: sqfs_destroy(file); free(output); return status; } squashfs-tools-ng-1.1.3/extras/list_files.c000066400000000000000000000062661410627516300207370ustar00rootroot00000000000000/* SPDX-License-Identifier: 0BSD */ /* * list_files.c * * Copyright (C) 2020 David Oberhollenzer */ #include "sqfs/compressor.h" #include "sqfs/dir_reader.h" #include "sqfs/id_table.h" #include "sqfs/inode.h" #include "sqfs/super.h" #include "sqfs/io.h" #include #include #include #include static void write_tree_dfs(const sqfs_tree_node_t *n) { const sqfs_tree_node_t *p; unsigned int mask, level; int i; for (n = n->children; n != NULL; n = n->next) { level = 0; mask = 0; for (p = n->parent; p->parent != NULL; p = p->parent) { if (p->next != NULL) mask |= 1 << level; ++level; } for (i = level - 1; i >= 0; --i) fputs(mask & (1 << i) ? "│ " : " ", stdout); fputs(n->next == NULL ? "└─ " : "├─ ", stdout); fputs((const char *)n->name, stdout); if (n->inode->base.type == SQFS_INODE_SLINK) { printf(" ⭢ %.*s", (int)n->inode->data.slink.target_size, (const char *)n->inode->extra); } else if (n->inode->base.type == SQFS_INODE_EXT_SLINK) { printf(" ⭢ %.*s", (int)n->inode->data.slink_ext.target_size, (const char *)n->inode->extra); } fputc('\n', stdout); write_tree_dfs(n); } } int main(int argc, char **argv) { int ret, status = EXIT_FAILURE; sqfs_compressor_config_t cfg; sqfs_compressor_t *cmp; sqfs_tree_node_t *root = NULL; sqfs_id_table_t *idtbl; sqfs_dir_reader_t *dr; sqfs_file_t *file; sqfs_super_t super; /* open the SquashFS file we want to read */ if (argc != 2) { fputs("Usage: list_files \n", stderr); return EXIT_FAILURE; } file = sqfs_open_file(argv[1], SQFS_FILE_OPEN_READ_ONLY); if (file == NULL) { perror(argv[1]); return EXIT_FAILURE; } /* read the super block, create a compressor and process the compressor options */ if (sqfs_super_read(&super, file)) { fprintf(stderr, "%s: error reading super block.\n", argv[1]); goto out_fd; } sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&cfg, &cmp); if (ret != 0) { fprintf(stderr, "%s: error creating compressor: %d.\n", argv[1], ret); goto out_fd; } /* Create and read the UID/GID mapping table */ idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fputs("Error creating ID table.\n", stderr); goto out_cmp; } if (sqfs_id_table_read(idtbl, file, &super, cmp)) { fprintf(stderr, "%s: error loading ID table.\n", argv[1]); goto out_id; } /* create a directory reader and scan the entire directory hiearchy */ dr = sqfs_dir_reader_create(&super, cmp, file, 0); if (dr == NULL) { fprintf(stderr, "%s: error creating directory reader.\n", argv[1]); goto out_id; } if (sqfs_dir_reader_get_full_hierarchy(dr, idtbl, NULL, 0, &root)) { fprintf(stderr, "%s: error loading directory tree.\n", argv[1]); goto out; } /* fancy print the hierarchy */ printf("/\n"); write_tree_dfs(root); /* cleanup */ status = EXIT_SUCCESS; out: if (root != NULL) sqfs_dir_tree_destroy(root); sqfs_destroy(dr); out_id: sqfs_destroy(idtbl); out_cmp: sqfs_destroy(cmp); out_fd: sqfs_destroy(file); return status; } squashfs-tools-ng-1.1.3/extras/mk42sqfs.c000066400000000000000000000127711410627516300202520ustar00rootroot00000000000000/* SPDX-License-Identifier: 0BSD */ /* * mk42sqfs.c * * Copyright (C) 2020 David Oberhollenzer */ #include "sqfs/meta_writer.h" #include "sqfs/dir_writer.h" #include "sqfs/compressor.h" #include "sqfs/id_table.h" #include "sqfs/inode.h" #include "sqfs/super.h" #include "sqfs/io.h" #include #include #include const char *README = "This SuqashFS image contains ITSELF 42 times. Do NOT try to recursively\n" "scan or unpack it. You will end up with an infinite amount of data!\n"; static void add_padding(sqfs_file_t *file) { sqfs_u64 diff, size; size = file->get_size(file); diff = size % 4096; if (diff > 0) file->truncate(file, size + (4096 - diff)); } static sqfs_inode_generic_t *create_file_inode(sqfs_id_table_t *idtbl, unsigned int inode_num) { sqfs_inode_generic_t *inode; inode = calloc(1, sizeof(*inode) + sizeof(sqfs_u32)); inode->base.type = SQFS_INODE_FILE; inode->base.mode = SQFS_INODE_MODE_REG | 0644; inode->base.inode_number = inode_num; sqfs_id_table_id_to_index(idtbl, 0, &inode->base.uid_idx); sqfs_id_table_id_to_index(idtbl, 0, &inode->base.gid_idx); inode->data.file.file_size = 4096; inode->data.file.fragment_index = 0xFFFFFFFF; inode->payload_bytes_used = sizeof(sqfs_u32); inode->payload_bytes_available = sizeof(sqfs_u32); inode->extra[0] = (1 << 24) | inode->data.file.file_size; return inode; } int main(void) { sqfs_meta_writer_t *inode_m, *dir_m; int ret, status = EXIT_FAILURE; sqfs_compressor_config_t cfg; sqfs_inode_generic_t *inode; unsigned int i, inode_num; sqfs_dir_writer_t *dirwr; sqfs_compressor_t *cmp; sqfs_id_table_t *idtbl; sqfs_u64 block_start; sqfs_super_t super; sqfs_file_t *file; sqfs_u32 offset; char buffer[32]; /* get a file object referring to our destination file */ file = sqfs_open_file("42.sqfs", SQFS_FILE_OPEN_OVERWRITE); if (file == NULL) { fputs("Error opening output file.\n", stderr); return EXIT_FAILURE; } /* initialize the super block with sane values */ sqfs_super_init(&super, 4096, 0, SQFS_COMP_GZIP); if (sqfs_super_write(&super, file)) { fputs("Error witing super block.\n", stderr); goto out_file; } /* write the file data for the readme */ if (file->write_at(file, sizeof(super), README, strlen(README))) { fputs("Error writing file data!\n", stderr); goto out_file; } /* create a compressor */ sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, 0); ret = sqfs_compressor_create(&cfg, &cmp); if (ret != 0) { fprintf(stderr, "Error creating compressor: %d.\n", ret); goto out_file; } /* create meta data writers for inodes and directories */ inode_m = sqfs_meta_writer_create(file, cmp, 0); if (inode_m == NULL) { fputs("Error creating inode meta data writer.\n", stderr); goto out_cmp; } dir_m = sqfs_meta_writer_create(file, cmp, SQFS_META_WRITER_KEEP_IN_MEMORY); if (dir_m == NULL) { fputs("Error creating directory meta data writer.\n", stderr); goto out_im; } /* create a higher level directory writer on top of the meta writer */ dirwr = sqfs_dir_writer_create(dir_m, 0); if (dirwr == NULL) { fputs("Error creating directory writer.\n", stderr); goto out_dm; } /* create an ID table */ idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fputs("Error creating ID table.\n", stderr); goto out_dirwr; } /* generate inodes and directories */ super.inode_table_start = file->get_size(file); inode_num = 1; sqfs_dir_writer_begin(dirwr, 0); for (i = 0; i < 42; ++i) { sprintf(buffer, "%02u.sqfs", i + 1); inode = create_file_inode(idtbl, inode_num++); sqfs_meta_writer_get_position(inode_m, &block_start, &offset); sqfs_meta_writer_write_inode(inode_m, inode); sqfs_dir_writer_add_entry(dirwr, buffer, inode->base.inode_number, (block_start << 16) | offset, inode->base.mode); free(inode); } inode = create_file_inode(idtbl, inode_num++); inode->data.file.blocks_start = sizeof(super); inode->data.file.file_size = strlen(README); inode->extra[0] = (1 << 24) | inode->data.file.file_size; sqfs_meta_writer_get_position(inode_m, &block_start, &offset); sqfs_meta_writer_write_inode(inode_m, inode); sqfs_dir_writer_add_entry(dirwr, "README.txt", inode->base.inode_number, (block_start << 16) | offset, inode->base.mode); free(inode); sqfs_dir_writer_end(dirwr); /* create an inode for the root directory */ inode = sqfs_dir_writer_create_inode(dirwr, 0, 0xFFFFFFFF, 0); inode->base.mode = SQFS_INODE_MODE_DIR | 0755; inode->base.inode_number = inode_num++; sqfs_id_table_id_to_index(idtbl, 0, &inode->base.uid_idx); sqfs_id_table_id_to_index(idtbl, 0, &inode->base.gid_idx); sqfs_meta_writer_get_position(inode_m, &block_start, &offset); super.root_inode_ref = (block_start << 16) | offset; sqfs_meta_writer_write_inode(inode_m, inode); sqfs_free(inode); /* flush the meta data to the file */ sqfs_meta_writer_flush(inode_m); sqfs_meta_writer_flush(dir_m); super.directory_table_start = file->get_size(file); sqfs_meta_write_write_to_file(dir_m); sqfs_id_table_write(idtbl, file, &super, cmp); super.inode_count = inode_num - 2; super.bytes_used = 4096; if (sqfs_super_write(&super, file)) { fputs("Error update the final super block.\n", stderr); goto out_file; } add_padding(file); /* cleanup */ status = EXIT_SUCCESS; sqfs_destroy(idtbl); out_dirwr: sqfs_destroy(dirwr); out_dm: sqfs_destroy(dir_m); out_im: sqfs_destroy(inode_m); out_cmp: sqfs_destroy(cmp); out_file: sqfs_destroy(file); return status; } squashfs-tools-ng-1.1.3/extras/mknastyfs.c000066400000000000000000000115461410627516300206160ustar00rootroot00000000000000/* SPDX-License-Identifier: 0BSD */ /* * mknastyfs.c * * Copyright (C) 2020 David Oberhollenzer */ #include "sqfs/meta_writer.h" #include "sqfs/dir_writer.h" #include "sqfs/compressor.h" #include "sqfs/id_table.h" #include "sqfs/inode.h" #include "sqfs/super.h" #include "sqfs/io.h" #include #include #include static void add_padding(sqfs_file_t *file) { sqfs_u64 diff, size; size = file->get_size(file); diff = size % 4096; if (diff > 0) file->truncate(file, size + (4096 - diff)); } static sqfs_inode_generic_t *create_file_inode(sqfs_id_table_t *idtbl, unsigned int inode_num) { sqfs_inode_generic_t *inode; inode = calloc(1, sizeof(*inode) + sizeof(sqfs_u32)); inode->base.type = SQFS_INODE_FILE; inode->base.mode = SQFS_INODE_MODE_REG | 0644; inode->base.inode_number = inode_num; sqfs_id_table_id_to_index(idtbl, 0, &inode->base.uid_idx); sqfs_id_table_id_to_index(idtbl, 0, &inode->base.gid_idx); inode->data.file.file_size = 1337; inode->data.file.fragment_index = 0xFFFFFFFF; inode->payload_bytes_used = sizeof(sqfs_u32); inode->payload_bytes_available = sizeof(sqfs_u32); inode->extra[0] = (1 << 24) | inode->data.file.file_size; return inode; } int main(void) { sqfs_meta_writer_t *inode_m, *dir_m; int ret, status = EXIT_FAILURE; sqfs_compressor_config_t cfg; sqfs_inode_generic_t *inode; sqfs_dir_writer_t *dirwr; sqfs_compressor_t *cmp; sqfs_id_table_t *idtbl; unsigned int inode_num; sqfs_u64 block_start; sqfs_super_t super; sqfs_file_t *file; sqfs_u32 offset; /* get a file object referring to our destination file */ file = sqfs_open_file("nasty.sqfs", 0); if (file == NULL) { fputs("Error opening output file.\n", stderr); return EXIT_FAILURE; } /* initialize the super block with sane values */ sqfs_super_init(&super, 4096, 0, SQFS_COMP_GZIP); if (sqfs_super_write(&super, file)) { fputs("Error witing super block.\n", stderr); goto out_file; } /* create a compressor */ sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, 0); ret = sqfs_compressor_create(&cfg, &cmp); if (ret != 0) { fprintf(stderr, "Error creating compressor: %d.\n", ret); goto out_file; } /* create meta data writers for inodes and directories */ inode_m = sqfs_meta_writer_create(file, cmp, 0); if (inode_m == NULL) { fputs("Error creating inode meta data writer.\n", stderr); goto out_cmp; } dir_m = sqfs_meta_writer_create(file, cmp, SQFS_META_WRITER_KEEP_IN_MEMORY); if (dir_m == NULL) { fputs("Error creating directory meta data writer.\n", stderr); goto out_im; } /* create a higher level directory writer on top of the meta writer */ dirwr = sqfs_dir_writer_create(dir_m, 0); if (dirwr == NULL) { fputs("Error creating directory writer.\n", stderr); goto out_dm; } /* create an ID table */ idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fputs("Error creating ID table.\n", stderr); goto out_dirwr; } /* generate inodes and directories */ super.inode_table_start = file->get_size(file); inode_num = 1; sqfs_dir_writer_begin(dirwr, 0); inode = create_file_inode(idtbl, inode_num++); sqfs_meta_writer_get_position(inode_m, &block_start, &offset); sqfs_meta_writer_write_inode(inode_m, inode); sqfs_dir_writer_add_entry(dirwr, "..", inode->base.inode_number, (block_start << 16) | offset, inode->base.mode); free(inode); inode = create_file_inode(idtbl, inode_num++); sqfs_meta_writer_get_position(inode_m, &block_start, &offset); sqfs_meta_writer_write_inode(inode_m, inode); sqfs_dir_writer_add_entry(dirwr, "/etc/passwd", inode->base.inode_number, (block_start << 16) | offset, inode->base.mode); free(inode); sqfs_dir_writer_end(dirwr); inode = sqfs_dir_writer_create_inode(dirwr, 0, 0xFFFFFFFF, 0); inode->base.mode = SQFS_INODE_MODE_DIR | 0755; inode->base.inode_number = inode_num++; sqfs_id_table_id_to_index(idtbl, 0, &inode->base.uid_idx); sqfs_id_table_id_to_index(idtbl, 0, &inode->base.gid_idx); sqfs_meta_writer_get_position(inode_m, &block_start, &offset); super.root_inode_ref = (block_start << 16) | offset; sqfs_meta_writer_write_inode(inode_m, inode); sqfs_free(inode); /* flush the meta data to the file */ sqfs_meta_writer_flush(inode_m); sqfs_meta_writer_flush(dir_m); super.directory_table_start = file->get_size(file); sqfs_meta_write_write_to_file(dir_m); sqfs_id_table_write(idtbl, file, &super, cmp); super.inode_count = inode_num - 2; super.bytes_used = file->get_size(file); if (sqfs_super_write(&super, file)) { fputs("Error update the final super block.\n", stderr); goto out_file; } add_padding(file); /* cleanup */ status = EXIT_SUCCESS; sqfs_destroy(idtbl); out_dirwr: sqfs_destroy(dirwr); out_dm: sqfs_destroy(dir_m); out_im: sqfs_destroy(inode_m); out_cmp: sqfs_destroy(cmp); out_file: sqfs_destroy(file); return status; } squashfs-tools-ng-1.1.3/include/000077500000000000000000000000001410627516300165415ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/include/array.h000066400000000000000000000032071410627516300200320ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * array.h * * Copyright (C) 2021 David Oberhollenzer */ #ifndef ARRAY_H #define ARRAY_H #include "sqfs/predef.h" #include "sqfs/error.h" #include #include #include typedef struct { /* sizeof a single element */ size_t size; /* total number of elements available */ size_t count; /* actually used number of elements available */ size_t used; void *data; } array_t; static SQFS_INLINE void *array_get(array_t *array, size_t index) { if (index >= array->used) return NULL; return (char *)array->data + array->size * index; } static SQFS_INLINE int array_set(array_t *array, size_t index, const void *data) { if (index >= array->used) return SQFS_ERROR_OUT_OF_BOUNDS; memcpy((char *)array->data + array->size * index, data, array->size); return 0; } static SQFS_INLINE void array_sort_range(array_t *array, size_t start, size_t count, int (*compare_fun)(const void *a, const void *b)) { if (start < array->used) { if (count > (array->used - start)) count = array->used - start; qsort((char *)array->data + array->size * start, count, array->size, compare_fun); } } #ifdef __cplusplus extern "C" { #endif SQFS_INTERNAL int array_init(array_t *array, size_t size, size_t capacity); SQFS_INTERNAL int array_init_copy(array_t *array, const array_t *src); SQFS_INTERNAL void array_cleanup(array_t *array); SQFS_INTERNAL int array_append(array_t *array, const void *data); SQFS_INTERNAL int array_set_capacity(array_t *array, size_t capacity); #ifdef __cplusplus } #endif #endif /* ARRAY_H */ squashfs-tools-ng-1.1.3/include/common.h000066400000000000000000000045051410627516300202060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * common.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef COMMON_H #define COMMON_H #include "config.h" #include "sqfs/xattr_reader.h" #include "sqfs/inode.h" #include "sqfs/table.h" #include "sqfs/data_reader.h" #include "sqfs/dir_reader.h" #include "sqfs/block.h" #include "sqfs/xattr.h" #include "sqfs/dir.h" #include "simple_writer.h" #include "compress_cli.h" #include "fstream.h" #include "compat.h" #include "fstree.h" #include "tar.h" #include typedef struct sqfs_hard_link_t { struct sqfs_hard_link_t *next; sqfs_u32 inode_number; char *target; } sqfs_hard_link_t; #define container_of(ptr, type, member) \ ((type *)((char *)ptr - offsetof(type, member))) int inode_stat(const sqfs_tree_node_t *node, struct stat *sb); char *sqfs_tree_node_get_path(const sqfs_tree_node_t *node); int sqfs_data_reader_dump(const char *name, sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, ostream_t *fp, size_t block_size); int write_data_from_file(const char *filename, sqfs_block_processor_t *data, sqfs_inode_generic_t **inode, sqfs_file_t *file, int flags); void sqfs_perror(const char *file, const char *action, int error_code); int sqfs_tree_find_hard_links(const sqfs_tree_node_t *root, sqfs_hard_link_t **out); /* A wrapper around mkdir() that behaves like 'mkdir -p'. It tries to create every component of the given path and skips already existing entries. Returns 0 on success. */ int mkdir_p(const char *path); /* A common implementation of the '--version' command line flag. */ void print_version(const char *progname); /* Parse a number optionally followed by a KMG suffix (case insensitive). Prints an error message to stderr and returns -1 on failure, 0 on success. The "what" string is used to prefix error messages (perror style). If reference is non-zero, the suffix '%' can be used to compute the result as a multiple of the reference value. */ int parse_size(const char *what, size_t *out, const char *str, size_t reference); void print_size(sqfs_u64 size, char *buffer, bool round_to_int); ostream_t *data_writer_ostream_create(const char *filename, sqfs_block_processor_t *proc, sqfs_inode_generic_t **inode, int flags); #endif /* COMMON_H */ squashfs-tools-ng-1.1.3/include/compat.h000066400000000000000000000130141410627516300201740ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compat.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef COMPAT_H #define COMPAT_H #include "sqfs/predef.h" #include "config.h" #include #if defined(__GNUC__) && __GNUC__ >= 5 # define SZ_ADD_OV __builtin_add_overflow # define SZ_MUL_OV __builtin_mul_overflow #elif defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5 # if SIZE_MAX <= UINT_MAX # define SZ_ADD_OV __builtin_uadd_overflow # define SZ_MUL_OV __builtin_umul_overflow # elif SIZE_MAX == ULONG_MAX # define SZ_ADD_OV __builtin_uaddl_overflow # define SZ_MUL_OV __builtin_umull_overflow # elif SIZE_MAX == ULLONG_MAX # define SZ_ADD_OV __builtin_uaddll_overflow # define SZ_MUL_OV __builtin_umulll_overflow # else # error Cannot determine maximum value of size_t # endif #else static inline int _sz_add_overflow(size_t a, size_t b, size_t *res) { *res = a + b; return (*res < a) ? 1 : 0; } static inline int _sz_mul_overflow(size_t a, size_t b, size_t *res) { *res = a * b; return (b > 0 && (a > SIZE_MAX / b)) ? 1 : 0; } # define SZ_ADD_OV _sz_add_overflow # define SZ_MUL_OV _sz_mul_overflow #endif #if defined(_WIN32) || defined(__WINDOWS__) # define PRI_U64 "%I64u" # define PRI_U32 "%I32u" #else # include # define PRI_U64 "%" PRIu64 # define PRI_U32 "%" PRIu32 #endif #if SIZE_MAX <= UINT_MAX # define PRI_SZ "%u" #elif SIZE_MAX == ULONG_MAX # define PRI_SZ "%lu" #elif defined(_WIN32) && SIZE_MAX == UINT64_MAX # define PRI_SZ "%I64u" #else # error Cannot figure out propper printf specifier for size_t #endif #if defined(__APPLE__) #include #define htole16(x) OSSwapHostToLittleInt16(x) #define htole32(x) OSSwapHostToLittleInt32(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define le32toh(x) OSSwapLittleToHostInt32(x) #define le16toh(x) OSSwapLittleToHostInt16(x) #define le64toh(x) OSSwapLittleToHostInt64(x) #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(_WIN32) || defined(__WINDOWS__) #define htole16(x) (x) #define htole32(x) (x) #define htole64(x) (x) #define le16toh(x) (x) #define le32toh(x) (x) #define le64toh(x) (x) #define WIN32_LEAN_AND_MEAN #include #else #include #endif #if defined(_WIN32) || defined(__WINDOWS__) #include "sqfs/inode.h" #define S_IFSOCK SQFS_INODE_MODE_SOCK #define S_IFLNK SQFS_INODE_MODE_LNK #define S_IFREG SQFS_INODE_MODE_REG #define S_IFBLK SQFS_INODE_MODE_BLK #define S_IFDIR SQFS_INODE_MODE_DIR #define S_IFCHR SQFS_INODE_MODE_CHR #define S_IFIFO SQFS_INODE_MODE_FIFO #define S_IFMT SQFS_INODE_MODE_MASK #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) #define S_ISUID SQFS_INODE_SET_UID #define S_ISGID SQFS_INODE_SET_GID #define S_ISVTX SQFS_INODE_STICKY #define S_IRWXU SQFS_INODE_OWNER_MASK #define S_IRUSR SQFS_INODE_OWNER_R #define S_IWUSR SQFS_INODE_OWNER_W #define S_IXUSR SQFS_INODE_OWNER_X #define S_IRWXG SQFS_INODE_GROUP_MASK #define S_IRGRP SQFS_INODE_GROUP_R #define S_IWGRP SQFS_INODE_GROUP_W #define S_IXGRP SQFS_INODE_GROUP_X #define S_IRWXO SQFS_INODE_OTHERS_MASK #define S_IROTH SQFS_INODE_OTHERS_R #define S_IWOTH SQFS_INODE_OTHERS_W #define S_IXOTH SQFS_INODE_OTHERS_X struct stat { sqfs_u32 st_dev; sqfs_u32 st_ino; sqfs_u16 st_mode; sqfs_u16 st_nlink; sqfs_u32 st_uid; sqfs_u32 st_gid; sqfs_u32 st_rdev; sqfs_u64 st_size; sqfs_u32 st_blksize; sqfs_u32 st_blocks; sqfs_u64 st_atime; sqfs_u64 st_mtime; sqfs_u64 st_ctime; }; /* lifted from musl libc */ #define major(x) \ ((unsigned)( (((x)>>31>>1) & 0xfffff000) | (((x)>>8) & 0x00000fff) )) #define minor(x) \ ((unsigned)( (((x)>>12) & 0xffffff00) | ((x) & 0x000000ff) )) #define makedev(x,y) ( \ (((x)&0xfffff000ULL) << 32) | \ (((x)&0x00000fffULL) << 8) | \ (((y)&0xffffff00ULL) << 12) | \ (((y)&0x000000ffULL)) ) #define AT_FDCWD ((int)0xDEADBEEF) #define AT_SYMLINK_NOFOLLOW (0x01) int fchownat(int dirfd, const char *path, int uid, int gid, int flags); int fchmodat(int dirfd, const char *path, int mode, int flags); int chdir(const char *path); void w32_perror(const char *str); #else #include #include #include #include #if defined(__linux__) || defined(__GLIBC__) #include #endif #endif #ifndef HAVE_STRNDUP char *strndup(const char *str, size_t max_len); #endif #ifndef HAVE_GETOPT extern char *optarg; extern int optind, opterr, optopt, optpos, optreset; void __getopt_msg(const char *a, const char *b, const char *c, size_t l); int getopt(int argc, char * const argv[], const char *optstring); #endif #ifndef HAVE_GETOPT_LONG struct option { const char *name; int has_arg; int *flag; int val; }; #define no_argument 0 #define required_argument 1 #define optional_argument 2 int getopt_long(int, char *const *, const char *, const struct option *, int *); #endif #ifndef HAVE_GETSUBOPT int getsubopt(char **opt, char *const *keys, char **val); #endif #if defined(_WIN32) || defined(__WINDOWS__) WCHAR *path_to_windows(const char *input); #endif #ifdef HAVE_FNMATCH #include #else #define FNM_PATHNAME 0x1 #define FNM_NOMATCH 1 #define FNM_NOSYS (-1) int fnmatch(const char *, const char *, int); #endif #endif /* COMPAT_H */ squashfs-tools-ng-1.1.3/include/compress_cli.h000066400000000000000000000015731410627516300214020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compress_cli.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef COMPRESS_CLI_H #define COMPRESS_CLI_H #include "sqfs/compressor.h" #include "sqfs/super.h" #include "sqfs/block.h" #include "sqfs/error.h" #include "sqfs/io.h" #ifdef __cplusplus extern "C" { #endif void compressor_print_available(void); SQFS_COMPRESSOR compressor_get_default(void); int compressor_cfg_init_options(sqfs_compressor_config_t *cfg, SQFS_COMPRESSOR id, size_t block_size, char *options); void compressor_print_help(SQFS_COMPRESSOR id); /* Create an liblzo2 based LZO compressor. XXX: This must be in libcommon instead of libsquashfs for legal reasons. */ int lzo_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); #ifdef __cplusplus } #endif #endif /* COMPRESS_CLI_H */ squashfs-tools-ng-1.1.3/include/fstream.h000066400000000000000000000260641410627516300203630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstream.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef FSTREAM_H #define FSTREAM_H #include "sqfs/predef.h" #if defined(__GNUC__) || defined(__clang__) # define PRINTF_ATTRIB(fmt, elipsis) \ __attribute__ ((format (printf, fmt, elipsis))) #else # define PRINTF_ATTRIB(fmt, elipsis) #endif /** * @struct ostream_t * * @extends sqfs_object_t * * @brief An append-only data stream. */ typedef struct ostream_t { sqfs_object_t base; int (*append)(struct ostream_t *strm, const void *data, size_t size); int (*append_sparse)(struct ostream_t *strm, size_t size); int (*flush)(struct ostream_t *strm); const char *(*get_filename)(struct ostream_t *strm); } ostream_t; /** * @struct istream_t * * @extends sqfs_object_t * * @brief A sequential, read-only data stream. */ typedef struct istream_t { sqfs_object_t base; size_t buffer_used; size_t buffer_offset; bool eof; sqfs_u8 *buffer; int (*precache)(struct istream_t *strm); const char *(*get_filename)(struct istream_t *strm); } istream_t; enum { OSTREAM_OPEN_OVERWRITE = 0x01, OSTREAM_OPEN_SPARSE = 0x02, }; enum { ISTREAM_LINE_LTRIM = 0x01, ISTREAM_LINE_RTRIM = 0x02, ISTREAM_LINE_SKIP_EMPTY = 0x04, }; enum { /** * @brief Deflate compressor with gzip headers. * * This actually creates a gzip compatible file, including a * gzip header and trailer. */ FSTREAM_COMPRESSOR_GZIP = 1, FSTREAM_COMPRESSOR_XZ = 2, FSTREAM_COMPRESSOR_ZSTD = 3, FSTREAM_COMPRESSOR_BZIP2 = 4, FSTREAM_COMPRESSOR_MIN = 1, FSTREAM_COMPRESSOR_MAX = 4, }; #ifdef __cplusplus extern "C" { #endif /** * @brief Create an output stream that writes to a file. * * @memberof ostream_t * * If the file does not yet exist, it is created. If it does exist this * function fails, unless the flag OSTREAM_OPEN_OVERWRITE is set, in which * case the file is opened and its contents are discarded. * * If the flag OSTREAM_OPEN_SPARSE is set, the underlying implementation tries * to support sparse output files. If the flag is not set, holes will always * be filled with zero bytes. * * @param path A path to the file to open or create. * @param flags A combination of flags controling how to open/create the file. * * @return A pointer to an output stream on success, NULL on failure. */ SQFS_INTERNAL ostream_t *ostream_open_file(const char *path, int flags); /** * @brief Create an output stream that writes to standard output. * * @memberof ostream_t * * @return A pointer to an output stream on success, NULL on failure. */ SQFS_INTERNAL ostream_t *ostream_open_stdout(void); /** * @brief Create an input stream that reads from a file. * * @memberof istream_t * * @param path A path to the file to open or create. * * @return A pointer to an output stream on success, NULL on failure. */ SQFS_INTERNAL istream_t *istream_open_file(const char *path); /** * @brief Create an input stream that reads from standard input. * * @memberof istream_t * * @return A pointer to an input stream on success, NULL on failure. */ SQFS_INTERNAL istream_t *istream_open_stdin(void); /** * @brief Create an output stream that transparently compresses data. * * @memberof ostream_t * * This function creates an output stream that transparently compresses all * data appended to it and writes the compressed data to an underlying, wrapped * output stream. * * The new stream takes ownership of the wrapped stream and destroys it when * the compressor stream is destroyed. If this function fails, the wrapped * stream is also destroyed. * * @param strm A pointer to another stream that should be wrapped. * @param comp_id An identifier describing the compressor to use. * * @return A pointer to an output stream on success, NULL on failure. */ SQFS_INTERNAL ostream_t *ostream_compressor_create(ostream_t *strm, int comp_id); /** * @brief Create an input stream that transparently uncompresses data. * * @memberof istream_t * * This function creates an input stream that wraps an underlying input stream * that is compressed and transparently uncompresses the data when reading * from it. * * The new stream takes ownership of the wrapped stream and destroys it when * the compressor stream is destroyed. If this function fails, the wrapped * stream is also destroyed. * * @param strm A pointer to another stream that should be wrapped. * @param comp_id An identifier describing the compressor to use. * * @return A pointer to an input stream on success, NULL on failure. */ SQFS_INTERNAL istream_t *istream_compressor_create(istream_t *strm, int comp_id); /** * @brief Probe the buffered data in an istream to check if it is compressed. * * @memberof istream_t * * This function peeks into the internal buffer of an input stream to check * for magic signatures of various compressors. * * @param strm A pointer to an input stream to check * @param probe A callback used to check if raw/decoded data matches an * expected format. Returns 0 if not, -1 on failure and +1 * on success. * * @return A compressor ID on success, 0 if no match was found, -1 on failure. */ SQFS_INTERNAL int istream_detect_compressor(istream_t *strm, int (*probe)(const sqfs_u8 *data, size_t size)); /** * @brief Append a block of data to an output stream. * * @memberof ostream_t * * @param strm A pointer to an output stream. * @param data A pointer to the data block to append. * @param size The number of bytes to append. * * @return Zero on success, -1 on failure. */ SQFS_INTERNAL int ostream_append(ostream_t *strm, const void *data, size_t size); /** * @brief Append a number of zero bytes to an output stream. * * @memberof ostream_t * * If the unerlying implementation supports sparse files, this function can be * used to create a "hole". If the implementation does not support it, a * fallback is used that just appends a block of zeros manualy. * * @param strm A pointer to an output stream. * @param size The number of zero bytes to append. * * @return Zero on success, -1 on failure. */ SQFS_INTERNAL int ostream_append_sparse(ostream_t *strm, size_t size); /** * @brief Process all pending, buffered data and flush it to disk. * * @memberof ostream_t * * If the stream performs some kind of transformation (e.g. transparent data * compression), flushing caues the wrapped format to insert a termination * token. Only call this function when you are absolutely DONE appending data, * shortly before destroying the stream. * * @param strm A pointer to an output stream. * * @return Zero on success, -1 on failure. */ SQFS_INTERNAL int ostream_flush(ostream_t *strm); /** * @brief Get the underlying filename of a output stream. * * @memberof ostream_t * * @param strm The output stream to get the filename from. * * @return A string holding the underlying filename. */ SQFS_INTERNAL const char *ostream_get_filename(ostream_t *strm); /** * @brief Printf like function that appends to an output stream * * @memberof ostream_t * * @param strm The output stream to append to. * @param fmt A printf style format string. * * @return The number of characters written on success, -1 on failure. */ SQFS_INTERNAL int ostream_printf(ostream_t *strm, const char *fmt, ...) PRINTF_ATTRIB(2, 3); /** * @brief Read a line of text from an input stream * * @memberof istream_t * * The line returned is allocated using malloc and must subsequently be * freed when it is no longer needed. The line itself is always null-terminated * and never includes the line break characters (LF or CR-LF). * * If the flag @ref ISTREAM_LINE_LTRIM is set, leading white space characters * are removed. If the flag @ref ISTREAM_LINE_RTRIM is set, trailing white space * characters are remvoed. * * If the flag @ref ISTREAM_LINE_SKIP_EMPTY is set and a line is discovered to * be empty (after the optional trimming), the function discards the empty line * and retries. The given line_num pointer is used to increment the line * number. * * @param strm A pointer to an input stream. * @param out Returns a pointer to a line on success. * @param line_num This is incremented if lines are skipped. * @param flags A combination of flags controling the functions behaviour. * * @return Zero on success, a negative value on error, a positive value if * end-of-file was reached without reading any data. */ SQFS_INTERNAL int istream_get_line(istream_t *strm, char **out, size_t *line_num, int flags); /** * @brief Read data from an input stream * * @memberof istream_t * * @param strm A pointer to an input stream. * @param data A buffer to read into. * @param size The number of bytes to read into the buffer. * * @return The number of bytes actually read on success, -1 on failure, * 0 on end-of-file. */ SQFS_INTERNAL sqfs_s32 istream_read(istream_t *strm, void *data, size_t size); /** * @brief Adjust and refill the internal buffer of an input stream * * @memberof istream_t * * This function resets the buffer offset of an input stream (moving any unread * data up front if it has to) and calls an internal callback of the input * stream to fill the rest of the buffer to the extent possible. * * @param strm A pointer to an input stream. * * @return 0 on success, -1 on failure. */ SQFS_INTERNAL int istream_precache(istream_t *strm); /** * @brief Get the underlying filename of an input stream. * * @memberof istream_t * * @param strm The input stream to get the filename from. * * @return A string holding the underlying filename. */ SQFS_INTERNAL const char *istream_get_filename(istream_t *strm); /** * @brief Skip over a number of bytes in an input stream. * * @memberof istream_t * * @param strm A pointer to an input stream. * @param size The number of bytes to seek forward. * * @return Zero on success, -1 on failure. */ SQFS_INTERNAL int istream_skip(istream_t *strm, sqfs_u64 size); /** * @brief Read data from an input stream and append it to an output stream * * @memberof ostream_t * * @param out A pointer to an output stream to append to. * @param in A pointer to an input stream to read from. * @param size The number of bytes to copy over. * * @return The number of bytes copied on success, -1 on failure, * 0 on end-of-file. */ SQFS_INTERNAL sqfs_s32 ostream_append_from_istream(ostream_t *out, istream_t *in, sqfs_u32 size); /** * @brief Resolve a compressor name to an ID. * * @param name A compressor name. * * @return A compressor ID on success, -1 on failure. */ SQFS_INTERNAL int fstream_compressor_id_from_name(const char *name); /** * @brief Resolve a id to a compressor name. * * @param id A compressor ID. * * @return A compressor name on success, NULL on failure. */ SQFS_INTERNAL const char *fstream_compressor_name_from_id(int id); /** * @brief Check if support for a given compressor has been built in. * * @param id A compressor ID. * * @return True if the compressor is supported, false if not. */ SQFS_INTERNAL bool fstream_compressor_exists(int id); #ifdef __cplusplus } #endif #endif /* FSTREAM_H */ squashfs-tools-ng-1.1.3/include/fstree.h000066400000000000000000000177671410627516300202240ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef FSTREE_H #define FSTREE_H #include "config.h" #include #include #include #include #include "sqfs/predef.h" #include "compat.h" enum { DIR_SCAN_KEEP_TIME = 0x01, DIR_SCAN_ONE_FILESYSTEM = 0x02, DIR_SCAN_NO_RECURSION = 0x04, DIR_SCAN_NO_SOCK = 0x0008, DIR_SCAN_NO_SLINK = 0x0010, DIR_SCAN_NO_FILE = 0x0020, DIR_SCAN_NO_BLK = 0x0040, DIR_SCAN_NO_DIR = 0x0080, DIR_SCAN_NO_CHR = 0x0100, DIR_SCAN_NO_FIFO = 0x0200, }; #define FSTREE_MODE_HARD_LINK (0) #define FSTREE_MODE_HARD_LINK_RESOLVED (1) typedef struct tree_node_t tree_node_t; typedef struct file_info_t file_info_t; typedef struct dir_info_t dir_info_t; typedef struct fstree_t fstree_t; /* Optionally used by fstree_from_dir and fstree_from_subdir to execute custom actions for each discovered node. If it returns a value > 0, the new node is discarded, if < 0, scanning is aborted and returns a failure status. */ typedef int (*scan_node_callback)(void *user, fstree_t *fs, tree_node_t *node); /* Additional meta data stored in a tree_node_t for regular files. */ struct file_info_t { /* Linked list pointer for files in fstree_t */ file_info_t *next; /* Path to the input file. */ char *input_file; sqfs_inode_generic_t *inode; }; /* Additional meta data stored in a tree_node_t for directories */ struct dir_info_t { /* Linked list head for children in the directory */ tree_node_t *children; /* Set to true for implicitly generated directories. */ bool created_implicitly; /* Used by recursive tree walking code to avoid hard link loops */ bool visited; }; /* A node in a file system tree */ struct tree_node_t { /* Parent directory children linked list pointer. */ tree_node_t *next; /* Root node has this set to NULL. */ tree_node_t *parent; /* For the root node, this points to an empty string. */ char *name; sqfs_u32 xattr_idx; sqfs_u32 uid; sqfs_u32 gid; sqfs_u32 inode_num; sqfs_u32 mod_time; sqfs_u16 mode; sqfs_u16 link_count; /* SquashFS inode refernce number. 32 bit offset of the meta data block start (relative to inode table start), shifted left by 16 and ored with a 13 bit offset into the uncompressed meta data block. Generated on the fly when writing inodes. */ sqfs_u64 inode_ref; /* Type specific data. "target" pointer is into payload area below. */ union { dir_info_t dir; file_info_t file; char *target; sqfs_u64 devno; tree_node_t *target_node; } data; sqfs_u8 payload[]; }; /* Encapsulates a file system tree */ struct fstree_t { struct stat defaults; size_t unique_inode_count; /* flat array of all nodes that have an inode number */ tree_node_t **inodes; tree_node_t *root; /* linear linked list of all regular files */ file_info_t *files; }; /* Initializing means copying over the default values and creating a root node. On error, an error message is written to stderr. The string `defaults` can specify default attributes (mode, uid, gid, mtime) as a comma separated list of key value paris (=[,...]). The string is passed to getsubopt and will be altered. Returns 0 on success. */ int fstree_init(fstree_t *fs, char *defaults); void fstree_cleanup(fstree_t *fs); /* Create a tree node from a struct stat, node name and extra data. For symlinks, the extra part is interpreted as target. For regular files, it is interpreted as input path (can be NULL). The name doesn't have to be null terminated, a length has to be specified. This function does not print anything to stderr, instead it sets an appropriate errno value. The resulting node can be freed with a single free() call. */ tree_node_t *fstree_mknode(tree_node_t *parent, const char *name, size_t name_len, const char *extra, const struct stat *sb); /* Add a node to an fstree at a specific path. If some components of the path don't exist, they are created as directories with default permissions, like mkdir -p would, and marked as implcitily created. A subsequent call that tries to create an existing tree node will fail, except if the target is an implicitly created directory node and the call tries to create it as a directory (this will simply overwrite the permissions and ownership). The implicitly created flag is then cleared. Subsequent attempts to create an existing directory again will then also fail. This function does not print anything to stderr, instead it sets an appropriate errno value. Internally it uses fstree_mknode to create the node. */ tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, const struct stat *sb, const char *extra); /* Parses the file format accepted by gensquashfs and produce a file system tree from it. File input paths are interpreted as relative to the current working directory. On failure, an error report with filename and line number is written to stderr. Returns 0 on success. */ int fstree_from_file(fstree_t *fs, const char *filename, const char *basepath); /* This function performs all the necessary post processing steps on the file system tree, i.e. recursively sorting all directory entries by name, allocating inode numbers, resolving hard links and stringing all files nodes together into a linked list. The total inode count is stored in unique_inode_count. The head of the file list is pointed to by fs->files. The "inodes" array is allocated and each node that has an inode number is mapped into the array at index inode_num - 1. Returns 0 on success, prints to stderr on failure. */ int fstree_post_process(fstree_t *fs); /* Generate a string holding the full path of a node. Returned string must be freed. Returns NULL on failure and sets errno. */ char *fstree_get_path(tree_node_t *node); /* Resolve a path to a tree node. Returns NULL on failure and sets errno. If "create_implicitly" is set to true, the function acts essentially like mkdir_p if part of the path doesn't exist yet. If "stop_at_parent" is true, the function stops at the last component and returns the parent or would-be-parent of the last path component, but doesn't check if the last path component exists or not. */ tree_node_t *fstree_get_node_by_path(fstree_t *fs, tree_node_t *root, const char *path, bool create_implicitly, bool stop_at_parent); /* Convert back to forward slashed, remove all preceeding and trailing slashes, collapse all sequences of slashes, remove all path components that are '.' and returns failure state if one of the path components is '..'. Returns 0 on success. */ int canonicalize_name(char *filename); /* Returns true if a given filename is sane, false if it is not (e.g. contains slashes or it is equal to '.' or '..'). If check_os_specific is true, this also checks if the filename contains a character, or is equal to a name, that is black listed on the current OS. E.g. on Windows, a file named "COM0" or "AUX" is a no-no. */ bool is_filename_sane(const char *name, bool check_os_specific); /* Add a hard link node. Returns NULL on failure and sets errno. */ tree_node_t *fstree_add_hard_link(fstree_t *fs, const char *path, const char *target); /* Resolve a hard link node and replace it with a direct pointer to the target. Returns 0 on success. On failure, errno is set. */ int fstree_resolve_hard_link(fstree_t *fs, tree_node_t *node); /* Recursively scan a directory to build a file system tree. Returns 0 on success, prints to stderr on failure. */ int fstree_from_dir(fstree_t *fs, tree_node_t *root, const char *path, scan_node_callback cb, void *user, unsigned int flags); /* Same as fstree_from_dir, but scans a sub-directory inside the specified path. Returns 0 on success, prints to stderr on failure. */ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, const char *path, const char *subdir, scan_node_callback cb, void *user, unsigned int flags); #endif /* FSTREE_H */ squashfs-tools-ng-1.1.3/include/hash_table.h000066400000000000000000000062511410627516300210100ustar00rootroot00000000000000/* * Copyright © 2009,2012 Intel Corporation * * 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 (including the next * paragraph) 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. * * Authors: * Eric Anholt * */ #ifndef _HASH_TABLE_H #define _HASH_TABLE_H #include #include #include #include "sqfs/predef.h" struct hash_entry { sqfs_u32 hash; const void *key; void *data; }; struct hash_table { struct hash_entry *table; sqfs_u32 (*key_hash_function)(void *user, const void *key); bool (*key_equals_function)(void *user, const void *a, const void *b); const void *deleted_key; void *user; sqfs_u32 size; sqfs_u32 rehash; sqfs_u64 size_magic; sqfs_u64 rehash_magic; sqfs_u32 max_entries; sqfs_u32 size_index; sqfs_u32 entries; sqfs_u32 deleted_entries; }; SQFS_INTERNAL struct hash_table * hash_table_create(sqfs_u32 (*key_hash_function)(void *user, const void *key), bool (*key_equals_function)(void *user, const void *a, const void *b)); SQFS_INTERNAL struct hash_table * hash_table_clone(struct hash_table *src); SQFS_INTERNAL void hash_table_destroy(struct hash_table *ht, void (*delete_function)(struct hash_entry *entry)); SQFS_INTERNAL struct hash_entry * hash_table_insert_pre_hashed(struct hash_table *ht, sqfs_u32 hash, const void *key, void *data); SQFS_INTERNAL struct hash_entry * hash_table_search_pre_hashed(struct hash_table *ht, sqfs_u32 hash, const void *key); SQFS_INTERNAL struct hash_entry *hash_table_next_entry(struct hash_table *ht, struct hash_entry *entry); /** * This foreach function is safe against deletion (which just replaces * an entry's data with the deleted marker), but not against insertion * (which may rehash the table, making entry a dangling pointer). */ #define hash_table_foreach(ht, entry) \ for (struct hash_entry *entry = hash_table_next_entry(ht, NULL); \ entry != NULL; \ entry = hash_table_next_entry(ht, entry)) #endif /* _HASH_TABLE_H */ squashfs-tools-ng-1.1.3/include/mempool.h000066400000000000000000000011131410627516300203560ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * mempool.h * * Copyright (C) 2021 David Oberhollenzer */ #ifndef MEMPOOL_H #define MEMPOOL_H #include "compat.h" #include "sqfs/predef.h" typedef struct mem_pool_t mem_pool_t; #ifdef __cplusplus extern "C" { #endif SQFS_INTERNAL mem_pool_t *mem_pool_create(size_t obj_size); SQFS_INTERNAL void mem_pool_destroy(mem_pool_t *mem); SQFS_INTERNAL void *mem_pool_allocate(mem_pool_t *mem); SQFS_INTERNAL void mem_pool_free(mem_pool_t *mem, void *ptr); #ifdef __cplusplus } #endif #endif /* MEMPOOL_H */ squashfs-tools-ng-1.1.3/include/rbtree.h000066400000000000000000000026641410627516300202050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * rbtree.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef SQFS_RBTREE_H #define SQFS_RBTREE_H #include "config.h" #include "sqfs/predef.h" #include "mempool.h" #include "compat.h" #include typedef struct rbtree_node_t { struct rbtree_node_t *left; struct rbtree_node_t *right; sqfs_u32 value_offset; sqfs_u32 is_red : 1; sqfs_u32 pad0 : 31; sqfs_u8 data[]; } rbtree_node_t; typedef struct rbtree_t { rbtree_node_t *root; #ifndef NO_CUSTOM_ALLOC mem_pool_t *pool; #endif int (*key_compare)(const void *ctx, const void *lhs, const void *hrs); size_t key_size; size_t key_size_padded; size_t value_size; void *key_context; } rbtree_t; static SQFS_INLINE void *rbtree_node_key(rbtree_node_t *n) { return n->data; } static SQFS_INLINE void *rbtree_node_value(rbtree_node_t *n) { return n->data + n->value_offset; } SQFS_INTERNAL int rbtree_init(rbtree_t *tree, size_t keysize, size_t valuesize, int(*key_compare)(const void *, const void *, const void *)); SQFS_INTERNAL void rbtree_cleanup(rbtree_t *tree); SQFS_INTERNAL int rbtree_copy(const rbtree_t *tree, rbtree_t *out); SQFS_INTERNAL int rbtree_insert(rbtree_t *tree, const void *key, const void *value); SQFS_INTERNAL rbtree_node_t *rbtree_lookup(const rbtree_t *tree, const void *key); #ifdef __cplusplus } #endif #endif /* TOOLS_RBTREE_H */ squashfs-tools-ng-1.1.3/include/simple_writer.h000066400000000000000000000037511410627516300216050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * simple_writer.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef SIMPLE_WRITER_H #define SIMPLE_WRITER_H #include "config.h" #include "sqfs/block_processor.h" #include "sqfs/block_writer.h" #include "sqfs/xattr_writer.h" #include "sqfs/meta_writer.h" #include "sqfs/frag_table.h" #include "sqfs/dir_writer.h" #include "sqfs/compressor.h" #include "sqfs/id_table.h" #include "sqfs/error.h" #include "sqfs/io.h" #include "fstree.h" typedef struct { const char *filename; sqfs_block_writer_t *blkwr; sqfs_frag_table_t *fragtbl; sqfs_block_processor_t *data; sqfs_dir_writer_t *dirwr; sqfs_meta_writer_t *dm; sqfs_meta_writer_t *im; sqfs_compressor_t *cmp; sqfs_compressor_t *uncmp; sqfs_id_table_t *idtbl; sqfs_file_t *outfile; sqfs_super_t super; fstree_t fs; sqfs_xattr_writer_t *xwr; } sqfs_writer_t; typedef struct { const char *filename; char *fs_defaults; char *comp_extra; size_t block_size; size_t devblksize; size_t max_backlog; size_t num_jobs; int outmode; SQFS_COMPRESSOR comp_id; bool exportable; bool no_xattr; bool quiet; } sqfs_writer_cfg_t; #ifdef __cplusplus extern "C" { #endif void sqfs_writer_cfg_init(sqfs_writer_cfg_t *cfg); int sqfs_writer_init(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *wrcfg); int sqfs_writer_finish(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *cfg); void sqfs_writer_cleanup(sqfs_writer_t *sqfs, int status); /* High level helper function to serialize an entire file system tree to a squashfs inode table and directory table. The super block is update accordingly. The function internally creates two meta data writers and uses meta_writer_write_inode to serialize the inode table of the fstree. Returns 0 on success. Prints error messages to stderr on failure. The filename is used to prefix error messages. */ int sqfs_serialize_fstree(const char *filename, sqfs_writer_t *wr); #ifdef __cplusplus } #endif #endif /* SIMPLE_WRITER_H */ squashfs-tools-ng-1.1.3/include/sqfs/000077500000000000000000000000001410627516300175155ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/include/sqfs/block.h000066400000000000000000000100421410627516300207550ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * block.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_BLOCK_H #define SQFS_BLOCK_H #include "sqfs/predef.h" /** * @file block.h * * @brief Contains on-disk data structures for data block management. */ #define SQFS_META_BLOCK_SIZE 8192 #define SQFS_IS_BLOCK_COMPRESSED(size) (((size) & (1 << 24)) == 0) #define SQFS_ON_DISK_BLOCK_SIZE(size) ((size) & ((1 << 24) - 1)) #define SQFS_IS_SPARSE_BLOCK(size) (SQFS_ON_DISK_BLOCK_SIZE(size) == 0) /** * @struct sqfs_fragment_t * * @brief Data structure that makes up the fragment table entries. */ struct sqfs_fragment_t { /** * @brief Location of the fragment block on-disk. */ sqfs_u64 start_offset; /** * @brief Size of the fragment block in bytes. */ sqfs_u32 size; /** * @brief Unused. Always initialize this to 0. */ sqfs_u32 pad0; }; /** * @enum SQFS_BLK_FLAGS * * @brief Generic flags that tell the processor what to do with a block and * flags that the processor sets when it is done with a block. */ typedef enum { /** * @brief Only calculate checksum, do NOT compress the data. * * If set, the blocks of a file will not be compressed by the * @ref sqfs_block_processor_t. */ SQFS_BLK_DONT_COMPRESS = 0x0001, /** * @brief Align the block on disk to device block size. * * If set, the @ref sqfs_block_processor_t will add padding before the * first block of the affected file and after the last block. */ SQFS_BLK_ALIGN = 0x0002, /** * @brief Don't add the tail end of a file to a fragment block. * * If set, the @ref sqfs_block_processor_t will always generate a final * block for a file, even if it is truncated. It will not add the * tail end to a fragment block. */ SQFS_BLK_DONT_FRAGMENT = 0x0004, /** * @brief Surpress deduplication. * * If set on fragments or the last block of a file, it is always * written to disk, even if a duplicate copy already exists. */ SQFS_BLK_DONT_DEDUPLICATE = 0x0008, /** * @brief Supress sparse block detection. * * If set, sparse blocks are no longer checked and flagged as such and * are instead processed like normal blocks. */ SQFS_BLK_IGNORE_SPARSE = 0x0010, /** * @brief Don't compute block data checksums. */ SQFS_BLK_DONT_HASH = 0x0020, /** * @brief Set by the @ref sqfs_block_processor_t if it determines a * block of a file to be sparse, i.e. only zero bytes. */ SQFS_BLK_IS_SPARSE = 0x0400, /** * @brief Set by the @ref sqfs_block_processor_t on the first * block of a file. */ SQFS_BLK_FIRST_BLOCK = 0x0800, /** * @brief Set by the @ref sqfs_block_processor_t on the last * block of a file. */ SQFS_BLK_LAST_BLOCK = 0x1000, /** * @brief Set by the @ref sqfs_block_processor_t to indicate that a * block is a tail end of a file and the block. */ SQFS_BLK_IS_FRAGMENT = 0x2000, /** * @brief Set by the @ref sqfs_block_processor_t on fragment blocks that * it generates. */ SQFS_BLK_FRAGMENT_BLOCK = 0x4000, /** * @brief Set by @ref sqfs_block_processor_t if the block was * actually compressed. */ SQFS_BLK_IS_COMPRESSED = 0x8000, /** * @brief The combination of all flags that are user settable. */ SQFS_BLK_USER_SETTABLE_FLAGS = 0x003F, SQFS_BLK_FLAGS_ALL = 0xFC3F, } SQFS_BLK_FLAGS; #endif /* SQFS_BLOCK_H */ squashfs-tools-ng-1.1.3/include/sqfs/block_processor.h000066400000000000000000000313621410627516300230640ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * block_processor.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SFQS_BLOCK_PROCESSOR_H #define SFQS_BLOCK_PROCESSOR_H #include "sqfs/predef.h" /** * @file block_processor.h * * @brief Contains declarations for the data block processor. */ /** * @struct sqfs_block_processor_t * * @brief Abstracts generating of file data and fragment blocks. * * @implements sqfs_object_t * * This data structure provides a simple begin/append/end interface * to generate file data blocks (see @ref sqfs_block_processor_begin_file, * @ref sqfs_block_processor_append and @ref sqfs_block_processor_end * respectively). * * Internally it takes care of partitioning data in the correct block sizes, * adding tail-ens to fragment blocks, compressing the data, deduplicating data * and finally writing it to disk. * * This object is not copyable, i.e. @ref sqfs_copy will always return NULL. */ /** * @struct sqfs_block_processor_stats_t * * @brief Used to store runtime statistics about * the @ref sqfs_block_processor_t. */ struct sqfs_block_processor_stats_t { /** * @brief Holds the size of the structure. * * If a later version of libsquashfs expands this structure, the value * of this field can be used to check at runtime whether the newer * fields are avaialable or not. */ size_t size; /** * @brief Total number of bytes fed into the front end API. */ sqfs_u64 input_bytes_read; /** * @brief Total number of bytes sent down to the block processor. * * This is the sum of generated, compressed blocks, including blocks * that were possibly deduplicated by the block writer and not * counting padding that the block writer may have added. */ sqfs_u64 output_bytes_generated; /** * @brief Total number of data blocks produced. */ sqfs_u64 data_block_count; /** * @brief Total number of fragment blocks produced. */ sqfs_u64 frag_block_count; /** * @brief Total number of sparse blocks encountered. */ sqfs_u64 sparse_block_count; /** * @brief Total number of tail-end fragments produced. * * This number also includes the fragments that have later been * eliminated by deduplication. */ sqfs_u64 total_frag_count; /** * @brief Total number of tail-end fragments actually stored in * fragment blocks. * * This number does not include the fragments that have been * eliminated by deduplication. */ sqfs_u64 actual_frag_count; }; /** * @struct sqfs_block_processor_desc_t * * @brief Encapsulates a description for an @ref sqfs_block_processor_t * * An instance of this struct is used by @ref sqfs_block_processor_create_ex to * instantiate block processor objects. */ struct sqfs_block_processor_desc_t { /** * @brief Holds the size of the structure. * * If a later version of libsquashfs expands this structure, the value * of this field can be used to check at runtime whether the newer * fields are avaialable or not. * * If @ref sqfs_block_processor_create_ex is given a struct whose size * it does not recognize, it returns @ref SQFS_ERROR_ARG_INVALID. */ sqfs_u32 size; /** * @brief The maximum size of a data block. */ sqfs_u32 max_block_size; /** * @brief The number of worker threads to create. */ sqfs_u32 num_workers; /** * @brief The maximum number of blocks currently in flight. * * When trying to add more, enqueueing blocks until the * in-flight block count drops below the threshold. */ sqfs_u32 max_backlog; /** * @brief A pointer to a compressor. * * If multiple worker threads are used, the deep copy function of the * compressor is used to create several instances that don't interfere * with each other. This means, the compressor implementation must be * able to create copies of itself that can be used independendly and * concurrently. */ sqfs_compressor_t *cmp; /** * @brief A block writer to send to finished blocks to. */ sqfs_block_writer_t *wr; /** * @brief A fragment table to use for storing block locations. */ sqfs_frag_table_t *tbl; /** * @brief Pointer to a file to read back fragment blocks from. * * If file and uncmp are not NULL, the file is used to read back * fragment blocks during fragment deduplication and verify possible * matches. If either of them are NULL, the deduplication relies on * fragment size and hash alone. */ sqfs_file_t *file; /** * @brief A pointer to a compressor the decompresses data. * * @copydoc file */ sqfs_compressor_t *uncmp; }; #ifdef __cplusplus extern "C" { #endif /** * @brief Create a data block processor. * * @memberof sqfs_block_processor_t * * @param max_block_size The maximum size of a data block. Required for the * internal scratch buffer used for compressing data. * @param cmp A pointer to a compressor. If multiple worker threads are used, * the deep copy function of the compressor is used to create * several instances that don't interfere with each other. * @param num_workers The number of worker threads to create. * @param max_backlog The maximum number of blocks currently in flight. When * trying to add more, enqueueing blocks until the in-flight * block count drops below the threshold. * @param wr A block writer to send to finished blocks to. * @param tbl A fragment table to use for storing fragment and fragment block * locations. * * @return A pointer to a block processor object on success, NULL on allocation * failure or on failure to create and initialize the worker threads. */ SQFS_API sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, sqfs_compressor_t *cmp, unsigned int num_workers, size_t max_backlog, sqfs_block_writer_t *wr, sqfs_frag_table_t *tbl); /** * @brief Create a data block processor. * * @memberof sqfs_block_processor_t * * @param desc A pointer to an extensible structure that holds the description * of the block processor. * @param out On success, returns the pointer to the newly created block * processor object. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, sqfs_block_processor_t **out); /** * @brief Start writing a file. * * @memberof sqfs_block_processor_t * * After calling this function, call @ref sqfs_block_processor_append * repeatedly to add data to the file. Finally * call @ref sqfs_block_processor_end_file when you * are done. After writing all files, use @ref sqfs_block_processor_finish to * wait until all blocks that are still in flight are done and written to disk. * * The specified pointer to an inode pointer is kept internally and updated with * the compressed block sizes and final destinations of the file and possible * fragment. Since the inode may have to be grown to larger size, the actual * inode pointer can change over time. Furthermore, since there can still be * blocks in-flight even after calling @ref sqfs_block_processor_end_file, the * data in the inode and the value of the pointer may still change. The only * point at which the data writer is guaranteed to not touch them anymore is * after @ref sqfs_block_processor_sync or @ref sqfs_block_processor_finish has * returned. * * @param proc A pointer to a data writer object. * @param inode An optional pointer to a pointer to an inode. If not NULL, the * block processor creates a file inode and stores a pointer to * it here and keeps updating the inode as the file grows. * @param user An optional user data pointer that is passed to the * the @ref sqfs_block_writer_t for each file data block. * @param flags A combination of @ref SQFS_BLK_FLAGS that can be used to * micro manage how the data is processed. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_block_processor_begin_file(sqfs_block_processor_t *proc, sqfs_inode_generic_t **inode, void *user, sqfs_u32 flags); /** * @brief Append data to the current file. * * @memberof sqfs_block_processor_t * * Call this after @ref sqfs_block_processor_begin_file to add data to a file. * * @param proc A pointer to a data writer object. * @param data A pointer to a buffer to read data from. * @param size How many bytes should be copied out of the given * buffer and written to disk. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_block_processor_append(sqfs_block_processor_t *proc, const void *data, size_t size); /** * @brief Stop writing the current file and flush everything that is * buffered internally. * * @memberof sqfs_block_processor_t * * The counter part to @ref sqfs_block_processor_begin_file. * * Even after calling this, there might still be data blocks in-flight. * Use @ref sqfs_block_processor_finish when you are done writing files to force * the remaining blocks to be processed and written to disk. * * @param proc A pointer to a data writer object. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_block_processor_end_file(sqfs_block_processor_t *proc); /** * @brief Submit a raw block for processing. * * @memberof sqfs_block_processor_t * * This function provides an alternative to the simple file front end to submit * raw data blocks to the processor. It will throw an error if called in between * @ref sqfs_block_processor_begin_file and @ref sqfs_block_processor_end_file. * * The flags aren't sanity checked (besides being a subset * of @ref SQFS_BLK_FLAGS_ALL), so in contrast to the simple file API, you can * shoot yourself in the foot as hard as you want. * * If not specified otherwise through flags, the fragment block/deduplication * and fragment consolidation are still in effect and sparse blocks are * discarded. * * @param proc A pointer to a block processor object. * @param user An optional user data pointer stored with the block and passed on * to the underlying @ref sqfs_block_writer_t. * @param flags A combination of @ref SQFS_BLK_FLAGS that can be used to * micro manage how the data is processed. * @param data The data to write to the block. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_block_processor_submit_block(sqfs_block_processor_t *proc, void *user, sqfs_u32 flags, const void *data, size_t size); /** * @brief Wait for the in-flight data blocks to finish. * * @memberof sqfs_block_processor_t * * @param proc A pointer to a block processor object. * * @return Zero on success, an @ref SQFS_ERROR value on failure. The failure * return value can either be an error encountered during enqueueing, * processing or writing to disk. */ SQFS_API int sqfs_block_processor_sync(sqfs_block_processor_t *proc); /** * @brief Wait for the in-flight data blocks to finish and finally flush the * current fragment block. * * @memberof sqfs_block_processor_t * * This does essentially the same as @ref sqfs_block_processor_sync, but after * syncing, it also flushes the current fragment block, even if it isn't full * yet and waits for it to be completed as well. * * @param proc A pointer to a block processor object. * * @return Zero on success, an @ref SQFS_ERROR value on failure. The failure * return value can either be an error encountered during enqueueing, * processing or writing to disk. */ SQFS_API int sqfs_block_processor_finish(sqfs_block_processor_t *proc); /** * @brief Get accumulated runtime statistics from a block processor * * @memberof sqfs_block_processor_t * * @param proc A pointer to a data writer object. * * @return A pointer to a @ref sqfs_block_processor_stats_t structure. */ SQFS_API const sqfs_block_processor_stats_t *sqfs_block_processor_get_stats(const sqfs_block_processor_t *proc); #ifdef __cplusplus } #endif #endif /* SFQS_BLOCK_PROCESSOR_H */ squashfs-tools-ng-1.1.3/include/sqfs/block_writer.h000066400000000000000000000114701410627516300223570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * block_writer.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_BLOCK_WRITER_H #define SQFS_BLOCK_WRITER_H #include "sqfs/predef.h" /** * @file block_writer.h * * @brief Contains declarations for the @ref sqfs_block_writer_t structure. */ /** * @interface sqfs_block_writer_t * * @implements sqfs_object_t * * @brief Abstracts writing and deduplicating of data and fragment blocks. * * A default reference implementation can be obtaiend * through @ref sqfs_block_writer_create. The default implementation is not * copyable, i.e. @ref sqfs_copy will always return NULL. */ struct sqfs_block_writer_t { sqfs_object_t base; /** * @brief Submit a data block to a block writer. * * @memberof sqfs_block_writer_t * * If the @ref SQFS_BLK_FIRST_BLOCK flag is set, the data block writer * memorizes the starting location and block index of the block. If the * @ref SQFS_BLK_LAST_BLOCK flag is set, it uses those stored locations * to do block deduplication. * * If the flag @ref SQFS_BLK_ALIGN is set in combination with the * @ref SQFS_BLK_FIRST_BLOCK, the file size is padded to a multiple of * the device block size before writing. If it is set together with the * @ref SQFS_BLK_LAST_BLOCK flag, the padding is added afterwards. * * @param wr A pointer to a block writer. * @param user An optional user data pointer. * The @ref sqfs_block_processor_t can be told to pass this * on to the block writer for each block. * @param size The size of the block to write. * @param checksum A 32 bit checksum of the block data. * @param flags A combination of @ref SQFS_BLK_FLAGS flag bits * describing the block. * @arapm data A pointer to the data to write. * @param location Returns the location where the block has been * written. If the @ref SQFS_BLK_LAST_BLOCK flag was set, * deduplication is performed and this returns the (new) location * of the first block instead. * * @return Zero on success, an @ref SQFS_ERROR error on failure. */ int (*write_data_block)(sqfs_block_writer_t *wr, void *user, sqfs_u32 size, sqfs_u32 checksum, sqfs_u32 flags, const sqfs_u8 *data, sqfs_u64 *location); /** * @brief Get the number of blocks actually written to disk. * * @memberof sqfs_block_writer_t * * @param wr A pointer to a block writer. * * @return The number of blocks written, excluding deduplicated blocks. */ sqfs_u64 (*get_block_count)(const sqfs_block_writer_t *wr); }; /** * @enum SQFS_BLOCK_WRITER_FLAGS * * @brief Flags that can be passed to @ref sqfs_block_writer_create */ typedef enum { /** * @brief If set, only compare checksums when deduplicating blocks. * * Since squashfs-tools-ng version 1.1, the default for the block * writer is to compare checksum & size for blocks during deduplication * and then read the potential match back from disk and do a byte for * byte comparison to make absolutely sure they match. * * If this flag is set, the hash & size check is treated as being * sufficient for block deduplication, which does increase performance, * but risks data loss or corruption if a hash collision occours. */ SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY = 0x01, /** * @brief A combination of all valid flags. */ SQFS_BLOCK_WRITER_ALL_FLAGS = 0x01 } SQFS_BLOCK_WRITER_FLAGS; #ifdef __cplusplus extern "C" { #endif /** * @brief Create an instance of a default block writer implementation. * * @memberof sqfs_block_writer_t * * @param file A pointer to a file object that data should be appended to. * @param devblksz The underlying device block size if output data * should be aligned. * @param flags A combination of @ref SQFS_BLOCK_WRITER_FLAGS values. If an * unknown flag is set, creation will fail. * * @return A pointer to a new block writer on success, NULL on failure. */ SQFS_API sqfs_block_writer_t *sqfs_block_writer_create(sqfs_file_t *file, size_t devblksz, sqfs_u32 flags); #ifdef __cplusplus } #endif #endif /* SQFS_BLOCK_WRITER_H */ squashfs-tools-ng-1.1.3/include/sqfs/compressor.h000066400000000000000000000270151410627516300220670ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * compressor.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_COMPRESSOR_H #define SQFS_COMPRESSOR_H #include "sqfs/predef.h" #include "sqfs/super.h" /** * @file compressor.h * * @brief Contains declarations to everything related to data compression. */ /** * @interface sqfs_compressor_t * * @extends sqfs_object_t * * @brief Encapsultes a compressor with a simple interface to compress or * extract chunks of data. */ struct sqfs_compressor_t { sqfs_object_t base; /** * @brief Get the current compressor configuration. * * @param cmp A pointer to the compressor object. * @param cfg A pointer to the object to write the configuration to. */ void (*get_configuration)(const sqfs_compressor_t *cmp, sqfs_compressor_config_t *cfg); /** * @brief Write compressor options to disk if non-default settings * have been used. * * The options are stored in an uncompressed meta data block directly * after the super block. * * @param cmp A pointer to a compressor object. * @param file A file to write to. * * @return The number of bytes written on success, 0 means default * settings are used. A negative value is an @ref SQFS_ERROR * identifier. */ int (*write_options)(sqfs_compressor_t *cmp, sqfs_file_t *file); /** * @brief Read compressor options from disk. * * @param cmp A pointer to a compressor object. * @param file A file to read from. * * @return Zero on success or an @ref SQFS_ERROR value. */ int (*read_options)(sqfs_compressor_t *cmp, sqfs_file_t *file); /** * @brief Compress or uncompress a chunk of data. * * @param cmp A pointer to a compressor object. * @param in A pointer to the input buffer to read from. * @param size The number of bytes to read from the input and compress. * @param out The destination buffer to write the result to. * @param outsize The available space in the destination buffer. * * @return The number of bytes written to the buffer, a negative * value is an @ref SQFS_ERROR value. The value 0 means * the output buffer was too small when extracting or that * the result is larger than the input when compressing. */ sqfs_s32 (*do_block)(sqfs_compressor_t *cmp, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize); }; /** * @struct sqfs_compressor_config_t * * @brief Configuration parameters for instantiating a compressor backend. * * The unused fields MUST be set to 0. The easiest way to do this is by always * clearing the struct using memset before setting anything, or using * @ref sqfs_compressor_config_init to set defaults and then modify the struct * from there. */ struct sqfs_compressor_config_t { /** * @brief An @ref SQFS_COMPRESSOR identifier */ sqfs_u16 id; /** * @brief A combination of @ref SQFS_COMP_FLAG flags. */ sqfs_u16 flags; /** * @brief The intended data block size. */ sqfs_u32 block_size; /** * @brief Compression level. * * Valid range and default value depend on the selected compressor. */ sqfs_u32 level; /** * @brief Backend specific options for fine tuing. */ union { /** * @brief Options for the zlib compressor. */ struct { /** * @brief Deflate window size. Value between 8 and 15. * * Default is 15, i.e. 32k window. */ sqfs_u16 window_size; sqfs_u8 padd0[14]; } gzip; /** * @brief Options for the lzo compressor. */ struct { /** * @brief Which variant of lzo should be used. * * An @ref SQFS_LZO_ALGORITHM value. Default is * @ref SQFS_LZO1X_999, i.e. best compression. */ sqfs_u16 algorithm; sqfs_u8 padd0[14]; } lzo; /** * @brief Options for the LZMA and XZ (LZMA v2) compressors. */ struct { /** * @brief LZMA dictionary size. * * This value must either be a power of two or the sum * of two consecutive powers of two. * * Default is setting this to the same as the * block size. */ sqfs_u32 dict_size; /** * @brief Number of literal context bits. * * How many of the highest bits of the previous * uncompressed byte to take into account when * predicting the bits of the next byte. * * The sum lc + lp must be at MOST 4. Default value of * lc is 3. */ sqfs_u8 lc; /** * @brief Number of literal position bits. * * lp affects what kind of alignment in the uncompressed * data is assumed when encoding bytes. * See pb below for more information about alignment. * * The sum lc + lp must be at MOST 4. Default value of * lp is 0. */ sqfs_u8 lp; /** * @brief Number of position bits. * * This is the log2 of the assumed underlying alignment * of the input data, i.e. pb=0 means single byte * allignment, pb=1 means 16 bit, 2 means 32 bit. * * When the alignment is known, setting pb may reduce * the file size. * * The default value is 2, i.e. 32 bit alignment. */ sqfs_u8 pb; sqfs_u8 padd0[9]; } xz, lzma; sqfs_u64 padd0[2]; } opt; }; /** * @enum SQFS_COMP_FLAG * * @brief Flags for configuring the compressor. */ typedef enum { /** * @brief For LZ4, set this to use high compression mode. */ SQFS_COMP_FLAG_LZ4_HC = 0x0001, SQFS_COMP_FLAG_LZ4_ALL = 0x0001, /** * @brief Tell the LZMAv1 compressor to try the "extreme" option. * * The "extreme" option means that the compressor should try some * strategies that it normally wouldn't, that may drastically increase * compression time, but will not increase the decompressors memory * consumption. */ SQFS_COMP_FLAG_LZMA_EXTREME = 0x0001, SQFS_COMP_FLAG_LZMA_ALL = 0x0001, /** * @brief For XZ, set this to select the x86 BCJ filter. */ SQFS_COMP_FLAG_XZ_X86 = 0x0001, /** * @brief For XZ, set this to select the PowerPC BCJ filter. */ SQFS_COMP_FLAG_XZ_POWERPC = 0x0002, /** * @brief For XZ, set this to select the Itanium BCJ filter. */ SQFS_COMP_FLAG_XZ_IA64 = 0x0004, /** * @brief For XZ, set this to select the ARM BCJ filter. */ SQFS_COMP_FLAG_XZ_ARM = 0x0008, /** * @brief For XZ, set this to select the ARM Thumb BCJ filter. */ SQFS_COMP_FLAG_XZ_ARMTHUMB = 0x0010, /** * @brief For XZ, set this to select the Sparc BCJ filter. */ SQFS_COMP_FLAG_XZ_SPARC = 0x0020, /** * @brief Tell the XZ compressor to try the "extreme" option. */ SQFS_COMP_FLAG_XZ_EXTREME = 0x0100, SQFS_COMP_FLAG_XZ_ALL = 0x013F, /** * @brief For zlib deflate, set this to try the default strategy. */ SQFS_COMP_FLAG_GZIP_DEFAULT = 0x0001, /** * @brief For zlib deflate, set this to try the "filtered" strategy. */ SQFS_COMP_FLAG_GZIP_FILTERED = 0x0002, /** * @brief For zlib deflate, set this to try the huffman only strategy. */ SQFS_COMP_FLAG_GZIP_HUFFMAN = 0x0004, /** * @brief For zlib deflate, set this to try the RLE strategy. */ SQFS_COMP_FLAG_GZIP_RLE = 0x0008, /** * @brief For zlib deflate, set this to try the fixed strategy. */ SQFS_COMP_FLAG_GZIP_FIXED = 0x0010, SQFS_COMP_FLAG_GZIP_ALL = 0x001F, /** * @brief Set this if the compressor should actually extract * instead of compress data. */ SQFS_COMP_FLAG_UNCOMPRESS = 0x8000, SQFS_COMP_FLAG_GENERIC_ALL = 0x8000, } SQFS_COMP_FLAG; /** * @enum SQFS_LZO_ALGORITHM * * @brief The available LZO algorithms. */ typedef enum { SQFS_LZO1X_1 = 0, SQFS_LZO1X_1_11 = 1, SQFS_LZO1X_1_12 = 2, SQFS_LZO1X_1_15 = 3, SQFS_LZO1X_999 = 4, } SQFS_LZO_ALGORITHM; #define SQFS_GZIP_DEFAULT_LEVEL (9) #define SQFS_GZIP_DEFAULT_WINDOW (15) #define SQFS_LZO_DEFAULT_ALG SQFS_LZO1X_999 #define SQFS_LZO_DEFAULT_LEVEL (8) #define SQFS_ZSTD_DEFAULT_LEVEL (15) #define SQFS_GZIP_MIN_LEVEL (1) #define SQFS_GZIP_MAX_LEVEL (9) #define SQFS_LZO_MIN_LEVEL (0) #define SQFS_LZO_MAX_LEVEL (9) #define SQFS_ZSTD_MIN_LEVEL (1) #define SQFS_ZSTD_MAX_LEVEL (22) #define SQFS_GZIP_MIN_WINDOW (8) #define SQFS_GZIP_MAX_WINDOW (15) #define SQFS_XZ_MIN_LEVEL (0) #define SQFS_XZ_MAX_LEVEL (9) #define SQFS_XZ_DEFAULT_LEVEL (6) #define SQFS_XZ_MIN_LC 0 #define SQFS_XZ_MAX_LC 4 #define SQFS_XZ_DEFAULT_LC 3 #define SQFS_XZ_MIN_LP 0 #define SQFS_XZ_MAX_LP 4 #define SQFS_XZ_DEFAULT_LP 0 #define SQFS_XZ_MIN_PB 0 #define SQFS_XZ_MAX_PB 4 #define SQFS_XZ_DEFAULT_PB 2 #define SQFS_LZMA_MIN_LEVEL (0) #define SQFS_LZMA_MAX_LEVEL (9) #define SQFS_LZMA_DEFAULT_LEVEL (5) #define SQFS_LZMA_MIN_LC 0 #define SQFS_LZMA_MAX_LC 4 #define SQFS_LZMA_DEFAULT_LC 3 #define SQFS_LZMA_MIN_LP 0 #define SQFS_LZMA_MAX_LP 4 #define SQFS_LZMA_DEFAULT_LP 0 #define SQFS_LZMA_MIN_PB 0 #define SQFS_LZMA_MAX_PB 4 #define SQFS_LZMA_DEFAULT_PB 2 #define SQFS_LZMA_MIN_DICT_SIZE SQFS_META_BLOCK_SIZE #define SQFS_LZMA_MAX_DICT_SIZE SQFS_MAX_BLOCK_SIZE #define SQFS_XZ_MIN_DICT_SIZE SQFS_META_BLOCK_SIZE #define SQFS_XZ_MAX_DICT_SIZE SQFS_MAX_BLOCK_SIZE #ifdef __cplusplus extern "C" { #endif /** * @brief Initialize a compressor configuration * * The detail configuration options are all initialized to the defaults for * the compressor in question. * * @param cfg A pointer to a compressor configuration to initialize * @param id The compressor id to set. * @param block_size The block size to set. * @param flags The compressor flags to set. * * @return Zero on success, an @ref SQFS_ERROR value if some of the options * don't make sense (e.g. unknown flags are used). */ SQFS_API int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, SQFS_COMPRESSOR id, size_t block_size, sqfs_u16 flags); /** * @brief Create an instance of a compressor implementation. * * If this function returns @ref SQFS_ERROR_UNSUPPORTED, it can mean that * either the compressor is not supported at all by the version of libsquashfs * you are using, or that the specific configuration that has been requested * is not supported (e.g. unknown flags, or the local version can only * uncompress, but not compress). * * @param cfg A pointer to a compressor configuration. * @param out Returns a pointer to the compressor on success. * * @return Zero on success, an SQFS_ERROR code on failure. */ SQFS_API int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); /** * @brief Get the name of a compressor backend from its ID. * * @param id An @ref SQFS_COMPRESSOR identifier. * * @return A string holding the name of the compressor, NULL if the compressor * ID is not known. */ SQFS_API const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id); /** * @brief Get the compressor ID using just the name of the backend. * * @param name The name of the compressor backend. * * @return A positive, @ref SQFS_COMPRESSOR identifier on success * or @ref SQFS_ERROR_UNSUPPORTED if the backend is unknown. */ SQFS_API int sqfs_compressor_id_from_name(const char *name); #ifdef __cplusplus } #endif #endif /* SQFS_COMPRESSOR_H */ squashfs-tools-ng-1.1.3/include/sqfs/data_reader.h000066400000000000000000000121761410627516300221300ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * data_reader.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_DATA_READER_H #define SQFS_DATA_READER_H #include "sqfs/predef.h" /** * @file data_reader.h * * @brief Contains declarations for the @ref sqfs_data_reader_t. */ /** * @struct sqfs_data_reader_t * * @implements sqfs_object_t * * @brief Abstracts access to data blocks stored in a SquashFS image. * * A SquashFS image can contain a series of file data blocks between the * super block and the inode table. Blocks may or may not be compressed. * Data chunks that are smaller than the block size indicated by the super * block (such as the final chunk of a file or an entire file that is smaller * than a signle block) can be grouped in a single fragment block. * * Regular file inodes referre to the location of the first block and store a * sequence of block sizes for all consequitve blocks, as well as a fragment * index and fragment offset which is resolved through a fragment table. * * The data reader abstracts all of this away in a simple interface that allows * reading file data through an inode description and a location in the file. */ #ifdef __cplusplus extern "C" { #endif /** * @brief Create a data reader instance. * * @memberof sqfs_data_reader_t * * @param file A file interface through which to access the * underlying filesystem image. * @param block_size The data block size from the super block. * @param cmp A compressor to use for uncompressing blocks read from disk. * @param flags Currently must be 0 or the function will fail. * * @return A pointer to a new data reader object. NULL means * allocation failure. */ SQFS_API sqfs_data_reader_t *sqfs_data_reader_create(sqfs_file_t *file, size_t block_size, sqfs_compressor_t *cmp, sqfs_u32 flags); /** * @brief Read and decode the fragment table from disk. * * @memberof sqfs_data_reader_t * * @param data A pointer to a data reader object. * @param super A pointer to the super block. * * @return Zero on succcess, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data, const sqfs_super_t *super); /** * @brief Get the tail end of a file. * * @memberof sqfs_data_reader_t * * @param data A pointer to a data reader object. * @param inode A pointer to the inode describing the file. * @param size Returns the size of the data read. * @param out Returns a pointer to the raw data that must be * released using @ref sqfs_free. * * @return Zero on succcess, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, size_t *size, sqfs_u8 **out); /** * @brief Get a full sized data block of a file by block index. * * @memberof sqfs_data_reader_t * * @param data A pointer to a data reader object. * @param inode A pointer to the inode describing the file. * @param index The block index in the inodes block list. * @param size Returns the size of the data read. * @param out Returns a pointer to the raw data that must be * released using @ref sqfs_free. * * @return Zero on succcess, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_data_reader_get_block(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, size_t index, size_t *size, sqfs_u8 **out); /** * @brief A simple UNIX-read-like function to read data from a file. * * @memberof sqfs_data_reader_t * * This function acts like the read system call in a Unix-like OS. It takes * care of reading accross data blocks and fragment internally, using a * data and fragment block cache. * * @param data A pointer to a data reader object. * @param inode A pointer to the inode describing the file. * @param offset An arbitrary byte offset into the uncompressed file. * @param buffer Returns the data read from the file. * @param size The number of uncompressed bytes to read from the given offset. * * @return The number of bytes read on succcess, zero if attempting to read * past the end of the file and a negative @ref SQFS_ERROR value * on failure. */ SQFS_API sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, sqfs_u64 offset, void *buffer, sqfs_u32 size); #ifdef __cplusplus } #endif #endif /* SQFS_DATA_READER_H */ squashfs-tools-ng-1.1.3/include/sqfs/dir.h000066400000000000000000000065751410627516300204610ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * dir.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_DIR_H #define SQFS_DIR_H #include "sqfs/predef.h" /** * @file dir.h * * @brief Contains on-disk data structures for the directory table and * declarations for the @ref sqfs_dir_writer_t. */ #define SQFS_MAX_DIR_ENT 256 /** * @struct sqfs_dir_header_t * * @brief On-disk data structure of a directory header * * See @ref sqfs_dir_writer_t for an overview on how SquashFS stores * directories on disk. */ struct sqfs_dir_header_t { /** * @brief The number of @ref sqfs_dir_entry_t entries that are * following. * * This value is stored off by one and the total count must not * exceed 256. */ sqfs_u32 count; /** * @brief The location of the meta data block containing the inodes for * the entries that follow, relative to the start of the inode * table. */ sqfs_u32 start_block; /** * @brief The inode number of the first entry. */ sqfs_u32 inode_number; }; /** * @struct sqfs_dir_entry_t * * @brief On-disk data structure of a directory entry. Many of these * follow a single @ref sqfs_dir_header_t. * * See @ref sqfs_dir_writer_t for an overview on how SquashFS stores * directories on disk. */ struct sqfs_dir_entry_t { /** * @brief An offset into the uncompressed meta data block containing * the coresponding inode. */ sqfs_u16 offset; /** * @brief Signed difference of the inode number from the one * in the @ref sqfs_dir_header_t. */ sqfs_s16 inode_diff; /** * @brief The @ref SQFS_INODE_TYPE value for the inode that this * entry represents. */ sqfs_u16 type; /** * @brief The size of the entry name * * This value is stored off-by-one. */ sqfs_u16 size; /** * @brief The name of the directory entry (no trailing null-byte). */ sqfs_u8 name[]; }; /** * @struct sqfs_dir_index_t * * @brief On-disk data structure of a directory index. A series of those * can follow an @ref sqfs_inode_dir_ext_t. * * See @ref sqfs_dir_writer_t for an overview on how SquashFS stores * directories on disk. */ struct sqfs_dir_index_t { /** * @brief Linear byte offset into the decompressed directory listing. */ sqfs_u32 index; /** * @brief Location of the meta data block, relative to the directory * table start. */ sqfs_u32 start_block; /** * @brief Size of the name of the first entry after the header. * * This value is stored off-by-one. */ sqfs_u32 size; /** * @brief Name of the name of the first entry after the header. * * No trailing null-byte. */ sqfs_u8 name[]; }; #endif /* SQFS_DIR_H */ squashfs-tools-ng-1.1.3/include/sqfs/dir_reader.h000066400000000000000000000236721410627516300220000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * dir_reader.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_DIR_READER_H #define SQFS_DIR_READER_H #include "sqfs/predef.h" /** * @file dir_reader.h * * @brief Contains declarations for the @ref sqfs_dir_reader_t */ /** * @struct sqfs_dir_reader_t * * @implements sqfs_object_t * * @brief Abstracts reading of directory entries * * SquashFS stores directory listings and inode structures separated from * each other in meta data blocks. * * The sqfs_dir_reader_t abstracts access to the filesystem tree in a SquashFS * through a fairly simple interface. It keeps two meta data readers internally * for reading directory listings and inodes. Externally, it offers a few * simple functions for iterating over the contents of a directory that * completely take care of fetching/decoding headers and sifting through the * multi level hierarchie used for storing them on disk. * * See @ref sqfs_dir_writer_t for an overview on how directory entries are * stored in SquashFS. * * The reader also abstracts easy access to the underlying inodes, allowing * direct access to the inode referred to by a directory entry. */ /** * @enum SQFS_TREE_FILTER_FLAGS * * @brief Filter flags for @ref sqfs_dir_reader_get_full_hierarchy */ typedef enum { /** * @brief Omit device special files from the final tree. */ SQFS_TREE_NO_DEVICES = 0x01, /** * @brief Omit socket files from the final tree. */ SQFS_TREE_NO_SOCKETS = 0x02, /** * @brief Omit named pipes from the final tree. */ SQFS_TREE_NO_FIFO = 0x04, /** * @brief Omit symbolic links from the final tree. */ SQFS_TREE_NO_SLINKS = 0x08, /** * @brief Omit empty directories from the final tree. * * If a directory is not empty on-disk, but ends up empty after * applying all the other filter rules, it is also omitted. */ SQFS_TREE_NO_EMPTY = 0x10, /** * @brief Do not recurse into sub directories. * * If the start node is a directory, the tree deserializer will still * recurse into it, but it will not go beyond that. */ SQFS_TREE_NO_RECURSE = 0x20, /** * @brief Store the list of parent nodes all the way to the target node * * When traversing towards the selected node, also collect the chain * of parent nodes with the subtree stored at the end. */ SQFS_TREE_STORE_PARENTS = 0x40, SQFS_TREE_ALL_FLAGS = 0x7F, } SQFS_TREE_FILTER_FLAGS; /** * @struct sqfs_tree_node_t * * @brief Encapsulates a node in the filesystem tree read by * @ref sqfs_dir_reader_get_full_hierarchy. */ struct sqfs_tree_node_t { /** * @brief Pointer to parent, NULL for the root node */ sqfs_tree_node_t *parent; /** * @brief For directories, a linked list of children. */ sqfs_tree_node_t *children; /** * @brief Linked list next pointer for children list. */ sqfs_tree_node_t *next; /** * @brief Inode representing this element in the tree. */ sqfs_inode_generic_t *inode; /** * @brief Resolved 32 bit user ID from the inode */ sqfs_u32 uid; /** * @brief Resolved 32 bit group ID from the inode */ sqfs_u32 gid; /** * @brief null-terminated entry name. */ sqfs_u8 name[]; }; #ifdef __cplusplus extern "C" { #endif /** * @brief Create a directory reader. * * @memberof sqfs_dir_reader_t * * @param super A pointer to the super block. Kept internally an used for * resolving table positions. * @param cmp A compressor to use for unpacking meta data blocks. * @param file The input file to read from. * @param flags Currently must be zero or the function fails. * * @return A new directory reader on success, NULL on allocation failure. */ SQFS_API sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, sqfs_compressor_t *cmp, sqfs_file_t *file, sqfs_u32 flags); /** * @brief Navigate a directory reader to the location of a directory * represented by an inode. * * @memberof sqfs_dir_reader_t * * This function seeks to the meta data block containing the directory * listing that the given inode referes to and resets the internal state. * After that, consequtive cals to @ref sqfs_dir_reader_read can be made * to iterate over the directory contents. * * @param rd A pointer to a directory reader. * @param inode An directory or extended directory inode. * @param flags Currently must be zero or the function fails. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *inode, sqfs_u32 flags); /** * @brief Reset a directory reader back to the beginning of the listing. * * @memberof sqfs_dir_reader_t * * @param rd A pointer to a directory reader. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd); /** * @brief Seek through the current directory listing to locate an * entry by name. * * @memberof sqfs_dir_reader_t * * @param rd A pointer to a directory reader. * @param name The name of the entry to find. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name); /** * @brief Read a directory entry and advance the internal position indicator * to the next one. * * @memberof sqfs_dir_reader_t * * Call this function repeatedly to iterate over a directory listing. It * returns a positive number to indicate that it couldn't fetch any more data * because the end of the listing was reached. A negative value indicates an * error. * * After calling this function, you can use @ref sqfs_dir_reader_get_inode to * read the full inode structure that the current entry referes to. * * @param rd A pointer to a directory reader. * @param out Returns a pointer to a directory entry on success that can be * freed with a single @ref sqfs_free call. * * @return Zero on success, an @ref SQFS_ERROR value on failure, a positive * number if the end of the current directory listing has been reached. */ SQFS_API int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out); /** * @brief Read the inode that the current directory entry points to. * * @memberof sqfs_dir_reader_t * * @param rd A pointer to a directory reader. * @param out Returns a pointer to a generic inode that can be freed with a * single @ref sqfs_free call. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, sqfs_inode_generic_t **inode); /** * @brief Read the root inode using the location given by the super block. * * @memberof sqfs_dir_reader_t * * @param rd A pointer to a directory reader. * @param out Returns a pointer to a generic inode that can be freed with a * single @ref sqfs_free call. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, sqfs_inode_generic_t **inode); /** * @brief Find an inode through path traversal starting from the root or a * given node downwards. * * @memberof sqfs_dir_reader_t * * @param rd A pointer to a directory reader. * @param start If not NULL, path traversal starts at this node downwards. If * set to NULL, start at the root node. * @param path A path to resolve into an inode. Forward or backward slashes can * be used to separate path components. Resolving '.' or '..' is * not supported. * @param out Returns a pointer to a generic inode that can be freed with a * single @ref sqfs_free call. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *start, const char *path, sqfs_inode_generic_t **out); /** * @brief High level helper function for deserializing the entire file system * hierarchy into an in-memory tree structure. * * @memberof sqfs_dir_reader_t * * This function internally navigates to a specified inode using * @ref sqfs_dir_reader_find_by_path and starting from that recursively * deserializes the entire hierarchy into a tree structure holding all inodes. * * @param rd A pointer to a directory reader. * @param path A path to resolve into an inode. Forward or backward slashes can * be used to separate path components. Resolving '.' or '..' is * not supported. Can be set to NULL to get the root inode. * @param flags A combination of @ref SQFS_TREE_FILTER_FLAGS flags. * @param out Returns the top most tree node. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd, const sqfs_id_table_t *idtbl, const char *path, sqfs_u32 flags, sqfs_tree_node_t **out); /** * @brief Recursively destroy a tree of @ref sqfs_tree_node_t nodes * * This function can be used to clean up after * @ref sqfs_dir_reader_get_full_hierarchy. * * @param root A pointer to the root node. */ SQFS_API void sqfs_dir_tree_destroy(sqfs_tree_node_t *root); #ifdef __cplusplus } #endif #endif /* SQFS_DIR_READER_H */ squashfs-tools-ng-1.1.3/include/sqfs/dir_writer.h000066400000000000000000000245471410627516300220540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * dir_writer.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_DIR_WRITER_H #define SQFS_DIR_WRITER_H #include "sqfs/predef.h" /** * @file dir_writer.h * * @brief Contains declarations for the @ref sqfs_dir_writer_t. */ /** * @struct sqfs_dir_writer_t * * @implements sqfs_object_t * * @brief Abstracts generating of directory entries * * SquashFS stores directory entries and inodes separated from each other. The * inodes are stored in a series of meta data blocks before another series of * meta data blocks that contain the directory entries. Directory inodes point * to meta data block (and offset) where its contents are listed and the * entries in turn point back to the inodes that represent them. * * There are some rules to this. Directory entries have to be written in * ASCIIbetical ordering. Up to 256 entries are preceeded by a header. The * entries use delta encoding for inode numbers and block locations relative to * the header, so every time the inodes cross a meta data block boundary, if * the difference in inode number gets too large, or if the entry count would * exceed 256, a new header has to be emitted. Even if the inode pointed to is * an extended type, the entry in the header still has to indicate the base * type. * * In addtion to that, extended directory inodes can contain an index for * faster lookup. The index points to each header and requires a new header to * be emitted if the entries cross a block boundary. * * The dir writer takes care of all of this and provides a simple interface for * adding entries. Internally it fills data into a meta data writer and * generates an index that it can, on request, write to another meta data * writer used for inodes. * * This object is not copyable, i.e. @ref sqfs_copy will always return NULL. */ /** * @enum SQFS_DIR_WRITER_CREATE_FLAGS * * @brief Flags that can be set for @ref sqfs_dir_writer_create */ typedef enum { /** * @brief Record all inode locations to create an export table. * * For NFS export support, SquashFS needs an extra table that maps * inode numbers directly to on-disk locations. * * Since the @ref sqfs_dir_writer_t "sees" all inode numbers and * coresponding locations and easily create such a table. * * If this flag is set for @ref sqfs_dir_writer_create, the result * directory wrter collects such a table which it can then write to * disk using @ref sqfs_dir_writer_write_export_table. */ SQFS_DIR_WRITER_CREATE_EXPORT_TABLE = 0x01, SQFS_DIR_WRITER_CREATE_ALL_FLAGS = 0x01 } SQFS_DIR_WRITER_CREATE_FLAGS; #ifdef __cplusplus extern "C" { #endif /** * @brief Create a directory writer. * * @memberof sqfs_dir_writer_t * * @param dm A pointer to a meta data writer that the generated directory * entries should be written to. * @param flags A combination of @ref SQFS_DIR_WRITER_CREATE_FLAGS. * * @return A pointer to a directory writer on success, NULL on * allocation failure or if flags has unknown flags set. */ SQFS_API sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, sqfs_u32 flags); /** * @brief Begin writing a directory, i.e. reset and initialize all internal * state neccessary. * * @memberof sqfs_dir_writer_t * * @param writer A pointer to a directory writer object. * @param flags A currently unused flag field. Must be set to 0. * * @return Zero on success, a @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer, sqfs_u32 flags); /** * @brief Add add a directory entry. * * @memberof sqfs_dir_writer_t * * @param writer A pointer to a directory writer object. * @param name The name of the directory entry. * @param inode_num The inode number of the entry. * @param inode_ref A reference to the inode, i.e. the meta data block offset * is stored in bits 16 to 48 and the lower 16 bit hold an * offset into the block. * @param mode A file mode, i.e. type and permission bits from which the entry * type is derived internally. * * @return Zero on success, a @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, sqfs_u32 inode_num, sqfs_u64 inode_ref, sqfs_u16 mode); /** * @brief Finish writing a directory listing and write everything out to the * meta data writer. * * @memberof sqfs_dir_writer_t * * @param writer A pointer to a directory writer object. * * @return Zero on success, a @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_writer_end(sqfs_dir_writer_t *writer); /** * @brief Get the total, uncompressed size of the last written * directory in bytes. * * @memberof sqfs_dir_writer_t * * Call this function after @ref sqfs_dir_writer_end to get the uncompressed * size of the directory listing that is required for the directory inodes. * And also to determine which kind of directory inode to create. * * Note that this size is only what was written to disk. SquashFS directory * inodes need you to add 3 to this value, to account for "." and ".." entries * which are not actually stored on disk. The @ref sqfs_dir_writer_create_inode * function takes this into account and adds the 3 internally. * * @param writer A pointer to a directory writer object. * * @return The size of the entire, uncompressed listing in bytes. */ SQFS_API size_t sqfs_dir_writer_get_size(const sqfs_dir_writer_t *writer); /** * @brief Get the numer of entries written to the last directory. * * @memberof sqfs_dir_writer_t * * Call this function after @ref sqfs_dir_writer_end to get the total * number of entries written to the directory. * * @param writer A pointer to a directory writer object. * * @return The number of entries in the directory. */ SQFS_API size_t sqfs_dir_writer_get_entry_count(const sqfs_dir_writer_t *writer); /** * @brief Get the location of the last written directory. * * @memberof sqfs_dir_writer_t * * Call this function after @ref sqfs_dir_writer_end to get the location of * the directory listing that is required for the directory inodes. * * @param writer A pointer to a directory writer object. * * @return A meta data reference, i.e. bits 16 to 48 contain the block start * and the lower 16 bit an offset into the uncompressed block. */ SQFS_API sqfs_u64 sqfs_dir_writer_get_dir_reference(const sqfs_dir_writer_t *writer); /** * @brief Get the size of the index of the last written directory. * * @memberof sqfs_dir_writer_t * * Call this function after @ref sqfs_dir_writer_end to get the size of * the directory index that is required for extended directory inodes. * * @param writer A pointer to a directory writer object. * * @return The number of index entries. */ SQFS_API size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer); /** * @brief Helper function for creating an inode from the last directory. * * @memberof sqfs_dir_writer_t * * Call this function after @ref sqfs_dir_writer_end to create a bare bones * inode structure for the directory. The directory information is filled in * completely and the type is set, the rest of the basic information such as * permission bits, owner and timestamp is left untouched. * * If the generated inode is an extended directory inode, you can use another * convenience function called @ref sqfs_dir_writer_write_index to write the * index meta data after writing the inode itself. * * @note The size is already adjusted for the required off-by-3 value. * * @param writer A pointer to a directory writer object. * @param hlinks The number of hard links pointing to the directory. * @param xattr If set to something other than 0xFFFFFFFF, an extended * directory inode is created with xattr index set. * @param parent_ino The inode number of the parent directory. * * @return A generic inode or NULL on allocation failure. */ SQFS_API sqfs_inode_generic_t *sqfs_dir_writer_create_inode(const sqfs_dir_writer_t *writer, size_t hlinks, sqfs_u32 xattr, sqfs_u32 parent_ino); /** * @brief Write an export table to a SquashFS image. * * @memberof sqfs_dir_writer_t * * If the @ref sqfs_dir_writer_t was created with the * @ref SQFS_DIR_WRITER_CREATE_EXPORT_TABLE flag set, it has an internal table * that maps all inode numbers to inode references. After writing the fragment * table, this function can be used to write this inode mapping table for NFS * export support. * * It is safe to call this function if the writer has been created without the * flag. In this case, it is simply a noop. * * In theory, the writer "sees" the entire directory tree and for each entry, * the inode number and on-disk location, so it can build this table. The only * inode it never sees is the root inode, so that information has to be passed * to this function to add it to the table just before writing it to disk. * * @param writer A pointer to a directory writer object. * @param file The ouput file to write the table to. * @param cmp The compressor to use to compress the table. * @param root_inode_num The inode number of the root inode. * @param root_inode_ref An inode reference for the root inode. * @param super A pointer to the super block. Location of the export table and * the exportable flag are set. * * @return Zero on success, a @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_dir_writer_write_export_table(sqfs_dir_writer_t *writer, sqfs_file_t *file, sqfs_compressor_t *cmp, sqfs_u32 root_inode_num, sqfs_u64 root_inode_ref, sqfs_super_t *super); #ifdef __cplusplus } #endif #endif /* SQFS_DIR_WRITER_H */ squashfs-tools-ng-1.1.3/include/sqfs/error.h000066400000000000000000000104471410627516300210250ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * error.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_ERROR_H #define SQFS_ERROR_H /** * @file error.h * * @brief Contains the @ref SQFS_ERROR enumerator. */ /** * @enum SQFS_ERROR * * @brief Error codes that can be returned by various libsquashfs functions. */ typedef enum { /** * @brief Allocation using malloc or calloc failed (returned NULL). */ SQFS_ERROR_ALLOC = -1, /** * @brief Generic I/O error if a file read or write operation failed. */ SQFS_ERROR_IO = -2, /** * @brief Generic compressor error returned if compressing data failed * (some kind of internal error) or extracting failed (typically * means the data is corrupted). */ SQFS_ERROR_COMPRESSOR = -3, /** * @brief An internal error of the "this wasn't supposed to happen" * kind that cannot easily be mapped to something usefull. */ SQFS_ERROR_INTERNAL = -4, /** * @brief Attempted to read an on-disk data structure that appears to * be corrupted, i.e. contains obvious non-sense values. */ SQFS_ERROR_CORRUPTED = -5, /** * @brief Attempted to use an unsupported feature (e.g. an unknown * compressor or xattr type). */ SQFS_ERROR_UNSUPPORTED = -6, /** * @brief Attempted to read a data structure into memory would * overflow the addressable memory. Usually indicates a * corrupted or maliciously manipulated SquashFS filesystem. */ SQFS_ERROR_OVERFLOW = -7, /** * @brief Attempted to perform an out-of-bounds read. If this happens * when following a reference stored in a data structure, it * usually indicates a corrupted or maliciously manipulated * SquashFS filesystem. */ SQFS_ERROR_OUT_OF_BOUNDS = -8, /** * @brief Specific error when reading the super block. * * Could not find the magic. */ SFQS_ERROR_SUPER_MAGIC = -9, /** * @brief Specific error when reading the super block. * * The version indicated be the filesystem is not supported. */ SFQS_ERROR_SUPER_VERSION = -10, /** * @brief Specific error when reading or initializing the super block. * * The block size specified is either not a power of 2, or outside the * legal range (4k to 1M). */ SQFS_ERROR_SUPER_BLOCK_SIZE = -11, /** * @brief Expected a directory (inode), found something else instead. * * Generated when trying to resolve a path but a part of the the path * turned out to not be a directory. Also generated when trying to * read directory entries from something that isn't a directory. */ SQFS_ERROR_NOT_DIR = -12, /** * @brief A specified path, or a part of it, does not exist. */ SQFS_ERROR_NO_ENTRY = -13, /** * @brief Detected a hard link loop while walking a filesystem tree. */ SQFS_ERROR_LINK_LOOP = -14, /** * @brief Tried to perform an file operation on something that isn't * a regular file or a regular file inode. */ SQFS_ERROR_NOT_FILE = -15, /** * @brief An invalid argument was passed to a library function. */ SQFS_ERROR_ARG_INVALID = -16, /** * @brief Library functions were called an a nonsensical order. * * Some libsquashfs functions operate on an object with an internal * state. Depending on the state, calling a function might not make * sense at all (e.g. calling foo_end before foo_begin). In that case, * this error is returned, signifying to the caller that the sequence * makes not sense, but the object itself is unchanged, no action was * performed and the object can still be used. */ SQFS_ERROR_SEQUENCE = -17, } SQFS_ERROR; #endif /* SQFS_ERROR_H */ squashfs-tools-ng-1.1.3/include/sqfs/frag_table.h000066400000000000000000000113321410627516300217540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * frag_table.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_FRAG_TABLE_H #define SQFS_FRAG_TABLE_H #include "sqfs/predef.h" /** * @file frag_table.h * * @brief Contains declarations for the @ref sqfs_frag_table_t data structure. */ /** * @struct sqfs_frag_table_t * * @implements sqfs_object_t * * @brief Abstracts reading, writing and management of the fragment table. */ #ifdef __cplusplus extern "C" { #endif /** * @brief Create a fragment table. * * @memberof sqfs_frag_table_t * * @param flags Currently must be set to 0, otherwise this function fails. * * @return A pointer to a new fragment table object on success, NULL on failure. */ SQFS_API sqfs_frag_table_t *sqfs_frag_table_create(sqfs_u32 flags); /** * @brief Load a fragment table from a SquashFS image. * * @memberof sqfs_frag_table_t * * @param tbl The fragment table object to load into. * @param file The file to load the table from. * @param super The super block that describes the location * and size of the table. * @param cmp The compressor to use for uncompressing the table. * * @return Zero on success, an @ref SQFS_ERROR on failure. */ SQFS_API int sqfs_frag_table_read(sqfs_frag_table_t *tbl, sqfs_file_t *file, const sqfs_super_t *super, sqfs_compressor_t *cmp); /** * @brief Write a fragment table to a SquashFS image. * * @memberof sqfs_frag_table_t * * The data from the table is compressed and appended to the given file. The * information about the tables size and location are stored in the given super * block. Super block flags are updated as well. * * @param tbl The fragment table object to store on disk. * @param file The file to append the table to. * @param super The super block that should be updated with the location * and size of the table. * @param cmp The compressor to use for compressing the table. * * @return Zero on success, an @ref SQFS_ERROR on failure. */ SQFS_API int sqfs_frag_table_write(sqfs_frag_table_t *tbl, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp); /** * @brief Resolve a fragment block index to its description. * * @memberof sqfs_frag_table_t * * @param tbl A pointer to the fragmen table object. * @param index The index into the table. * @param out Returns the data from the table on success. * * @return Zero on success, an @ref SQFS_ERROR on failure (e.g. index is * out of bounds). */ SQFS_API int sqfs_frag_table_lookup(sqfs_frag_table_t *tbl, sqfs_u32 index, sqfs_fragment_t *out); /** * @brief Append a table entry to a fragment table. * * @memberof sqfs_frag_table_t * * @param tbl A pointer to the fragmen table object. * @param location The absolute on-disk location of the new fragment block. * @param out The size of the block. Has bit 24 set if compressed. * @param index If not NULL, returns the allocated table index. * * @return Zero on success, an @ref SQFS_ERROR on failure (e.g. allocation * failure). */ SQFS_API int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, sqfs_u32 size, sqfs_u32 *index); /** * @brief Modify an existing entry in a fragment table. * * @memberof sqfs_frag_table_t * * @param tbl A pointer to the fragmen table object. * @param index The fragment table index. * @param location The absolute on-disk location of the fragment block. * @param out The size of the block. Has bit 24 set if compressed. * * @return Zero on success, an @ref SQFS_ERROR on * failure (e.g. out of bounds). */ SQFS_API int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, sqfs_u64 location, sqfs_u32 size); /** * @brief Get the number of entries stored in a fragment table. * * @memberof sqfs_frag_table_t * * @param tbl A pointer to the fragmen table object. * * @return The number of entries currently stored in the table. */ SQFS_API size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl); #ifdef __cplusplus } #endif #endif /* SQFS_FRAG_TABLE_H */ squashfs-tools-ng-1.1.3/include/sqfs/id_table.h000066400000000000000000000074111410627516300214340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * id_table.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_ID_TABLE_H #define SQFS_ID_TABLE_H #include "sqfs/predef.h" /** * @file id_table.h * * @brief Contains declarations for the @ref sqfs_id_table_t data structure. */ /** * @struct sqfs_id_table_t * * @implements sqfs_object_t * * @brief A simple data structure that encapsulates ID to index mapping for * user and group IDs. * * SquashFS does not store user and group IDs in inodes directly. Instead, it * collects the unique 32 bit IDs in a table with at most 64k entries and * stores a 16 bit index into the inode. This allows SquashFS to only have 16 * bit UID/GID entries in the inodes but actually have 32 bit UIDs/GIDs under * the hood (at least 64k selected ones). */ #ifdef __cplusplus extern "C" { #endif /** * @brief Create an ID table object. * * @memberof sqfs_id_table_t * * @param flags Currently must be set to 0 or creating the table fails. * * @return A pointer to an ID table object, NULL on allocation failure. */ SQFS_API sqfs_id_table_t *sqfs_id_table_create(sqfs_u32 flags); /** * @brief Resolve a 32 bit ID to a unique 16 bit index. * * @memberof sqfs_id_table_t * * @param tbl A pointer to an ID table object. * @param id The ID to resolve. * @param out Returns the unique table index. * * @return Zero on success, an @ref SQFS_ERROR on failure. */ SQFS_API int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, sqfs_u32 id, sqfs_u16 *out); /** * @brief Write an ID table to disk. * * @memberof sqfs_id_table_t * * @param tbl A pointer to an ID table object. * @param file The underlying file to append the table to. * @param super A pointer to a super block in which to store the ID table * start location. * @param cmp A compressor to use to compress the ID table. * * @return Zero on success, an @ref SQFS_ERROR on failure. */ SQFS_API int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp); /** * @brief Read an ID table from disk. * * @memberof sqfs_id_table_t * * @param tbl A pointer to an ID table object. * @param file The underlying file to read the table from. * @param super A pointer to a super block from which to get * the ID table location. * @param cmp A compressor to use to extract compressed table blocks. * * @return Zero on success, an @ref SQFS_ERROR on failure. */ SQFS_API int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, const sqfs_super_t *super, sqfs_compressor_t *cmp); /** * @brief Resolve a 16 bit index to a 32 bit ID. * * @memberof sqfs_id_table_t * * @param tbl A pointer to an ID table object. * @param index The table index to resolve. * @param out Returns the underlying 32 bit ID that the index maps to. * * @return Zero on success, an @ref SQFS_ERROR on failure. */ SQFS_API int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, sqfs_u16 index, sqfs_u32 *out); #ifdef __cplusplus } #endif #endif /* SQFS_ID_TABLE_H */ squashfs-tools-ng-1.1.3/include/sqfs/inode.h000066400000000000000000000465571410627516300210050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * inode.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_INODE_H #define SQFS_INODE_H #include "sqfs/predef.h" /** * @file inode.h * * @brief Contains on-disk data structures used for inodes. */ /** * @enum SQFS_INODE_TYPE * * @brief Used by @ref sqfs_inode_t to identify the inode type. */ typedef enum { SQFS_INODE_DIR = 1, SQFS_INODE_FILE = 2, SQFS_INODE_SLINK = 3, SQFS_INODE_BDEV = 4, SQFS_INODE_CDEV = 5, SQFS_INODE_FIFO = 6, SQFS_INODE_SOCKET = 7, SQFS_INODE_EXT_DIR = 8, SQFS_INODE_EXT_FILE = 9, SQFS_INODE_EXT_SLINK = 10, SQFS_INODE_EXT_BDEV = 11, SQFS_INODE_EXT_CDEV = 12, SQFS_INODE_EXT_FIFO = 13, SQFS_INODE_EXT_SOCKET = 14, } SQFS_INODE_TYPE; /** * @enum SQFS_INODE_MODE * * @brief Mode bits for the @ref sqfs_inode_t mode field. * * This is basically the same that mode bits in struct stat store on Unix-like * systems. It is duplicated here for portability with non-POSIX platforms. In * case you are not familiar with Unix file permissions, a brief description * follows. * * There are 3 fields with permissions: * - of the user that owns the file * - of the group that the file belongs to * - everybody else * * Each field holds 3 bits: X, W and R meaning execute, write and read access * respectively. There are 2 special cases: On a directory, execute means * entering the directory and accessing its contents. Read and write refere * to reading or changing the list of entries. For symlinks, the permissions * are meaningless and have all bits set. * * Besides the permissions, there are 3 more bits: * - sticky * - set group id * - set user id * * Nowadays, the later two mean executing a program makes it run as the group * or user (respectively) that owns it instead of the user that actually ran * the program. On directories, the sticky bit means that its contents can only * be deleted by the actual owner, even if others have write permissions. All * other uses of those bits are obscure, historic and differ between flavours * of Unix. * * The remaining 4 bits (adding up to a total of 16) specify the type of file: * - named pipe, aka fifo * - character device * - directory * - block device * - regular file * - symlink * - socket */ typedef enum { SQFS_INODE_OTHERS_X = 00001, SQFS_INODE_OTHERS_W = 00002, SQFS_INODE_OTHERS_R = 00004, SQFS_INODE_OTHERS_MASK = 00007, SQFS_INODE_GROUP_X = 00010, SQFS_INODE_GROUP_W = 00020, SQFS_INODE_GROUP_R = 00040, SQFS_INODE_GROUP_MASK = 00070, SQFS_INODE_OWNER_X = 00100, SQFS_INODE_OWNER_W = 00200, SQFS_INODE_OWNER_R = 00400, SQFS_INODE_OWNER_MASK = 00700, SQFS_INODE_STICKY = 01000, SQFS_INODE_SET_GID = 02000, SQFS_INODE_SET_UID = 04000, SQFS_INODE_MODE_FIFO = 0010000, SQFS_INODE_MODE_CHR = 0020000, SQFS_INODE_MODE_DIR = 0040000, SQFS_INODE_MODE_BLK = 0060000, SQFS_INODE_MODE_REG = 0100000, SQFS_INODE_MODE_LNK = 0120000, SQFS_INODE_MODE_SOCK = 0140000, SQFS_INODE_MODE_MASK = 0170000, } SQFS_INODE_MODE; /** * @struct sqfs_inode_t * * @brief Common inode structure * * This structure holds the fields common for all inodes. Depending on the type * field, a specific inode structure follows. */ struct sqfs_inode_t { /** * @brief An @ref SQFS_INODE_TYPE value. */ sqfs_u16 type; /** * @brief Mode filed holding permission bits only. The type is derived * from the type field. * * This field holds a combination of @ref SQFS_INODE_MODE flags. */ sqfs_u16 mode; /** * @brief An index into the ID table where the owner UID is located. */ sqfs_u16 uid_idx; /** * @brief An index into the ID table where the owner GID is located. */ sqfs_u16 gid_idx; /** * @brief Last modifcation time. * * This field counts seconds (not counting leap seconds) since 00:00, * Jan 1 1970 UTC. This field is unsigned, so it expires in the year * 2106 (as opposed to 2038). */ sqfs_u32 mod_time; /** * @brief Unique inode number */ sqfs_u32 inode_number; }; /** * @struct sqfs_inode_dev_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_BDEV * or @ref SQFS_INODE_CDEV. */ struct sqfs_inode_dev_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Device number. */ sqfs_u32 devno; }; /** * @struct sqfs_inode_dev_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_BDEV * or @ref SQFS_INODE_EXT_CDEV. */ struct sqfs_inode_dev_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Device number. */ sqfs_u32 devno; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_ipc_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_FIFO * or @ref SQFS_INODE_SOCKET. */ struct sqfs_inode_ipc_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; }; /** * @struct sqfs_inode_ipc_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_FIFO * or @ref SQFS_INODE_EXT_SOCKET. */ struct sqfs_inode_ipc_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_slink_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_SLINK. * * The declaration does not contain the flexible array member of the symlink * target because @ref sqfs_inode_generic_t would otherwies be impossible to * implement without violating the C standard. */ struct sqfs_inode_slink_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Size of the symlink target in bytes */ sqfs_u32 target_size; /*sqfs_u8 target[];*/ }; /** * @struct sqfs_inode_slink_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_SLINK. * * The declaration does not contain the flexible array member of the symlink * target because it is wedged right in between the target size and the xattr * identifier. */ struct sqfs_inode_slink_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Size of the symlink target in bytes */ sqfs_u32 target_size; /*sqfs_u8 target[];*/ /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_file_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_FILE. * * The declaration does not contain the flexible array member for the data * block sizes because @ref sqfs_inode_generic_t would otherwies be impossible * to implement without violating the C standard. * * For each data block, the inode is followed by a 32 bit integer that holds * the on-disk size of the compressed block in bytes and has bit number 24 * set if the block is stored uncompressed. * * If a block size is specified as zero, it is assumed to be an entire block * filled with zero bytes. */ struct sqfs_inode_file_t { /** * @brief Absolute position of the first data block. */ sqfs_u32 blocks_start; /** * @brief Index into the fragment table or 0xFFFFFFFF if unused. */ sqfs_u32 fragment_index; /** * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF * if unused. */ sqfs_u32 fragment_offset; /** * @brief Total, uncompressed size of the file in bytes. */ sqfs_u32 file_size; /*sqfs_u32 block_sizes[];*/ }; /** * @struct sqfs_inode_file_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_FILE. * * @copydoc sqfs_inode_file_t */ struct sqfs_inode_file_ext_t { /** * @brief Absolute position of the first data block. */ sqfs_u64 blocks_start; /** * @brief Total, uncompressed size of the file in bytes. */ sqfs_u64 file_size; /** * @brief If the file is sparse, holds the number of bytes not written * to disk because of the omitted sparse blocks. */ sqfs_u64 sparse; /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Index into the fragment table or 0xFFFFFFFF if unused. */ sqfs_u32 fragment_idx; /** * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF * if unused. */ sqfs_u32 fragment_offset; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; /*sqfs_u32 block_sizes[];*/ }; /** * @struct sqfs_inode_dir_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_DIR. */ struct sqfs_inode_dir_t { /** * @brief Offset from the directory table start to the location of the * meta data block containing the first directory header. */ sqfs_u32 start_block; /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Combined size of all directory entries and headers in bytes. * * The value stored here is off by 3 bytes, i.e. 3 bytes larger than * the actual listing on disk. The Linux implementation of SquashFS * uses readdir() offsets 0 and 1 to synthesize "." and ".." entries, * and after that looks into the actual directory. * * Why store an off-by-3 value on disk instead of faking it in the * kernel and have something consistent here? Beats me, but that's * how it is implemented. */ sqfs_u16 size; /** * @brief Offset into the uncompressed start block where the header can * be found. */ sqfs_u16 offset; /** * @brief Inode number of the parent directory containing * this directory inode. */ sqfs_u32 parent_inode; }; /** * @struct sqfs_inode_dir_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_DIR. */ struct sqfs_inode_dir_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Size of all directory entries and headers in bytes plus 3. */ sqfs_u32 size; /** * @brief Offset from the directory table start to the location of the * meta data block containing the first directory header. */ sqfs_u32 start_block; /** * @brief Inode number of the parent directory containing * this directory inode. */ sqfs_u32 parent_inode; /** * @brief Number of directory index entries following the inode * * This number counts the number of @ref sqfs_dir_index_t entries * following the inode. */ sqfs_u16 inodex_count; /** * @brief Offset into the uncompressed start block where the header can * be found. */ sqfs_u16 offset; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_generic_t * * @brief A generic inode structure that combines all others and provides * additional information. * * A few helper functions exist for working with this. For instance, * @ref sqfs_meta_reader_read_inode can read an inode from disk and assemble it * into an instance of this structure. Similarly, the * @ref sqfs_meta_writer_write_inode function can break it down into encoded, * on-disk structures and write them to disk. */ struct sqfs_inode_generic_t { /** * @brief The common fields for all inodes. */ sqfs_inode_t base; /** * @brief Maximum number of available data bytes in the payload. * * This is used for dynamically growing an inode. The actual number * of used payload bytes is stored in @ref payload_bytes_used. */ sqfs_u32 payload_bytes_available; /** * @brief Number of used data bytes in the payload. * * For file inodes, stores the number of blocks used. For extended * directory inodes, stores the number of payload bytes following * for the directory index. */ sqfs_u32 payload_bytes_used; /** * @brief Type specific inode data. */ union { sqfs_inode_dev_t dev; sqfs_inode_dev_ext_t dev_ext; sqfs_inode_ipc_t ipc; sqfs_inode_ipc_ext_t ipc_ext; sqfs_inode_slink_t slink; sqfs_inode_slink_ext_t slink_ext; sqfs_inode_file_t file; sqfs_inode_file_ext_t file_ext; sqfs_inode_dir_t dir; sqfs_inode_dir_ext_t dir_ext; } data; /** * @brief Holds type specific extra data, such as symlink target. * * For regular file inodes, this is an array of block sizes. For symlink * inodes, this is actually a string holding the target. For extended * directory inodes, this is actually a blob of tightly packed directory * index entries. */ sqfs_u32 extra[]; }; #ifdef __cplusplus extern "C" { #endif /** * @brief Get the number of file blocks in a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * * @return The number of blocks. */ static SQFS_INLINE size_t sqfs_inode_get_file_block_count(const sqfs_inode_generic_t *inode) { return inode->payload_bytes_used / sizeof(sqfs_u32); } /** * @brief Get the extended attribute index of an inode * * @memberof sqfs_inode_generic_t * * For basic inodes, this returns the inode index 0xFFFFFFFF, i.e. the * sentinel value indicating that there are no xattrs. * * @param inode A pointer to an inode. * @param out Returns the extended attribute index on success. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, sqfs_u32 *out); /** * @brief Set the extended attribute index of an inode. * * @memberof sqfs_inode_generic_t * * For basic inodes, this function promes the inodes to extended inodes if the * index is not 0xFFFFFFFF. If the index is 0xFFFFFFFF, the function tries to * demote extended inode to a basic inode after setting the index. * * @param inode A pointer to an inode. * @param index The extended attribute index. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index); /** * @brief Convert a basic inode to an extended inode. * * @memberof sqfs_inode_generic_t * * For inodes that already have an extended type, this is a no-op. * * @param inode A pointer to an inode. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_make_extended(sqfs_inode_generic_t *inode); /** * @brief Convert an extended inode to a basic inode if possible. * * @memberof sqfs_inode_generic_t * * For inodes that already have a basic type, this is a no-op. If the inode * has values set that the coresponding basic type doesn't support (e.g. it * has an xattr index set or a regular file which requires 64 bit size * counter), it is left as an extended type and success state is returned. * * @param inode A pointer to an inode. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_make_basic(sqfs_inode_generic_t *inode); /** * @brief Update the file size of a regular file inode. * * @memberof sqfs_inode_generic_t * * If the new size is wider than 32 bit, a basic file inode is transparently * promoted to an extended file inode. For extended inodes, if the new size * is small enough and was the only requirement for the extended type, the * node is transparently demoted to a basic file inode. * * @param inode A pointer to an inode. * @param size The new size to set. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size); /** * @brief Update the location of the first data block of a regular file inode. * * @memberof sqfs_inode_generic_t * * If the new location is wider than 32 bit, a basic file inode is * transparently promoted to an extended file inode. For extended inodes, * if the new size is small enough and was the only requirement for the * extended type, the node is transparently demoted to a basic file inode. * * @param inode A pointer to an inode. * @param location The new location to set. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, sqfs_u64 location); /** * @brief Update the file fragment location of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param index The new fragment index to set. * @param offset The new fragment offset to set. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, sqfs_u32 index, sqfs_u32 offset); /** * @brief Get the file size of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param size Returns the file size. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size); /** * @brief Get the file fragment location of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param index Returns the fragment index. * @param offset Returns the fragment offset. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, sqfs_u32 *index, sqfs_u32 *offset); /** * @brief Get the location of the first data block of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param location Returns the location. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, sqfs_u64 *location); /** * @brief Unpack the a directory index structure from an inode. * * @memberof sqfs_inode_generic_t * * The generic inode contains in its payload the raw directory index (with * bytes swapped to host enian), but still with single byte alignment. This * function seeks through the blob using an integer index (not offset) and * fiddles the raw data out into a propperly aligned, external structure. * * @param inode A pointer to an inode. * @param out Returns the index entry. Can be freed with a single * @ref sqfs_free call. * @param index An index value between 0 and inodex_count. * * @return Zero on success, @ref SQFS_ERROR_OUT_OF_BOUNDS if the given index * points outside the directory index. Can return other error codes * (e.g. if the inode isn't a directory or allocation failed). */ SQFS_API int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, sqfs_dir_index_t **out, size_t index); #ifdef __cplusplus } #endif #endif /* SQFS_INODE_H */ squashfs-tools-ng-1.1.3/include/sqfs/io.h000066400000000000000000000111611410627516300202750ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * io.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_IO_H #define SQFS_IO_H #include "sqfs/predef.h" /** * @file io.h * * @brief Contains the @ref sqfs_file_t interface for abstracting file I/O */ /** * @enum SQFS_FILE_OPEN_FLAGS * * @brief Flags for @ref sqfs_open_file */ typedef enum { /** * @brief If set, access the file for reading only * * If not set, the file is expected to have a zero size after opening * which can be grown with successive writes to end of the file. * * Opening an existing file with this flag cleared results in failure, * unless the @ref SQFS_FILE_OPEN_OVERWRITE flag is also set. */ SQFS_FILE_OPEN_READ_ONLY = 0x01, /** * @brief If the read only flag is not set, overwrite any * existing file. * * If the file alrady exists, it is truncated to zero bytes size and * overwritten. */ SQFS_FILE_OPEN_OVERWRITE = 0x02, SQFS_FILE_OPEN_ALL_FLAGS = 0x03, } SQFS_FILE_OPEN_FLAGS; /** * @interface sqfs_file_t * * @extends sqfs_object_t * * @brief Abstracts file I/O to make it easy to embedd SquashFS. * * Files are only copyable if they are read only, i.e. if a file has been * opened with write access, @ref sqfs_copy will always return NULL. The * other data types inside libsquashfs assume this to hold for all * implementations of this interface. */ struct sqfs_file_t { sqfs_object_t base; /** * @brief Read a chunk of data from an absolute position. * * @param file A pointer to the file object. * @param offset An absolute offset to read data from. * @param buffer A pointer to a buffer to copy the data to. * @param size The number of bytes to read from the file. * * @return Zero on success, an @ref SQFS_ERROR identifier on failure * that the data structures in libsquashfs that use this return * directly to the caller. */ int (*read_at)(sqfs_file_t *file, sqfs_u64 offset, void *buffer, size_t size); /** * @brief Write a chunk of data at an absolute position. * * @param file A pointer to the file object. * @param offset An absolute offset to write data to. * @param buffer A pointer to a buffer to write to the file. * @param size The number of bytes to write from the buffer. * * @return Zero on success, an @ref SQFS_ERROR identifier on failure * that the data structures in libsquashfs that use this return * directly to the caller. */ int (*write_at)(sqfs_file_t *file, sqfs_u64 offset, const void *buffer, size_t size); /** * @brief Get the number of bytes currently stored in the file. * * @param file A pointer to the file object. */ sqfs_u64 (*get_size)(const sqfs_file_t *file); /** * @brief Extend or shrink a file to a specified size. * * @param file A pointer to the file object. * @param size The new capacity of the file in bytes. * * @return Zero on success, an @ref SQFS_ERROR identifier on failure * that the data structures in libsquashfs that use this return * directly to the caller. */ int (*truncate)(sqfs_file_t *file, sqfs_u64 size); }; #ifdef __cplusplus extern "C" { #endif /** * @brief Open a file through the operating systems filesystem API * * This function internally creates an instance of a reference implementation * of the @ref sqfs_file_t interface that uses the operating systems native * API for file I/O. * * On Unix-like systems, if the open call fails, this function makes sure to * preserves the value in errno indicating the underlying problem. * * @param filename The name of the file to open. * @param flags A set of @ref SQFS_FILE_OPEN_FLAGS. * * @return A pointer to a file object on success, NULL on allocation failure, * failure to open the file or if an unknown flag was set. */ SQFS_API sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags); #ifdef __cplusplus } #endif #endif /* SQFS_IO_H */ squashfs-tools-ng-1.1.3/include/sqfs/meta_reader.h000066400000000000000000000140271410627516300221420ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * meta_reader.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_META_READER_H #define SQFS_META_READER_H #include "sqfs/predef.h" /** * @file meta_reader.h * * @brief Contains declarations for the @ref sqfs_meta_reader_t. */ /** * @struct sqfs_meta_reader_t * * @implements sqfs_object_t * * @brief Abstracts reading of meta data blocks. * * SquashFS stores meta data by dividing it into fixed size (8k) chunks * that are written to disk with a small header indicating the on-disk * size and whether it is compressed or not. * * Data written to meta data blocks doesn't have to be aligned, i.e. * SquashFS doesn't care if an object is written across two blocks. * * The main task of the meta data read is to provide a simple read and seek * functions that transparently take care of fetching and uncompressing blocks * from disk and reading transparently across block boarders if required. */ #ifdef __cplusplus extern "C" { #endif /** * @brief Create a meta data reader * * @memberof sqfs_meta_reader_t * * @param file A pointer to a file object to read from * @param cmp A compressor to use for unpacking compressed meta data blocks * @param start A lower limit for the blocks to be read. Every seek to an offset * below that is interpreted as an out-of-bounds read. * @param limit An upper limit for the blocks to read. Every seek to an offset * afer that is interpreted as an out-of-bounds read. * * @return A pointer to a meta data reader on success, NULL on * allocation failure. */ SQFS_API sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, sqfs_compressor_t *cmp, sqfs_u64 start, sqfs_u64 limit); /** * @brief Seek to a specific meta data block and offset. * * @memberof sqfs_meta_reader_t * * The underlying block is fetched from disk and uncompressed, unless it * already is the current block. * * @param m A pointer to a meta data reader. * @param block_start Absolute position where the block header can be found. * @param offset A byte offset into the uncompressed block. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, sqfs_u64 block_start, size_t offset); /** * @brief Get the current position that the next read will read from. * * @memberof sqfs_meta_reader_t * * @param m A pointer to a meta data reader. * @param block_start Absolute position where the current block is. * @param offset A byte offset into the uncompressed block. */ SQFS_API void sqfs_meta_reader_get_position(const sqfs_meta_reader_t *m, sqfs_u64 *block_start, size_t *offset); /** * @brief Read a chunk of data from a meta data reader. * * @memberof sqfs_meta_reader_t * * If the meta data reader reaches the end of the current block before filling * the destination buffer, it transparently reads the next block from disk and * uncompresses it. * * @param m A pointer to a meta data reader. * @param data A pointer to copy the data to. * @param size The numbre of bytes to read. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size); /** * @brief Read and decode a directory header from a meta data reader. * * @memberof sqfs_meta_reader_t * * This is a convenience function on to of @ref sqfs_meta_reader_read that * reads and decodes a directory header from a meta data block. * * @param m A pointer to a meta data reader. * @param hdr A pointer to a directory header to fill. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, sqfs_dir_header_t *hdr); /** * @brief Read and decode a directory header from a meta data reader. * * @memberof sqfs_meta_reader_t * * This is a convenience function on to of @ref sqfs_meta_reader_read that * reads and decodes a directory entry. * * @param m A pointer to a meta data reader. * @param ent Returns a pointer to a directory entry. Can be released with a * single @ref sqfs_free call. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, sqfs_dir_entry_t **ent); /** * @brief Read and decode an inode from a meta data reader. * * @memberof sqfs_meta_reader_t * * This is a convenience function on to of @ref sqfs_meta_reader_seek and * @ref sqfs_meta_reader_read that reads and decodes an inode. * * @param ir A pointer to a meta data reader. * @param super A pointer to the super block, required for figuring out the * size of file inodes. * @param block_start The meta data block to seek to for reading the inode. * @param offset A byte offset within the uncompressed block where the * inode is. * @param out Returns a pointer to an inode. Can be released with a * single @ref sqfs_free call. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, const sqfs_super_t *super, sqfs_u64 block_start, size_t offset, sqfs_inode_generic_t **out); #ifdef __cplusplus } #endif #endif /* SQFS_META_READER_H */ squashfs-tools-ng-1.1.3/include/sqfs/meta_writer.h000066400000000000000000000144451410627516300222200ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * meta_writer.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_META_WRITER_H #define SQFS_META_WRITER_H #include "sqfs/predef.h" /** * @file meta_writer.h * * @brief Contains declarations for the @ref sqfs_meta_writer_t. */ /** * @struct sqfs_meta_writer_t * * @implements sqfs_object_t * * @brief Abstracts generating of meta data blocks, either in memory or * directly on disk. * * SquashFS stores meta data by dividing it into fixed size (8k) chunks * that are written to disk with a small header indicating the on-disk * size and whether it is compressed or not. * * Data written to meta data blocks doesn't have to be aligned, i.e. * SquashFS doesn't care if an object is written across two blocks. * * The main task of the meta data writer is to provide a simple append * function that transparently takes care of chopping data up into blocks, * compressing the blocks and pre-pending a header. * * This object is not copyable, i.e. @ref sqfs_copy will always return NULL. */ /** * @enum SQFS_META_WRITER_FLAGS * * @brief Possible flags for @ref sqfs_meta_writer_create. */ typedef enum { /** * @brief If set, keep finished blocks in memory. * * To write them to disk, explicitly call * @ref sqfs_meta_write_write_to_file. */ SQFS_META_WRITER_KEEP_IN_MEMORY = 0x01, SQFS_META_WRITER_ALL_FLAGS = 0x01, } SQFS_META_WRITER_FLAGS; #ifdef __cplusplus extern "C" { #endif /** * @brief Create a meta data writer. * * @memberof sqfs_meta_writer_t * * @note The meta writer internally keeps references to the pointers passed * to this function, so don't destroy them before destroying the * meta writer. * * @param file An output file to write the data to. * @param cmp A compressor to use. * @param flags A combination of @ref SQFS_META_WRITER_FLAGS. * * @return A pointer to a meta writer on success, NULL on allocation failure * or if an unknown flag was set. */ SQFS_API sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, sqfs_compressor_t *cmp, sqfs_u32 flags); /** * @brief Finish the current block, even if it isn't full yet. * * @memberof sqfs_meta_writer_t * * This function forces the meta writer to compress and store the block it * is currently writing to, even if it isn't full yet, and either write it * out to disk (or append it to the in memory chain if told to keep blocks * in memory). * * @param m A pointer to a meta data writer. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_writer_flush(sqfs_meta_writer_t *m); /** * @brief Finish the current block, even if it isn't full yet. * * @memberof sqfs_meta_writer_t * * This function forces reads a speicifed number of bytes from a given data * block and appends it to the meta data block that the writer is currently * working on. If the block becomes full, it is compressed, written to disk * and a new block is started that the remaining data is written to. * * @param m A pointer to a meta data writer. * @param data A pointer to a chunk of data to append to the writer. * @param size The number of data bytes to append. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, size_t size); /** * @brief Query the current block start position and offset within the block * * @memberof sqfs_meta_writer_t * * Get the byte offset relative to the first block that the current block will * start at once it is written to disk and get the byte offset within this * block that the next call to @ref sqfs_meta_writer_append will start writing * data at. * * @param m A pointer to a meta data writer. * @param block_start Returns the offset of the current block from the first. * @param offset Returns an offset into the current block where the next write * starts. */ SQFS_API void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, sqfs_u64 *block_start, sqfs_u32 *offset); /** * @brief Reset all internal state, including the current block start position. * * @memberof sqfs_meta_writer_t * * This functions forces the meta data writer to forget everything that * happened since it was created, so it can be recycled. * * The data written is not lost, unless it was kept in memory and never written * out to disk. */ SQFS_API void sqfs_meta_writer_reset(sqfs_meta_writer_t *m); /** * @brief Write all blocks collected in memory to disk * * @memberof sqfs_meta_writer_t * * If the meta writer was created with the flag set to store blocks in * memory instead of writing them to disk, calling this function forces * the meta writer to write out all blocks it collected so far. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m); /** * @brief A convenience function for encoding and writing an inode * * @memberof sqfs_meta_writer_t * * The SquashFS inode table is essentially a series of meta data blocks * containing variable sized inodes. This function takes a generic inode * structure, encodes it and writes the result to a meta data writer * using @ref sqfs_meta_writer_append internally. * * @param iw A pointer to a meta data writer. * @param n A pointer to an inode. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *iw, const sqfs_inode_generic_t *n); #ifdef __cplusplus } #endif #endif /* SQFS_META_WRITER_H */ squashfs-tools-ng-1.1.3/include/sqfs/predef.h000066400000000000000000000156671410627516300211520ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * predef.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_PREDEF_H #define SQFS_PREDEF_H /** * @file predef.h * * @brief Includes forward declarations of data structures, * macros and integer types. */ #include #include #include #if defined(_WIN32) || defined(__CYGWIN__) #if defined(SQFS_STATIC) #define SQFS_API #elif defined(SQFS_BUILDING_DLL) #if defined(__GNUC__) || defined(__clang__) #define SQFS_API __attribute__ ((dllexport)) #else #define SQFS_API __declspec(dllexport) #endif #else #if defined(__GNUC__) || defined(__clang__) #define SQFS_API __attribute__ ((dllimport)) #else #define SQFS_API __declspec(dllimport) #endif #endif #define SQFS_INTERNAL #else #if defined(__GNUC__) || defined(__clang__) #define SQFS_API __attribute__ ((visibility ("default"))) #define SQFS_INTERNAL __attribute__ ((visibility ("hidden"))) #else #define SQFS_API #define SQFS_INTERNAL #endif #endif #ifdef _MSC_VER #define SQFS_INLINE __forceinline #else #define SQFS_INLINE __inline__ __attribute__((always_inline)) #endif typedef uint8_t sqfs_u8; typedef uint16_t sqfs_u16; typedef uint32_t sqfs_u32; typedef uint64_t sqfs_u64; typedef int8_t sqfs_s8; typedef int16_t sqfs_s16; typedef int32_t sqfs_s32; typedef int64_t sqfs_s64; typedef struct sqfs_block_processor_t sqfs_block_processor_t; typedef struct sqfs_compressor_config_t sqfs_compressor_config_t; typedef struct sqfs_compressor_t sqfs_compressor_t; typedef struct sqfs_dir_writer_t sqfs_dir_writer_t; typedef struct sqfs_dir_reader_t sqfs_dir_reader_t; typedef struct sqfs_id_table_t sqfs_id_table_t; typedef struct sqfs_meta_reader_t sqfs_meta_reader_t; typedef struct sqfs_meta_writer_t sqfs_meta_writer_t; typedef struct sqfs_xattr_reader_t sqfs_xattr_reader_t; typedef struct sqfs_file_t sqfs_file_t; typedef struct sqfs_tree_node_t sqfs_tree_node_t; typedef struct sqfs_data_reader_t sqfs_data_reader_t; typedef struct sqfs_block_hooks_t sqfs_block_hooks_t; typedef struct sqfs_xattr_writer_t sqfs_xattr_writer_t; typedef struct sqfs_frag_table_t sqfs_frag_table_t; typedef struct sqfs_block_writer_t sqfs_block_writer_t; typedef struct sqfs_block_writer_stats_t sqfs_block_writer_stats_t; typedef struct sqfs_block_processor_stats_t sqfs_block_processor_stats_t; typedef struct sqfs_block_processor_desc_t sqfs_block_processor_desc_t; typedef struct sqfs_fragment_t sqfs_fragment_t; typedef struct sqfs_dir_header_t sqfs_dir_header_t; typedef struct sqfs_dir_entry_t sqfs_dir_entry_t; typedef struct sqfs_dir_index_t sqfs_dir_index_t; typedef struct sqfs_inode_t sqfs_inode_t; typedef struct sqfs_inode_dev_t sqfs_inode_dev_t; typedef struct sqfs_inode_dev_ext_t sqfs_inode_dev_ext_t; typedef struct sqfs_inode_ipc_t sqfs_inode_ipc_t; typedef struct sqfs_inode_ipc_ext_t sqfs_inode_ipc_ext_t; typedef struct sqfs_inode_slink_t sqfs_inode_slink_t; typedef struct sqfs_inode_slink_ext_t sqfs_inode_slink_ext_t; typedef struct sqfs_inode_file_t sqfs_inode_file_t; typedef struct sqfs_inode_file_ext_t sqfs_inode_file_ext_t; typedef struct sqfs_inode_dir_t sqfs_inode_dir_t; typedef struct sqfs_inode_dir_ext_t sqfs_inode_dir_ext_t; typedef struct sqfs_inode_generic_t sqfs_inode_generic_t; typedef struct sqfs_super_t sqfs_super_t; typedef struct sqfs_xattr_entry_t sqfs_xattr_entry_t; typedef struct sqfs_xattr_value_t sqfs_xattr_value_t; typedef struct sqfs_xattr_id_t sqfs_xattr_id_t; typedef struct sqfs_xattr_id_table_t sqfs_xattr_id_table_t; /** * @interface sqfs_object_t * * @brief Base interface for all libsquashfs in-memory data structures. */ typedef struct sqfs_object_t { void (*destroy)(struct sqfs_object_t *instance); struct sqfs_object_t *(*copy)(const struct sqfs_object_t *orig); } sqfs_object_t; /** * @brief Destroy an object and free all its memory * * @memberof sqfs_object_t * * @param obj A pointer to an object */ static SQFS_INLINE void sqfs_destroy(void *obj) { ((sqfs_object_t *)obj)->destroy((sqfs_object_t *)obj); } /** * @brief Create a deep copy of an object if possible. * * @memberof sqfs_object_t * * @param obj A pointer to an object * * @return A pointer to a new object, instantiated from the old on success, * NULL on failure. */ static SQFS_INLINE void *sqfs_copy(const void *obj) { if (((const sqfs_object_t *)obj)->copy != NULL) { return ((const sqfs_object_t *)obj)-> copy((const sqfs_object_t *)obj); } return NULL; } #ifdef __cplusplus extern "C" { #endif /** * @brief Free a block of memory created by the squashfs library * * Some objects in the squashfs library allocate temporary chunks of memory * and return pointers to them, with the expectation that the user takes care * of freeing them memory again. * * In the past this worked without hitches, because most sane operating systems * have a single, global C library that everything is linked against. On * systems like Windows that have a huge fragmentation of runtime libraries * this fails, because the library and application can easily end up linked * against different runtime libraries, making it impossible for the program * to free the memory. * * To re-create the same situation on Linux, one would have to link a program * and the squashfs library dynamically, while linking at least one of them * statically against the C runtime, or intentionally linking against two * different runtimes. * * This function mitigates the arising problem by exposing the free() function * of the libraries runtime to the application, so it can propperly release * memory again to the correct heap. * * To maintain API/ABI compatibillity, it is guaranteed that for all versions * of libsquashfs with major version 1, this function does nothing other than * call free() on the C runtime that the library was linked against. If just * calling free() works on your system, it will continue to work, and older * application will continue to work with newer versions of libsquashfs 1.x. * Going forward, new applications using libsquashfs should use this function * instead for better portabillity. * * @param ptr A pointer to a memory block returned by a libsquashfs function. */ SQFS_API void sqfs_free(void *ptr); #ifdef __cplusplus } #endif #endif /* SQFS_PREDEF_H */ squashfs-tools-ng-1.1.3/include/sqfs/super.h000066400000000000000000000206441410627516300210320ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * super.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_SUPER_H #define SQFS_SUPER_H #include "sqfs/predef.h" /** * @file super.h * * @brief Contains on-disk data structures, identifiers and functions for the * SquashFS super block. */ #define SQFS_MAGIC 0x73717368 #define SQFS_VERSION_MAJOR 4 #define SQFS_VERSION_MINOR 0 #define SQFS_DEVBLK_SIZE 4096 #define SQFS_MIN_BLOCK_SIZE (4 * 1024) #define SQFS_MAX_BLOCK_SIZE (1024 * 1024) #define SQFS_DEFAULT_BLOCK_SIZE (128 * 1024) /** * @struct sqfs_super_t * * @brief The SquashFS super block, located at the beginning of the file system * to describe the layout of the filesystem. */ struct sqfs_super_t { /** * @brief Magic number. Must be set to SQFS_MAGIC. */ sqfs_u32 magic; /** * @brief Total number of inodes. */ sqfs_u32 inode_count; /** * @brief Last time the filesystem was modified. * * This field counts seconds (not counting leap seconds) since 00:00, * Jan 1 1970 UTC. This field is unsigned, so it expires in the year * 2106 (as opposed to 2038). */ sqfs_u32 modification_time; /** * @brief The data block size in bytes. * * Must be a power of 2, no less than 4k and not larger than 1M. */ sqfs_u32 block_size; /** * @brief The number of fragment blocks in the data area. */ sqfs_u32 fragment_entry_count; /** * @brief Identifies the compressor that has been used. * * Valid identifiers are in the @ref SQFS_COMPRESSOR enum. */ sqfs_u16 compression_id; /** * @brief The log2 of the block_size field for sanity checking * * Must be no less than 12 and not larger than 20. */ sqfs_u16 block_log; /** * @brief A combination of @ref SQFS_SUPER_FLAGS flags * * Most of the flags that can be set here are informative only. */ sqfs_u16 flags; /** * @brief The total number of unique user or group IDs. */ sqfs_u16 id_count; /** * @brief Must be @ref SQFS_VERSION_MAJOR */ sqfs_u16 version_major; /** * @brief Must be @ref SQFS_VERSION_MINOR */ sqfs_u16 version_minor; /** * @brief A reference to the root inode * * The bits 16 to 48 hold an offset that is added to inode_table_start * to get the location of the meta data block containing the inode. * The lower 16 bits hold a byte offset into the uncompressed block. */ sqfs_u64 root_inode_ref; /** * @brief Total size of the file system in bytes, not counting padding */ sqfs_u64 bytes_used; /** * @brief On-disk location of the ID table * * This value must point to a location after the directory table and * (if present) after the export and fragment tables, but before the * xattr table. */ sqfs_u64 id_table_start; /** * @brief On-disk location of the extended attribute table (if present) * * See @ref sqfs_xattr_reader_t for an overview on how SquashFS stores * extended attributes on disk. * * This value must either point to a location after the ID table, or * it must be set to 0xFFFFFFFF to indicate the table is not present. */ sqfs_u64 xattr_id_table_start; /** * @brief On-disk location of the first meta data block containing * the inodes * * This value must point to a location before the directory table. */ sqfs_u64 inode_table_start; /** * @brief On-disk location of the first meta data block containing * the directory entries * * This value must point to a location after the inode table but * before the fragment, export, ID and xattr tables. */ sqfs_u64 directory_table_start; /** * @brief On-disk location of the fragment table (if present) * * This value must either point to a location after the directory * table, but before the export, ID and xattr tables, or it must be * set to 0xFFFFFFFF to indicate that the table is not present. */ sqfs_u64 fragment_table_start; /** * @brief On-disk location of the export table (if present) * * This value must either point to a location after directory table * (and if present after the fragment table), but before the ID table, * or it must be set to 0xFFFFFFFF to indicate that the table is not * present. */ sqfs_u64 export_table_start; }; /** * @enum SQFS_COMPRESSOR * * @brief Set in @ref sqfs_super_t to identify the compresser used by the * filesystem. * * Most of the flags that can be set are informative only. */ typedef enum { SQFS_COMP_GZIP = 1, SQFS_COMP_LZMA = 2, SQFS_COMP_LZO = 3, SQFS_COMP_XZ = 4, SQFS_COMP_LZ4 = 5, SQFS_COMP_ZSTD = 6, SQFS_COMP_MIN = 1, SQFS_COMP_MAX = 6, } SQFS_COMPRESSOR; /** * @enum SQFS_SUPER_FLAGS * * @brief Flags that can be set in @ref sqfs_super flags field. */ typedef enum { /** * @brief Set to indicate that meta data blocks holding the inodes are * stored uncompressed. */ SQFS_FLAG_UNCOMPRESSED_INODES = 0x0001, /** * @brief Set to indicate that all data blocks are stored uncompressed. */ SQFS_FLAG_UNCOMPRESSED_DATA = 0x0002, /** * @brief Set to indicate that all fragment blocks are stored * uncompressed. */ SQFS_FLAG_UNCOMPRESSED_FRAGMENTS = 0x0008, /** * @brief Set to indicate that there are no fragment blocks. */ SQFS_FLAG_NO_FRAGMENTS = 0x0010, /** * @brief Set to indicate that fragments have been generated for all * files that are not a multiple of the block size in size. */ SQFS_FLAG_ALWAYS_FRAGMENTS = 0x0020, /** * @brief Set to indicate that data blocks have not been deduplicated. */ SQFS_FLAG_NO_DUPLICATES = 0x0040, /** * @brief Set to indicate that an NFS export table is present. */ SQFS_FLAG_EXPORTABLE = 0x0080, /** * @brief Set to indicate that meta data blocks holding extended * attributes are stored uncompressed. */ SQFS_FLAG_UNCOMPRESSED_XATTRS = 0x0100, /** * @brief Set to indicate that the filesystem does not * contain extended attributes. */ SQFS_FLAG_NO_XATTRS = 0x0200, /** * @brief Set to indicate that a single, uncompressed meta data block * with compressor options follows the super block. */ SQFS_FLAG_COMPRESSOR_OPTIONS = 0x0400, /** * @brief Set to indicate that meta data blocks holding the IDs are * stored uncompressed. */ SQFS_FLAG_UNCOMPRESSED_IDS = 0x0800, } SQFS_SUPER_FLAGS; #ifdef __cplusplus extern "C" { #endif /** * @brief Initialize the SquashFS super block. * * @memberof sqfs_super_t * * @param super A pointer to a super block structure. * @param block_size The uncompressed size of the data blocks in bytes. * @param mtime The modification time stamp to set. * @param compressor The compressor ID to set. * * @return Zero on success, an @ref SQFS_ERROR value if one of the * fields does not hold a valid value. */ SQFS_API int sqfs_super_init(sqfs_super_t *super, size_t block_size, sqfs_u32 mtime, SQFS_COMPRESSOR compressor); /** * @brief Encode and write a SquashFS super block to disk. * * @memberof sqfs_super_t * * @param super A pointer to the super block structure to write. * @param file A file object through which to access the filesystem image. * * @return Zero on success, an @ref SQFS_ERROR value if one of the * fields does not hold a valid value. */ SQFS_API int sqfs_super_write(const sqfs_super_t *super, sqfs_file_t *file); /** * @brief Read a SquashFS super block from disk, decode it and check the fields * * @memberof sqfs_super_t * * @param super A pointer to the super block structure to fill. * @param file A file object through which to access the filesystem image. * * @return Zero on success, an @ref SQFS_ERROR value if one of the * fields does not hold a valid value. */ SQFS_API int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file); #ifdef __cplusplus } #endif #endif /* SQFS_SUPER_H */ squashfs-tools-ng-1.1.3/include/sqfs/table.h000066400000000000000000000065011410627516300207570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * table.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_TABLE_H #define SQFS_TABLE_H #include "sqfs/predef.h" /** * @file table.h * * @brief Contains helper functions for reading or writing tables. */ #ifdef __cplusplus extern "C" { #endif /** * @brief Write a table to disk. * * This function takes an in-memory array, breaks it down into meta data * blocks, compresses and writes those blocks to the given output file and * then writes a raw list of 64 bit absolute locations of each meta data * block. The position of the location list is returned through a pointer. * * @param file The output file to write to. * @param cmp A compressor to use for compressing the meta data blocks. * @param data A pointer to a array to divide into blocks and write to disk. * @param table_size The size of the input array in bytes. * @param start Returns the absolute position of the location list. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, const void *data, size_t table_size, sqfs_u64 *start); /** * @brief Read a table from a SquashFS filesystem. * * This function takes an absolute position and an array size as input. It * then computes the number of meta data blocks required to store this array * and reads that many 64 bit integers from the given start location. Each * integer is interpreted as the location of a meta data block containing the * respective array chunk. * * The entire data encoded in that way is read and uncompressed into memory. * * @param file An input file to read from. * @param cmp A compressor to use for uncompressing the meta data block. * @param table_size The size of the entire array in bytes. * @param location The absolute position of the location list. * @param lower_limit The lowest "sane" position at which to expect a meta * data block. Anything less than that is interpreted * as an out-of-bounds read. * @param upper_limit The highest "sane" position at which to expect a meta * data block. Anything after that is interpreted as an * out-of-bounds read. * @param out Returns a pointer to the table in memory. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, size_t table_size, sqfs_u64 location, sqfs_u64 lower_limit, sqfs_u64 upper_limit, void **out); #ifdef __cplusplus } #endif #endif /* SQFS_TABLE_H */ squashfs-tools-ng-1.1.3/include/sqfs/xattr.h000066400000000000000000000116461410627516300210400ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_XATTR_H #define SQFS_XATTR_H #include "sqfs/predef.h" /** * @file xattr.h * * @brief Contains on-disk data structures for storing extended attributes. */ /** * @enum SQFS_XATTR_TYPE * * Used by @ref sqfs_xattr_entry_t to encodes the xattr prefix. */ typedef enum { SQFS_XATTR_USER = 0, SQFS_XATTR_TRUSTED = 1, SQFS_XATTR_SECURITY = 2, SQFS_XATTR_FLAG_OOL = 0x100, SQFS_XATTR_PREFIX_MASK = 0xFF, } SQFS_XATTR_TYPE; /** * @struct sqfs_xattr_entry_t * * @brief On-disk data structure that holds a single xattr key * * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended * attributes on disk. */ struct sqfs_xattr_entry_t { /** * @brief Encodes the prefix of the key * * A @ref SQFS_XATTR_TYPE value. If the @ref SQFS_XATTR_FLAG_OOL is * set, the value that follows is not actually a string but a 64 bit * reference to the location where the value is actually stored. */ sqfs_u16 type; /** * @brief The size in bytes of the suffix string that follows */ sqfs_u16 size; sqfs_u8 key[]; }; /** * @struct sqfs_xattr_value_t * * @brief On-disk data structure that holds a single xattr value * * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended * attributes on disk. */ struct sqfs_xattr_value_t { /** * @brief The exact size in bytes of the value that follows */ sqfs_u32 size; sqfs_u8 value[]; }; /** * @struct sqfs_xattr_id_t * * @brief On-disk data structure that describes a set of key-value pairs * * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended * attributes on disk. */ struct sqfs_xattr_id_t { /** * @brief Location of the first key-value pair * * This is a reference, i.e. the bits 16 to 48 hold an offset that is * added to xattr_table_start from @ref sqfs_xattr_id_table_t to get * the location of a meta data block that contains the first key-value * pair. The lower 16 bits store an offset into the uncompressed meta * data block. */ sqfs_u64 xattr; /** * @brief Number of consecutive key-value pairs */ sqfs_u32 count; /** * @brief Total size of the uncompressed key-value pairs in bytes, * including data structures used to encode them. */ sqfs_u32 size; }; /** * @struct sqfs_xattr_id_table_t * * @brief On-disk data structure that the super block points to * * Indicates the locations of the xattr key-value pairs and descriptor array. * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended * attributes on disk. */ struct sqfs_xattr_id_table_t { /** * @brief The location of the first meta data block holding the key * value pairs. */ sqfs_u64 xattr_table_start; /** * @brief The total number of descriptors (@ref sqfs_xattr_id_t) */ sqfs_u32 xattr_ids; /** * @brief Unused, alwayas set this to 0 when writing! */ sqfs_u32 unused; /** * @brief Holds the locations of the meta data blocks that contain the * @ref sqfs_xattr_id_t descriptor array. */ sqfs_u64 locations[]; }; #ifdef __cplusplus extern "C" { #endif /** * @brief Resolve an xattr identifier to the coresponding prefix * * Like many file systems, SquashFS stores xattrs be cutting off the common * prefix of the key string and storing an enumerator instead to save memory. * * This function takes an @ref SQFS_XATTR_TYPE identifier and returns the * coresponding prefix string, including the '.' at the end that separates * the prefix from the rest of the key. */ SQFS_API const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id); /** * @brief Resolve an xattr prefix into an identifier * * Like many file systems, SquashFS stores xattrs be cutting off the common * prefix of the key string and storing an enumerator instead to save memory. * * This function takes a key and finds the enumerator value that represents * its prefix. An error value is returned if the given prefix isn't supported. * * @return On success an @ref SQFS_XATTR_TYPE. If not supported, the * @ref SQFS_ERROR_UNSUPPORTED error code. */ SQFS_API int sqfs_get_xattr_prefix_id(const char *key); #ifdef __cplusplus } #endif #endif /* SQFS_XATTR_H */ squashfs-tools-ng-1.1.3/include/sqfs/xattr_reader.h000066400000000000000000000164251410627516300223620ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_reader.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_XATTR_READER_H #define SQFS_XATTR_READER_H #include "sqfs/predef.h" /** * @file xattr_reader.h * * @brief Contains declarations for the @ref sqfs_xattr_reader_t. */ /** * @struct sqfs_xattr_reader_t * * @implements sqfs_object_t * * @brief Abstracts read access to extended attributes in a SquashFS filesystem * * SquashFS stores extended attributes using multiple levels of indirection. * First of all, the key-value pairs of each inode (that has extended * attributes) are deduplicated and stored consecutively in meta data blocks. * Furthermore, a value can be stored out-of-band, i.e. it holds a reference to * another location from which the value has to be read. * * For each unique set of key-value pairs, a descriptor object is generated * that holds the location of the first pair, the number of pairs and the total * size used on disk. The array of descriptor objects is stored in multiple * meta data blocks. Each inode that has extended attributes holds a 32 bit * index into the descriptor array. * * The third layer of indirection is yet another table that points to the * locations of the previous two tables. Its location is in turn stored in * the super block. * * The sqfs_xattr_reader_t data structure takes care of the low level details * of loading and parsing the data. * * After creating an instance using @ref sqfs_xattr_reader_create, simply call * @ref sqfs_xattr_reader_load_locations to load and parse all of the location * tables. Then use @ref sqfs_xattr_reader_get_desc to resolve a 32 bit index * from an inode to a descriptor structure. Use @ref sqfs_xattr_reader_seek_kv * to point the reader to the start of the key-value pairs and the call * @ref sqfs_xattr_reader_read_key and @ref sqfs_xattr_reader_read_value * consecutively to read and decode each key-value pair. */ #ifdef __cplusplus extern "C" { #endif /** * @brief Create an xattr reader * * @memberof sqfs_xattr_reader_t * * This function creates an object that abstracts away read only access to * the extended attributes in a SquashFS filesystem. * * After creating a reader and before using it, call * @ref sqfs_xattr_reader_load_locations to load and parse the location * information required to look up xattr key-value pairs. * * All pointers passed to this function are stored internally for later use. * Do not destroy any of the pointed to objects before destroying the xattr * reader. * * @param flags Currently must be set to 0, or creation will fail. * * @return A pointer to a new xattr reader instance on success, NULL on * allocation failure. */ SQFS_API sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags); /** * @brief Load the locations of the xattr meta data blocks into memory * * @memberof sqfs_xattr_reader_t * * This function must be called explicitly after an xattr reader has been * created to load the actual location table from disk. * * @param xr A pointer to an xattr reader instance. * @param super A pointer to the SquashFS super block required to find the * location tables. * @param file A pointer to a file object that contains the SquashFS filesystem. * @param cmp A pointer to a compressor used to uncompress the loaded meta data * blocks. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, sqfs_file_t *file, sqfs_compressor_t *cmp); /** * @brief Resolve an xattr index from an inode to an xattr description * * @memberof sqfs_xattr_reader_t * * This function takes an xattr index from an extended inode type and resolves * it to a descriptor that points to location of the key-value pairs and * indicates how many key-value pairs to read from there. * * @param xr A pointer to an xattr reader instance * @param idx The xattr index to resolve * @param desc Used to return the description * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, sqfs_xattr_id_t *desc); /** * @brief Resolve an xattr index from an inode to an xattr description * * @memberof sqfs_xattr_reader_t * * This function takes an xattr descriptor object and seeks to the meta data * block containing the key-value pairs. The individual pairs can then be read * using consecutive calls to @ref sqfs_xattr_reader_read_key and * @ref sqfs_xattr_reader_read_value. * * @param xr A pointer to an xattr reader instance * @param desc The descriptor holding the location of the key-value pairs * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, const sqfs_xattr_id_t *desc); /** * @brief Read the next xattr key * * @memberof sqfs_xattr_reader_t * * After setting the start position using @ref sqfs_xattr_reader_seek_kv, this * function reads and decodes an xattr key and advances the internal position * indicator to the location after the key. The value can then be read using * using @ref sqfs_xattr_reader_read_value. After reading the value, the next * key can be read by calling this function again. * * @param xr A pointer to an xattr reader instance * @param key_out Used to return the decoded key. The underlying memory can be * released using a single @ref sqfs_free call. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, sqfs_xattr_entry_t **key_out); /** * @brief Read the xattr value belonging to the last read key * * @memberof sqfs_xattr_reader_t * * After calling @ref sqfs_xattr_reader_read_key, this function can read and * decode the asociated value. The internal location indicator is then advanced * past the key to the next value, so @ref sqfs_xattr_reader_read_key can be * called again to read the next key. * * @param xr A pointer to an xattr reader instance. * @param key A pointer to the decoded key object. * @param val_out Used to return the decoded value. The underlying memory can * be released using a single @ref sqfs_free call. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, const sqfs_xattr_entry_t *key, sqfs_xattr_value_t **val_out); #ifdef __cplusplus } #endif #endif /* SQFS_XATTR_READER_H */ squashfs-tools-ng-1.1.3/include/sqfs/xattr_writer.h000066400000000000000000000113351410627516300224270ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_writer.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_XATRR_WRITER_H #define SQFS_XATRR_WRITER_H #include "sqfs/predef.h" /** * @file xattr_writer.h * * @brief Contains declarations for the @ref sqfs_xattr_writer_t. */ /** * @struct sqfs_xattr_writer_t * * @implements sqfs_object_t * * @brief Abstracts writing of extended attributes to a SquashFS filesystem. * * This data structure provides a simple, abstract interface to recording * extended attributes that hads out 32 bit tokens required for inodes to * refere to them. * * Use @ref sqfs_xattr_writer_begin to start a block of key-value pairs, then * add the individual pairs with @ref sqfs_xattr_writer_add and finaly use * @ref sqfs_xattr_writer_end which returns the required token. * * Finally, use @ref sqfs_xattr_writer_flush to have the extended attributes * written to disk. * * The writer internally takes care of propper deduplication and packaging * everything up in compressed meta data blocks in the multi-level hierarchy * used by SquashFS. See @ref sqfs_xattr_reader_t for a brief overview on how * SquashFS stores extended attributes. */ #ifdef __cplusplus extern "C" { #endif /** * @brief Create an xattr writer instance. * * @memberof sqfs_xattr_writer_t * * @param flags Currently must be zero or the function fails. * * @return A pointer to a new xattr writer, NULL on allocation failure. */ SQFS_API sqfs_xattr_writer_t *sqfs_xattr_writer_create(sqfs_u32 flags); /** * @brief Begin recording a block of key-value pairs. * * @memberof sqfs_xattr_writer_t * * Use @ref sqfs_xattr_writer_add to add the individual pairs. Call * @ref sqfs_xattr_writer_end when you are done. * * @param xwr A pointer to an xattr writer instance. * @param flags Currently must be zero, or the function will fail. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr, sqfs_u32 flags); /** * @brief Add a key-value pair to the current block. * * @memberof sqfs_xattr_writer_t * * @param xwr A pointer to an xattr writer instance. * @param key The xattr key string. * @param value The associated value to store. * @param size The size of the value blob. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, const void *value, size_t size); /** * @brief Finish a generating a key-value block. * * @memberof sqfs_xattr_writer_t * * This function internally takes care of deduplicating the current block * and generates the coresponding 32 bit xattr token used by SquashFS inodes. * The token it returns can be one it returned previously if the block turns * out to be a duplicate of a previous block. * * @param xwr A pointer to an xattr writer instance. * @param out Returns an ID that has to be set to the inode that the block of * key-value pairs belongs to. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out); /** * @brief Write all recorded key-value pairs to disk. * * @memberof sqfs_xattr_writer_t * * This function takes care of generating the extended attribute tables * used by SquashFS. Call it after you are donew with all other meta data * tables, since SquashFS requires it to be the very last thing in the * file system. * * @param xwr A pointer to an xattr writer instance. * @param file The output file to write the tables to. * @param super The super block to update with the table locations and flags. * @param cmp The compressor to user to compress the tables. * * @return Zero on success, a negative @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_xattr_writer_flush(const sqfs_xattr_writer_t *xwr, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp); #ifdef __cplusplus } #endif #endif /* SQFS_XATRR_WRITER_H */ squashfs-tools-ng-1.1.3/include/str_table.h000066400000000000000000000034401410627516300206720ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * str_table.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef STR_TABLE_H #define STR_TABLE_H #include "sqfs/predef.h" #include "array.h" #include "hash_table.h" typedef struct { size_t index; size_t refcount; char string[]; } str_bucket_t; /* Stores strings in a hash table and assigns an incremental, unique ID to each string. Subsequent additions return the existing ID. The ID can be used for (hopefully) constant time lookup of the original string. */ typedef struct { /* an array that resolves index to bucket pointer */ array_t bucket_ptrs; /* hash table with the string buckets attached */ struct hash_table *ht; /* the next ID we are going to allocate */ size_t next_index; } str_table_t; /* the number of strings currently stored in the table */ static SQFS_INLINE size_t str_table_count(const str_table_t *table) { return table->next_index; } /* `size` is the number of hash table buckets to use internally. */ SQFS_INTERNAL int str_table_init(str_table_t *table); SQFS_INTERNAL void str_table_cleanup(str_table_t *table); SQFS_INTERNAL int str_table_copy(str_table_t *dst, const str_table_t *src); /* Resolve a string to an incremental, unique ID. */ SQFS_INTERNAL int str_table_get_index(str_table_t *table, const char *str, size_t *idx); /* Resolve a unique ID to the string it represents. Returns NULL if the ID is unknown, i.e. out of bounds. */ SQFS_INTERNAL const char *str_table_get_string(const str_table_t *table, size_t index); SQFS_INTERNAL void str_table_add_ref(str_table_t *table, size_t index); SQFS_INTERNAL void str_table_del_ref(str_table_t *table, size_t index); SQFS_INTERNAL size_t str_table_get_ref_count(const str_table_t *table, size_t index); #endif /* STR_TABLE_H */ squashfs-tools-ng-1.1.3/include/tar.h000066400000000000000000000061221410627516300175010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef TAR_H #define TAR_H #include "config.h" #include "compat.h" #include "fstream.h" #include #include #include typedef struct sparse_map_t { struct sparse_map_t *next; sqfs_u64 offset; sqfs_u64 count; } sparse_map_t; typedef struct { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char typeflag; char linkname[100]; char magic[6]; char version[2]; char uname[32]; char gname[32]; char devmajor[8]; char devminor[8]; union { struct { char prefix[155]; char padding[12]; } posix; struct { char atime[12]; char ctime[12]; char offset[12]; char deprecated[4]; char unused; struct { char offset[12]; char numbytes[12]; } sparse[4]; char isextended; char realsize[12]; char padding[17]; } gnu; } tail; } tar_header_t; typedef struct { struct { char offset[12]; char numbytes[12]; } sparse[21]; char isextended; char padding[7]; } gnu_sparse_t; typedef struct tar_xattr_t { struct tar_xattr_t *next; char *key; sqfs_u8 *value; size_t value_len; char data[]; } tar_xattr_t; typedef struct { struct stat sb; char *name; char *link_target; sparse_map_t *sparse; sqfs_u64 actual_size; sqfs_u64 record_size; bool unknown_record; bool is_hard_link; tar_xattr_t *xattr; /* broken out since struct stat could contain 32 bit values on 32 bit systems. */ sqfs_s64 mtime; } tar_header_decoded_t; #define TAR_TYPE_FILE '0' #define TAR_TYPE_LINK '1' #define TAR_TYPE_SLINK '2' #define TAR_TYPE_CHARDEV '3' #define TAR_TYPE_BLOCKDEV '4' #define TAR_TYPE_DIR '5' #define TAR_TYPE_FIFO '6' #define TAR_TYPE_GNU_SLINK 'K' #define TAR_TYPE_GNU_PATH 'L' #define TAR_TYPE_GNU_SPARSE 'S' #define TAR_TYPE_PAX 'x' #define TAR_TYPE_PAX_GLOBAL 'g' #define TAR_MAGIC "ustar" #define TAR_VERSION "00" #define TAR_MAGIC_OLD "ustar " #define TAR_VERSION_OLD " " #define TAR_RECORD_SIZE (512) /* Returns < 0 on failure, > 0 if cannot encode, 0 on success. Prints error/warning messages to stderr. The counter is an incremental record counter used if additional headers need to be generated. */ int write_tar_header(ostream_t *fp, const struct stat *sb, const char *name, const char *slink_target, const tar_xattr_t *xattr, unsigned int counter); int write_hard_link(ostream_t *fp, const struct stat *sb, const char *name, const char *target, unsigned int counter); /* calcuate and skip the zero padding */ int skip_padding(istream_t *fp, sqfs_u64 size); /* round up to block size and skip the entire entry */ int skip_entry(istream_t *fp, sqfs_u64 size); int read_header(istream_t *fp, tar_header_decoded_t *out); void free_xattr_list(tar_xattr_t *list); void clear_header(tar_header_decoded_t *hdr); /* Write zero bytes to an output file to padd it to the tar record size. Returns 0 on success. On failure, prints error message to stderr. */ int padd_file(ostream_t *fp, sqfs_u64 size); #endif /* TAR_H */ squashfs-tools-ng-1.1.3/include/threadpool.h000066400000000000000000000071341410627516300210600ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool.h * * Copyright (C) 2021 David Oberhollenzer */ #ifndef THREADPOOL_H #define THREADPOOL_H #include "sqfs/predef.h" typedef int (*thread_pool_worker_t)(void *user, void *work_item); /** * @struct thread_pool_t * * @brief A thread pool with a ticket number based work item ordering. * * While the order in which items are non-deterministic, the thread pool * implementation internally uses a ticket system to ensure the completed * items are deqeueued in the same order that they were enqueued. */ typedef struct thread_pool_t { /** * @brief Shutdown and destroy a thread pool. * * @param pool A pointer to a pool returned by thread_pool_create */ void (*destroy)(struct thread_pool_t *pool); /** * @brief Get the actual number of worker threads available. * * @return A number greater or equal to 1. */ size_t (*get_worker_count)(struct thread_pool_t *pool); /** * @brief Change the user data pointer for a thread pool worker * by index. * * @param idx A zero-based index into the worker list. * @param ptr A user pointer that this specific worker thread should * pass to the worker callback. */ void (*set_worker_ptr)(struct thread_pool_t *pool, size_t idx, void *ptr); /** * @brief Submit a work item to a thread pool. * * This function will fail on allocation failure or if the internal * error state is set was set by one of the workers. * * @param ptr A pointer to a work object to enqueue. * * @return Zero on success. */ int (*submit)(struct thread_pool_t *pool, void *ptr); /** * @brief Wait for a work item to be completed. * * This function dequeues a single completed work item. It may block * until one of the worker threads signals completion of an additional * item. * * This function guarantees to return the items in the same order as * they were submitted, so the function can actually block longer than * necessary, because it has to wait until the next item in sequence * is finished. * * @return A pointer to a new work item or NULL if there are none * in the pipeline. */ void *(*dequeue)(struct thread_pool_t *pool); /** * @brief Get the internal worker return status value. * * If the worker functions returns a non-zero exit status in one of the * worker threads, the thread pool stors the value internally and shuts * down. This function can be used to retrieve the value. * * @return A non-zero value returned by the worker callback or zero if * everything is A-OK. */ int (*get_status)(struct thread_pool_t *pool); } thread_pool_t; #ifdef __cplusplus extern "C" { #endif /** * @brief Create a thread pool instance. * * @param num_jobs The number of worker threads to launch. * @param worker A function to call from the worker threads to process * the work items. * * @return A pointer to a thread pool on success, NULL on failure. */ SQFS_INTERNAL thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker); /** * @brief Create a serial mockup thread pool implementation. * * This returns a @ref thread_pool_t implementation that, instead of running a * thread pool actually does the work in-situ when dequeueing. * * @param worker A function to call from the worker threads to process * the work items. * * @return A pointer to a thread pool on success, NULL on failure. */ SQFS_INTERNAL thread_pool_t *thread_pool_create_serial(thread_pool_worker_t worker); #ifdef __cplusplus } #endif #endif /* THREADPOOL_H */ squashfs-tools-ng-1.1.3/include/util.h000066400000000000000000000017721410627516300176760ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * util.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef SQFS_UTIL_H #define SQFS_UTIL_H #include "config.h" #include "sqfs/predef.h" #include "compat.h" #include /* Helper for allocating data structures with flexible array members. 'base_size' is the size of the struct itself, 'item_size' the size of a single array element and 'nmemb' the number of elements. Iternally checks for arithmetic overflows when allocating the combined thing. */ SQFS_INTERNAL void *alloc_flex(size_t base_size, size_t item_size, size_t nmemb); /* Basically the same as calloc, but *ALWAYS* does overflow checking */ SQFS_INTERNAL void *alloc_array(size_t item_size, size_t nmemb); SQFS_INTERNAL sqfs_u32 xxh32(const void *input, const size_t len); /* Returns true if the given region of memory is filled with zero-bytes only. */ SQFS_INTERNAL bool is_memory_zero(const void *blob, size_t size); #endif /* SQFS_UTIL_H */ squashfs-tools-ng-1.1.3/include/w32threadwrap.h000066400000000000000000000040231410627516300214060ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * w32threadwrap.h * * Copyright (C) 2021 David Oberhollenzer */ #ifndef W32THREADWRAP_H #define W32THREADWRAP_H #include "sqfs/predef.h" #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include typedef unsigned int sigset_t; typedef HANDLE pthread_t; typedef CRITICAL_SECTION pthread_mutex_t; typedef CONDITION_VARIABLE pthread_cond_t; static inline int pthread_create(pthread_t *thread, const void *attr, LPTHREAD_START_ROUTINE proc, LPVOID arg) { HANDLE hnd = CreateThread(NULL, 0, proc, arg, 0, 0); (void)attr; if (hnd == NULL) return -1; *thread = hnd; return 0; } static int pthread_join(pthread_t thread, void **retval) { WaitForSingleObject(thread, INFINITE); CloseHandle(thread); if (retval != NULL) *retval = NULL; return 0; } static inline int pthread_mutex_init(pthread_mutex_t *mutex, const void *attr) { (void)attr; InitializeCriticalSection(mutex); return 0; } static inline int pthread_mutex_lock(pthread_mutex_t *mutex) { EnterCriticalSection(mutex); return 0; } static inline int pthread_mutex_unlock(pthread_mutex_t *mutex) { LeaveCriticalSection(mutex); return 0; } static inline void pthread_mutex_destroy(pthread_mutex_t *mutex) { DeleteCriticalSection(mutex); } static inline int pthread_cond_init(pthread_cond_t *cond, const void *attr) { (void)attr; InitializeConditionVariable(cond); return 0; } static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mtx) { return SleepConditionVariableCS(cond, mtx, INFINITE) != 0; } static inline int pthread_cond_broadcast(pthread_cond_t *cond) { WakeAllConditionVariable(cond); return 0; } static inline void pthread_cond_destroy(pthread_cond_t *cond) { (void)cond; } #define SIG_SETMASK (0) static inline int sigfillset(sigset_t *set) { *set = 0xFFFFFFFF; return 0; } static inline int pthread_sigmask(int how, const sigset_t *set, const sigset_t *old) { (void)how; (void)set; (void)old; return 0; } #endif /* W32THREADWRAP_H */ squashfs-tools-ng-1.1.3/lib/000077500000000000000000000000001410627516300156645ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/common/000077500000000000000000000000001410627516300171545ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/common/Makemodule.am000066400000000000000000000016251410627516300215620ustar00rootroot00000000000000libcommon_a_SOURCES = lib/common/inode_stat.c lib/common/hardlink.c libcommon_a_SOURCES += lib/common/print_version.c lib/common/data_reader_dump.c libcommon_a_SOURCES += lib/common/compress.c lib/common/comp_opt.c libcommon_a_SOURCES += lib/common/data_writer.c include/common.h libcommon_a_SOURCES += lib/common/get_path.c lib/common/data_writer_ostream.c libcommon_a_SOURCES += lib/common/perror.c libcommon_a_SOURCES += lib/common/mkdir_p.c lib/common/parse_size.c libcommon_a_SOURCES += lib/common/print_size.c include/simple_writer.h libcommon_a_SOURCES += include/compress_cli.h libcommon_a_SOURCES += lib/common/writer/init.c lib/common/writer/cleanup.c libcommon_a_SOURCES += lib/common/writer/serialize_fstree.c libcommon_a_SOURCES += lib/common/writer/finish.c libcommon_a_CFLAGS = $(AM_CFLAGS) $(LZO_CFLAGS) if WITH_LZO libcommon_a_SOURCES += lib/common/comp_lzo.c endif noinst_LIBRARIES += libcommon.a squashfs-tools-ng-1.1.3/lib/common/comp_lzo.c000066400000000000000000000151031410627516300211420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * comp_lzo.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compress_cli.h" #include "compat.h" #include #include #include #include #define LZO_MAX_SIZE(size) (size + (size / 16) + 64 + 3) #define LZO_NUM_ALGS (sizeof(lzo_algs) / sizeof(lzo_algs[0])) typedef int (*lzo_cb_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem); static const struct { lzo_cb_t compress; size_t bufsize; } lzo_algs[] = { [SQFS_LZO1X_1] = { .compress = lzo1x_1_compress, .bufsize = LZO1X_1_MEM_COMPRESS, }, [SQFS_LZO1X_1_11] = { .compress = lzo1x_1_11_compress, .bufsize = LZO1X_1_11_MEM_COMPRESS, }, [SQFS_LZO1X_1_12] = { .compress = lzo1x_1_12_compress, .bufsize = LZO1X_1_12_MEM_COMPRESS, }, [SQFS_LZO1X_1_15] = { .compress = lzo1x_1_15_compress, .bufsize = LZO1X_1_15_MEM_COMPRESS, }, [SQFS_LZO1X_999] = { .compress = lzo1x_999_compress, .bufsize = LZO1X_999_MEM_COMPRESS, }, }; typedef struct { sqfs_compressor_t base; size_t block_size; int algorithm; int level; size_t buf_size; size_t work_size; sqfs_u8 buffer[]; } lzo_compressor_t; typedef struct { sqfs_u32 algorithm; sqfs_u32 level; } lzo_options_t; static int lzo_write_options(sqfs_compressor_t *base, sqfs_file_t *file) { lzo_compressor_t *lzo = (lzo_compressor_t *)base; sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; lzo_options_t opt; sqfs_u16 header; int ret; if (lzo->algorithm == SQFS_LZO_DEFAULT_ALG && lzo->level == SQFS_LZO_DEFAULT_LEVEL) { return 0; } opt.algorithm = htole32(lzo->algorithm); if (lzo->algorithm == SQFS_LZO1X_999) { opt.level = htole32(lzo->level); } else { opt.level = 0; } header = htole16(0x8000 | sizeof(opt)); memcpy(buffer, &header, sizeof(header)); memcpy(buffer + 2, &opt, sizeof(opt)); ret = file->write_at(file, sizeof(sqfs_super_t), buffer, sizeof(buffer)); return ret ? ret : (int)sizeof(buffer); } static int lzo_read_options(sqfs_compressor_t *base, sqfs_file_t *file) { lzo_compressor_t *lzo = (lzo_compressor_t *)base; sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; lzo_options_t opt; sqfs_u16 header; int ret; ret = file->read_at(file, sizeof(sqfs_super_t), buffer, sizeof(buffer)); if (ret) return ret; memcpy(&header, buffer, sizeof(header)); if (le16toh(header) != (0x8000 | sizeof(opt))) return SQFS_ERROR_CORRUPTED; memcpy(&opt, buffer + 2, sizeof(opt)); lzo->algorithm = le32toh(opt.algorithm); lzo->level = le32toh(opt.level); switch(lzo->algorithm) { case SQFS_LZO1X_1: case SQFS_LZO1X_1_11: case SQFS_LZO1X_1_12: case SQFS_LZO1X_1_15: if (lzo->level != 0) return SQFS_ERROR_UNSUPPORTED; break; case SQFS_LZO1X_999: if (lzo->level < 1 || lzo->level > 9) return SQFS_ERROR_UNSUPPORTED; break; default: return SQFS_ERROR_UNSUPPORTED; } return 0; } static sqfs_s32 lzo_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { lzo_compressor_t *lzo = (lzo_compressor_t *)base; void *scratch; lzo_uint len; int ret; if (size >= 0x7FFFFFFF) return 0; scratch = lzo->buffer + lzo->work_size; len = lzo->buf_size - lzo->work_size; if (lzo->algorithm == SQFS_LZO1X_999 && lzo->level != SQFS_LZO_DEFAULT_LEVEL) { ret = lzo1x_999_compress_level(in, size, scratch, &len, lzo->buffer, NULL, 0, 0, lzo->level); } else { ret = lzo_algs[lzo->algorithm].compress(in, size, scratch, &len, lzo->buffer); } if (ret != LZO_E_OK) return SQFS_ERROR_COMPRESSOR; if (len < size && len <= outsize) { memcpy(out, scratch, len); return len; } return 0; } static sqfs_s32 lzo_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { lzo_compressor_t *lzo = (lzo_compressor_t *)base; lzo_uint len = outsize; int ret; if (outsize >= 0x7FFFFFFF) return 0; ret = lzo1x_decompress_safe(in, size, out, &len, lzo->buffer); if (ret != LZO_E_OK) return SQFS_ERROR_COMPRESSOR; return len; } static void lzo_get_configuration(const sqfs_compressor_t *base, sqfs_compressor_config_t *cfg) { const lzo_compressor_t *lzo = (const lzo_compressor_t *)base; memset(cfg, 0, sizeof(*cfg)); cfg->id = SQFS_COMP_LZO; cfg->block_size = lzo->block_size; cfg->opt.lzo.algorithm = lzo->algorithm; cfg->level = lzo->level; if (base->do_block == lzo_uncomp_block) cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; } static sqfs_object_t *lzo_create_copy(const sqfs_object_t *cmp) { const lzo_compressor_t *other = (const lzo_compressor_t *)cmp; lzo_compressor_t *lzo; lzo = calloc(1, sizeof(*lzo) + other->buf_size); if (lzo == NULL) return NULL; memcpy(lzo, other, sizeof(*lzo)); return (sqfs_object_t *)lzo; } static void lzo_destroy(sqfs_object_t *base) { free(base); } int lzo_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { sqfs_compressor_t *base; lzo_compressor_t *lzo; size_t scratch_size; if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzo.algorithm >= LZO_NUM_ALGS || lzo_algs[cfg->opt.lzo.algorithm].compress == NULL) { return SQFS_ERROR_UNSUPPORTED; } if (cfg->opt.lzo.algorithm == SQFS_LZO1X_999) { if (cfg->level > SQFS_LZO_MAX_LEVEL) return SQFS_ERROR_UNSUPPORTED; } else if (cfg->level != 0) { return SQFS_ERROR_UNSUPPORTED; } /* XXX: liblzo does not do bounds checking internally, we need our own internal scratch buffer at worst case size... */ if (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) { scratch_size = 0; } else { scratch_size = cfg->block_size; if (scratch_size < SQFS_META_BLOCK_SIZE) scratch_size = SQFS_META_BLOCK_SIZE; scratch_size = LZO_MAX_SIZE(scratch_size); } /* ...in addition to the LZO work space buffer of course */ scratch_size += lzo_algs[cfg->opt.lzo.algorithm].bufsize; lzo = calloc(1, sizeof(*lzo) + scratch_size); base = (sqfs_compressor_t *)lzo; if (lzo == NULL) return SQFS_ERROR_ALLOC; lzo->block_size = cfg->block_size; lzo->algorithm = cfg->opt.lzo.algorithm; lzo->level = cfg->level; lzo->buf_size = scratch_size; lzo->work_size = lzo_algs[cfg->opt.lzo.algorithm].bufsize; base->get_configuration = lzo_get_configuration; base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? lzo_uncomp_block : lzo_comp_block; base->write_options = lzo_write_options; base->read_options = lzo_read_options; ((sqfs_object_t *)base)->copy = lzo_create_copy; ((sqfs_object_t *)base)->destroy = lzo_destroy; *out = base; return 0; } squashfs-tools-ng-1.1.3/lib/common/comp_opt.c000066400000000000000000000247271410627516300211540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * comp_opt.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "common.h" #include #include #include #include #include typedef struct { const char *name; sqfs_u16 flag; } flag_t; static const flag_t gzip_flags[] = { { "default", SQFS_COMP_FLAG_GZIP_DEFAULT }, { "filtered", SQFS_COMP_FLAG_GZIP_FILTERED }, { "huffman", SQFS_COMP_FLAG_GZIP_HUFFMAN }, { "rle", SQFS_COMP_FLAG_GZIP_RLE }, { "fixed", SQFS_COMP_FLAG_GZIP_FIXED }, }; static const flag_t xz_flags[] = { { "x86", SQFS_COMP_FLAG_XZ_X86 }, { "powerpc", SQFS_COMP_FLAG_XZ_POWERPC }, { "ia64", SQFS_COMP_FLAG_XZ_IA64 }, { "arm", SQFS_COMP_FLAG_XZ_ARM }, { "armthumb", SQFS_COMP_FLAG_XZ_ARMTHUMB }, { "sparc", SQFS_COMP_FLAG_XZ_SPARC }, { "extreme", SQFS_COMP_FLAG_XZ_EXTREME }, }; static const flag_t lzma_flags[] = { { "extreme", SQFS_COMP_FLAG_LZMA_EXTREME }, }; static const flag_t lz4_flags[] = { { "hc", SQFS_COMP_FLAG_LZ4_HC }, }; static const struct { const flag_t *flags; size_t count; } comp_flags[SQFS_COMP_MAX + 1] = { [SQFS_COMP_GZIP] = { gzip_flags, sizeof(gzip_flags) / sizeof(flag_t) }, [SQFS_COMP_XZ] = { xz_flags, sizeof(xz_flags) / sizeof(flag_t) }, [SQFS_COMP_LZMA] = { lzma_flags, sizeof(lzma_flags) / sizeof(flag_t) }, [SQFS_COMP_LZ4] = { lz4_flags, sizeof(lz4_flags) / sizeof(flag_t) }, }; static const char *lzo_algs[] = { [SQFS_LZO1X_1] = "lzo1x_1", [SQFS_LZO1X_1_11] = "lzo1x_1_11", [SQFS_LZO1X_1_12] = "lzo1x_1_12", [SQFS_LZO1X_1_15] = "lzo1x_1_15", [SQFS_LZO1X_999] = "lzo1x_999", }; static int set_flag(sqfs_compressor_config_t *cfg, const char *name) { const flag_t *flags = comp_flags[cfg->id].flags; size_t i, num_flags = comp_flags[cfg->id].count; for (i = 0; i < num_flags; ++i) { if (strcmp(flags[i].name, name) == 0) { cfg->flags |= flags[i].flag; return 0; } } return -1; } static int find_lzo_alg(sqfs_compressor_config_t *cfg, const char *name) { size_t i; for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) { if (strcmp(lzo_algs[i], name) == 0) { cfg->opt.lzo.algorithm = i; return 0; } } return -1; } enum { OPT_WINDOW = 0, OPT_LEVEL, OPT_ALG, OPT_DICT, OPT_LC, OPT_LP, OPT_PB, OPT_COUNT, }; static char *const token[] = { [OPT_WINDOW] = (char *)"window", [OPT_LEVEL] = (char *)"level", [OPT_ALG] = (char *)"algorithm", [OPT_DICT] = (char *)"dictsize", [OPT_LC] = (char *)"lc", [OPT_LP] = (char *)"lp", [OPT_PB] = (char *)"pb", NULL }; static int opt_available[SQFS_COMP_MAX + 1] = { [SQFS_COMP_GZIP] = (1 << OPT_WINDOW) | (1 << OPT_LEVEL), [SQFS_COMP_XZ] = (1 << OPT_LEVEL) | (1 << OPT_DICT) | (1 << OPT_LC) | (1 << OPT_LP) | (1 << OPT_PB), [SQFS_COMP_LZMA] = (1 << OPT_LEVEL) | (1 << OPT_DICT) | (1 << OPT_LC) | (1 << OPT_LP) | (1 << OPT_PB), [SQFS_COMP_ZSTD] = (1 << OPT_LEVEL), [SQFS_COMP_LZO] = (1 << OPT_LEVEL) | (1 << OPT_ALG), }; static const struct { int min; int max; } value_range[SQFS_COMP_MAX + 1][OPT_COUNT] = { [SQFS_COMP_GZIP] = { [OPT_LEVEL] = { SQFS_GZIP_MIN_LEVEL, SQFS_GZIP_MAX_LEVEL }, [OPT_WINDOW] = { SQFS_GZIP_MIN_WINDOW, SQFS_GZIP_MAX_WINDOW }, }, [SQFS_COMP_XZ] = { [OPT_LEVEL] = { SQFS_XZ_MIN_LEVEL, SQFS_XZ_MAX_LEVEL }, [OPT_DICT] = { SQFS_XZ_MIN_DICT_SIZE, SQFS_XZ_MAX_DICT_SIZE }, [OPT_LC] = { SQFS_XZ_MIN_LC, SQFS_XZ_MAX_LC }, [OPT_LP] = { SQFS_XZ_MIN_LP, SQFS_XZ_MAX_LP }, [OPT_PB] = { SQFS_XZ_MIN_PB, SQFS_XZ_MAX_PB }, }, [SQFS_COMP_LZMA] = { [OPT_LEVEL] = { SQFS_LZMA_MIN_LEVEL, SQFS_LZMA_MAX_LEVEL }, [OPT_DICT] = { SQFS_LZMA_MIN_DICT_SIZE, SQFS_LZMA_MAX_DICT_SIZE }, [OPT_LC] = { SQFS_LZMA_MIN_LC, SQFS_LZMA_MAX_LC }, [OPT_LP] = { SQFS_LZMA_MIN_LP, SQFS_LZMA_MAX_LP }, [OPT_PB] = { SQFS_LZMA_MIN_PB, SQFS_LZMA_MAX_PB }, }, [SQFS_COMP_ZSTD] = { [OPT_LEVEL] = { SQFS_ZSTD_MIN_LEVEL, SQFS_ZSTD_MAX_LEVEL }, }, [SQFS_COMP_LZO] = { [OPT_LEVEL] = { SQFS_LZO_MIN_LEVEL, SQFS_LZO_MAX_LEVEL }, }, }; int compressor_cfg_init_options(sqfs_compressor_config_t *cfg, SQFS_COMPRESSOR id, size_t block_size, char *options) { char *subopts, *value; int opt, ival; size_t szval; if (sqfs_compressor_config_init(cfg, id, block_size, 0)) return -1; if (options == NULL) return 0; subopts = options; while (*subopts != '\0') { opt = getsubopt(&subopts, token, &value); if (opt < 0) { if (set_flag(cfg, value)) goto fail_opt; continue; } if (!(opt_available[cfg->id] & (1 << opt))) goto fail_opt; if (value == NULL) goto fail_value; if (opt == OPT_ALG) { if (find_lzo_alg(cfg, value)) goto fail_lzo_alg; continue; } if (opt == OPT_DICT) { if (parse_size("Parsing LZMA dictionary size", &szval, value, cfg->block_size)) { return -1; } ival = szval; } else { ival = strtol(value, NULL, 10); } if (ival < value_range[cfg->id][opt].min) goto fail_range; if (ival > value_range[cfg->id][opt].max) goto fail_range; switch (opt) { case OPT_LEVEL: cfg->level = ival; break; case OPT_LC: cfg->opt.xz.lc = ival; break; case OPT_LP: cfg->opt.xz.lp = ival; break; case OPT_PB: cfg->opt.xz.pb = ival; break; case OPT_WINDOW: cfg->opt.gzip.window_size = ival; break; case OPT_DICT: cfg->opt.xz.dict_size = ival; break; default: break; } } if (cfg->id == SQFS_COMP_XZ || cfg->id == SQFS_COMP_LZMA) { if ((cfg->opt.xz.lp + cfg->opt.xz.lc) > 4) goto fail_sum_lp_lc; } return 0; fail_sum_lp_lc: fputs("Sum of XZ lc + lp must not exceed 4.\n", stderr); return -1; fail_lzo_alg: fprintf(stderr, "Unknown lzo variant '%s'.\n", value); return -1; fail_range: fprintf(stderr, "`%s` must be a number between %d and %d.\n", token[opt], value_range[cfg->id][opt].min, value_range[cfg->id][opt].max); return -1; fail_opt: fprintf(stderr, "Unknown compressor option '%s'.\n", value); return -1; fail_value: fprintf(stderr, "Missing value for compressor option '%s'.\n", token[opt]); return -1; } typedef void (*compressor_help_fun_t)(void); static void gzip_print_help(void) { size_t i; printf( "Available options for gzip compressor:\n" "\n" " level= Compression level. Value from 1 to 9.\n" " Defaults to %d.\n" " window= Deflate compression window size. Value from 8 to 15.\n" " Defaults to %d.\n" "\n" "In additon to the options, one or more strategies can be specified.\n" "If multiple stratgies are provided, the one yielding the best compression\n" "ratio will be used.\n" "\n" "The following strategies are available:\n", SQFS_GZIP_DEFAULT_LEVEL, SQFS_GZIP_DEFAULT_WINDOW); for (i = 0; i < sizeof(gzip_flags) / sizeof(gzip_flags[0]); ++i) printf("\t%s\n", gzip_flags[i].name); } static void lz4_print_help(void) { fputs("Available options for lz4 compressor:\n" "\n" " hc If present, use slower but better compressing\n" " variant of lz4.\n" "\n", stdout); } static void lzo_print_help(void) { size_t i; fputs("Available options for lzo compressor:\n" "\n" " algorithm= Specify the variant of lzo to use.\n" " Defaults to 'lzo1x_999'.\n" " level= For lzo1x_999, the compression level.\n" " Value from 1 to 9. Defaults to 8.\n" " Ignored if algorithm is not lzo1x_999.\n" "\n" "Available algorithms:\n", stdout); for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) printf("\t%s\n", lzo_algs[i]); } static void xz_lzma_print_help(void) { size_t i; printf( "Available options for LZMA and XZ (LZMA v2) compressors:\n" "\n" " dictsize= Dictionary size. Either a value in bytes or a\n" " percentage of the block size. Defaults to 100%%.\n" " The suffix '%%' indicates a percentage. 'K' and 'M'\n" " can also be used for kibi and mebi bytes\n" " respecitively.\n" " level= Compression level. Value from %d to %d.\n" " For XZ, defaults to %d, for LZMA defaults to %d.\n" " lc= Number of literal context bits.\n" " How many of the highest bits of the previous\n" " uncompressed byte to take into account when\n" " predicting the bits of the next byte.\n" " Default is %d.\n" " lp= Number of literal position bits.\n" " Affects what kind of alignment in the uncompressed\n" " data is assumed when encoding bytes.\n" " Default is %d.\n" " pb= Number of position bits.\n" " This is the log2 of the assumed underlying alignment\n" " of the input data, i.e. pb=0 means single byte\n" " allignment, pb=1 means 16 bit, 2 means 32 bit.\n" " Default is %d.\n" " extreme If this flag is set, try to crunch the data extra hard\n" " without increasing the decompressors memory\n" " requirements." "\n" "If values are set, the sum of lc + lp must not exceed 4.\n" "The maximum for pb is %d.\n" "\n" "In additon to the options, for the XZ compressor, one or more bcj filters\n" "can be specified.\n" "If multiple filters are provided, the one yielding the best compression\n" "ratio will be used.\n" "\n" "The following filters are available:\n", SQFS_XZ_MIN_LEVEL, SQFS_XZ_MAX_LEVEL, SQFS_XZ_DEFAULT_LEVEL, SQFS_LZMA_DEFAULT_LEVEL, SQFS_XZ_DEFAULT_LC, SQFS_XZ_DEFAULT_LP, SQFS_XZ_DEFAULT_PB, SQFS_XZ_MAX_PB); for (i = 0; i < sizeof(xz_flags) / sizeof(xz_flags[0]); ++i) printf("\t%s\n", xz_flags[i].name); } static void zstd_print_help(void) { printf("Available options for zstd compressor:\n" "\n" " level= Set compression level. Defaults to %d.\n" " Maximum is %d.\n" "\n", SQFS_ZSTD_DEFAULT_LEVEL, SQFS_ZSTD_MAX_LEVEL); } static const compressor_help_fun_t helpfuns[SQFS_COMP_MAX + 1] = { [SQFS_COMP_GZIP] = gzip_print_help, [SQFS_COMP_XZ] = xz_lzma_print_help, [SQFS_COMP_LZMA] = xz_lzma_print_help, [SQFS_COMP_LZO] = lzo_print_help, [SQFS_COMP_LZ4] = lz4_print_help, [SQFS_COMP_ZSTD] = zstd_print_help, }; void compressor_print_help(SQFS_COMPRESSOR id) { if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX) return; if (helpfuns[id] == NULL) return; helpfuns[id](); } squashfs-tools-ng-1.1.3/lib/common/compress.c000066400000000000000000000031311410627516300211510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compress.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compress_cli.h" #include #include static int cmp_ids[] = { SQFS_COMP_XZ, SQFS_COMP_ZSTD, SQFS_COMP_GZIP, SQFS_COMP_LZ4, SQFS_COMP_LZO, }; SQFS_COMPRESSOR compressor_get_default(void) { sqfs_compressor_config_t cfg; sqfs_compressor_t *temp; size_t i; int ret; for (i = 0; i < sizeof(cmp_ids) / sizeof(cmp_ids[0]); ++i) { sqfs_compressor_config_init(&cfg, cmp_ids[i], SQFS_DEFAULT_BLOCK_SIZE, 0); ret = sqfs_compressor_create(&cfg, &temp); if (ret == 0) { sqfs_destroy(temp); return cmp_ids[i]; } } #ifdef WITH_LZO return SQFS_COMP_LZO; #else assert(0); #endif } void compressor_print_available(void) { sqfs_compressor_config_t cfg; sqfs_compressor_t *temp; bool have_compressor; int i, ret, defcomp; const char *name; defcomp = compressor_get_default(); fputs("Available SquashFS block compressors:\n", stdout); for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) { sqfs_compressor_config_init(&cfg, i, SQFS_DEFAULT_BLOCK_SIZE, 0); ret = sqfs_compressor_create(&cfg, &temp); have_compressor = false; if (ret == 0) { sqfs_destroy(temp); have_compressor = true; } else { #ifdef WITH_LZO if (i == SQFS_COMP_LZO) have_compressor = true; #endif } if (have_compressor) { name = sqfs_compressor_name_from_id(i); if (defcomp == i) { printf("\t%s (default)\n", name); } else { printf("\t%s\n", name); } } } fputc('\n', stdout); } squashfs-tools-ng-1.1.3/lib/common/data_reader_dump.c000066400000000000000000000024551410627516300226060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * data_reader_dump.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include #include #include int sqfs_data_reader_dump(const char *name, sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, ostream_t *fp, size_t block_size) { size_t i, diff, chunk_size; sqfs_u64 filesz; sqfs_u8 *chunk; int err; sqfs_inode_get_file_size(inode, &filesz); for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { diff = (filesz < block_size) ? filesz : block_size; if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { if (ostream_append_sparse(fp, diff)) return -1; } else { err = sqfs_data_reader_get_block(data, inode, i, &chunk_size, &chunk); if (err) { sqfs_perror(name, "reading data block", err); return -1; } err = ostream_append(fp, chunk, chunk_size); free(chunk); if (err) return -1; } filesz -= diff; } if (filesz > 0) { err = sqfs_data_reader_get_fragment(data, inode, &chunk_size, &chunk); if (err) { sqfs_perror(name, "reading fragment block", err); return -1; } err = ostream_append(fp, chunk, chunk_size); free(chunk); if (err) return -1; } return 0; } squashfs-tools-ng-1.1.3/lib/common/data_writer.c000066400000000000000000000022101410627516300216200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * data_writer.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" static sqfs_u8 buffer[4096]; int write_data_from_file(const char *filename, sqfs_block_processor_t *data, sqfs_inode_generic_t **inode, sqfs_file_t *file, int flags) { sqfs_u64 filesz, offset; size_t diff; int ret; ret = sqfs_block_processor_begin_file(data, inode, NULL, flags); if (ret) { sqfs_perror(filename, "beginning file data blocks", ret); return -1; } filesz = file->get_size(file); for (offset = 0; offset < filesz; offset += diff) { if (filesz - offset > sizeof(buffer)) { diff = sizeof(buffer); } else { diff = filesz - offset; } ret = file->read_at(file, offset, buffer, diff); if (ret) { sqfs_perror(filename, "reading file range", ret); return -1; } ret = sqfs_block_processor_append(data, buffer, diff); if (ret) { sqfs_perror(filename, "packing file data", ret); return -1; } } ret = sqfs_block_processor_end_file(data); if (ret) { sqfs_perror(filename, "finishing file data", ret); return -1; } return 0; } squashfs-tools-ng-1.1.3/lib/common/data_writer_ostream.c000066400000000000000000000034341410627516300233630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * data_writer_ostream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "common.h" #include typedef struct{ ostream_t base; sqfs_block_processor_t *proc; const char *filename; } data_writer_ostream_t; static int stream_append(ostream_t *base, const void *data, size_t size) { data_writer_ostream_t *strm = (data_writer_ostream_t *)base; int ret; ret = sqfs_block_processor_append(strm->proc, data, size); if (ret != 0) { sqfs_perror(strm->filename, NULL, ret); return -1; } return 0; } static int stream_flush(ostream_t *base) { data_writer_ostream_t *strm = (data_writer_ostream_t *)base; int ret; ret = sqfs_block_processor_end_file(strm->proc); if (ret != 0) { sqfs_perror(strm->filename, NULL, ret); return -1; } return 0; } static const char *stream_get_filename(ostream_t *base) { data_writer_ostream_t *strm = (data_writer_ostream_t *)base; return strm->filename; } static void stream_destroy(sqfs_object_t *base) { free(base); } ostream_t *data_writer_ostream_create(const char *filename, sqfs_block_processor_t *proc, sqfs_inode_generic_t **inode, int flags) { data_writer_ostream_t *strm = calloc(1, sizeof(*strm)); sqfs_object_t *obj = (sqfs_object_t *)strm; ostream_t *base = (ostream_t *)strm; int ret; if (strm == NULL) { perror(filename); return NULL; } ret = sqfs_block_processor_begin_file(proc, inode, NULL, flags); if (ret != 0) { sqfs_perror(filename, NULL, ret); free(strm); return NULL; } strm->proc = proc; strm->filename = filename; base->append = stream_append; base->flush = stream_flush; base->get_filename = stream_get_filename; obj->destroy = stream_destroy; return base; } squashfs-tools-ng-1.1.3/lib/common/get_path.c000066400000000000000000000015471410627516300211220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include char *sqfs_tree_node_get_path(const sqfs_tree_node_t *node) { const sqfs_tree_node_t *it; char *str, *ptr; size_t len = 0; if (node->parent == NULL) { if (node->name[0] != '\0') return strdup((const char *)node->name); return strdup("/"); } for (it = node; it != NULL && it->parent != NULL; it = it->parent) { len += strlen((const char *)it->name) + 1; } str = malloc(len + 1); if (str == NULL) return NULL; ptr = str + len; *ptr = '\0'; for (it = node; it != NULL && it->parent != NULL; it = it->parent) { len = strlen((const char *)it->name); ptr -= len; memcpy(ptr, (const char *)it->name, len); *(--ptr) = '/'; } return str; } squashfs-tools-ng-1.1.3/lib/common/hardlink.c000066400000000000000000000043761410627516300211260ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * hardlink.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include "rbtree.h" #include #include #include static int map_nodes(rbtree_t *inumtree, sqfs_hard_link_t **out, const sqfs_tree_node_t *n) { const sqfs_tree_node_t *target; sqfs_hard_link_t *lnk; rbtree_node_t *tn; sqfs_u32 idx; int ret; /* XXX: refuse to generate hard links to directories */ if (n->children != NULL) { for (n = n->children; n != NULL; n = n->next) { if (map_nodes(inumtree, out, n)) return -1; } return 0; } if (!is_filename_sane((const char *)n->name, false)) return 0; idx = n->inode->base.inode_number; tn = rbtree_lookup(inumtree, &idx); if (tn == NULL) { ret = rbtree_insert(inumtree, &idx, &n); if (ret != 0) goto fail_insert; return 0; } target = *((const sqfs_tree_node_t **)rbtree_node_value(tn)); lnk = calloc(1, sizeof(*lnk)); if (lnk == NULL) goto fail_oom; lnk->inode_number = idx; lnk->target = sqfs_tree_node_get_path(target); if (lnk->target == NULL) goto fail_oom; if (canonicalize_name(lnk->target) == 0) { lnk->next = (*out); (*out) = lnk; } else { free(lnk->target); free(lnk); } return 0; fail_oom: fputs("detecting hard links in file system tree: out of memory\n", stderr); free(lnk); return -1; fail_insert: sqfs_perror(NULL, "detecting hard links in file system tree", ret); return -1; } static int compare_inum(const void *ctx, const void *lhs, const void *rhs) { sqfs_u32 l = *((const sqfs_u32 *)lhs), r = *((const sqfs_u32 *)rhs); (void)ctx; return l < r ? -1 : (l > r ? 1 : 0); } int sqfs_tree_find_hard_links(const sqfs_tree_node_t *root, sqfs_hard_link_t **out) { sqfs_hard_link_t *lnk = NULL; rbtree_t inumtree; int ret; ret = rbtree_init(&inumtree, sizeof(sqfs_u32), sizeof(sqfs_tree_node_t *), compare_inum); if (ret != 0) { sqfs_perror(NULL, "detecting hard links in file system tree", ret); return -1; } ret = map_nodes(&inumtree, out, root); rbtree_cleanup(&inumtree); if (ret != 0) { while ((*out) != NULL) { lnk = (*out); (*out) = lnk->next; free(lnk->target); free(lnk); } return -1; } return 0; } squashfs-tools-ng-1.1.3/lib/common/inode_stat.c000066400000000000000000000040431410627516300214520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * inode_stat.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include #include int inode_stat(const sqfs_tree_node_t *node, struct stat *sb) { memset(sb, 0, sizeof(*sb)); sb->st_mode = node->inode->base.mode; sb->st_uid = node->uid; sb->st_gid = node->gid; sb->st_atime = node->inode->base.mod_time; sb->st_mtime = node->inode->base.mod_time; sb->st_ctime = node->inode->base.mod_time; sb->st_ino = node->inode->base.inode_number; sb->st_nlink = 1; sb->st_blksize = 512; switch (node->inode->base.type) { case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: sb->st_rdev = node->inode->data.dev.devno; sb->st_nlink = node->inode->data.dev.nlink; break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: sb->st_rdev = node->inode->data.dev_ext.devno; sb->st_nlink = node->inode->data.dev_ext.nlink; break; case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: sb->st_nlink = node->inode->data.ipc.nlink; break; case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: sb->st_nlink = node->inode->data.ipc_ext.nlink; break; case SQFS_INODE_SLINK: sb->st_size = node->inode->data.slink.target_size; sb->st_nlink = node->inode->data.slink.nlink; break; case SQFS_INODE_EXT_SLINK: sb->st_size = node->inode->data.slink_ext.target_size; sb->st_nlink = node->inode->data.slink_ext.nlink; break; case SQFS_INODE_FILE: sb->st_size = node->inode->data.file.file_size; break; case SQFS_INODE_EXT_FILE: sb->st_size = node->inode->data.file_ext.file_size; sb->st_nlink = node->inode->data.file_ext.nlink; break; case SQFS_INODE_DIR: sb->st_size = node->inode->data.dir.size; sb->st_nlink = node->inode->data.dir.nlink; break; case SQFS_INODE_EXT_DIR: sb->st_size = node->inode->data.dir_ext.size; sb->st_nlink = node->inode->data.dir_ext.nlink; break; default: break; } sb->st_blocks = sb->st_size / sb->st_blksize; if (sb->st_size % sb->st_blksize) sb->st_blocks += 1; return 0; } squashfs-tools-ng-1.1.3/lib/common/mkdir_p.c000066400000000000000000000054161410627516300207530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mkdir_p.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include #include #include #ifdef _WIN32 /* Supported paths: - :\ - \\\\ - \\?\:\ - \\?\UNC\\\ - Relative path not starting with '\' */ static WCHAR *skip_unc_path(WCHAR *ptr) { /* server */ if (*ptr == '\0' || *ptr == '\\') return NULL; while (*ptr != '\0' && *ptr != '\\') ++ptr; if (*(ptr++) != '\\') return NULL; /* share */ if (*ptr == '\0' || *ptr == '\\') return NULL; while (*ptr != '\0' && *ptr != '\\') ++ptr; return (*ptr == '\\') ? (ptr + 1) : ptr; } static WCHAR *skip_prefix(WCHAR *ptr) { if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\') return ptr + 3; if (ptr[0] == '\\' && ptr[1] == '\\') { if (ptr[2] == '?') { if (ptr[3] != '\\') return NULL; ptr += 4; if ((ptr[0] == 'u' || ptr[0] == 'U') && (ptr[1] == 'n' || ptr[1] == 'N') && (ptr[2] == 'c' || ptr[2] == 'C') && ptr[3] == '\\') { ptr += 4; return skip_unc_path(ptr); } if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\') return ptr + 3; return NULL; } return skip_unc_path(ptr); } if (ptr[0] == '\\') return NULL; return ptr; } int mkdir_p(const char *path) { WCHAR *wpath, *ptr, *end; DWORD error; bool done; wpath = path_to_windows(path); if (wpath == NULL) return -1; ptr = skip_prefix(wpath); if (ptr == NULL) { fprintf(stderr, "Illegal or unsupported path: %s\n", path); goto fail; } while (*ptr != '\0') { if (*ptr == '\\') { ++ptr; continue; } for (end = ptr; *end != '\0' && *end != '\\'; ++end) ++end; if (*end == '\\') { done = false; *end = '\0'; } else { done = true; } if (!CreateDirectoryW(wpath, NULL)) { error = GetLastError(); if (error != ERROR_ALREADY_EXISTS) { fprintf(stderr, "Creating %s: %ld\n", path, error); goto fail; } } if (!done) { *end = '\\'; ptr = end + 1; } } free(wpath); return 0; fail: free(wpath); return -1; } #else int mkdir_p(const char *path) { size_t i, len; char *buffer; while (path[0] == '/' && path[1] == '/') ++path; if (*path == '\0' || (path[0] == '/' && path[1] == '\0')) return 0; len = strlen(path) + 1; buffer = alloca(len); for (i = 0; i < len; ++i) { if (i > 0 && (path[i] == '/' || path[i] == '\0')) { buffer[i] = '\0'; if (mkdir(buffer, 0755) != 0) { if (errno != EEXIST) { fprintf(stderr, "mkdir %s: %s\n", buffer, strerror(errno)); return -1; } } } buffer[i] = path[i]; } return 0; } #endif squashfs-tools-ng-1.1.3/lib/common/parse_size.c000066400000000000000000000024621410627516300214700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * parse_size.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include int parse_size(const char *what, size_t *out, const char *str, size_t reference) { const char *in = str; size_t acc = 0, x; if (!isdigit(*in)) goto fail_nan; while (isdigit(*in)) { x = *(in++) - '0'; if (SZ_MUL_OV(acc, 10, &acc)) goto fail_ov; if (SZ_ADD_OV(acc, x, &acc)) goto fail_ov; } switch (*in) { case 'k': case 'K': if (SZ_MUL_OV(acc, 1024, &acc)) goto fail_ov; ++in; break; case 'm': case 'M': if (SZ_MUL_OV(acc, 1048576, &acc)) goto fail_ov; ++in; break; case 'g': case 'G': if (SZ_MUL_OV(acc, 1073741824, &acc)) goto fail_ov; ++in; break; case '%': if (reference == 0) goto fail_suffix; if (SZ_MUL_OV(acc, reference, &acc)) goto fail_ov; acc /= 100; break; case '\0': break; default: goto fail_suffix; } if (*in != '\0') goto fail_suffix; *out = acc; return 0; fail_nan: fprintf(stderr, "%s: '%s' is not a number.\n", what, str); return -1; fail_ov: fprintf(stderr, "%s: numeric overflow parsing '%s'.\n", what, str); return -1; fail_suffix: fprintf(stderr, "%s: unknown suffix in '%s'.\n", what, str); return -1; } squashfs-tools-ng-1.1.3/lib/common/perror.c000066400000000000000000000034101410627516300206270ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * print_version.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include void sqfs_perror(const char *file, const char *action, int error_code) { const char *errstr; switch (error_code) { case SQFS_ERROR_ALLOC: errstr = "out of memory"; break; case SQFS_ERROR_IO: errstr = "I/O error"; break; case SQFS_ERROR_COMPRESSOR: errstr = "internal compressor error"; break; case SQFS_ERROR_INTERNAL: errstr = "internal error"; break; case SQFS_ERROR_CORRUPTED: errstr = "data corrupted"; break; case SQFS_ERROR_UNSUPPORTED: errstr = "unknown or not supported"; break; case SQFS_ERROR_OVERFLOW: errstr = "numeric overflow"; break; case SQFS_ERROR_OUT_OF_BOUNDS: errstr = "location out of bounds"; break; case SFQS_ERROR_SUPER_MAGIC: errstr = "wrong magic value in super block"; break; case SFQS_ERROR_SUPER_VERSION: errstr = "wrong squashfs version in super block"; break; case SQFS_ERROR_SUPER_BLOCK_SIZE: errstr = "invalid block size specified in super block"; break; case SQFS_ERROR_NOT_DIR: errstr = "target is not a directory"; break; case SQFS_ERROR_NO_ENTRY: errstr = "no such file or directory"; break; case SQFS_ERROR_LINK_LOOP: errstr = "hard link loop detected"; break; case SQFS_ERROR_NOT_FILE: errstr = "target is not a file"; break; case SQFS_ERROR_ARG_INVALID: errstr = "invalid argument"; break; case SQFS_ERROR_SEQUENCE: errstr = "illegal oder of operations"; break; default: errstr = "libsquashfs returned an unknown error code"; break; } if (file != NULL) fprintf(stderr, "%s: ", file); if (action != NULL) fprintf(stderr, "%s: ", action); fprintf(stderr, "%s.\n", errstr); } squashfs-tools-ng-1.1.3/lib/common/print_size.c000066400000000000000000000014531410627516300215110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * print_size.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "common.h" void print_size(sqfs_u64 size, char *buffer, bool round_to_int) { static const char *fractions = "0112334456678899"; static const char *suffices = "kMGTPEZY"; unsigned int fraction; int suffix = -1; while (size > 1024) { ++suffix; fraction = size % 1024; size /= 1024; } if (suffix >= 0) { fraction /= 64; if (round_to_int) { size = fraction >= 8 ? (size + 1) : size; sprintf(buffer, "%u%c", (unsigned int)size, suffices[suffix]); } else { sprintf(buffer, "%u.%c%c", (unsigned int)size, fractions[fraction], suffices[suffix]); } } else { sprintf(buffer, "%u", (unsigned int)size); } } squashfs-tools-ng-1.1.3/lib/common/print_version.c000066400000000000000000000013321410627516300222200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * print_version.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #define LICENSE_SHORT "GPLv3+" #define LICENSE_LONG "GNU GPL version 3 or later" #define LICENSE_URL "https://gnu.org/licenses/gpl.html" static const char *version_string = "%s (%s) %s\n" "Copyright (c) 2019 David Oberhollenzer et al\n" "License " LICENSE_SHORT ": " LICENSE_LONG " <" LICENSE_URL ">.\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"; void print_version(const char *progname) { printf(version_string, progname, PACKAGE_NAME, PACKAGE_VERSION); } squashfs-tools-ng-1.1.3/lib/common/writer/000077500000000000000000000000001410627516300204705ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/common/writer/cleanup.c000066400000000000000000000014741410627516300222710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * cleanup.c * * Copyright (C) 2019 David Oberhollenzer */ #include "simple_writer.h" #include void sqfs_writer_cleanup(sqfs_writer_t *sqfs, int status) { if (sqfs->xwr != NULL) sqfs_destroy(sqfs->xwr); sqfs_destroy(sqfs->dirwr); sqfs_destroy(sqfs->dm); sqfs_destroy(sqfs->im); sqfs_destroy(sqfs->idtbl); sqfs_destroy(sqfs->data); sqfs_destroy(sqfs->blkwr); sqfs_destroy(sqfs->fragtbl); sqfs_destroy(sqfs->cmp); sqfs_destroy(sqfs->uncmp); fstree_cleanup(&sqfs->fs); sqfs_destroy(sqfs->outfile); if (status != EXIT_SUCCESS) { #if defined(_WIN32) || defined(__WINDOWS__) WCHAR *path = path_to_windows(sqfs->filename); if (path != NULL) DeleteFileW(path); free(path); #else unlink(sqfs->filename); #endif } } squashfs-tools-ng-1.1.3/lib/common/writer/finish.c000066400000000000000000000104571410627516300221230ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * finish.c * * Copyright (C) 2019 David Oberhollenzer */ #include "simple_writer.h" #include "common.h" #include static void print_statistics(const sqfs_super_t *super, const sqfs_block_processor_t *blk, const sqfs_block_writer_t *wr) { const sqfs_block_processor_stats_t *proc_stats; sqfs_u64 bytes_written, blocks_written; char read_sz[32], written_sz[32]; size_t ratio; proc_stats = sqfs_block_processor_get_stats(blk); blocks_written = wr->get_block_count(wr); bytes_written = super->inode_table_start - sizeof(*super); if (proc_stats->input_bytes_read > 0) { ratio = (100 * bytes_written) / proc_stats->input_bytes_read; } else { ratio = 100; } print_size(proc_stats->input_bytes_read, read_sz, false); print_size(bytes_written, written_sz, false); fputs("---------------------------------------------------\n", stdout); printf("Data bytes read: %s\n", read_sz); printf("Data bytes written: %s\n", written_sz); printf("Data compression ratio: " PRI_SZ "%%\n", ratio); fputc('\n', stdout); printf("Data blocks written: " PRI_U64 "\n", blocks_written); printf("Out of which where fragment blocks: " PRI_U64 "\n", proc_stats->frag_block_count); printf("Duplicate blocks omitted: " PRI_U64 "\n", proc_stats->data_block_count + proc_stats->frag_block_count - blocks_written); printf("Sparse blocks omitted: " PRI_U64 "\n", proc_stats->sparse_block_count); fputc('\n', stdout); printf("Fragments actually written: " PRI_U64 "\n", proc_stats->actual_frag_count); printf("Duplicated fragments omitted: " PRI_U64 "\n", proc_stats->total_frag_count - proc_stats->actual_frag_count); printf("Total number of inodes: %u\n", super->inode_count); printf("Number of unique group/user IDs: %u\n", super->id_count); fputc('\n', stdout); } static int padd_sqfs(sqfs_file_t *file, sqfs_u64 size, size_t blocksize) { size_t padd_sz = size % blocksize; int status = -1; sqfs_u8 *buffer; if (padd_sz == 0) return 0; padd_sz = blocksize - padd_sz; buffer = calloc(1, padd_sz); if (buffer == NULL) goto fail_errno; if (file->write_at(file, file->get_size(file), buffer, padd_sz)) { goto fail_errno; } status = 0; out: free(buffer); return status; fail_errno: perror("padding output file to block size"); goto out; } int sqfs_writer_finish(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *cfg) { int ret; if (!cfg->quiet) fputs("Waiting for remaining data blocks...\n", stdout); ret = sqfs_block_processor_finish(sqfs->data); if (ret) { sqfs_perror(cfg->filename, "finishing data blocks", ret); return -1; } if (!cfg->quiet) fputs("Writing inodes and directories...\n", stdout); sqfs->super.inode_count = sqfs->fs.unique_inode_count; if (sqfs_serialize_fstree(cfg->filename, sqfs)) return -1; if (!cfg->quiet) fputs("Writing fragment table...\n", stdout); ret = sqfs_frag_table_write(sqfs->fragtbl, sqfs->outfile, &sqfs->super, sqfs->cmp); if (ret) { sqfs_perror(cfg->filename, "writing fragment table", ret); return -1; } if (cfg->exportable) { if (!cfg->quiet) fputs("Writing export table...\n", stdout); ret = sqfs_dir_writer_write_export_table(sqfs->dirwr, sqfs->outfile, sqfs->cmp, sqfs->fs.root->inode_num, sqfs->fs.root->inode_ref, &sqfs->super); if (ret) return -1; } if (!cfg->quiet) fputs("Writing ID table...\n", stdout); ret = sqfs_id_table_write(sqfs->idtbl, sqfs->outfile, &sqfs->super, sqfs->cmp); if (ret) { sqfs_perror(cfg->filename, "writing ID table", ret); return -1; } if (!cfg->no_xattr) { if (!cfg->quiet) fputs("Writing extended attributes...\n", stdout); ret = sqfs_xattr_writer_flush(sqfs->xwr, sqfs->outfile, &sqfs->super, sqfs->cmp); if (ret) { sqfs_perror(cfg->filename, "writing extended attributes", ret); return -1; } } sqfs->super.bytes_used = sqfs->outfile->get_size(sqfs->outfile); ret = sqfs_super_write(&sqfs->super, sqfs->outfile); if (ret) { sqfs_perror(cfg->filename, "updating super block", ret); return -1; } if (padd_sqfs(sqfs->outfile, sqfs->super.bytes_used, cfg->devblksize)) { return -1; } if (!cfg->quiet) print_statistics(&sqfs->super, sqfs->data, sqfs->blkwr); return 0; } squashfs-tools-ng-1.1.3/lib/common/writer/init.c000066400000000000000000000113541410627516300216030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * init.c * * Copyright (C) 2019 David Oberhollenzer */ #include "simple_writer.h" #include "compress_cli.h" #include "common.h" #include #include #ifdef HAVE_SYS_SYSINFO_H #include static size_t os_get_num_jobs(void) { int nprocs; nprocs = get_nprocs_conf(); return nprocs < 1 ? 1 : nprocs; } #else static size_t os_get_num_jobs(void) { return 1; } #endif void sqfs_writer_cfg_init(sqfs_writer_cfg_t *cfg) { memset(cfg, 0, sizeof(*cfg)); cfg->num_jobs = os_get_num_jobs(); cfg->block_size = SQFS_DEFAULT_BLOCK_SIZE; cfg->devblksize = SQFS_DEVBLK_SIZE; cfg->comp_id = compressor_get_default(); } int sqfs_writer_init(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *wrcfg) { sqfs_block_processor_desc_t blkdesc; sqfs_compressor_config_t cfg; int ret, flags; sqfs->filename = wrcfg->filename; if (compressor_cfg_init_options(&cfg, wrcfg->comp_id, wrcfg->block_size, wrcfg->comp_extra)) { return -1; } sqfs->outfile = sqfs_open_file(wrcfg->filename, wrcfg->outmode); if (sqfs->outfile == NULL) { perror(wrcfg->filename); return -1; } if (fstree_init(&sqfs->fs, wrcfg->fs_defaults)) goto fail_file; ret = sqfs_compressor_create(&cfg, &sqfs->cmp); #ifdef WITH_LZO if (cfg.id == SQFS_COMP_LZO) { if (sqfs->cmp != NULL) sqfs_destroy(sqfs->cmp); ret = lzo_compressor_create(&cfg, &sqfs->cmp); } #endif if (ret != 0) { sqfs_perror(wrcfg->filename, "creating compressor", ret); goto fail_fs; } cfg.flags |= SQFS_COMP_FLAG_UNCOMPRESS; ret = sqfs_compressor_create(&cfg, &sqfs->uncmp); #ifdef WITH_LZO if (cfg.id == SQFS_COMP_LZO) { if (ret == 0 && sqfs->uncmp != NULL) sqfs_destroy(sqfs->uncmp); ret = lzo_compressor_create(&cfg, &sqfs->uncmp); } #endif if (ret != 0) { sqfs_perror(wrcfg->filename, "creating uncompressor", ret); goto fail_cmp; } ret = sqfs_super_init(&sqfs->super, wrcfg->block_size, sqfs->fs.defaults.st_mtime, wrcfg->comp_id); if (ret) { sqfs_perror(wrcfg->filename, "initializing super block", ret); goto fail_uncmp; } ret = sqfs_super_write(&sqfs->super, sqfs->outfile); if (ret) { sqfs_perror(wrcfg->filename, "writing super block", ret); goto fail_uncmp; } ret = sqfs->cmp->write_options(sqfs->cmp, sqfs->outfile); if (ret < 0) { sqfs_perror(wrcfg->filename, "writing compressor options", ret); goto fail_uncmp; } if (ret > 0) sqfs->super.flags |= SQFS_FLAG_COMPRESSOR_OPTIONS; sqfs->blkwr = sqfs_block_writer_create(sqfs->outfile, wrcfg->devblksize, 0); if (sqfs->blkwr == NULL) { perror("creating block writer"); goto fail_uncmp; } sqfs->fragtbl = sqfs_frag_table_create(0); if (sqfs->fragtbl == NULL) { perror("creating fragment table"); goto fail_blkwr; } memset(&blkdesc, 0, sizeof(blkdesc)); blkdesc.size = sizeof(blkdesc); blkdesc.max_block_size = wrcfg->block_size; blkdesc.num_workers = wrcfg->num_jobs; blkdesc.max_backlog = wrcfg->max_backlog; blkdesc.cmp = sqfs->cmp; blkdesc.wr = sqfs->blkwr; blkdesc.tbl = sqfs->fragtbl; blkdesc.file = sqfs->outfile; blkdesc.uncmp = sqfs->uncmp; ret = sqfs_block_processor_create_ex(&blkdesc, &sqfs->data); if (ret != 0) { sqfs_perror(wrcfg->filename, "creating data block processor", ret); goto fail_fragtbl; } sqfs->idtbl = sqfs_id_table_create(0); if (sqfs->idtbl == NULL) { sqfs_perror(wrcfg->filename, "creating ID table", SQFS_ERROR_ALLOC); goto fail_data; } if (!wrcfg->no_xattr) { sqfs->xwr = sqfs_xattr_writer_create(0); if (sqfs->xwr == NULL) { sqfs_perror(wrcfg->filename, "creating xattr writer", SQFS_ERROR_ALLOC); goto fail_id; } } sqfs->im = sqfs_meta_writer_create(sqfs->outfile, sqfs->cmp, 0); if (sqfs->im == NULL) { fputs("Error creating inode meta data writer.\n", stderr); goto fail_xwr; } sqfs->dm = sqfs_meta_writer_create(sqfs->outfile, sqfs->cmp, SQFS_META_WRITER_KEEP_IN_MEMORY); if (sqfs->dm == NULL) { fputs("Error creating directory meta data writer.\n", stderr); goto fail_im; } flags = 0; if (wrcfg->exportable) flags |= SQFS_DIR_WRITER_CREATE_EXPORT_TABLE; sqfs->dirwr = sqfs_dir_writer_create(sqfs->dm, flags); if (sqfs->dirwr == NULL) { fputs("Error creating directory table writer.\n", stderr); goto fail_dm; } return 0; fail_dm: sqfs_destroy(sqfs->dm); fail_im: sqfs_destroy(sqfs->im); fail_xwr: if (sqfs->xwr != NULL) sqfs_destroy(sqfs->xwr); fail_id: sqfs_destroy(sqfs->idtbl); fail_data: sqfs_destroy(sqfs->data); fail_fragtbl: sqfs_destroy(sqfs->fragtbl); fail_blkwr: sqfs_destroy(sqfs->blkwr); fail_uncmp: sqfs_destroy(sqfs->uncmp); fail_cmp: sqfs_destroy(sqfs->cmp); fail_fs: fstree_cleanup(&sqfs->fs); fail_file: sqfs_destroy(sqfs->outfile); return -1; } squashfs-tools-ng-1.1.3/lib/common/writer/serialize_fstree.c000066400000000000000000000105541410627516300242000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * serialize_fstree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include #include #include #include static sqfs_inode_generic_t *tree_node_to_inode(tree_node_t *node) { sqfs_inode_generic_t *inode; size_t extra = 0; if (S_ISLNK(node->mode)) extra = strlen(node->data.target); inode = calloc(1, sizeof(*inode) + extra); if (inode == NULL) { perror("creating inode"); return NULL; } switch (node->mode & S_IFMT) { case S_IFSOCK: inode->base.type = SQFS_INODE_SOCKET; inode->data.ipc.nlink = node->link_count; break; case S_IFIFO: inode->base.type = SQFS_INODE_FIFO; inode->data.ipc.nlink = node->link_count; break; case S_IFLNK: inode->base.type = SQFS_INODE_SLINK; inode->data.slink.nlink = node->link_count; inode->data.slink.target_size = extra; memcpy(inode->extra, node->data.target, extra); break; case S_IFBLK: inode->base.type = SQFS_INODE_BDEV; inode->data.dev.nlink = node->link_count; inode->data.dev.devno = node->data.devno; break; case S_IFCHR: inode->base.type = SQFS_INODE_CDEV; inode->data.dev.nlink = node->link_count; inode->data.dev.devno = node->data.devno; break; default: assert(0); } return inode; } static sqfs_inode_generic_t *write_dir_entries(const char *filename, sqfs_dir_writer_t *dirw, tree_node_t *node) { sqfs_u32 xattr, parent_inode; sqfs_inode_generic_t *inode; tree_node_t *it, *tgt; int ret; ret = sqfs_dir_writer_begin(dirw, 0); if (ret) goto fail; for (it = node->data.dir.children; it != NULL; it = it->next) { if (it->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { tgt = it->data.target_node; } else { tgt = it; } ret = sqfs_dir_writer_add_entry(dirw, it->name, tgt->inode_num, tgt->inode_ref, tgt->mode); if (ret) goto fail; } ret = sqfs_dir_writer_end(dirw); if (ret) goto fail; xattr = node->xattr_idx; parent_inode = (node->parent == NULL) ? 0 : node->parent->inode_num; inode = sqfs_dir_writer_create_inode(dirw, 0, xattr, parent_inode); if (inode == NULL) { ret = SQFS_ERROR_ALLOC; goto fail; } if (inode->base.type == SQFS_INODE_DIR) { inode->data.dir.nlink = node->link_count; } else { inode->data.dir_ext.nlink = node->link_count; } return inode; fail: sqfs_perror(filename, "recoding directory entries", ret); return NULL; } static int serialize_tree_node(const char *filename, sqfs_writer_t *wr, tree_node_t *n) { sqfs_inode_generic_t *inode; sqfs_u32 offset; sqfs_u64 block; int ret; if (S_ISDIR(n->mode)) { inode = write_dir_entries(filename, wr->dirwr, n); ret = SQFS_ERROR_INTERNAL; } else if (S_ISREG(n->mode)) { inode = n->data.file.inode; n->data.file.inode = NULL; ret = SQFS_ERROR_INTERNAL; if (inode->base.type == SQFS_INODE_FILE && n->link_count > 1) { sqfs_inode_make_extended(inode); inode->data.file_ext.nlink = n->link_count; } else { inode->data.file_ext.nlink = n->link_count; } } else { inode = tree_node_to_inode(n); ret = SQFS_ERROR_ALLOC; } if (inode == NULL) return ret; inode->base.mode = n->mode; inode->base.mod_time = n->mod_time; inode->base.inode_number = n->inode_num; sqfs_inode_set_xattr_index(inode, n->xattr_idx); ret = sqfs_id_table_id_to_index(wr->idtbl, n->uid, &inode->base.uid_idx); if (ret) goto out; ret = sqfs_id_table_id_to_index(wr->idtbl, n->gid, &inode->base.gid_idx); if (ret) goto out; sqfs_meta_writer_get_position(wr->im, &block, &offset); n->inode_ref = (block << 16) | offset; ret = sqfs_meta_writer_write_inode(wr->im, inode); out: free(inode); return ret; } int sqfs_serialize_fstree(const char *filename, sqfs_writer_t *wr) { size_t i; int ret; wr->super.inode_table_start = wr->outfile->get_size(wr->outfile); for (i = 0; i < wr->fs.unique_inode_count; ++i) { ret = serialize_tree_node(filename, wr, wr->fs.inodes[i]); if (ret) goto out; } ret = sqfs_meta_writer_flush(wr->im); if (ret) goto out; ret = sqfs_meta_writer_flush(wr->dm); if (ret) goto out; wr->super.root_inode_ref = wr->fs.root->inode_ref; wr->super.directory_table_start = wr->outfile->get_size(wr->outfile); ret = sqfs_meta_write_write_to_file(wr->dm); if (ret) goto out; ret = 0; out: if (ret) sqfs_perror(filename, "storing filesystem tree", ret); return ret; } squashfs-tools-ng-1.1.3/lib/compat/000077500000000000000000000000001410627516300171475ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/compat/Makemodule.am000066400000000000000000000006641410627516300215570ustar00rootroot00000000000000libcompat_a_SOURCES = lib/compat/getsubopt.c libcompat_a_SOURCES += lib/compat/strndup.c lib/compat/mockups.c libcompat_a_SOURCES += lib/compat/chdir.c include/compat.h libcompat_a_SOURCES += lib/compat/path_to_windows.c libcompat_a_SOURCES += lib/compat/w32_perror.c libcompat_a_SOURCES += lib/compat/fnmatch.c libcompat_a_SOURCES += lib/compat/getopt.c libcompat_a_SOURCES += lib/compat/getopt_long.c noinst_LIBRARIES += libcompat.a squashfs-tools-ng-1.1.3/lib/compat/chdir.c000066400000000000000000000010371410627516300204050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * chdir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compat.h" #ifdef _WIN32 #include #include int chdir(const char *path) { WCHAR *wpath; int ret; wpath = path_to_windows(path); if (wpath == NULL) return -1; if (!SetCurrentDirectoryW(wpath)) { fprintf(stderr, "Switching to directory '%s': %ld\n", path, GetLastError()); ret = -1; } else { ret = 0; } free(wpath); return ret; } #endif squashfs-tools-ng-1.1.3/lib/compat/fnmatch.c000066400000000000000000000151621410627516300207400ustar00rootroot00000000000000/* * An implementation of what I call the "Sea of Stars" algorithm for * POSIX fnmatch(). The basic idea is that we factor the pattern into * a head component (which we match first and can reject without ever * measuring the length of the string), an optional tail component * (which only exists if the pattern contains at least one star), and * an optional "sea of stars", a set of star-separated components * between the head and tail. After the head and tail matches have * been removed from the input string, the components in the "sea of * stars" are matched sequentially by searching for their first * occurrence past the end of the previous match. * * - Rich Felker, April 2012 */ #include "compat.h" #include #include #ifndef HAVE_FNMATCH #define END 0 #define UNMATCHABLE -2 #define BRACKET -3 #define QUESTION -4 #define STAR -5 static int str_next(const char *str, size_t n, size_t *step) { if (!n) { *step = 0; return 0; } if (str[0] >= 128U) { wchar_t wc; int k = mbtowc(&wc, str, n); if (k<0) { *step = 1; return -1; } *step = k; return wc; } *step = 1; return str[0]; } static int pat_next(const char *pat, size_t m, size_t *step) { int esc = 0; if (!m || !*pat) { *step = 0; return END; } *step = 1; if (pat[0]=='\\' && pat[1]) { *step = 2; pat++; esc = 1; goto escaped; } if (pat[0]=='[') { size_t k = 1; if (k= 128U) { wchar_t wc; int k = mbtowc(&wc, pat, m); if (k<0) { *step = 0; return UNMATCHABLE; } *step = k + esc; return wc; } return pat[0]; } static int match_bracket(const char *p, int k, int kfold) { wchar_t wc; int inv = 0; p++; if (*p=='^' || *p=='!') { inv = 1; p++; } if (*p==']') { if (k==']') return !inv; p++; } else if (*p=='-') { if (k=='-') return !inv; p++; } wc = p[-1]; for (; *p != ']'; p++) { if (p[0]=='-' && p[1]!=']') { wchar_t wc2; int l = mbtowc(&wc2, p+1, 4); if (l < 0) return 0; if (wc <= wc2) if ((unsigned)k-wc <= wc2-wc || (unsigned)kfold-wc <= wc2-wc) return !inv; p += l-1; continue; } if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) { const char *p0 = p+2; int z = p[1]; p+=3; while (p[-1]!=z || p[0]!=']') p++; if (z == ':' && p-1-p0 < 16) { char buf[16]; memcpy(buf, p0, p-1-p0); buf[p-1-p0] = 0; if (iswctype(k, wctype(buf)) || iswctype(kfold, wctype(buf))) return !inv; } continue; } if (*p < 128U) { wc = (unsigned char)*p; } else { int l = mbtowc(&wc, p, 4); if (l < 0) return 0; p += l-1; } if (wc==k || wc==kfold) return !inv; } return inv; } static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n) { const char *p, *ptail, *endpat; const char *s, *stail, *endstr; size_t pinc, sinc, tailcnt=0; int c, k, kfold; for (;;) { switch ((c = pat_next(pat, m, &pinc))) { case UNMATCHABLE: return FNM_NOMATCH; case STAR: pat++; m--; break; default: k = str_next(str, n, &sinc); if (k <= 0) return (c==END) ? 0 : FNM_NOMATCH; str += sinc; n -= sinc; kfold = k; if (c == BRACKET) { if (!match_bracket(pat, k, kfold)) return FNM_NOMATCH; } else if (c != QUESTION && k != c && kfold != c) { return FNM_NOMATCH; } pat+=pinc; m-=pinc; continue; } break; } /* Compute real pat length if it was initially unknown/-1 */ m = strnlen(pat, m); endpat = pat + m; /* Find the last * in pat and count chars needed after it */ for (p=ptail=pat; pstr && tailcnt; tailcnt--) { if (s[-1] < 128U || MB_CUR_MAX==1) s--; else while ((unsigned char)*--s-0x80U<0x40 && s>str); } if (tailcnt) return FNM_NOMATCH; stail = s; /* Check that the pat and str tails match */ p = ptail; for (;;) { c = pat_next(p, endpat-p, &pinc); p += pinc; if ((k = str_next(s, endstr-s, &sinc)) <= 0) { if (c != END) return FNM_NOMATCH; break; } s += sinc; kfold = k; if (c == BRACKET) { if (!match_bracket(p-pinc, k, kfold)) return FNM_NOMATCH; } else if (c != QUESTION && k != c && kfold != c) { return FNM_NOMATCH; } } /* We're all done with the tails now, so throw them out */ endstr = stail; endpat = ptail; /* Match pattern components until there are none left */ while (pat 0) str += sinc; else for (str++; str_next(str, endstr-str, &sinc)<0; str++); } return 0; } int fnmatch(const char *pat, const char *str, int flags) { const char *s, *p; size_t inc; int c; if (flags & FNM_PATHNAME) for (;;) { for (s=str; *s && *s!='/'; s++); for (p=pat; (c=pat_next(p, -1, &inc))!=END && c!='/'; p+=inc); if (c!=*s) return FNM_NOMATCH; if (fnmatch_internal(pat, p-pat, str, s-str)) return FNM_NOMATCH; if (!c) return 0; str = s+1; pat = p+inc; } return fnmatch_internal(pat, -1, str, -1); } #endif /* HAVE_FNMATCH */ squashfs-tools-ng-1.1.3/lib/compat/getopt.c000066400000000000000000000033671410627516300206260ustar00rootroot00000000000000#include "compat.h" #include #include #include #include #ifndef HAVE_GETOPT char *optarg; int optind=1, opterr=1, optopt, optpos, optreset=0; void __getopt_msg(const char *a, const char *b, const char *c, size_t l) { fputs(a, stderr); fwrite(b, strlen(b), 1, stderr); fwrite(c, 1, l, stderr); putc('\n', stderr); } int getopt(int argc, char * const argv[], const char *optstring) { int i; wchar_t c, d; int k, l; char *optchar; if (!optind || __optreset) { optreset = 0; optpos = 0; optind = 1; } if (optind >= argc || !argv[optind]) return -1; if (argv[optind][0] != '-') { if (optstring[0] == '-') { optarg = argv[optind++]; return 1; } return -1; } if (!argv[optind][1]) return -1; if (argv[optind][1] == '-' && !argv[optind][2]) return optind++, -1; if (!optpos) optpos++; if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { k = 1; c = 0xfffd; /* replacement char */ } optchar = argv[optind]+optpos; optpos += k; if (!argv[optind][optpos]) { optind++; optpos = 0; } if (optstring[0] == '-' || optstring[0] == '+') optstring++; i = 0; d = 0; do { l = mbtowc(&d, optstring+i, MB_LEN_MAX); if (l>0) i+=l; else i++; } while (l && d != c); if (d != c || c == ':') { optopt = c; if (optstring[0] != ':' && opterr) __getopt_msg(argv[0], ": unrecognized option: ", optchar, k); return '?'; } if (optstring[i] == ':') { optarg = 0; if (optstring[i+1] != ':' || optpos) { optarg = argv[optind++] + optpos; optpos = 0; } if (optind > argc) { optopt = c; if (optstring[0] == ':') return ':'; if (opterr) __getopt_msg(argv[0], ": option requires an argument: ", optchar, k); return '?'; } } return c; } #endif squashfs-tools-ng-1.1.3/lib/compat/getopt_long.c000066400000000000000000000041741410627516300216420ustar00rootroot00000000000000#include "compat.h" #include #include #include #include #include #include #ifndef HAVE_GETOPT_LONG static void permute(char *const *argv, int dest, int src) { char **av = (char **)argv; char *tmp = av[src]; int i; for (i=src; i>dest; i--) av[i] = av[i-1]; av[dest] = tmp; } int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) { optarg = 0; if (longopts && argv[optind][0] == '-' && (argv[optind][1] == '-' && argv[optind][2])) { int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; int i, cnt, match; char *arg, *opt, *start = argv[optind]+1; for (cnt=i=0; longopts[i].name; i++) { const char *name = longopts[i].name; opt = start; if (*opt == '-') opt++; while (*opt && *opt != '=' && *opt == *name) name++, opt++; if (*opt && *opt != '=') continue; arg = opt; match = i; if (!*name) { cnt = 1; break; } cnt++; } if (cnt==1) { i = match; opt = arg; optind++; if (*opt == '=') { if (!longopts[i].has_arg) { optopt = longopts[i].val; if (colon || !opterr) return '?'; __getopt_msg(argv[0], ": option does not take an argument: ", longopts[i].name, strlen(longopts[i].name)); return '?'; } optarg = opt+1; } else if (longopts[i].has_arg == required_argument) { if (!(optarg = argv[optind])) { optopt = longopts[i].val; if (colon) return ':'; if (!opterr) return '?'; __getopt_msg(argv[0], ": option requires an argument: ", longopts[i].name, strlen(longopts[i].name)); return '?'; } optind++; } if (idx) *idx = i; if (longopts[i].flag) { *longopts[i].flag = longopts[i].val; return 0; } return longopts[i].val; } if (argv[optind][1] == '-') { optopt = 0; if (!colon && opterr) __getopt_msg(argv[0], cnt ? ": option is ambiguous: " : ": unrecognized option: ", argv[optind]+2, strlen(argv[optind]+2)); optind++; return '?'; } } return getopt(argc, argv, optstring); } #endif squashfs-tools-ng-1.1.3/lib/compat/getsubopt.c000066400000000000000000000007171410627516300213340ustar00rootroot00000000000000#include "compat.h" #include #include #ifndef HAVE_GETSUBOPT int getsubopt(char **opt, char *const *keys, char **val) { char *s = *opt; int i; *val = NULL; *opt = strchr(s, ','); if (*opt) *(*opt)++ = 0; else *opt = s + strlen(s); for (i=0; keys[i]; i++) { size_t l = strlen(keys[i]); if (strncmp(keys[i], s, l)) continue; if (s[l] == '=') *val = s + l + 1; else if (s[l]) continue; return i; } return -1; } #endif squashfs-tools-ng-1.1.3/lib/compat/mockups.c000066400000000000000000000017221410627516300207760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mockups.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compat.h" #include #ifdef _WIN32 int fchownat(int dirfd, const char *path, int uid, int gid, int flags) { if (dirfd != AT_FDCWD) { fputs("[FIXME] fchownat stub only supports AT_FDCWD!\n", stderr); return -1; } if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { fputs("[FIXME] fchownat stub used with an unknown flag!\n", stderr); return -1; } (void)path; (void)uid; (void)gid; return 0; } int fchmodat(int dirfd, const char *path, int mode, int flags) { if (dirfd != AT_FDCWD) { fputs("[FIXME] fchmodat stub only supports AT_FDCWD!\n", stderr); return -1; } if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { fputs("[FIXME] fchmodat stub used with an unknown flag!\n", stderr); return -1; } (void)path; (void)mode; return 0; } #endif squashfs-tools-ng-1.1.3/lib/compat/path_to_windows.c000066400000000000000000000015671410627516300225340ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * path_to_windows.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compat.h" #include #include #if defined(_WIN32) || defined(__WINDOWS__) WCHAR *path_to_windows(const char *input) { WCHAR *wpath, *ptr; DWORD length; length = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); if (length <= 0) { fprintf(stderr, "Converting UTF-8 path to UTF-16: %ld\n", GetLastError()); return NULL; } wpath = calloc(sizeof(wpath[0]), length + 1); if (wpath == NULL) { fprintf(stderr, "Converting UTF-8 path to UTF-16: out of memory\n"); return NULL; } MultiByteToWideChar(CP_UTF8, 0, input, -1, wpath, length + 1); wpath[length] = '\0'; for (ptr = wpath; *ptr != '\0'; ++ptr) { if (*ptr == '/') *ptr = '\\'; } return wpath; } #endif squashfs-tools-ng-1.1.3/lib/compat/strndup.c000066400000000000000000000007461410627516300210210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * strndup.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compat.h" #include #include #ifndef HAVE_STRNDUP char *strndup(const char *str, size_t max_len) { size_t len = 0; char *out; while (len < max_len && str[len] != '\0') ++len; out = malloc(len + 1); if (out != NULL) { memcpy(out, str, len); out[len] = '\0'; } return out; } #endif squashfs-tools-ng-1.1.3/lib/compat/w32_perror.c000066400000000000000000000012621410627516300213200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * w32_perror.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compat.h" #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include void w32_perror(const char *str) { DWORD nStatus = GetLastError(); LPVOID msg = NULL; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, nStatus, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msg, 0, NULL); fprintf(stderr, "%s: %s\n", str, (const char *)msg); if (msg != NULL) LocalFree(msg); } #endif squashfs-tools-ng-1.1.3/lib/fstream/000077500000000000000000000000001410627516300173255ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/fstream/Makemodule.am000066400000000000000000000031551410627516300217330ustar00rootroot00000000000000libfstream_a_SOURCES = include/fstream.h libfstream_a_SOURCES += lib/fstream/internal.h libfstream_a_SOURCES += lib/fstream/ostream.c lib/fstream/printf.c libfstream_a_SOURCES += lib/fstream/istream.c lib/fstream/get_line.c libfstream_a_SOURCES += lib/fstream/compressor.c libfstream_a_SOURCES += lib/fstream/compress/ostream_compressor.c libfstream_a_SOURCES += lib/fstream/uncompress/istream_compressor.c libfstream_a_SOURCES += lib/fstream/uncompress/autodetect.c libfstream_a_CFLAGS = $(AM_CFLAGS) $(ZLIB_CFLAGS) $(XZ_CFLAGS) libfstream_a_CFLAGS += $(ZSTD_CFLAGS) $(BZIP2_CFLAGS) libfstream_a_CPPFLAGS = $(AM_CPPFLAGS) if WINDOWS libfstream_a_SOURCES += lib/fstream/win32/ostream.c libfstream_a_SOURCES += lib/fstream/win32/istream.c libfstream_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 else libfstream_a_SOURCES += lib/fstream/unix/ostream.c libfstream_a_SOURCES += lib/fstream/unix/istream.c endif if WITH_XZ libfstream_a_SOURCES += lib/fstream/compress/xz.c lib/fstream/uncompress/xz.c libfstream_a_CPPFLAGS += -DWITH_XZ endif if WITH_GZIP libfstream_a_SOURCES += lib/fstream/compress/gzip.c libfstream_a_SOURCES += lib/fstream/uncompress/gzip.c libfstream_a_CPPFLAGS += -DWITH_GZIP if WITH_OWN_ZLIB libfstream_a_CPPFLAGS += -I$(top_srcdir)/lib/zlib endif endif if WITH_ZSTD libfstream_a_SOURCES += lib/fstream/compress/zstd.c libfstream_a_SOURCES += lib/fstream/uncompress/zstd.c libfstream_a_CPPFLAGS += -DWITH_ZSTD endif if WITH_BZIP2 libfstream_a_SOURCES += lib/fstream/compress/bzip2.c libfstream_a_SOURCES += lib/fstream/uncompress/bzip2.c libfstream_a_CPPFLAGS += -DWITH_BZIP2 endif noinst_LIBRARIES += libfstream.a squashfs-tools-ng-1.1.3/lib/fstream/compress/000077500000000000000000000000001410627516300211605ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/fstream/compress/bzip2.c000066400000000000000000000041661410627516300223610ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * bzip2.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include typedef struct { ostream_comp_t base; bz_stream strm; } ostream_bzip2_t; static int flush_inbuf(ostream_comp_t *base, bool finish) { ostream_bzip2_t *bzip2 = (ostream_bzip2_t *)base; size_t have; int ret; bzip2->strm.next_in = (char *)base->inbuf; if (base->inbuf_used > sizeof(base->inbuf)) base->inbuf_used = sizeof(base->inbuf); if ((sizeof(size_t) > sizeof(unsigned int)) && (base->inbuf_used > (size_t)UINT_MAX)) { bzip2->strm.avail_in = UINT_MAX; } else { bzip2->strm.avail_in = (unsigned int)base->inbuf_used; } for (;;) { bzip2->strm.next_out = (char *)base->outbuf; bzip2->strm.avail_out = sizeof(base->outbuf); ret = BZ2_bzCompress(&bzip2->strm, finish ? BZ_FINISH : BZ_RUN); if (ret < 0 && ret != BZ_OUTBUFF_FULL) { fprintf(stderr, "%s: internal error in bzip2 " "compressor.\n", base->wrapped->get_filename(base->wrapped)); return -1; } have = sizeof(base->outbuf) - bzip2->strm.avail_out; if (base->wrapped->append(base->wrapped, base->outbuf, have)) return -1; if (ret == BZ_STREAM_END || ret == BZ_OUTBUFF_FULL || bzip2->strm.avail_in == 0) { break; } } if (bzip2->strm.avail_in > 0) { memmove(base->inbuf, bzip2->strm.next_in, bzip2->strm.avail_in); } base->inbuf_used = bzip2->strm.avail_in; return 0; } static void cleanup(ostream_comp_t *base) { ostream_bzip2_t *bzip2 = (ostream_bzip2_t *)base; BZ2_bzCompressEnd(&bzip2->strm); } ostream_comp_t *ostream_bzip2_create(const char *filename) { ostream_bzip2_t *bzip2 = calloc(1, sizeof(*bzip2)); ostream_comp_t *base = (ostream_comp_t *)bzip2; if (bzip2 == NULL) { fprintf(stderr, "%s: creating bzip2 compressor: %s.\n", filename, strerror(errno)); return NULL; } if (BZ2_bzCompressInit(&bzip2->strm, 9, 0, 30) != BZ_OK) { fprintf(stderr, "%s: error initializing bzip2 compressor.\n", filename); free(bzip2); return NULL; } base->flush_inbuf = flush_inbuf; base->cleanup = cleanup; return base; } squashfs-tools-ng-1.1.3/lib/fstream/compress/gzip.c000066400000000000000000000036571410627516300223100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * gzip.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include typedef struct { ostream_comp_t base; z_stream strm; } ostream_gzip_t; static int flush_inbuf(ostream_comp_t *base, bool finish) { ostream_gzip_t *gzip = (ostream_gzip_t *)base; size_t have; int ret; if (base->inbuf_used > sizeof(base->inbuf)) base->inbuf_used = sizeof(base->inbuf); if (sizeof(size_t) > sizeof(uInt)) { gzip->strm.avail_in = ~((uInt)0); if ((size_t)gzip->strm.avail_in > base->inbuf_used) gzip->strm.avail_in = (uInt)base->inbuf_used; } else { gzip->strm.avail_in = (uInt)base->inbuf_used; } gzip->strm.next_in = base->inbuf; do { gzip->strm.avail_out = BUFSZ; gzip->strm.next_out = base->outbuf; ret = deflate(&gzip->strm, finish ? Z_FINISH : Z_NO_FLUSH); if (ret == Z_STREAM_ERROR) { fprintf(stderr, "%s: internal error in gzip compressor.\n", base->wrapped->get_filename(base->wrapped)); return -1; } have = BUFSZ - gzip->strm.avail_out; if (base->wrapped->append(base->wrapped, base->outbuf, have)) return -1; } while (gzip->strm.avail_out == 0); base->inbuf_used = 0; return 0; } static void cleanup(ostream_comp_t *base) { ostream_gzip_t *gzip = (ostream_gzip_t *)base; deflateEnd(&gzip->strm); } ostream_comp_t *ostream_gzip_create(const char *filename) { ostream_gzip_t *gzip = calloc(1, sizeof(*gzip)); ostream_comp_t *base = (ostream_comp_t *)gzip; int ret; if (gzip == NULL) { fprintf(stderr, "%s: creating gzip wrapper: %s.\n", filename, strerror(errno)); return NULL; } ret = deflateInit2(&gzip->strm, 9, Z_DEFLATED, 16 + 15, 8, Z_DEFAULT_STRATEGY); if (ret != Z_OK) { fprintf(stderr, "%s: internal error creating gzip compressor.\n", filename); free(gzip); return NULL; } base->flush_inbuf = flush_inbuf; base->cleanup = cleanup; return base; } squashfs-tools-ng-1.1.3/lib/fstream/compress/ostream_compressor.c000066400000000000000000000041721410627516300252560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * ostream_compressor.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" static int comp_append(ostream_t *strm, const void *data, size_t size) { ostream_comp_t *comp = (ostream_comp_t *)strm; size_t diff; while (size > 0) { if (comp->inbuf_used >= BUFSZ) { if (comp->flush_inbuf(comp, false)) return -1; } diff = BUFSZ - comp->inbuf_used; if (diff > size) diff = size; memcpy(comp->inbuf + comp->inbuf_used, data, diff); comp->inbuf_used += diff; data = (const char *)data + diff; size -= diff; } return 0; } static int comp_flush(ostream_t *strm) { ostream_comp_t *comp = (ostream_comp_t *)strm; if (comp->inbuf_used > 0) { if (comp->flush_inbuf(comp, true)) return -1; } return comp->wrapped->flush(comp->wrapped); } static const char *comp_get_filename(ostream_t *strm) { ostream_comp_t *comp = (ostream_comp_t *)strm; return comp->wrapped->get_filename(comp->wrapped); } static void comp_destroy(sqfs_object_t *obj) { ostream_comp_t *comp = (ostream_comp_t *)obj; comp->cleanup(comp); sqfs_destroy(comp->wrapped); free(comp); } ostream_t *ostream_compressor_create(ostream_t *strm, int comp_id) { ostream_comp_t *comp = NULL; sqfs_object_t *obj; ostream_t *base; switch (comp_id) { case FSTREAM_COMPRESSOR_GZIP: #ifdef WITH_GZIP comp = ostream_gzip_create(strm->get_filename(strm)); #endif break; case FSTREAM_COMPRESSOR_XZ: #ifdef WITH_XZ comp = ostream_xz_create(strm->get_filename(strm)); #endif break; case FSTREAM_COMPRESSOR_ZSTD: #if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) comp = ostream_zstd_create(strm->get_filename(strm)); #endif break; case FSTREAM_COMPRESSOR_BZIP2: #ifdef WITH_BZIP2 comp = ostream_bzip2_create(strm->get_filename(strm)); #endif break; default: break; } if (comp == NULL) return NULL; comp->wrapped = strm; comp->inbuf_used = 0; base = (ostream_t *)comp; base->append = comp_append; base->flush = comp_flush; base->get_filename = comp_get_filename; obj = (sqfs_object_t *)comp; obj->destroy = comp_destroy; return base; } squashfs-tools-ng-1.1.3/lib/fstream/compress/xz.c000066400000000000000000000032141410627516300217650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xz.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include typedef struct { ostream_comp_t base; lzma_stream strm; } ostream_xz_t; static int flush_inbuf(ostream_comp_t *base, bool finish) { ostream_xz_t *xz = (ostream_xz_t *)base; lzma_ret ret_xz; size_t have; xz->strm.next_in = base->inbuf; xz->strm.avail_in = base->inbuf_used; do { xz->strm.next_out = base->outbuf; xz->strm.avail_out = BUFSZ; ret_xz = lzma_code(&xz->strm, finish ? LZMA_FINISH : LZMA_RUN); if ((ret_xz != LZMA_OK) && (ret_xz != LZMA_STREAM_END)) { fprintf(stderr, "%s: internal error in XZ compressor.\n", base->wrapped->get_filename(base->wrapped)); return -1; } have = BUFSZ - xz->strm.avail_out; if (base->wrapped->append(base->wrapped, base->outbuf, have)) return -1; } while (xz->strm.avail_out == 0); base->inbuf_used = 0; return 0; } static void cleanup(ostream_comp_t *base) { ostream_xz_t *xz = (ostream_xz_t *)base; lzma_end(&xz->strm); } ostream_comp_t *ostream_xz_create(const char *filename) { ostream_xz_t *xz = calloc(1, sizeof(*xz)); ostream_comp_t *base = (ostream_comp_t *)xz; lzma_ret ret_xz; if (xz == NULL) { fprintf(stderr, "%s: creating xz wrapper: %s.\n", filename, strerror(errno)); return NULL; } ret_xz = lzma_easy_encoder(&xz->strm, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); if (ret_xz != LZMA_OK) { fprintf(stderr, "%s: error initializing XZ compressor\n", filename); free(xz); return NULL; } base->flush_inbuf = flush_inbuf; base->cleanup = cleanup; return base; } squashfs-tools-ng-1.1.3/lib/fstream/compress/zstd.c000066400000000000000000000035431410627516300223150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * zstd.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include #ifdef HAVE_ZSTD_STREAM typedef struct { ostream_comp_t base; ZSTD_CStream *strm; } ostream_zstd_t; static int flush_inbuf(ostream_comp_t *base, bool finish) { ostream_zstd_t *zstd = (ostream_zstd_t *)base; ZSTD_EndDirective op; ZSTD_outBuffer out; ZSTD_inBuffer in; size_t ret; op = finish ? ZSTD_e_end : ZSTD_e_continue; do { memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); in.src = base->inbuf; in.size = base->inbuf_used; out.dst = base->outbuf; out.size = BUFSZ; ret = ZSTD_compressStream2(zstd->strm, &out, &in, op); if (ZSTD_isError(ret)) { fprintf(stderr, "%s: error in zstd compressor.\n", base->wrapped->get_filename(base->wrapped)); return -1; } if (base->wrapped->append(base->wrapped, base->outbuf, out.pos)) { return -1; } if (in.pos < in.size) { base->inbuf_used = in.size - in.pos; memmove(base->inbuf, base->inbuf + in.pos, base->inbuf_used); } else { base->inbuf_used = 0; } } while (finish && ret != 0); return 0; } static void cleanup(ostream_comp_t *base) { ostream_zstd_t *zstd = (ostream_zstd_t *)base; ZSTD_freeCStream(zstd->strm); } ostream_comp_t *ostream_zstd_create(const char *filename) { ostream_zstd_t *zstd = calloc(1, sizeof(*zstd)); ostream_comp_t *base = (ostream_comp_t *)zstd; if (zstd == NULL) { fprintf(stderr, "%s: creating zstd wrapper: %s.\n", filename, strerror(errno)); return NULL; } zstd->strm = ZSTD_createCStream(); if (zstd->strm == NULL) { fprintf(stderr, "%s: error creating zstd decoder.\n", filename); free(zstd); return NULL; } base->flush_inbuf = flush_inbuf; base->cleanup = cleanup; return base; } #endif /* HAVE_ZSTD_STREAM */ squashfs-tools-ng-1.1.3/lib/fstream/compressor.c000066400000000000000000000022511410627516300216650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compressor.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" int fstream_compressor_id_from_name(const char *name) { if (strcmp(name, "gzip") == 0) return FSTREAM_COMPRESSOR_GZIP; if (strcmp(name, "xz") == 0) return FSTREAM_COMPRESSOR_XZ; if (strcmp(name, "zstd") == 0) return FSTREAM_COMPRESSOR_ZSTD; if (strcmp(name, "bzip2") == 0) return FSTREAM_COMPRESSOR_BZIP2; return -1; } const char *fstream_compressor_name_from_id(int id) { if (id == FSTREAM_COMPRESSOR_GZIP) return "gzip"; if (id == FSTREAM_COMPRESSOR_XZ) return "xz"; if (id == FSTREAM_COMPRESSOR_ZSTD) return "zstd"; if (id == FSTREAM_COMPRESSOR_BZIP2) return "bzip2"; return NULL; } bool fstream_compressor_exists(int id) { switch (id) { #ifdef WITH_GZIP case FSTREAM_COMPRESSOR_GZIP: return true; #endif #ifdef WITH_XZ case FSTREAM_COMPRESSOR_XZ: return true; #endif #if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) case FSTREAM_COMPRESSOR_ZSTD: return true; #endif #ifdef WITH_BZIP2 case FSTREAM_COMPRESSOR_BZIP2: return true; #endif default: break; } return false; } squashfs-tools-ng-1.1.3/lib/fstream/get_line.c000066400000000000000000000037061410627516300212650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_line.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" static void ltrim(char *buffer) { size_t i = 0; while (isspace(buffer[i])) ++i; if (i > 0) memmove(buffer, buffer + i, strlen(buffer + i) + 1); } static void rtrim(char *buffer) { size_t i = strlen(buffer); while (i > 0 && isspace(buffer[i - 1])) --i; buffer[i] = '\0'; } static size_t trim(char *buffer, int flags) { if (flags & ISTREAM_LINE_LTRIM) ltrim(buffer); if (flags & ISTREAM_LINE_RTRIM) rtrim(buffer); return strlen(buffer); } int istream_get_line(istream_t *strm, char **out, size_t *line_num, int flags) { char *line = NULL, *new; size_t i, line_len = 0; bool have_line = false; *out = NULL; for (;;) { if (istream_precache(strm)) return -1; if (strm->buffer_used == 0) { if (line_len == 0) goto out_eof; line_len = trim(line, flags); if (line_len == 0 && (flags & ISTREAM_LINE_SKIP_EMPTY)) { goto out_eof; } break; } for (i = 0; i < strm->buffer_used; ++i) { if (strm->buffer[i] == '\n') break; } if (i < strm->buffer_used) { have_line = true; strm->buffer_offset = i + 1; if (i > 0 && strm->buffer[i - 1] == '\r') --i; } else { strm->buffer_offset = i; } new = realloc(line, line_len + i + 1); if (new == NULL) goto fail_errno; line = new; memcpy(line + line_len, strm->buffer, i); line_len += i; line[line_len] = '\0'; if (have_line) { line_len = trim(line, flags); if (line_len == 0 && (flags & ISTREAM_LINE_SKIP_EMPTY)) { free(line); line = NULL; have_line = false; *line_num += 1; continue; } break; } } *out = line; return 0; fail_errno: fprintf(stderr, "%s: " PRI_SZ ": %s.\n", strm->get_filename(strm), *line_num, strerror(errno)); free(line); *out = NULL; return -1; out_eof: free(line); *out = NULL; return 1; } squashfs-tools-ng-1.1.3/lib/fstream/internal.h000066400000000000000000000030501410627516300213100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * internal.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef INTERNAL_H #define INTERNAL_H #include "config.h" #include "compat.h" #include "fstream.h" #include #include #include #include #include #include #include #include #include #define BUFSZ (262144) typedef struct ostream_comp_t { ostream_t base; ostream_t *wrapped; size_t inbuf_used; sqfs_u8 inbuf[BUFSZ]; sqfs_u8 outbuf[BUFSZ]; int (*flush_inbuf)(struct ostream_comp_t *ostrm, bool finish); void (*cleanup)(struct ostream_comp_t *ostrm); } ostream_comp_t; typedef struct istream_comp_t { istream_t base; istream_t *wrapped; sqfs_u8 uncompressed[BUFSZ]; bool eof; void (*cleanup)(struct istream_comp_t *strm); } istream_comp_t; #ifdef __cplusplus extern "C" { #endif SQFS_INTERNAL ostream_comp_t *ostream_gzip_create(const char *filename); SQFS_INTERNAL ostream_comp_t *ostream_xz_create(const char *filename); SQFS_INTERNAL ostream_comp_t *ostream_zstd_create(const char *filename); SQFS_INTERNAL ostream_comp_t *ostream_bzip2_create(const char *filename); SQFS_INTERNAL istream_comp_t *istream_gzip_create(const char *filename); SQFS_INTERNAL istream_comp_t *istream_xz_create(const char *filename); SQFS_INTERNAL istream_comp_t *istream_zstd_create(const char *filename); SQFS_INTERNAL istream_comp_t *istream_bzip2_create(const char *filename); #ifdef __cplusplus } #endif #endif /* INTERNAL_H */ squashfs-tools-ng-1.1.3/lib/fstream/istream.c000066400000000000000000000033041410627516300211350ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * istream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" sqfs_s32 istream_read(istream_t *strm, void *data, size_t size) { sqfs_s32 total = 0; size_t diff; if (size > 0x7FFFFFFF) size = 0x7FFFFFFF; while (size > 0) { if (strm->buffer_offset >= strm->buffer_used) { if (istream_precache(strm)) return -1; if (strm->buffer_used == 0) break; } diff = strm->buffer_used - strm->buffer_offset; if (diff > size) diff = size; memcpy(data, strm->buffer + strm->buffer_offset, diff); data = (char *)data + diff; strm->buffer_offset += diff; size -= diff; total += diff; } return total; } int istream_precache(istream_t *strm) { if (strm->buffer_offset >= strm->buffer_used) { strm->buffer_offset = 0; strm->buffer_used = 0; } else if (strm->buffer_offset > 0) { memmove(strm->buffer, strm->buffer + strm->buffer_offset, strm->buffer_used - strm->buffer_offset); strm->buffer_used -= strm->buffer_offset; strm->buffer_offset = 0; } if (strm->eof) return 0; return strm->precache(strm); } const char *istream_get_filename(istream_t *strm) { return strm->get_filename(strm); } int istream_skip(istream_t *strm, sqfs_u64 size) { size_t diff; while (size > 0) { if (strm->buffer_offset >= strm->buffer_used) { if (istream_precache(strm)) return -1; if (strm->buffer_used == 0) { fprintf(stderr, "%s: unexpected end-of-file\n", strm->get_filename(strm)); return -1; } } diff = strm->buffer_used - strm->buffer_offset; if ((sqfs_u64)diff > size) diff = size; strm->buffer_offset += diff; size -= diff; } return 0; } squashfs-tools-ng-1.1.3/lib/fstream/ostream.c000066400000000000000000000027201410627516300211440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * ostream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" static int append_sparse_fallback(ostream_t *strm, size_t size) { char buffer[512]; size_t diff; memset(buffer, 0, sizeof(buffer)); while (size > 0) { diff = size < sizeof(buffer) ? size : sizeof(buffer); if (strm->append(strm, buffer, diff)) return -1; size -= diff; } return 0; } int ostream_append(ostream_t *strm, const void *data, size_t size) { return strm->append(strm, data, size); } int ostream_append_sparse(ostream_t *strm, size_t size) { if (strm->append_sparse == NULL) return append_sparse_fallback(strm, size); return strm->append_sparse(strm, size); } int ostream_flush(ostream_t *strm) { return strm->flush(strm); } const char *ostream_get_filename(ostream_t *strm) { return strm->get_filename(strm); } sqfs_s32 ostream_append_from_istream(ostream_t *out, istream_t *in, sqfs_u32 size) { sqfs_s32 total = 0; size_t diff; if (size > 0x7FFFFFFF) size = 0x7FFFFFFF; while (size > 0) { if (in->buffer_offset >= in->buffer_used) { if (istream_precache(in)) return -1; if (in->buffer_used == 0) break; } diff = in->buffer_used - in->buffer_offset; if (diff > size) diff = size; if (out->append(out, in->buffer + in->buffer_offset, diff)) return -1; in->buffer_offset += diff; size -= diff; total += diff; } return total; } squashfs-tools-ng-1.1.3/lib/fstream/printf.c000066400000000000000000000007461410627516300210020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * printf.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" int ostream_printf(ostream_t *strm, const char *fmt, ...) { char *temp = NULL; va_list ap; int ret; va_start(ap, fmt); ret = vasprintf(&temp, fmt, ap); if (ret < 0) perror(strm->get_filename(strm)); va_end(ap); if (ret < 0) return -1; if (strm->append(strm, temp, ret)) ret = -1; free(temp); return ret; } squashfs-tools-ng-1.1.3/lib/fstream/uncompress/000077500000000000000000000000001410627516300215235ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/fstream/uncompress/autodetect.c000066400000000000000000000025031410627516300240300ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * autodetect.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" static const struct { int id; const sqfs_u8 *value; size_t len; } magic[] = { { FSTREAM_COMPRESSOR_GZIP, (const sqfs_u8 *)"\x1F\x8B\x08", 3 }, { FSTREAM_COMPRESSOR_XZ, (const sqfs_u8 *)("\xFD" "7zXZ"), 6 }, { FSTREAM_COMPRESSOR_ZSTD, (const sqfs_u8 *)"\x28\xB5\x2F\xFD", 4 }, { FSTREAM_COMPRESSOR_BZIP2, (const sqfs_u8 *)"BZh", 3 }, }; int istream_detect_compressor(istream_t *strm, int (*probe)(const sqfs_u8 *data, size_t size)) { size_t i; int ret; ret = istream_precache(strm); if (ret != 0) return ret; if (probe != NULL) { ret = probe(strm->buffer + strm->buffer_offset, strm->buffer_used - strm->buffer_offset); if (ret < 0) return ret; /* XXX: this means the data is uncompressed. We do this check first since it might be perfectly OK for the uncompressed data to contain a magic number from the table. */ if (ret > 0) return 0; } for (i = 0; i < sizeof(magic) / sizeof(magic[0]); ++i) { if ((strm->buffer_used - strm->buffer_offset) < magic[i].len) continue; ret = memcmp(strm->buffer + strm->buffer_offset, magic[i].value, magic[i].len); if (ret == 0) return magic[i].id; } return 0; } squashfs-tools-ng-1.1.3/lib/fstream/uncompress/bzip2.c000066400000000000000000000047071410627516300227250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * bzip2.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include typedef struct { istream_comp_t base; bool initialized; bz_stream strm; } istream_bzip2_t; static int precache(istream_t *base) { istream_bzip2_t *bzip2 = (istream_bzip2_t *)base; istream_t *wrapped = ((istream_comp_t *)base)->wrapped; size_t avail; int ret; for (;;) { if (!bzip2->initialized) { if (BZ2_bzDecompressInit(&bzip2->strm, 0, 0) != BZ_OK) { fprintf(stderr, "%s: error initializing " "bzip2 decompressor.\n", wrapped->get_filename(wrapped)); return -1; } bzip2->initialized = true; } ret = istream_precache(wrapped); if (ret != 0) return ret; avail = wrapped->buffer_used; if ((sizeof(size_t) > sizeof(unsigned int)) && (avail > (size_t)UINT_MAX)) { avail = UINT_MAX; } bzip2->strm.next_in = (char *)wrapped->buffer; bzip2->strm.avail_in = (unsigned int)avail; if (base->buffer_used > BUFSZ) base->buffer_used = BUFSZ; avail = BUFSZ - base->buffer_used; if ((sizeof(size_t) > sizeof(unsigned int)) && (avail > (size_t)UINT_MAX)) { avail = UINT_MAX; } bzip2->strm.next_out = (char *)base->buffer + base->buffer_used; bzip2->strm.avail_out = (unsigned int)avail; if (bzip2->strm.avail_out < 1) break; ret = BZ2_bzDecompress(&bzip2->strm); if (ret < 0) { fprintf(stderr, "%s: internal error in bzip2 " "decompressor.\n", wrapped->get_filename(wrapped)); return -1; } base->buffer_used = BUFSZ - bzip2->strm.avail_out; wrapped->buffer_offset = wrapped->buffer_used - bzip2->strm.avail_in; if (ret == BZ_STREAM_END) { if (istream_precache(wrapped)) return -1; BZ2_bzDecompressEnd(&bzip2->strm); bzip2->initialized = false; if (wrapped->buffer_used == 0) { base->eof = true; break; } } } return 0; } static void cleanup(istream_comp_t *base) { istream_bzip2_t *bzip2 = (istream_bzip2_t *)base; if (bzip2->initialized) BZ2_bzDecompressEnd(&bzip2->strm); } istream_comp_t *istream_bzip2_create(const char *filename) { istream_bzip2_t *bzip2 = calloc(1, sizeof(*bzip2)); istream_comp_t *base = (istream_comp_t *)bzip2; if (bzip2 == NULL) { fprintf(stderr, "%s: creating bzip2 compressor: %s.\n", filename, strerror(errno)); return NULL; } ((istream_t *)base)->precache = precache; base->cleanup = cleanup; return base; } squashfs-tools-ng-1.1.3/lib/fstream/uncompress/gzip.c000066400000000000000000000042341410627516300226430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * gzip.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include typedef struct { istream_comp_t base; z_stream strm; } istream_gzip_t; static int precache(istream_t *base) { istream_t *wrapped = ((istream_comp_t *)base)->wrapped; istream_gzip_t *gzip = (istream_gzip_t *)base; size_t avail_in, avail_out; int ret; for (;;) { ret = istream_precache(wrapped); if (ret != 0) return ret; avail_in = wrapped->buffer_used; avail_out = BUFSZ - base->buffer_used; if (sizeof(size_t) > sizeof(uInt)) { gzip->strm.avail_in = ~((uInt)0U); gzip->strm.avail_out = ~((uInt)0U); if ((size_t)gzip->strm.avail_in > avail_in) gzip->strm.avail_in = (uInt)avail_in; if ((size_t)gzip->strm.avail_out > avail_out) gzip->strm.avail_out = (uInt)avail_out; } else { gzip->strm.avail_in = (uInt)avail_in; gzip->strm.avail_out = (uInt)avail_out; } gzip->strm.next_in = wrapped->buffer; gzip->strm.next_out = base->buffer + base->buffer_used; ret = inflate(&gzip->strm, Z_NO_FLUSH); wrapped->buffer_offset = wrapped->buffer_used - gzip->strm.avail_in; base->buffer_used = BUFSZ - gzip->strm.avail_out; if (ret == Z_BUF_ERROR) break; if (ret == Z_STREAM_END) { base->eof = true; break; } if (ret != Z_OK) { fprintf(stderr, "%s: internal error in gzip decoder.\n", wrapped->get_filename(wrapped)); return -1; } } return 0; } static void cleanup(istream_comp_t *base) { istream_gzip_t *gzip = (istream_gzip_t *)base; inflateEnd(&gzip->strm); } istream_comp_t *istream_gzip_create(const char *filename) { istream_gzip_t *gzip = calloc(1, sizeof(*gzip)); istream_comp_t *base = (istream_comp_t *)gzip; int ret; if (gzip == NULL) { fprintf(stderr, "%s: creating gzip decoder: %s.\n", filename, strerror(errno)); return NULL; } ret = inflateInit2(&gzip->strm, 16 + 15); if (ret != Z_OK) { fprintf(stderr, "%s: internal error creating gzip reader.\n", filename); free(gzip); return NULL; } ((istream_t *)base)->precache = precache; base->cleanup = cleanup; return base; } squashfs-tools-ng-1.1.3/lib/fstream/uncompress/istream_compressor.c000066400000000000000000000026521410627516300256140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * istream_compressor.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" static const char *comp_get_filename(istream_t *strm) { istream_comp_t *comp = (istream_comp_t *)strm; return comp->wrapped->get_filename(comp->wrapped); } static void comp_destroy(sqfs_object_t *obj) { istream_comp_t *comp = (istream_comp_t *)obj; comp->cleanup(comp); sqfs_destroy(comp->wrapped); free(comp); } istream_t *istream_compressor_create(istream_t *strm, int comp_id) { istream_comp_t *comp = NULL; sqfs_object_t *obj; istream_t *base; switch (comp_id) { case FSTREAM_COMPRESSOR_GZIP: #ifdef WITH_GZIP comp = istream_gzip_create(strm->get_filename(strm)); #endif break; case FSTREAM_COMPRESSOR_XZ: #ifdef WITH_XZ comp = istream_xz_create(strm->get_filename(strm)); #endif break; case FSTREAM_COMPRESSOR_ZSTD: #if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) comp = istream_zstd_create(strm->get_filename(strm)); #endif break; case FSTREAM_COMPRESSOR_BZIP2: #ifdef WITH_BZIP2 comp = istream_bzip2_create(strm->get_filename(strm)); #endif break; default: break; } if (comp == NULL) return NULL; comp->wrapped = strm; base = (istream_t *)comp; base->get_filename = comp_get_filename; base->buffer = comp->uncompressed; base->eof = false; obj = (sqfs_object_t *)comp; obj->destroy = comp_destroy; return base; } squashfs-tools-ng-1.1.3/lib/fstream/uncompress/xz.c000066400000000000000000000036141410627516300223340ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xz.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include typedef struct { istream_comp_t base; lzma_stream strm; } istream_xz_t; static int precache(istream_t *base) { istream_xz_t *xz = (istream_xz_t *)base; istream_t *wrapped = ((istream_comp_t *)base)->wrapped; lzma_action action; lzma_ret ret_xz; int ret; for (;;) { ret = istream_precache(wrapped); if (ret != 0) return ret; action = wrapped->eof ? LZMA_FINISH : LZMA_RUN; xz->strm.avail_in = wrapped->buffer_used; xz->strm.next_in = wrapped->buffer; xz->strm.avail_out = BUFSZ - base->buffer_used; xz->strm.next_out = base->buffer + base->buffer_used; ret_xz = lzma_code(&xz->strm, action); base->buffer_used = BUFSZ - xz->strm.avail_out; wrapped->buffer_offset = wrapped->buffer_used - xz->strm.avail_in; if (ret_xz == LZMA_BUF_ERROR) break; if (ret_xz == LZMA_STREAM_END) { base->eof = true; break; } if (ret_xz != LZMA_OK) { fprintf(stderr, "%s: internal error in xz decoder.\n", wrapped->get_filename(wrapped)); return -1; } } return 0; } static void cleanup(istream_comp_t *base) { istream_xz_t *xz = (istream_xz_t *)base; lzma_end(&xz->strm); } istream_comp_t *istream_xz_create(const char *filename) { istream_xz_t *xz = calloc(1, sizeof(*xz)); istream_comp_t *base = (istream_comp_t *)xz; sqfs_u64 memlimit = 65 * 1024 * 1024; lzma_ret ret_xz; if (xz == NULL) { fprintf(stderr, "%s: creating xz decoder: %s.\n", filename, strerror(errno)); return NULL; } ret_xz = lzma_stream_decoder(&xz->strm, memlimit, LZMA_CONCATENATED); if (ret_xz != LZMA_OK) { fprintf(stderr, "%s: error initializing xz decoder.\n", filename); free(xz); return NULL; } ((istream_t *)base)->precache = precache; base->cleanup = cleanup; return base; } squashfs-tools-ng-1.1.3/lib/fstream/uncompress/zstd.c000066400000000000000000000032611410627516300226550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * zstd.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #include #ifdef HAVE_ZSTD_STREAM typedef struct { istream_comp_t base; ZSTD_DStream* strm; } istream_zstd_t; static int precache(istream_t *base) { istream_zstd_t *zstd = (istream_zstd_t *)base; istream_t *wrapped = ((istream_comp_t *)base)->wrapped; ZSTD_outBuffer out; ZSTD_inBuffer in; size_t ret; if (istream_precache(wrapped)) return -1; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); in.src = wrapped->buffer; in.size = wrapped->buffer_used; out.dst = ((istream_comp_t *)base)->uncompressed + base->buffer_used; out.size = BUFSZ - base->buffer_used; ret = ZSTD_decompressStream(zstd->strm, &out, &in); if (ZSTD_isError(ret)) { fprintf(stderr, "%s: error in zstd decoder.\n", wrapped->get_filename(wrapped)); return -1; } wrapped->buffer_offset = in.pos; base->buffer_used += out.pos; return 0; } static void cleanup(istream_comp_t *base) { istream_zstd_t *zstd = (istream_zstd_t *)base; ZSTD_freeDStream(zstd->strm); } istream_comp_t *istream_zstd_create(const char *filename) { istream_zstd_t *zstd = calloc(1, sizeof(*zstd)); istream_comp_t *base = (istream_comp_t *)zstd; if (zstd == NULL) { fprintf(stderr, "%s: creating zstd decoder: %s.\n", filename, strerror(errno)); return NULL; } zstd->strm = ZSTD_createDStream(); if (zstd->strm == NULL) { fprintf(stderr, "%s: error creating zstd decoder.\n", filename); free(zstd); return NULL; } ((istream_t *)base)->precache = precache; base->cleanup = cleanup; return base; } #endif /* HAVE_ZSTD_STREAM */ squashfs-tools-ng-1.1.3/lib/fstream/unix/000077500000000000000000000000001410627516300203105ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/fstream/unix/istream.c000066400000000000000000000043011410627516300221160ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * istream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" typedef struct { istream_t base; char *path; int fd; bool eof; sqfs_u8 buffer[BUFSZ]; } file_istream_t; static int file_precache(istream_t *strm) { file_istream_t *file = (file_istream_t *)strm; ssize_t ret; size_t diff; while (strm->buffer_used < sizeof(file->buffer)) { diff = sizeof(file->buffer) - strm->buffer_used; ret = read(file->fd, strm->buffer + strm->buffer_used, diff); if (ret == 0) { file->eof = true; break; } if (ret < 0) { if (errno == EINTR) continue; perror(file->path); return -1; } strm->buffer_used += ret; } return 0; } static const char *file_get_filename(istream_t *strm) { file_istream_t *file = (file_istream_t *)strm; return file->path; } static void file_destroy(sqfs_object_t *obj) { file_istream_t *file = (file_istream_t *)obj; if (file->fd != STDIN_FILENO) close(file->fd); free(file->path); free(file); } istream_t *istream_open_file(const char *path) { file_istream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; istream_t *strm = (istream_t *)file; if (file == NULL) { perror(path); return NULL; } file->path = strdup(path); if (file->path == NULL) { perror(path); goto fail_free; } file->fd = open(path, O_RDONLY); if (file->fd < 0) { perror(path); goto fail_path; } strm->buffer = file->buffer; strm->precache = file_precache; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; fail_path: free(file->path); fail_free: free(file); return NULL; } istream_t *istream_open_stdin(void) { file_istream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; istream_t *strm = (istream_t *)file; if (file == NULL) goto fail; file->path = strdup("stdin"); if (file->path == NULL) goto fail; file->fd = STDIN_FILENO; strm->buffer = file->buffer; strm->precache = file_precache; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; fail: perror("creating file wrapper for stdin"); free(file); return NULL; } squashfs-tools-ng-1.1.3/lib/fstream/unix/ostream.c000066400000000000000000000061231410627516300221300ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * ostream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" typedef struct { ostream_t base; char *path; int fd; off_t sparse_count; off_t size; } file_ostream_t; static int file_append(ostream_t *strm, const void *data, size_t size) { file_ostream_t *file = (file_ostream_t *)strm; ssize_t ret; if (size == 0) return 0; if (file->sparse_count > 0) { if (lseek(file->fd, file->sparse_count, SEEK_CUR) == (off_t)-1) goto fail_errno; file->sparse_count = 0; } while (size > 0) { ret = write(file->fd, data, size); if (ret == 0) { fprintf(stderr, "%s: truncated data write.\n", file->path); return -1; } if (ret < 0) { if (errno == EINTR) continue; goto fail_errno; } file->size += ret; size -= ret; data = (const char *)data + ret; } return 0; fail_errno: perror(file->path); return -1; } static int file_append_sparse(ostream_t *strm, size_t size) { file_ostream_t *file = (file_ostream_t *)strm; file->sparse_count += size; file->size += size; return 0; } static int file_flush(ostream_t *strm) { file_ostream_t *file = (file_ostream_t *)strm; if (file->sparse_count > 0) { if (ftruncate(file->fd, file->size) != 0) goto fail; } if (fsync(file->fd) != 0) { if (errno == EINVAL) return 0; goto fail; } return 0; fail: perror(file->path); return -1; } static void file_destroy(sqfs_object_t *obj) { file_ostream_t *file = (file_ostream_t *)obj; if (file->fd != STDOUT_FILENO) close(file->fd); free(file->path); free(file); } static const char *file_get_filename(ostream_t *strm) { file_ostream_t *file = (file_ostream_t *)strm; return file->path; } ostream_t *ostream_open_file(const char *path, int flags) { file_ostream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; ostream_t *strm = (ostream_t *)file; if (file == NULL) { perror(path); return NULL; } file->path = strdup(path); if (file->path == NULL) { perror(path); goto fail_free; } if (flags & OSTREAM_OPEN_OVERWRITE) { file->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); } else { file->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); } if (file->fd < 0) { perror(path); goto fail_path; } if (flags & OSTREAM_OPEN_SPARSE) strm->append_sparse = file_append_sparse; strm->append = file_append; strm->flush = file_flush; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; fail_path: free(file->path); fail_free: free(file); return NULL; } ostream_t *ostream_open_stdout(void) { file_ostream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; ostream_t *strm = (ostream_t *)file; if (file == NULL) goto fail; file->path = strdup("stdout"); if (file->path == NULL) goto fail; file->fd = STDOUT_FILENO; strm->append = file_append; strm->flush = file_flush; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; fail: perror("creating file wrapper for stdout"); free(file); return NULL; } squashfs-tools-ng-1.1.3/lib/fstream/win32/000077500000000000000000000000001410627516300202675ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/fstream/win32/istream.c000066400000000000000000000045051410627516300221030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * istream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #define WIN32_LEAN_AND_MEAN #include typedef struct { istream_t base; char *path; HANDLE hnd; sqfs_u8 buffer[BUFSZ]; } file_istream_t; static int file_precache(istream_t *strm) { file_istream_t *file = (file_istream_t *)strm; DWORD diff, actual; HANDLE hnd; hnd = file->path == NULL ? GetStdHandle(STD_INPUT_HANDLE) : file->hnd; while (strm->buffer_used < sizeof(file->buffer)) { diff = sizeof(file->buffer) - strm->buffer_used; if (!ReadFile(hnd, strm->buffer + strm->buffer_used, diff, &actual, NULL)) { w32_perror(file->path == NULL ? "stdin" : file->path); return -1; } if (actual == 0) { strm->eof = true; break; } strm->buffer_used += actual; } return 0; } static const char *file_get_filename(istream_t *strm) { file_istream_t *file = (file_istream_t *)strm; return file->path == NULL ? "stdin" : file->path; } static void file_destroy(sqfs_object_t *obj) { file_istream_t *file = (file_istream_t *)obj; if (file->path != NULL) { CloseHandle(file->hnd); free(file->path); } free(file); } istream_t *istream_open_file(const char *path) { file_istream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; istream_t *strm = (istream_t *)file; if (file == NULL) { perror(path); return NULL; } file->path = strdup(path); if (file->path == NULL) { perror(path); goto fail_free; } file->hnd = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file->hnd == INVALID_HANDLE_VALUE) { perror(path); goto fail_path; } strm->buffer = file->buffer; strm->precache = file_precache; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; fail_path: free(file->path); fail_free: free(file); return NULL; } istream_t *istream_open_stdin(void) { file_istream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; istream_t *strm = (istream_t *)file; if (file == NULL) { perror("stdin"); return NULL; } strm->buffer = file->buffer; strm->precache = file_precache; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; } squashfs-tools-ng-1.1.3/lib/fstream/win32/ostream.c000066400000000000000000000072131410627516300221100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * ostream.c * * Copyright (C) 2019 David Oberhollenzer */ #include "../internal.h" #define WIN32_LEAN_AND_MEAN #include typedef struct { ostream_t base; char *path; HANDLE hnd; } file_ostream_t; static int w32_append(HANDLE hnd, const char *filename, const void *data, size_t size) { DWORD diff; while (size > 0) { if (!WriteFile(hnd, data, size, &diff, NULL)) { w32_perror(filename); return -1; } size -= diff; data = (const char *)data + diff; } return 0; } static int w32_flush(HANDLE hnd, const char *filename) { if (FlushFileBuffers(hnd) != 0) { w32_perror(filename); return -1; } return 0; } /*****************************************************************************/ static int file_append(ostream_t *strm, const void *data, size_t size) { file_ostream_t *file = (file_ostream_t *)strm; return w32_append(file->hnd, file->path, data, size); } static int file_append_sparse(ostream_t *strm, size_t size) { file_ostream_t *file = (file_ostream_t *)strm; LARGE_INTEGER pos; pos.QuadPart = size; if (!SetFilePointerEx(file->hnd, pos, NULL, FILE_CURRENT)) goto fail; if (!SetEndOfFile(file->hnd)) goto fail; return 0; fail: w32_perror(file->path); return -1; } static int file_flush(ostream_t *strm) { file_ostream_t *file = (file_ostream_t *)strm; return w32_flush(file->hnd, file->path); } static void file_destroy(sqfs_object_t *obj) { file_ostream_t *file = (file_ostream_t *)obj; CloseHandle(file->hnd); free(file->path); free(file); } static const char *file_get_filename(ostream_t *strm) { file_ostream_t *file = (file_ostream_t *)strm; return file->path; } /*****************************************************************************/ static int stdout_append(ostream_t *strm, const void *data, size_t size) { (void)strm; return w32_append(GetStdHandle(STD_OUTPUT_HANDLE), "stdout", data, size); } static int stdout_flush(ostream_t *strm) { (void)strm; return w32_flush(GetStdHandle(STD_OUTPUT_HANDLE), "stdout"); } static void stdout_destroy(sqfs_object_t *obj) { free(obj); } static const char *stdout_get_filename(ostream_t *strm) { (void)strm; return "stdout"; } /*****************************************************************************/ ostream_t *ostream_open_file(const char *path, int flags) { file_ostream_t *file = calloc(1, sizeof(*file)); sqfs_object_t *obj = (sqfs_object_t *)file; ostream_t *strm = (ostream_t *)file; int access_flags, creation_mode; if (file == NULL) { perror(path); return NULL; } file->path = strdup(path); if (file->path == NULL) { perror(path); goto fail_free; } access_flags = GENERIC_WRITE; if (flags & OSTREAM_OPEN_OVERWRITE) { creation_mode = CREATE_ALWAYS; } else { creation_mode = CREATE_NEW; } file->hnd = CreateFile(path, access_flags, 0, NULL, creation_mode, FILE_ATTRIBUTE_NORMAL, NULL); if (file->hnd == INVALID_HANDLE_VALUE) { w32_perror(path); goto fail_path; } if (flags & OSTREAM_OPEN_SPARSE) strm->append_sparse = file_append_sparse; strm->append = file_append; strm->flush = file_flush; strm->get_filename = file_get_filename; obj->destroy = file_destroy; return strm; fail_path: free(file->path); fail_free: free(file); return NULL; } ostream_t *ostream_open_stdout(void) { ostream_t *strm = calloc(1, sizeof(strm)); sqfs_object_t *obj = (sqfs_object_t *)strm; if (strm == NULL) { perror("creating stdout file wrapper"); return NULL; } strm->append = stdout_append; strm->flush = stdout_flush; strm->get_filename = stdout_get_filename; obj->destroy = stdout_destroy; return strm; } squashfs-tools-ng-1.1.3/lib/fstree/000077500000000000000000000000001410627516300171545ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/fstree/Makemodule.am000066400000000000000000000012531410627516300215570ustar00rootroot00000000000000libfstree_a_SOURCES = lib/fstree/fstree.c lib/fstree/fstree_from_file.c libfstree_a_SOURCES += lib/fstree/fstree_sort.c lib/fstree/hardlink.c libfstree_a_SOURCES += lib/fstree/post_process.c lib/fstree/get_path.c libfstree_a_SOURCES += lib/fstree/mknode.c lib/fstree/fstree_from_dir.c libfstree_a_SOURCES += lib/fstree/add_by_path.c lib/fstree/get_by_path.c libfstree_a_SOURCES += include/fstree.h lib/fstree/internal.h libfstree_a_SOURCES += lib/fstree/source_date_epoch.c libfstree_a_SOURCES += lib/fstree/canonicalize_name.c libfstree_a_SOURCES += lib/fstree/filename_sane.c libfstree_a_CFLAGS = $(AM_CFLAGS) libfstree_a_CPPFLAGS = $(AM_CPPFLAGS) noinst_LIBRARIES += libfstree.a squashfs-tools-ng-1.1.3/lib/fstree/add_by_path.c000066400000000000000000000022531410627516300215600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * add_by_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include #include tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, const struct stat *sb, const char *extra) { tree_node_t *child, *parent; const char *name; if (*path == '\0') { child = fs->root; assert(child != NULL); goto out; } parent = fstree_get_node_by_path(fs, fs->root, path, true, true); if (parent == NULL) return NULL; name = strrchr(path, '/'); name = (name == NULL ? path : (name + 1)); child = parent->data.dir.children; while (child != NULL && strcmp(child->name, name) != 0) child = child->next; out: if (child != NULL) { if (!S_ISDIR(child->mode) || !S_ISDIR(sb->st_mode) || !child->data.dir.created_implicitly) { errno = EEXIST; return NULL; } child->uid = sb->st_uid; child->gid = sb->st_gid; child->mode = sb->st_mode; child->mod_time = sb->st_mtime; child->data.dir.created_implicitly = false; return child; } return fstree_mknode(parent, name, strlen(name), extra, sb); } squashfs-tools-ng-1.1.3/lib/fstree/canonicalize_name.c000066400000000000000000000017411410627516300227620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * canonicalize_name.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" static void normalize_slashes(char *filename) { char *dst = filename, *src = filename; while (*src == '/') ++src; while (*src != '\0') { if (*src == '/') { while (*src == '/') ++src; if (*src == '\0') break; *(dst++) = '/'; } else { *(dst++) = *(src++); } } *dst = '\0'; } int canonicalize_name(char *filename) { char *dst = filename, *src = filename; normalize_slashes(filename); while (*src != '\0') { if (src[0] == '.') { if (src[1] == '\0') break; if (src[1] == '/') { src += 2; continue; } if (src[1] == '.' && (src[2] == '/' || src[2] == '\0')) return -1; } while (*src != '\0' && *src != '/') *(dst++) = *(src++); if (*src == '/') *(dst++) = *(src++); } *dst = '\0'; normalize_slashes(filename); return 0; } squashfs-tools-ng-1.1.3/lib/fstree/filename_sane.c000066400000000000000000000031571410627516300221140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * filename_sane.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) #ifdef _MSC_VER #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif static const char *bad_names[] = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", }; static bool is_allowed_by_os(const char *name) { size_t len, i; for (i = 0; i < sizeof(bad_names) / sizeof(bad_names[0]); ++i) { len = strlen(bad_names[i]); if (strncasecmp(name, bad_names[i], len) != 0) continue; if (name[len] == '\0') return false; if (name[len] == '.' && strchr(name + len + 1, '.') == NULL) return false; } return true; } #else static bool is_allowed_by_os(const char *name) { (void)name; return true; } #endif bool is_filename_sane(const char *name, bool check_os_specific) { if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) return false; if (check_os_specific && !is_allowed_by_os(name)) return false; while (*name != '\0') { if (*name == '/') return false; #if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) if (check_os_specific) { if (*name == '<' || *name == '>' || *name == ':') return false; if (*name == '"' || *name == '|' || *name == '?') return false; if (*name == '*' || *name == '\\' || *name <= 31) return false; } #endif ++name; } return true; } squashfs-tools-ng-1.1.3/lib/fstree/fstree.c000066400000000000000000000047131410627516300206150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" #include #include #include enum { DEF_UID = 0, DEF_GID, DEF_MODE, DEF_MTIME, }; static const char *defaults[] = { [DEF_UID] = "uid", [DEF_GID] = "gid", [DEF_MODE] = "mode", [DEF_MTIME] = "mtime", NULL }; static int process_defaults(struct stat *sb, char *subopts) { char *value; long lval; int i; while (*subopts != '\0') { i = getsubopt(&subopts, (char *const *)defaults, &value); if (value == NULL) { fprintf(stderr, "Missing value for option %s\n", defaults[i]); return -1; } switch (i) { case DEF_UID: lval = strtol(value, NULL, 0); if (lval < 0) goto fail_uv; if (lval > (long)INT32_MAX) goto fail_ov; sb->st_uid = lval; break; case DEF_GID: lval = strtol(value, NULL, 0); if (lval < 0) goto fail_uv; if (lval > (long)INT32_MAX) goto fail_ov; sb->st_gid = lval; break; case DEF_MODE: lval = strtol(value, NULL, 0); if (lval < 0) goto fail_uv; if (lval > 07777) goto fail_ov; sb->st_mode = S_IFDIR | (sqfs_u16)lval; break; case DEF_MTIME: lval = strtol(value, NULL, 0); if (lval < 0) goto fail_uv; if (lval > (long)INT32_MAX) goto fail_ov; sb->st_mtime = lval; break; default: fprintf(stderr, "Unknown option '%s'\n", value); return -1; } } return 0; fail_uv: fprintf(stderr, "%s: value must be positive\n", defaults[i]); return -1; fail_ov: fprintf(stderr, "%s: value too large\n", defaults[i]); return -1; } static void free_recursive(tree_node_t *n) { tree_node_t *it; if (S_ISDIR(n->mode)) { while (n->data.dir.children != NULL) { it = n->data.dir.children; n->data.dir.children = it->next; free_recursive(it); } } free(n); } int fstree_init(fstree_t *fs, char *defaults) { memset(fs, 0, sizeof(*fs)); fs->defaults.st_mode = S_IFDIR | 0755; fs->defaults.st_blksize = 512; fs->defaults.st_mtime = get_source_date_epoch(); if (defaults != NULL && process_defaults(&fs->defaults, defaults) != 0) return -1; fs->root = fstree_mknode(NULL, "", 0, NULL, &fs->defaults); if (fs->root == NULL) { perror("initializing file system tree"); return -1; } fs->root->data.dir.created_implicitly = true; return 0; } void fstree_cleanup(fstree_t *fs) { free_recursive(fs->root); free(fs->inodes); memset(fs, 0, sizeof(*fs)); } squashfs-tools-ng-1.1.3/lib/fstree/fstree_from_dir.c000066400000000000000000000227001410627516300224720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include #include #include #if defined(_WIN32) || defined(__WINDOWS__) #define UNIX_EPOCH_ON_W32 11644473600UL #define W32_TICS_PER_SEC 10000000UL static sqfs_u32 w32time_to_sqfs_time(const FILETIME *ft) { sqfs_u64 w32ts; w32ts = ft->dwHighDateTime; w32ts <<= 32UL; w32ts |= ft->dwLowDateTime; w32ts /= W32_TICS_PER_SEC; if (w32ts <= UNIX_EPOCH_ON_W32) return 0; w32ts -= UNIX_EPOCH_ON_W32; return (w32ts < 0x0FFFFFFFFUL) ? w32ts : 0xFFFFFFFF; } static int add_node(fstree_t *fs, tree_node_t *root, scan_node_callback cb, void *user, unsigned int flags, const LPWIN32_FIND_DATAW entry) { tree_node_t *n; DWORD length; if (entry->cFileName[0] == '.') { if (entry->cFileName[1] == '\0') return 0; if (entry->cFileName[1] == '.' && entry->cFileName[2] == '\0') return 0; } length = WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, -1, NULL, 0, NULL, NULL); if (length <= 0) { w32_perror("converting path to UTF-8"); return -1; } n = calloc(1, sizeof(*n) + length + 1); if (n == NULL) { fprintf(stderr, "creating tree node: out-of-memory\n"); return -1; } n->name = (char *)n->payload; WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, -1, n->name, length + 1, NULL, NULL); if (entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (flags & DIR_SCAN_NO_DIR) { free(n); return 0; } n->mode = S_IFDIR | 0755; } else { if (flags & DIR_SCAN_NO_FILE) { free(n); return 0; } n->mode = S_IFREG | 0644; } if (cb != NULL) { int ret = cb(user, fs, n); if (ret != 0) { free(n); return ret < 0 ? ret : 0; } } if (flags & DIR_SCAN_KEEP_TIME) { n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime)); } else { n->mod_time = fs->defaults.st_mtime; } n->parent = root; n->next = root->data.dir.children; root->data.dir.children = n; return 0; } static int scan_dir(fstree_t *fs, tree_node_t *root, const char *path, const WCHAR *wpath, scan_node_callback cb, void *user, unsigned int flags) { WIN32_FIND_DATAW entry; HANDLE dirhnd; dirhnd = FindFirstFileW(wpath, &entry); if (dirhnd == INVALID_HANDLE_VALUE) goto fail_perror; do { if (add_node(fs, root, cb, user, flags, &entry)) goto fail; } while (FindNextFileW(dirhnd, &entry)); if (GetLastError() != ERROR_NO_MORE_FILES) goto fail_perror; FindClose(dirhnd); return 0; fail_perror: w32_perror(path); fail: if (dirhnd != INVALID_HANDLE_VALUE) FindClose(dirhnd); return -1; } int fstree_from_dir(fstree_t *fs, tree_node_t *root, const char *path, scan_node_callback cb, void *user, unsigned int flags) { WCHAR *wpath = NULL, *new = NULL; size_t len, newlen; tree_node_t *n; /* path -> to_wchar(path) + L"\*" */ wpath = path_to_windows(path); if (wpath == NULL) { fprintf(stderr, "%s: allocation failure.\n", path); return -1; } for (len = 0; wpath[len] != '\0'; ++len) ; newlen = len + 1; if (len > 0 && wpath[len - 1] != '\\') newlen += 1; new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); if (new == NULL) { fprintf(stderr, "%s: allocation failure.\n", path); goto fail; } wpath = new; if (len > 0 && wpath[len - 1] != '\\') wpath[len++] = '\\'; wpath[len++] = '*'; wpath[len++] = '\0'; /* scan directory contents */ if (scan_dir(fs, root, path, wpath, cb, user, flags)) goto fail; free(wpath); wpath = NULL; /* recursion step */ if (flags & DIR_SCAN_NO_RECURSION) return 0; for (n = root->data.dir.children; n != NULL; n = n->next) { if (!S_ISDIR(n->mode)) continue; if (fstree_from_subdir(fs, n, path, n->name, cb, user, flags)) return -1; } return 0; fail: free(wpath); return -1; } int fstree_from_subdir(fstree_t *fs, tree_node_t *root, const char *path, const char *subdir, scan_node_callback cb, void *user, unsigned int flags) { size_t len, plen, slen; WCHAR *wpath = NULL; char *temp = NULL; tree_node_t *n; plen = strlen(path); slen = subdir == NULL ? 0 : strlen(subdir); if (slen == 0) return fstree_from_dir(fs, root, path, cb, user, flags); len = plen + 1 + slen + 2; temp = calloc(1, len + 1); if (temp == NULL) { fprintf(stderr, "%s/%s: allocation failure.\n", path, subdir); return -1; } memcpy(temp, path, plen); temp[plen] = '/'; memcpy(temp + plen + 1, subdir, slen); temp[plen + 1 + slen ] = '/'; temp[plen + 1 + slen + 1] = '*'; temp[plen + 1 + slen + 2] = '\0'; wpath = path_to_windows(temp); if (wpath == NULL) { fprintf(stderr, "%s: allocation failure.\n", temp); goto fail; } if (scan_dir(fs, root, temp, wpath, cb, user, flags)) goto fail; free(wpath); wpath = NULL; if (flags & DIR_SCAN_NO_RECURSION) { free(temp); return 0; } temp[plen + 1 + slen] = '\0'; for (n = root->data.dir.children; n != NULL; n = n->next) { if (!S_ISDIR(n->mode)) continue; if (fstree_from_subdir(fs, n, temp, n->name, cb, user, flags)) goto fail; } free(temp); return 0; fail: free(temp); free(wpath); return -1; } #else static void discard_node(tree_node_t *root, tree_node_t *n) { tree_node_t *it; if (n == root->data.dir.children) { root->data.dir.children = n->next; } else { it = root->data.dir.children; while (it != NULL && it->next != n) it = it->next; if (it != NULL) it->next = n->next; } free(n); } static int populate_dir(int dir_fd, fstree_t *fs, tree_node_t *root, dev_t devstart, scan_node_callback cb, void *user, unsigned int flags) { char *extra = NULL; struct dirent *ent; int ret, childfd; struct stat sb; tree_node_t *n; DIR *dir; dir = fdopendir(dir_fd); if (dir == NULL) { perror("fdopendir"); close(dir_fd); return -1; } /* XXX: fdopendir can dup and close dir_fd internally and still be compliant with the spec. */ dir_fd = dirfd(dir); for (;;) { errno = 0; ent = readdir(dir); if (ent == NULL) { if (errno) { perror("readdir"); goto fail; } break; } if (!strcmp(ent->d_name, "..") || !strcmp(ent->d_name, ".")) continue; if (fstatat(dir_fd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { perror(ent->d_name); goto fail; } switch (sb.st_mode & S_IFMT) { case S_IFSOCK: if (flags & DIR_SCAN_NO_SOCK) continue; break; case S_IFLNK: if (flags & DIR_SCAN_NO_SLINK) continue; break; case S_IFREG: if (flags & DIR_SCAN_NO_FILE) continue; break; case S_IFBLK: if (flags & DIR_SCAN_NO_BLK) continue; break; case S_IFCHR: if (flags & DIR_SCAN_NO_CHR) continue; break; case S_IFIFO: if (flags & DIR_SCAN_NO_FIFO) continue; break; default: break; } if ((flags & DIR_SCAN_ONE_FILESYSTEM) && sb.st_dev != devstart) continue; if (S_ISLNK(sb.st_mode)) { size_t size; if ((sizeof(sb.st_size) > sizeof(size_t)) && sb.st_size > SIZE_MAX) { errno = EOVERFLOW; goto fail_rdlink; } if (SZ_ADD_OV((size_t)sb.st_size, 1, &size)) { errno = EOVERFLOW; goto fail_rdlink; } extra = calloc(1, size); if (extra == NULL) goto fail_rdlink; if (readlinkat(dir_fd, ent->d_name, extra, (size_t)sb.st_size) < 0) { goto fail_rdlink; } extra[sb.st_size] = '\0'; } if (!(flags & DIR_SCAN_KEEP_TIME)) sb.st_mtime = fs->defaults.st_mtime; if (S_ISDIR(sb.st_mode) && (flags & DIR_SCAN_NO_DIR)) { n = fstree_get_node_by_path(fs, root, ent->d_name, false, false); if (n == NULL) continue; ret = 0; } else { n = fstree_mknode(root, ent->d_name, strlen(ent->d_name), extra, &sb); if (n == NULL) { perror("creating tree node"); goto fail; } ret = (cb == NULL) ? 0 : cb(user, fs, n); } free(extra); extra = NULL; if (ret < 0) goto fail; if (ret > 0) { discard_node(root, n); continue; } if (S_ISDIR(n->mode) && !(flags & DIR_SCAN_NO_RECURSION)) { childfd = openat(dir_fd, n->name, O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (childfd < 0) { perror(n->name); goto fail; } if (populate_dir(childfd, fs, n, devstart, cb, user, flags)) { goto fail; } } } closedir(dir); return 0; fail_rdlink: perror("readlink"); fail: closedir(dir); free(extra); return -1; } int fstree_from_subdir(fstree_t *fs, tree_node_t *root, const char *path, const char *subdir, scan_node_callback cb, void *user, unsigned int flags) { struct stat sb; int fd, subfd; if (!S_ISDIR(root->mode)) { fprintf(stderr, "scanning %s/%s into %s: target is not a directory\n", path, subdir == NULL ? "" : subdir, root->name); return -1; } fd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (fd < 0) { perror(path); return -1; } if (subdir != NULL) { subfd = openat(fd, subdir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (subfd < 0) { fprintf(stderr, "%s/%s: %s\n", path, subdir, strerror(errno)); close(fd); return -1; } close(fd); fd = subfd; } if (fstat(fd, &sb)) { fprintf(stderr, "%s/%s: %s\n", path, subdir == NULL ? "" : subdir, strerror(errno)); close(fd); return -1; } return populate_dir(fd, fs, root, sb.st_dev, cb, user, flags); } int fstree_from_dir(fstree_t *fs, tree_node_t *root, const char *path, scan_node_callback cb, void *user, unsigned int flags) { return fstree_from_subdir(fs, root, path, NULL, cb, user, flags); } #endif squashfs-tools-ng-1.1.3/lib/fstree/fstree_from_file.c000066400000000000000000000303501410627516300226330ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "fstream.h" #include "compat.h" #include #include #include #include #include struct glob_context { const char *filename; size_t line_num; struct stat *basic; unsigned int glob_flags; char *name_pattern; }; enum { GLOB_MODE_FROM_SRC = 0x01, GLOB_UID_FROM_SRC = 0x02, GLOB_GID_FROM_SRC = 0x04, GLOB_FLAG_PATH = 0x08, }; static const struct { const char *name; unsigned int clear_flag; unsigned int set_flag; } glob_scan_flags[] = { { "-type b", DIR_SCAN_NO_BLK, 0 }, { "-type c", DIR_SCAN_NO_CHR, 0 }, { "-type d", DIR_SCAN_NO_DIR, 0 }, { "-type p", DIR_SCAN_NO_FIFO, 0 }, { "-type f", DIR_SCAN_NO_FILE, 0 }, { "-type l", DIR_SCAN_NO_SLINK, 0 }, { "-type s", DIR_SCAN_NO_SOCK, 0 }, { "-xdev", 0, DIR_SCAN_ONE_FILESYSTEM }, { "-mount", 0, DIR_SCAN_ONE_FILESYSTEM }, { "-keeptime", 0, DIR_SCAN_KEEP_TIME }, { "-nonrecursive", 0, DIR_SCAN_NO_RECURSION }, }; static int add_generic(fstree_t *fs, const char *filename, size_t line_num, const char *path, struct stat *sb, const char *basepath, unsigned int glob_flags, const char *extra) { (void)basepath; (void)glob_flags; if (fstree_add_generic(fs, path, sb, extra) == NULL) { fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", filename, line_num, path, strerror(errno)); return -1; } return 0; } static int add_device(fstree_t *fs, const char *filename, size_t line_num, const char *path, struct stat *sb, const char *basepath, unsigned int glob_flags, const char *extra) { unsigned int maj, min; char c; if (sscanf(extra, "%c %u %u", &c, &maj, &min) != 3) { fprintf(stderr, "%s: " PRI_SZ ": " "expected ' major minor'\n", filename, line_num); return -1; } if (c == 'c' || c == 'C') { sb->st_mode |= S_IFCHR; } else if (c == 'b' || c == 'B') { sb->st_mode |= S_IFBLK; } else { fprintf(stderr, "%s: " PRI_SZ ": unknown device type '%c'\n", filename, line_num, c); return -1; } sb->st_rdev = makedev(maj, min); return add_generic(fs, filename, line_num, path, sb, basepath, glob_flags, NULL); } static int add_file(fstree_t *fs, const char *filename, size_t line_num, const char *path, struct stat *basic, const char *basepath, unsigned int glob_flags, const char *extra) { if (extra == NULL || *extra == '\0') extra = path; return add_generic(fs, filename, line_num, path, basic, basepath, glob_flags, extra); } static int add_hard_link(fstree_t *fs, const char *filename, size_t line_num, const char *path, struct stat *basic, const char *basepath, unsigned int glob_flags, const char *extra) { (void)basepath; (void)glob_flags; (void)basic; if (fstree_add_hard_link(fs, path, extra) == NULL) { fprintf(stderr, "%s: " PRI_SZ ": %s\n", filename, line_num, strerror(errno)); return -1; } return 0; } static int glob_node_callback(void *user, fstree_t *fs, tree_node_t *node) { struct glob_context *ctx = user; char *path; int ret; (void)fs; if (!(ctx->glob_flags & GLOB_MODE_FROM_SRC)) { node->mode &= ~(07777); node->mode |= ctx->basic->st_mode & 07777; } if (!(ctx->glob_flags & GLOB_UID_FROM_SRC)) node->uid = ctx->basic->st_uid; if (!(ctx->glob_flags & GLOB_GID_FROM_SRC)) node->gid = ctx->basic->st_gid; if (ctx->name_pattern != NULL) { if (ctx->glob_flags & GLOB_FLAG_PATH) { path = fstree_get_path(node); if (path == NULL) { fprintf(stderr, "%s: " PRI_SZ ": %s\n", ctx->filename, ctx->line_num, strerror(errno)); return -1; } ret = canonicalize_name(path); assert(ret == 0); ret = fnmatch(ctx->name_pattern, path, FNM_PATHNAME); free(path); } else { ret = fnmatch(ctx->name_pattern, node->name, 0); } if (ret != 0) return 1; } return 0; } static size_t name_string_length(const char *str) { size_t len = 0; int start; if (*str == '"' || *str == '\'') { start = *str; ++len; while (str[len] != '\0' && str[len] != start) ++len; if (str[len] == start) ++len; } else { while (str[len] != '\0' && !isspace(str[len])) ++len; } return len; } static void quote_remove(char *str) { char *dst = str; int start = *(str++); if (start != '\'' && start != '"') return; while (*str != start && *str != '\0') *(dst++) = *(str++); *(dst++) = '\0'; } static int glob_files(fstree_t *fs, const char *filename, size_t line_num, const char *path, struct stat *basic, const char *basepath, unsigned int glob_flags, const char *extra) { unsigned int scan_flags = 0, all_flags; struct glob_context ctx; bool first_clear_flag; size_t i, count, len; tree_node_t *root; int ret; memset(&ctx, 0, sizeof(ctx)); ctx.filename = filename; ctx.line_num = line_num; ctx.basic = basic; ctx.glob_flags = glob_flags; /* fetch the actual target node */ root = fstree_get_node_by_path(fs, fs->root, path, true, false); if (root == NULL) { fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", filename, line_num, path, strerror(errno)); return -1; } /* process options */ first_clear_flag = true; all_flags = DIR_SCAN_NO_BLK | DIR_SCAN_NO_CHR | DIR_SCAN_NO_DIR | DIR_SCAN_NO_FIFO | DIR_SCAN_NO_FILE | DIR_SCAN_NO_SLINK | DIR_SCAN_NO_SOCK; while (extra != NULL && *extra != '\0') { count = sizeof(glob_scan_flags) / sizeof(glob_scan_flags[0]); for (i = 0; i < count; ++i) { len = strlen(glob_scan_flags[i].name); if (strncmp(extra, glob_scan_flags[i].name, len) != 0) continue; if (isspace(extra[len])) { extra += len; while (isspace(*extra)) ++extra; break; } } if (i < count) { if (glob_scan_flags[i].clear_flag != 0 && first_clear_flag) { scan_flags |= all_flags; first_clear_flag = false; } scan_flags &= ~(glob_scan_flags[i].clear_flag); scan_flags |= glob_scan_flags[i].set_flag; continue; } if (strncmp(extra, "-name", 5) == 0 && isspace(extra[5])) { for (extra += 5; isspace(*extra); ++extra) ; len = name_string_length(extra); free(ctx.name_pattern); ctx.name_pattern = strndup(extra, len); extra += len; while (isspace(*extra)) ++extra; quote_remove(ctx.name_pattern); continue; } if (strncmp(extra, "-path", 5) == 0 && isspace(extra[5])) { for (extra += 5; isspace(*extra); ++extra) ; len = name_string_length(extra); free(ctx.name_pattern); ctx.name_pattern = strndup(extra, len); extra += len; while (isspace(*extra)) ++extra; quote_remove(ctx.name_pattern); ctx.glob_flags |= GLOB_FLAG_PATH; continue; } if (extra[0] == '-') { if (extra[1] == '-' && isspace(extra[2])) { extra += 2; while (isspace(*extra)) ++extra; break; } fprintf(stderr, "%s: " PRI_SZ ": unknown option.\n", filename, line_num); free(ctx.name_pattern); return -1; } else { break; } } if (extra != NULL && *extra == '\0') extra = NULL; /* do the scan */ if (basepath == NULL) { if (extra == NULL) { ret = fstree_from_dir(fs, root, ".", glob_node_callback, &ctx, scan_flags); } else { ret = fstree_from_dir(fs, root, extra, glob_node_callback, &ctx, scan_flags); } } else { ret = fstree_from_subdir(fs, root, basepath, extra, glob_node_callback, &ctx, scan_flags); } free(ctx.name_pattern); return ret; } static const struct callback_t { const char *keyword; unsigned int mode; bool need_extra; bool is_glob; bool allow_root; int (*callback)(fstree_t *fs, const char *filename, size_t line_num, const char *path, struct stat *sb, const char *basepath, unsigned int glob_flags, const char *extra); } file_list_hooks[] = { { "dir", S_IFDIR, false, false, true, add_generic }, { "slink", S_IFLNK, true, false, false, add_generic }, { "link", 0, true, false, false, add_hard_link }, { "nod", 0, true, false, false, add_device }, { "pipe", S_IFIFO, false, false, false, add_generic }, { "sock", S_IFSOCK, false, false, false, add_generic }, { "file", S_IFREG, false, false, false, add_file }, { "glob", 0, false, true, true, glob_files }, }; #define NUM_HOOKS (sizeof(file_list_hooks) / sizeof(file_list_hooks[0])) static char *skip_space(char *str) { if (!isspace(*str)) return NULL; while (isspace(*str)) ++str; return str; } static char *read_u32(char *str, sqfs_u32 *out, sqfs_u32 base) { *out = 0; if (!isdigit(*str)) return NULL; while (isdigit(*str)) { sqfs_u32 x = *(str++) - '0'; if (x >= base || (*out) > (0xFFFFFFFF - x) / base) return NULL; (*out) = (*out) * base + x; } return str; } static char *read_str(char *str, char **out) { *out = str; if (*str == '"') { char *ptr = str++; while (*str != '\0' && *str != '"') { if (str[0] == '\\' && (str[1] == '"' || str[1] == '\\')) { *(ptr++) = str[1]; str += 2; } else { *(ptr++) = *(str++); } } if (str[0] != '"' || !isspace(str[1])) return NULL; *ptr = '\0'; ++str; } else { while (*str != '\0' && !isspace(*str)) ++str; if (!isspace(*str)) return NULL; *(str++) = '\0'; } while (isspace(*str)) ++str; return str; } static int handle_line(fstree_t *fs, const char *filename, size_t line_num, char *line, const char *basepath) { const char *extra = NULL, *msg = NULL; const struct callback_t *cb = NULL; unsigned int glob_flags = 0; sqfs_u32 uid, gid, mode; struct stat sb; char *path; for (size_t i = 0; i < NUM_HOOKS; ++i) { size_t len = strlen(file_list_hooks[i].keyword); if (strncmp(file_list_hooks[i].keyword, line, len) != 0) continue; if (isspace(line[len])) { cb = file_list_hooks + i; line = skip_space(line + len); break; } } if (cb == NULL) goto fail_kw; if ((line = read_str(line, &path)) == NULL) goto fail_ent; if (canonicalize_name(path)) goto fail_ent; if (*path == '\0' && !cb->allow_root) goto fail_root; if (cb->is_glob && *line == '*') { ++line; mode = 0; glob_flags |= GLOB_MODE_FROM_SRC; } else { if ((line = read_u32(line, &mode, 8)) == NULL || mode > 07777) goto fail_mode; } if ((line = skip_space(line)) == NULL) goto fail_ent; if (cb->is_glob && *line == '*') { ++line; uid = 0; glob_flags |= GLOB_UID_FROM_SRC; } else { if ((line = read_u32(line, &uid, 10)) == NULL) goto fail_uid_gid; } if ((line = skip_space(line)) == NULL) goto fail_ent; if (cb->is_glob && *line == '*') { ++line; gid = 0; glob_flags |= GLOB_GID_FROM_SRC; } else { if ((line = read_u32(line, &gid, 10)) == NULL) goto fail_uid_gid; } if ((line = skip_space(line)) != NULL && *line != '\0') extra = line; if (cb->need_extra && extra == NULL) goto fail_no_extra; /* forward to callback */ memset(&sb, 0, sizeof(sb)); sb.st_mtime = fs->defaults.st_mtime; sb.st_mode = mode | cb->mode; sb.st_uid = uid; sb.st_gid = gid; return cb->callback(fs, filename, line_num, path, &sb, basepath, glob_flags, extra); fail_root: fprintf(stderr, "%s: " PRI_SZ ": cannot use / as argument for %s.\n", filename, line_num, cb->keyword); return -1; fail_no_extra: fprintf(stderr, "%s: " PRI_SZ ": missing argument for %s.\n", filename, line_num, cb->keyword); return -1; fail_uid_gid: msg = "uid & gid must be decimal numbers < 2^32"; goto out_desc; fail_mode: msg = "mode must be an octal number <= 07777"; goto out_desc; fail_kw: msg = "unknown entry type"; goto out_desc; fail_ent: msg = "error in entry description"; goto out_desc; out_desc: fprintf(stderr, "%s: " PRI_SZ ": %s.\n", filename, line_num, msg); fputs("expected: []\n", stderr); return -1; } int fstree_from_file(fstree_t *fs, const char *filename, const char *basepath) { size_t line_num = 1; istream_t *fp; char *line; int ret; fp = istream_open_file(filename); if (fp == NULL) return -1; for (;;) { ret = istream_get_line(fp, &line, &line_num, ISTREAM_LINE_LTRIM | ISTREAM_LINE_SKIP_EMPTY); if (ret < 0) return -1; if (ret > 0) break; if (line[0] != '#') { if (handle_line(fs, filename, line_num, line, basepath)) { goto fail_line; } } free(line); ++line_num; } sqfs_destroy(fp); return 0; fail_line: free(line); sqfs_destroy(fp); return -1; } squashfs-tools-ng-1.1.3/lib/fstree/fstree_sort.c000066400000000000000000000022131410627516300216550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_sort.c * * Copyright (C) 2019 David Oberhollenzer * Copyright (C) 2019 Zachary Dremann */ #include "internal.h" #include static tree_node_t *merge(tree_node_t *lhs, tree_node_t *rhs) { tree_node_t *it; tree_node_t *head = NULL; tree_node_t **next_ptr = &head; while (lhs != NULL && rhs != NULL) { if (strcmp(lhs->name, rhs->name) <= 0) { it = lhs; lhs = lhs->next; } else { it = rhs; rhs = rhs->next; } *next_ptr = it; next_ptr = &it->next; } it = (lhs != NULL ? lhs : rhs); *next_ptr = it; return head; } tree_node_t *tree_node_list_sort(tree_node_t *head) { tree_node_t *it, *half, *prev; it = half = prev = head; while (it != NULL) { prev = half; half = half->next; it = it->next; if (it != NULL) { it = it->next; } } // half refers to the (count/2)'th element ROUNDED UP. // It will be null therefore only in the empty and the // single element list if (half == NULL) { return head; } prev->next = NULL; return merge(tree_node_list_sort(head), tree_node_list_sort(half)); } squashfs-tools-ng-1.1.3/lib/fstree/get_by_path.c000066400000000000000000000024101410627516300216020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_by_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include static tree_node_t *child_by_name(tree_node_t *root, const char *name, size_t len) { tree_node_t *n = root->data.dir.children; while (n != NULL) { if (strncmp(n->name, name, len) == 0 && n->name[len] == '\0') break; n = n->next; } return n; } tree_node_t *fstree_get_node_by_path(fstree_t *fs, tree_node_t *root, const char *path, bool create_implicitly, bool stop_at_parent) { const char *end; tree_node_t *n; size_t len; while (*path != '\0') { while (*path == '/') ++path; if (!S_ISDIR(root->mode)) { errno = ENOTDIR; return NULL; } end = strchr(path, '/'); if (end == NULL) { if (stop_at_parent) break; len = strlen(path); } else { len = end - path; } n = child_by_name(root, path, len); if (n == NULL) { if (!create_implicitly) { errno = ENOENT; return NULL; } n = fstree_mknode(root, path, len, NULL, &fs->defaults); if (n == NULL) return NULL; n->data.dir.created_implicitly = true; } root = n; path = path + len; } return root; } squashfs-tools-ng-1.1.3/lib/fstree/get_path.c000066400000000000000000000013451410627516300211160ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include char *fstree_get_path(tree_node_t *node) { tree_node_t *it; char *str, *ptr; size_t len = 0; if (node->parent == NULL) return strdup("/"); for (it = node; it != NULL && it->parent != NULL; it = it->parent) { len += strlen(it->name) + 1; } str = malloc(len + 1); if (str == NULL) return NULL; ptr = str + len; *ptr = '\0'; for (it = node; it != NULL && it->parent != NULL; it = it->parent) { len = strlen(it->name); ptr -= len; memcpy(ptr, it->name, len); *(--ptr) = '/'; } return str; } squashfs-tools-ng-1.1.3/lib/fstree/hardlink.c000066400000000000000000000025661410627516300211250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * hardlink.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include #include tree_node_t *fstree_add_hard_link(fstree_t *fs, const char *path, const char *target) { struct stat sb; tree_node_t *n; memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFLNK | 0777; n = fstree_add_generic(fs, path, &sb, target); if (n != NULL) { if (canonicalize_name(n->data.target)) { free(n); errno = EINVAL; return NULL; } n->mode = FSTREE_MODE_HARD_LINK; } return n; } int fstree_resolve_hard_link(fstree_t *fs, tree_node_t *node) { tree_node_t *start = node; while (node->mode == FSTREE_MODE_HARD_LINK || node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { if (node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { node = node->data.target_node; } else { node = fstree_get_node_by_path(fs, fs->root, node->data.target, false, false); if (node == NULL) return -1; } if (node == start) { errno = EMLINK; return -1; } } if (S_ISDIR(node->mode)) { errno = EPERM; return -1; } if (node->link_count == 0x0FFFF) { errno = EMLINK; return -1; } start->mode = FSTREE_MODE_HARD_LINK_RESOLVED; start->data.target_node = node; node->link_count++; return 0; } squashfs-tools-ng-1.1.3/lib/fstree/internal.h000066400000000000000000000011041410627516300211350ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * internal.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef FSTREE_INTERNAL_H #define FSTREE_INTERNAL_H #include "config.h" #include "fstree.h" /* ASCIIbetically sort a linked list of tree nodes */ tree_node_t *tree_node_list_sort(tree_node_t *head); /* If the environment variable SOURCE_DATE_EPOCH is set to a parsable number that fits into an unsigned 32 bit value, return its value. Otherwise, default to 0. */ sqfs_u32 get_source_date_epoch(void); #endif /* FSTREE_INTERNAL_H */ squashfs-tools-ng-1.1.3/lib/fstree/mknode.c000066400000000000000000000030121410627516300205710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include #include tree_node_t *fstree_mknode(tree_node_t *parent, const char *name, size_t name_len, const char *extra, const struct stat *sb) { tree_node_t *n; size_t size; char *ptr; if (S_ISLNK(sb->st_mode) && extra == NULL) { errno = EINVAL; return NULL; } size = sizeof(tree_node_t) + name_len + 1; if (extra != NULL) size += strlen(extra) + 1; n = calloc(1, size); if (n == NULL) return NULL; if (parent != NULL) { n->next = parent->data.dir.children; parent->data.dir.children = n; n->parent = parent; } n->xattr_idx = 0xFFFFFFFF; n->uid = sb->st_uid; n->gid = sb->st_gid; n->mode = sb->st_mode; n->mod_time = sb->st_mtime; n->link_count = 1; n->name = (char *)n->payload; memcpy(n->name, name, name_len); if (extra != NULL) { ptr = n->name + name_len + 1; strcpy(ptr, extra); } else { ptr = NULL; } switch (sb->st_mode & S_IFMT) { case S_IFREG: n->data.file.input_file = ptr; break; case S_IFLNK: n->mode = S_IFLNK | 0777; n->data.target = ptr; break; case S_IFBLK: case S_IFCHR: n->data.devno = sb->st_rdev; break; case S_IFDIR: n->link_count = 2; break; default: break; } if (parent != NULL) { if (parent->link_count == 0x0FFFF) { free(n); errno = EMLINK; return NULL; } parent->link_count++; } return n; } squashfs-tools-ng-1.1.3/lib/fstree/post_process.c000066400000000000000000000112251410627516300220440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * post_process.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" #include #include #include #include #include static int alloc_inode_num_dfs(fstree_t *fs, tree_node_t *root) { bool has_subdirs = false; tree_node_t *it; size_t inum; for (it = root->data.dir.children; it != NULL; it = it->next) { if (S_ISDIR(it->mode)) { has_subdirs = true; break; } } if (has_subdirs) { for (it = root->data.dir.children; it != NULL; it = it->next) { if (S_ISDIR(it->mode)) { if (alloc_inode_num_dfs(fs, it)) return -1; } } } for (it = root->data.dir.children; it != NULL; it = it->next) { if (it->mode != FSTREE_MODE_HARD_LINK_RESOLVED) { if (SZ_ADD_OV(fs->unique_inode_count, 1, &inum)) goto fail_ov; if ((sizeof(size_t) > sizeof(sqfs_u32)) && inum > 0x0FFFFFFFFUL) { goto fail_ov; } it->inode_num = (sqfs_u32)inum; fs->unique_inode_count = inum; } } return 0; fail_ov: fputs("Too many inodes.\n", stderr); return -1; } static int resolve_hard_links_dfs(fstree_t *fs, tree_node_t *n) { tree_node_t *it; if (n->mode == FSTREE_MODE_HARD_LINK) { if (fstree_resolve_hard_link(fs, n)) goto fail_link; assert(n->mode == FSTREE_MODE_HARD_LINK_RESOLVED); it = n->data.target_node; if (S_ISDIR(it->mode) && it->data.dir.visited) goto fail_link_loop; } else if (S_ISDIR(n->mode)) { n->data.dir.visited = true; for (it = n->data.dir.children; it != NULL; it = it->next) { if (resolve_hard_links_dfs(fs, it)) return -1; } n->data.dir.visited = false; } return 0; fail_link: { char *path = fstree_get_path(n); fprintf(stderr, "Resolving hard link '%s' -> '%s': %s\n", path == NULL ? n->name : path, n->data.target, strerror(errno)); free(path); } return -1; fail_link_loop: { char *npath = fstree_get_path(n); char *tpath = fstree_get_path(it); fprintf(stderr, "Hard link loop detected in '%s' -> '%s'\n", npath == NULL ? n->name : npath, tpath == NULL ? it->name : tpath); free(npath); free(tpath); } return -1; } static void sort_recursive(tree_node_t *n) { n->data.dir.children = tree_node_list_sort(n->data.dir.children); for (n = n->data.dir.children; n != NULL; n = n->next) { if (S_ISDIR(n->mode)) sort_recursive(n); } } static file_info_t *file_list_dfs(tree_node_t *n) { if (S_ISREG(n->mode)) { n->data.file.next = NULL; return &n->data.file; } if (S_ISDIR(n->mode)) { file_info_t *list = NULL, *last = NULL; for (n = n->data.dir.children; n != NULL; n = n->next) { if (list == NULL) { list = file_list_dfs(n); if (list == NULL) continue; last = list; } else { last->next = file_list_dfs(n); } while (last->next != NULL) last = last->next; } return list; } return NULL; } static void map_inodes_dfs(fstree_t *fs, tree_node_t *n) { if (n->mode == FSTREE_MODE_HARD_LINK_RESOLVED) return; fs->inodes[n->inode_num - 1] = n; if (S_ISDIR(n->mode)) { for (n = n->data.dir.children; n != NULL; n = n->next) map_inodes_dfs(fs, n); } } static void reorder_hard_links(fstree_t *fs) { size_t i, j, tgt_idx; tree_node_t *it, *tgt; for (i = 0; i < fs->unique_inode_count; ++i) { if (!S_ISDIR(fs->inodes[i]->mode)) continue; it = fs->inodes[i]->data.dir.children; for (; it != NULL; it = it->next) { if (it->mode != FSTREE_MODE_HARD_LINK_RESOLVED) continue; tgt = it->data.target_node; tgt_idx = tgt->inode_num - 1; if (tgt_idx <= i) continue; /* TODO ? */ assert(!S_ISDIR(tgt->mode)); for (j = tgt_idx; j > i; --j) { fs->inodes[j] = fs->inodes[j - 1]; fs->inodes[j]->inode_num += 1; } fs->inodes[i] = tgt; /* XXX: the possible overflow is checked for during allocation */ tgt->inode_num = (sqfs_u32)(i + 1); ++i; } } } int fstree_post_process(fstree_t *fs) { size_t inum; sort_recursive(fs->root); if (resolve_hard_links_dfs(fs, fs->root)) return -1; fs->unique_inode_count = 0; if (alloc_inode_num_dfs(fs, fs->root)) return -1; if (SZ_ADD_OV(fs->unique_inode_count, 1, &inum)) goto fail_root_ov; if ((sizeof(size_t) > sizeof(sqfs_u32)) && inum > 0x0FFFFFFFFUL) goto fail_root_ov; fs->root->inode_num = (sqfs_u32)inum; fs->unique_inode_count = inum; fs->inodes = calloc(sizeof(fs->inodes[0]), fs->unique_inode_count); if (fs->inodes == NULL) { perror("Allocating inode list"); return -1; } map_inodes_dfs(fs, fs->root); reorder_hard_links(fs); fs->files = file_list_dfs(fs->root); return 0; fail_root_ov: fputs("Too many inodes, cannot allocate number for root.\n", stderr); return -1; } squashfs-tools-ng-1.1.3/lib/fstree/source_date_epoch.c000066400000000000000000000014701410627516300227750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * source_date_epoch.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" #include #include #include sqfs_u32 get_source_date_epoch(void) { const char *str, *ptr; sqfs_u32 x, tval = 0; str = getenv("SOURCE_DATE_EPOCH"); if (str == NULL || *str == '\0') return 0; for (ptr = str; *ptr != '\0'; ++ptr) { if (!isdigit(*ptr)) goto fail_nan; x = (*ptr) - '0'; if (tval > (UINT32_MAX - x) / 10) goto fail_ov; tval = tval * 10 + x; } return tval; fail_ov: fprintf(stderr, "WARNING: SOURCE_DATE_EPOCH=%s does not fit into " "32 bit integer\n", str); return 0; fail_nan: fprintf(stderr, "WARNING: SOURCE_DATE_EPOCH=%s is not a positive " "number\n", str); return 0; } squashfs-tools-ng-1.1.3/lib/lz4/000077500000000000000000000000001410627516300163755ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/lz4/Makemodule.am000066400000000000000000000003711410627516300210000ustar00rootroot00000000000000if WITH_OWN_LZ4 liblz4_la_SOURCES = lib/lz4/lz4.c lib/lz4/lz4.h liblz4_la_SOURCES += lib/lz4/lz4hc.c lib/lz4/lz4hc.h liblz4_la_CPPFLAGS = -I$(top_srcdir)/lib/lz4 -DLZ4_HEAPMODE=1 noinst_LTLIBRARIES += liblz4.la endif EXTRA_DIST += lib/lz4/README squashfs-tools-ng-1.1.3/lib/lz4/README000066400000000000000000000011041410627516300172510ustar00rootroot00000000000000This source has been extracted from the lz4 release tarball, version 1.9.2 released on August 20th, 2019. The source code originates from the "lib" subdirectory. The license is included in the subdirectory licenses/LZ4.txt in the tools-ng subdirectory of the squashfs-tools-ng source package. The following modifications have been made: - Always define LZ4LIB_API and LZ4LIB_STATIC_API to set default visibility to hidden, so the LZ4 functions aren't exported from libsquashfs. - Remove the streaming functions and most of the functions that aren't used by libsquashfs. squashfs-tools-ng-1.1.3/lib/lz4/lz4.c000066400000000000000000002141331410627516300172560ustar00rootroot00000000000000/* LZ4 - Fast LZ compression algorithm Copyright (C) 2011-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://www.lz4.org - LZ4 source repository : https://github.com/lz4/lz4 */ /*-************************************ * Tuning parameters **************************************/ /* * LZ4_HEAPMODE : * Select how default compression functions will allocate memory for their hash table, * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). */ #ifndef LZ4_HEAPMODE # define LZ4_HEAPMODE 0 #endif /* * ACCELERATION_DEFAULT : * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 */ #define ACCELERATION_DEFAULT 1 /*-************************************ * CPU Feature Detection **************************************/ /* LZ4_FORCE_MEMORY_ACCESS * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. * The below switch allow to select different access method for improved performance. * Method 0 (default) : use `memcpy()`. Safe and portable. * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets which assembly generation depends on alignment. * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. * Prefer these methods in priority order (0 > 1 > 2) */ #ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ # if defined(__GNUC__) && \ ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define LZ4_FORCE_MEMORY_ACCESS 2 # elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) # define LZ4_FORCE_MEMORY_ACCESS 1 # endif #endif /* * LZ4_FORCE_SW_BITCOUNT * Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ # define LZ4_FORCE_SW_BITCOUNT #endif /*-************************************ * Dependency **************************************/ /* * LZ4_SRC_INCLUDED: * Amalgamation flag, whether lz4.c is included */ #ifndef LZ4_SRC_INCLUDED # define LZ4_SRC_INCLUDED 1 #endif #ifndef LZ4_STATIC_LINKING_ONLY #define LZ4_STATIC_LINKING_ONLY #endif #ifndef LZ4_DISABLE_DEPRECATE_WARNINGS #define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ #endif #define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ #include "lz4.h" /* see also "memory routines" below */ /*-************************************ * Compiler Options **************************************/ #ifdef _MSC_VER /* Visual Studio */ # include # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ #endif /* _MSC_VER */ #ifndef LZ4_FORCE_INLINE # ifdef _MSC_VER /* Visual Studio */ # define LZ4_FORCE_INLINE static __forceinline # else # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # ifdef __GNUC__ # define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) # else # define LZ4_FORCE_INLINE static inline # endif # else # define LZ4_FORCE_INLINE static # endif /* __STDC_VERSION__ */ # endif /* _MSC_VER */ #endif /* LZ4_FORCE_INLINE */ /* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, * together with a simple 8-byte copy loop as a fall-back path. * However, this optimization hurts the decompression speed by >30%, * because the execution does not go to the optimized loop * for typical compressible data, and all of the preamble checks * before going to the fall-back path become useless overhead. * This optimization happens only with the -O3 flag, and -O2 generates * a simple 8-byte copy loop. * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 * functions are annotated with __attribute__((optimize("O2"))), * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute * of LZ4_wildCopy8 does not affect the compression speed. */ #if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) # define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2"))) # define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE #else # define LZ4_FORCE_O2_GCC_PPC64LE # define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static #endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else # define expect(expr,value) (expr) #endif #ifndef likely #define likely(expr) expect((expr) != 0, 1) #endif #ifndef unlikely #define unlikely(expr) expect((expr) != 0, 0) #endif /*-************************************ * Memory routines **************************************/ #include /* malloc, calloc, free */ #define ALLOC(s) malloc(s) #define ALLOC_AND_ZERO(s) calloc(1,s) #define FREEMEM(p) free(p) #include /* memset, memcpy */ #define MEM_INIT(p,v,s) memset((p),(v),(s)) /*-************************************ * Common Constants **************************************/ #define MINMATCH 4 #define WILDCOPYLENGTH 8 #define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ #define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ #define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ #define FASTLOOP_SAFE_DISTANCE 64 static const int LZ4_minLength = (MFLIMIT+1); #define KB *(1 <<10) #define MB *(1 <<20) #define GB *(1U<<30) #define LZ4_DISTANCE_ABSOLUTE_MAX 65535 #if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ # error "LZ4_DISTANCE_MAX is too big : must be <= 65535" #endif #define ML_BITS 4 #define ML_MASK ((1U<=1) # include #else # ifndef assert # define assert(condition) ((void)0) # endif #endif #define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ #if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) # include static int g_debuglog_enable = 1; # define DEBUGLOG(l, ...) { \ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ fprintf(stderr, __FILE__ ": "); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, " \n"); \ } } #else # define DEBUGLOG(l, ...) {} /* disabled */ #endif /*-************************************ * Types **************************************/ #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; typedef uintptr_t uptrval; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; typedef size_t uptrval; /* generally true, except OpenVMS-64 */ #endif #if defined(__x86_64__) typedef U64 reg_t; /* 64-bits in x32 mode */ #else typedef size_t reg_t; /* 32-bits in x32 mode */ #endif typedef enum { notLimited = 0, limitedOutput = 1, fillOutput = 2 } limitedOutput_directive; /*-************************************ * Reading and writing into memory **************************************/ static unsigned LZ4_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } #if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) /* lie to the compiler about data alignment; use with caution */ static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } #elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign; static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; } static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } #else /* safe and portable access using memcpy() */ static U16 LZ4_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; } static U32 LZ4_read32(const void* memPtr) { U32 val; memcpy(&val, memPtr, sizeof(val)); return val; } static reg_t LZ4_read_ARCH(const void* memPtr) { reg_t val; memcpy(&val, memPtr, sizeof(val)); return val; } static void LZ4_write16(void* memPtr, U16 value) { memcpy(memPtr, &value, sizeof(value)); } static void LZ4_write32(void* memPtr, U32 value) { memcpy(memPtr, &value, sizeof(value)); } #endif /* LZ4_FORCE_MEMORY_ACCESS */ static U16 LZ4_readLE16(const void* memPtr) { if (LZ4_isLittleEndian()) { return LZ4_read16(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; return (U16)((U16)p[0] + (p[1]<<8)); } } static void LZ4_writeLE16(void* memPtr, U16 value) { if (LZ4_isLittleEndian()) { LZ4_write16(memPtr, value); } else { BYTE* p = (BYTE*)memPtr; p[0] = (BYTE) value; p[1] = (BYTE)(value>>8); } } /* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ LZ4_FORCE_O2_INLINE_GCC_PPC64LE void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* const e = (BYTE*)dstEnd; do { memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ LZ4_FORCE_O2_INLINE_GCC_PPC64LE void LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* const e = (BYTE*)dstEnd; do { memcpy(d,s,16); memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH * - there is at least 8 bytes available to write after dstEnd */ LZ4_FORCE_O2_INLINE_GCC_PPC64LE void LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) { BYTE v[8]; assert(dstEnd >= dstPtr + MINMATCH); LZ4_write32(dstPtr, 0); /* silence an msan warning when offset==0 */ switch(offset) { case 1: memset(v, *srcPtr, 8); break; case 2: memcpy(v, srcPtr, 2); memcpy(&v[2], srcPtr, 2); memcpy(&v[4], &v[0], 4); break; case 4: memcpy(v, srcPtr, 4); memcpy(&v[4], srcPtr, 4); break; default: LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); return; } memcpy(dstPtr, v, 8); dstPtr += 8; while (dstPtr < dstEnd) { memcpy(dstPtr, v, 8); dstPtr += 8; } } #endif /*-************************************ * Common functions **************************************/ static unsigned LZ4_NbCommonBytes (reg_t val) { if (LZ4_isLittleEndian()) { if (sizeof(val)==8) { # if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanForward64( &r, (U64)val ); return (int)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_ctzll((U64)val) >> 3; # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanForward( &r, (U32)val ); return (int)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_ctz((U32)val) >> 3; # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif } } else /* Big Endian CPU */ { if (sizeof(val)==8) { /* 64-bits */ # if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse64( &r, val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_clzll((U64)val) >> 3; # else static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. Note that this code path is never triggered in 32-bits mode. */ unsigned r; if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse( &r, (unsigned long)val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_clz((U32)val) >> 3; # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif } } } #define STEPSIZE sizeof(reg_t) LZ4_FORCE_INLINE unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) { const BYTE* const pStart = pIn; if (likely(pIn < pInLimit-(STEPSIZE-1))) { reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; } else { return LZ4_NbCommonBytes(diff); } } while (likely(pIn < pInLimit-(STEPSIZE-1))) { reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } pIn += LZ4_NbCommonBytes(diff); return (unsigned)(pIn - pStart); } if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } if ((pIn compression run slower on incompressible data */ /*-************************************ * Local Structures and types **************************************/ typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; /** * This enum distinguishes several different modes of accessing previous * content in the stream. * * - noDict : There is no preceding content. * - withPrefix64k : Table entries up to ctx->dictSize before the current blob * blob being compressed are valid and refer to the preceding * content (of length ctx->dictSize), which is available * contiguously preceding in memory the content currently * being compressed. * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere * else in memory, starting at ctx->dictionary with length * ctx->dictSize. * - usingDictCtx : Like usingExtDict, but everything concerning the preceding * content is in a separate context, pointed to by * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table * entries in the current context that refer to positions * preceding the beginning of the current compression are * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx * ->dictSize describe the location and size of the preceding * content, and matches are found by looking in the ctx * ->dictCtx->hashTable. */ typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; /*-************************************ * Local Utils **************************************/ int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } /*-****************************** * Compression functions ********************************/ static U32 LZ4_hash4(U32 sequence, tableType_t const tableType) { if (tableType == byU16) return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); else return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } static U32 LZ4_hash5(U64 sequence, tableType_t const tableType) { const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; if (LZ4_isLittleEndian()) { const U64 prime5bytes = 889523592379ULL; return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); } else { const U64 prime8bytes = 11400714785074694791ULL; return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); } } LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) { if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); return LZ4_hash4(LZ4_read32(p), tableType); } static void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) { switch (tableType) { default: /* fallthrough */ case clearedTable: { /* illegal! */ assert(0); return; } case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } } } static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) { switch (tableType) { default: /* fallthrough */ case clearedTable: /* fallthrough */ case byPtr: { /* illegal! */ assert(0); return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } } } static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) { switch (tableType) { case clearedTable: { /* illegal! */ assert(0); return; } case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } } } LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 const h = LZ4_hashPosition(p, tableType); LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); } /* LZ4_getIndexOnHash() : * Index of match position registered in hash table. * hash position must be calculated by using base+index, or dictBase+index. * Assumption 1 : only valid if tableType == byU32 or byU16. * Assumption 2 : h is presumed valid (within limits of hash table) */ static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) { LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; assert(h < (1U << (LZ4_MEMORY_USAGE-2))); return hashTable[h]; } if (tableType == byU16) { const U16* const hashTable = (const U16*) tableBase; assert(h < (1U << (LZ4_MEMORY_USAGE-1))); return hashTable[h]; } assert(0); return 0; /* forbidden case */ } static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) { if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ } LZ4_FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, const void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 const h = LZ4_hashPosition(p, tableType); return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); } LZ4_FORCE_INLINE void LZ4_prepareTable(LZ4_stream_t_internal* const cctx, const int inputSize, const tableType_t tableType) { /* If compression failed during the previous step, then the context * is marked as dirty, therefore, it has to be fully reset. */ if (cctx->dirty) { DEBUGLOG(5, "LZ4_prepareTable: Full reset for %p", cctx); MEM_INIT(cctx, 0, sizeof(LZ4_stream_t_internal)); return; } /* If the table hasn't been used, it's guaranteed to be zeroed out, and is * therefore safe to use no matter what mode we're in. Otherwise, we figure * out if it's safe to leave as is or whether it needs to be reset. */ if (cctx->tableType != clearedTable) { assert(inputSize >= 0); if (cctx->tableType != tableType || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) || ((tableType == byU32) && cctx->currentOffset > 1 GB) || tableType == byPtr || inputSize >= 4 KB) { DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); cctx->currentOffset = 0; cctx->tableType = clearedTable; } else { DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); } } /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, is faster * than compressing without a gap. However, compressing with * currentOffset == 0 is faster still, so we preserve that case. */ if (cctx->currentOffset != 0 && tableType == byU32) { DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); cctx->currentOffset += 64 KB; } /* Finally, clear history */ cctx->dictCtx = NULL; cctx->dictionary = NULL; cctx->dictSize = 0; } /** LZ4_compress_generic() : inlined, to ensure branches are decided at compilation time */ LZ4_FORCE_INLINE int LZ4_compress_generic( LZ4_stream_t_internal* const cctx, const char* const source, char* const dest, const int inputSize, int *inputConsumed, /* only written when outputDirective == fillOutput */ const int maxOutputSize, const limitedOutput_directive outputDirective, const tableType_t tableType, const dict_directive dictDirective, const dictIssue_directive dictIssue, const int acceleration) { int result; const BYTE* ip = (const BYTE*) source; U32 const startIndex = cctx->currentOffset; const BYTE* base = (const BYTE*) source - startIndex; const BYTE* lowLimit; const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; const BYTE* const dictionary = dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; const U32 dictSize = dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ const BYTE* const dictEnd = dictionary + dictSize; const BYTE* anchor = (const BYTE*) source; const BYTE* const iend = ip + inputSize; const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; const BYTE* const matchlimit = iend - LASTLITERALS; /* the dictCtx currentOffset is indexed on the start of the dictionary, * while a dictionary in the current context precedes the currentOffset */ const BYTE* dictBase = (dictDirective == usingDictCtx) ? dictionary + dictSize - dictCtx->currentOffset : dictionary + dictSize - startIndex; BYTE* op = (BYTE*) dest; BYTE* const olimit = op + maxOutputSize; U32 offset = 0; U32 forwardH; DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType); /* If init conditions are not met, we don't have to mark stream * as having dirty context, since no action was taken yet */ if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported inputSize, too large (or negative) */ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ assert(acceleration >= 1); lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); /* Update context state */ if (dictDirective == usingDictCtx) { /* Subsequent linked blocks can't use the dictionary. */ /* Instead, they use the block we just compressed. */ cctx->dictCtx = NULL; cctx->dictSize = (U32)inputSize; } else { cctx->dictSize += (U32)inputSize; } cctx->currentOffset += (U32)inputSize; cctx->tableType = (U16)tableType; if (inputSizehashTable, tableType, base); ip++; forwardH = LZ4_hashPosition(ip, tableType); /* Main Loop */ for ( ; ; ) { const BYTE* match; BYTE* token; const BYTE* filledIp; /* Find a match */ if (tableType == byPtr) { const BYTE* forwardIp = ip; int step = 1; int searchMatchNb = acceleration << LZ4_skipTrigger; do { U32 const h = forwardH; ip = forwardIp; forwardIp += step; step = (searchMatchNb++ >> LZ4_skipTrigger); if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; assert(ip < mflimitPlusOne); match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); } while ( (match+LZ4_DISTANCE_MAX < ip) || (LZ4_read32(match) != LZ4_read32(ip)) ); } else { /* byU32, byU16 */ const BYTE* forwardIp = ip; int step = 1; int searchMatchNb = acceleration << LZ4_skipTrigger; do { U32 const h = forwardH; U32 const current = (U32)(forwardIp - base); U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); assert(matchIndex <= current); assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); ip = forwardIp; forwardIp += step; step = (searchMatchNb++ >> LZ4_skipTrigger); if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; assert(ip < mflimitPlusOne); if (dictDirective == usingDictCtx) { if (matchIndex < startIndex) { /* there was no match, try the dictionary */ assert(tableType == byU32); matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); match = dictBase + matchIndex; matchIndex += dictDelta; /* make dictCtx index comparable with current context */ lowLimit = dictionary; } else { match = base + matchIndex; lowLimit = (const BYTE*)source; } } else if (dictDirective==usingExtDict) { if (matchIndex < startIndex) { DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); assert(startIndex - matchIndex >= MINMATCH); match = dictBase + matchIndex; lowLimit = dictionary; } else { match = base + matchIndex; lowLimit = (const BYTE*)source; } } else { /* single continuous memory segment */ match = base + matchIndex; } forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ assert(matchIndex < current); if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) && (matchIndex+LZ4_DISTANCE_MAX < current)) { continue; } /* too far */ assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ if (LZ4_read32(match) == LZ4_read32(ip)) { if (maybe_extMem) offset = current - matchIndex; break; /* match found */ } } while(1); } /* Catch up */ filledIp = ip; while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } /* Encode Literals */ { unsigned const litLength = (unsigned)(ip - anchor); token = op++; if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } if ((outputDirective == fillOutput) && (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { op--; goto _last_literals; } if (litLength >= RUN_MASK) { int len = (int)(litLength - RUN_MASK); *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(litLength< olimit)) { /* the match was too close to the end, rewind and go to last literals */ op = token; goto _last_literals; } /* Encode Offset */ if (maybe_extMem) { /* static test */ DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); assert(offset <= LZ4_DISTANCE_MAX && offset > 0); LZ4_writeLE16(op, (U16)offset); op+=2; } else { DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); assert(ip-match <= LZ4_DISTANCE_MAX); LZ4_writeLE16(op, (U16)(ip - match)); op+=2; } /* Encode MatchLength */ { unsigned matchCode; if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) && (lowLimit==dictionary) /* match within extDict */ ) { const BYTE* limit = ip + (dictEnd-match); assert(dictEnd > match); if (limit > matchlimit) limit = matchlimit; matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); ip += (size_t)matchCode + MINMATCH; if (ip==limit) { unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); matchCode += more; ip += more; } DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); } else { matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); ip += (size_t)matchCode + MINMATCH; DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); } if ((outputDirective) && /* Check output buffer overflow */ (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { if (outputDirective == fillOutput) { /* Match description too long : reduce it */ U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; ip -= matchCode - newMatchCode; assert(newMatchCode < matchCode); matchCode = newMatchCode; if (unlikely(ip <= filledIp)) { /* We have already filled up to filledIp so if ip ends up less than filledIp * we have positions in the hash table beyond the current position. This is * a problem if we reuse the hash table. So we have to remove these positions * from the hash table. */ const BYTE* ptr; DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); for (ptr = ip; ptr <= filledIp; ++ptr) { U32 const h = LZ4_hashPosition(ptr, tableType); LZ4_clearHash(h, cctx->hashTable, tableType); } } } else { assert(outputDirective == limitedOutput); return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } } if (matchCode >= ML_MASK) { *token += ML_MASK; matchCode -= ML_MASK; LZ4_write32(op, 0xFFFFFFFF); while (matchCode >= 4*255) { op+=4; LZ4_write32(op, 0xFFFFFFFF); matchCode -= 4*255; } op += matchCode / 255; *op++ = (BYTE)(matchCode % 255); } else *token += (BYTE)(matchCode); } /* Ensure we have enough space for the last literals. */ assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); anchor = ip; /* Test end of chunk */ if (ip >= mflimitPlusOne) break; /* Fill table */ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); /* Test next position */ if (tableType == byPtr) { match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); LZ4_putPosition(ip, cctx->hashTable, tableType, base); if ( (match+LZ4_DISTANCE_MAX >= ip) && (LZ4_read32(match) == LZ4_read32(ip)) ) { token=op++; *token=0; goto _next_match; } } else { /* byU32, byU16 */ U32 const h = LZ4_hashPosition(ip, tableType); U32 const current = (U32)(ip-base); U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); assert(matchIndex < current); if (dictDirective == usingDictCtx) { if (matchIndex < startIndex) { /* there was no match, try the dictionary */ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); match = dictBase + matchIndex; lowLimit = dictionary; /* required for match length counter */ matchIndex += dictDelta; } else { match = base + matchIndex; lowLimit = (const BYTE*)source; /* required for match length counter */ } } else if (dictDirective==usingExtDict) { if (matchIndex < startIndex) { match = dictBase + matchIndex; lowLimit = dictionary; /* required for match length counter */ } else { match = base + matchIndex; lowLimit = (const BYTE*)source; /* required for match length counter */ } } else { /* single memory segment */ match = base + matchIndex; } LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); assert(matchIndex < current); if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) && (LZ4_read32(match) == LZ4_read32(ip)) ) { token=op++; *token=0; if (maybe_extMem) offset = current - matchIndex; DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); goto _next_match; } } /* Prepare next loop */ forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: /* Encode Last Literals */ { size_t lastRun = (size_t)(iend - anchor); if ( (outputDirective) && /* Check output buffer overflow */ (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { if (outputDirective == fillOutput) { /* adapt lastRun to fill 'dst' */ assert(olimit >= op); lastRun = (size_t)(olimit-op) - 1; lastRun -= (lastRun+240)/255; } else { assert(outputDirective == limitedOutput); return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } } if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRun< 0); return result; } int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; assert(ctx != NULL); if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; if (maxOutputSize >= LZ4_compressBound(inputSize)) { if (inputSize < LZ4_64Klimit) { return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); } else { const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); } } else { if (inputSize < LZ4_64Klimit) { return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); } else { const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); } } } static int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { int result; #if (LZ4_HEAPMODE) LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ if (ctxPtr == NULL) return 0; #else LZ4_stream_t ctx; LZ4_stream_t* const ctxPtr = &ctx; #endif result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); #if (LZ4_HEAPMODE) FREEMEM(ctxPtr); #endif return result; } int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) { return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); } /*-****************************** * Streaming functions ********************************/ #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : it reports an aligment of 8-bytes, while actually aligning LZ4_stream_t on 4 bytes. */ static size_t LZ4_stream_t_alignment(void) { struct { char c; LZ4_stream_t t; } t_a; return sizeof(t_a) - sizeof(t_a.t); } #endif LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) { DEBUGLOG(5, "LZ4_initStream"); if (buffer == NULL) { return NULL; } if (size < sizeof(LZ4_stream_t)) { return NULL; } #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : it reports an aligment of 8-bytes, while actually aligning LZ4_stream_t on 4 bytes. */ if (((size_t)buffer) & (LZ4_stream_t_alignment() - 1)) { return NULL; } /* alignment check */ #endif MEM_INIT(buffer, 0, sizeof(LZ4_stream_t)); return (LZ4_stream_t*)buffer; } /*-******************************* * Decompression functions ********************************/ typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; #undef MIN #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) /* Read the variable-length literal or match length. * * ip - pointer to use as input. * lencheck - end ip. Return an error if ip advances >= lencheck. * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so. * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so. * error (output) - error code. Should be set to 0 before call. */ typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; LZ4_FORCE_INLINE unsigned read_variable_length(const BYTE**ip, const BYTE* lencheck, int loop_check, int initial_check, variable_length_error* error) { unsigned length = 0; unsigned s; if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ *error = initial_error; return length; } do { s = **ip; (*ip)++; length += s; if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ *error = loop_error; return length; } } while (s==255); return length; } /*! LZ4_decompress_generic() : * This generic decompression function covers all use cases. * It shall be instantiated several times, using different sets of directives. * Note that it is important for performance that this function really get inlined, * in order to remove useless branches during compilation optimization. */ LZ4_FORCE_INLINE int LZ4_decompress_generic( const char* const src, char* const dst, int srcSize, int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */ earlyEnd_directive partialDecoding, /* full, partial */ dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ const BYTE* const dictStart, /* only if dict==usingExtDict */ const size_t dictSize /* note : = 0 if noDict */ ) { if (src == NULL) { return -1; } { const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; BYTE* op = (BYTE*) dst; BYTE* const oend = op + outputSize; BYTE* cpy; const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; const int safeDecode = (endOnInput==endOnInputSize); const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); /* Set up the "end" pointers for the shortcut. */ const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; const BYTE* match; size_t offset; unsigned token; size_t length; DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); /* Special cases */ assert(lowPrefix <= op); if ((endOnInput) && (unlikely(outputSize==0))) { /* Empty output buffer */ if (partialDecoding) return 0; return ((srcSize==1) && (*ip==0)) ? 0 : -1; } if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); } if ((endOnInput) && unlikely(srcSize==0)) { return -1; } /* Currently the fast loop shows a regression on qualcomm arm chips. */ #if LZ4_FAST_DEC_LOOP if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { DEBUGLOG(6, "skip fast decode loop"); goto safe_decode; } /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */ while (1) { /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ assert(oend - op >= FASTLOOP_SAFE_DISTANCE); if (endOnInput) { assert(ip < iend); } token = *ip++; length = token >> ML_BITS; /* literal length */ assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = ok; length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error); if (error == initial_error) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ /* copy literals */ cpy = op+length; LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); if (endOnInput) { /* LZ4_decompress_safe() */ if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } LZ4_wildCopy32(op, ip, cpy); } else { /* LZ4_decompress_fast() */ if (cpy>oend-8) { goto safe_literal_copy; } LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : * it doesn't know input length, and only relies on end-of-block properties */ } ip += length; op = cpy; } else { cpy = op+length; if (endOnInput) { /* LZ4_decompress_safe() */ DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); /* We don't need to check oend, since we check it once for each loop below */ if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ memcpy(op, ip, 16); } else { /* LZ4_decompress_fast() */ /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : * it doesn't know input length, and relies on end-of-block properties */ memcpy(op, ip, 8); if (length > 8) { memcpy(op+8, ip+8, 8); } } ip += length; op = cpy; } /* get offset */ offset = LZ4_readLE16(ip); ip+=2; match = op - offset; assert(match <= op); /* get matchlength */ length = token & ML_MASK; if (length == ML_MASK) { variable_length_error error = ok; if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error); if (error != ok) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; } } else { length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; } /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */ if ((dict == withPrefix64k) || (match >= lowPrefix)) { if (offset >= 8) { assert(match >= lowPrefix); assert(match <= op); assert(op + 18 <= oend); memcpy(op, match, 8); memcpy(op+8, match+8, 8); memcpy(op+16, match+16, 2); op += length; continue; } } } if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) { length = MIN(length, (size_t)(oend-op)); /* reach end of buffer */ } else { goto _output_error; /* end-of-block condition violated */ } } if (length <= (size_t)(lowPrefix-match)) { /* match fits entirely within external dictionary : just copy */ memmove(op, dictEnd - (lowPrefix-match), length); op += length; } else { /* match stretches into both external dictionary and current block */ size_t const copySize = (size_t)(lowPrefix - match); size_t const restSize = length - copySize; memcpy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ BYTE* const endOfMatch = op + restSize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) { *op++ = *copyFrom++; } } else { memcpy(op, lowPrefix, restSize); op += restSize; } } continue; } /* copy match within block */ cpy = op + length; assert((op <= oend) && (oend-op >= 32)); if (unlikely(offset<16)) { LZ4_memcpy_using_offset(op, match, cpy, offset); } else { LZ4_wildCopy32(op, match, cpy); } op = cpy; /* wildcopy correction */ } safe_decode: #endif /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ while (1) { token = *ip++; length = token >> ML_BITS; /* literal length */ assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ /* A two-stage shortcut for the most common case: * 1) If the literal length is 0..14, and there is enough space, * enter the shortcut and copy 16 bytes on behalf of the literals * (in the fast mode, only 8 bytes can be safely copied this way). * 2) Further if the match length is 4..18, copy 18 bytes in a similar * manner; but we ensure that there's enough space in the output for * those 18 bytes earlier, upon entering the shortcut (in other words, * there is a combined check for both stages). */ if ( (endOnInput ? length != RUN_MASK : length <= 8) /* strictly "less than" on input, to re-enter the loop with at least one byte */ && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { /* Copy the literals */ memcpy(op, ip, endOnInput ? 16 : 8); op += length; ip += length; /* The second stage: prepare for match copying, decode full info. * If it doesn't work out, the info won't be wasted. */ length = token & ML_MASK; /* match length */ offset = LZ4_readLE16(ip); ip += 2; match = op - offset; assert(match <= op); /* check overflow */ /* Do not deal with overlapping matches. */ if ( (length != ML_MASK) && (offset >= 8) && (dict==withPrefix64k || match >= lowPrefix) ) { /* Copy the match. */ memcpy(op + 0, match + 0, 8); memcpy(op + 8, match + 8, 8); memcpy(op +16, match +16, 2); op += length + MINMATCH; /* Both stages worked, load the next token. */ continue; } /* The second stage didn't work out, but the info is ready. * Propel it right to the point of match copying. */ goto _copy_match; } /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = ok; length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error); if (error == initial_error) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ } /* copy literals */ cpy = op+length; #if LZ4_FAST_DEC_LOOP safe_literal_copy: #endif LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) ) || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) { /* We've either hit the input parsing restriction or the output parsing restriction. * If we've hit the input parsing condition then this must be the last sequence. * If we've hit the output parsing condition then we are either using partialDecoding * or we've hit the output parsing condition. */ if (partialDecoding) { /* Since we are partial decoding we may be in this block because of the output parsing * restriction, which is not valid since the output buffer is allowed to be undersized. */ assert(endOnInput); /* If we're in this block because of the input parsing condition, then we must be on the * last sequence (or invalid), so we must check that we exactly consume the input. */ if ((ip+length>iend-(2+1+LASTLITERALS)) && (ip+length != iend)) { goto _output_error; } assert(ip+length <= iend); /* We are finishing in the middle of a literals segment. * Break after the copy. */ if (cpy > oend) { cpy = oend; assert(op<=oend); length = (size_t)(oend-op); } assert(ip+length <= iend); } else { /* We must be on the last sequence because of the parsing limitations so check * that we exactly regenerate the original size (must be exact when !endOnInput). */ if ((!endOnInput) && (cpy != oend)) { goto _output_error; } /* We must be on the last sequence (or invalid) because of the parsing limitations * so check that we exactly consume the input and don't overrun the output buffer. */ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { goto _output_error; } } memmove(op, ip, length); /* supports overlapping memory regions, which only matters for in-place decompression scenarios */ ip += length; op += length; /* Necessarily EOF when !partialDecoding. When partialDecoding * it is EOF if we've either filled the output buffer or hit * the input parsing restriction. */ if (!partialDecoding || (cpy == oend) || (ip == iend)) { break; } } else { LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ ip += length; op = cpy; } /* get offset */ offset = LZ4_readLE16(ip); ip+=2; match = op - offset; /* get matchlength */ length = token & ML_MASK; _copy_match: if (length == ML_MASK) { variable_length_error error = ok; length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error); if (error != ok) goto _output_error; if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ } length += MINMATCH; #if LZ4_FAST_DEC_LOOP safe_match_copy: #endif if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) length = MIN(length, (size_t)(oend-op)); else goto _output_error; /* doesn't respect parsing restriction */ } if (length <= (size_t)(lowPrefix-match)) { /* match fits entirely within external dictionary : just copy */ memmove(op, dictEnd - (lowPrefix-match), length); op += length; } else { /* match stretches into both external dictionary and current block */ size_t const copySize = (size_t)(lowPrefix - match); size_t const restSize = length - copySize; memcpy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ BYTE* const endOfMatch = op + restSize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) *op++ = *copyFrom++; } else { memcpy(op, lowPrefix, restSize); op += restSize; } } continue; } assert(match >= lowPrefix); /* copy match within block */ cpy = op + length; /* partialDecoding : may end anywhere within the block */ assert(op<=oend); if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { size_t const mlen = MIN(length, (size_t)(oend-op)); const BYTE* const matchEnd = match + mlen; BYTE* const copyEnd = op + mlen; if (matchEnd > op) { /* overlap copy */ while (op < copyEnd) { *op++ = *match++; } } else { memcpy(op, match, mlen); } op = copyEnd; if (op == oend) { break; } continue; } if (unlikely(offset<8)) { LZ4_write32(op, 0); /* silence msan warning when offset==0 */ op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += inc32table[offset]; memcpy(op+4, match, 4); match -= dec64table[offset]; } else { memcpy(op, match, 8); match += 8; } op += 8; if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ if (op < oCopyLimit) { LZ4_wildCopy8(op, match, oCopyLimit); match += oCopyLimit - op; op = oCopyLimit; } while (op < cpy) { *op++ = *match++; } } else { memcpy(op, match, 8); if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } } op = cpy; /* wildcopy correction */ } /* end of decoding */ if (endOnInput) { return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ } else { return (int) (((const char*)ip)-src); /* Nb of input bytes read */ } /* Overflow error detected */ _output_error: return (int) (-(((const char*)ip)-src))-1; } } /*===== Instantiate the API decoding functions. =====*/ LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, decode_full_block, noDict, (BYTE*)dest, NULL, 0); } #endif /* LZ4_COMMONDEFS_ONLY */ squashfs-tools-ng-1.1.3/lib/lz4/lz4.h000066400000000000000000000344701410627516300172670ustar00rootroot00000000000000/* * LZ4 - Fast LZ compression algorithm * Header File * Copyright (C) 2011-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 homepage : http://www.lz4.org - LZ4 source repository : https://github.com/lz4/lz4 */ #if defined (__cplusplus) extern "C" { #endif #ifndef LZ4_H_2983827168210 #define LZ4_H_2983827168210 /* --- Dependency --- */ #include /* size_t */ /** Introduction LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, scalable with multi-cores CPU. It features an extremely fast decoder, with speed in multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. The LZ4 compression library provides in-memory compression and decompression functions. It gives full buffer control to user. Compression can be done in: - a single step (described as Simple Functions) - a single step, reusing a context (described in Advanced Functions) - unbounded multiple steps (described as Streaming compression) lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). Decompressing such a compressed block requires additional metadata. Exact metadata depends on exact decompression function. For the typical case of LZ4_decompress_safe(), metadata includes block's compressed size, and maximum bound of decompressed size. Each application is free to encode and pass such metadata in whichever way it wants. lz4.h only handle blocks, it can not generate Frames. Blocks are different from Frames (doc/lz4_Frame_format.md). Frames bundle both blocks and metadata in a specified manner. Embedding metadata is required for compressed data to be self-contained and portable. Frame format is delivered through a companion API, declared in lz4frame.h. The `lz4` CLI can only manage frames. */ /*^*************************************************************** * Export parameters *****************************************************************/ /* * LZ4_DLL_EXPORT : * Enable exporting of functions when building a Windows DLL * LZ4LIB_VISIBILITY : * Control library symbols visibility. */ #ifndef LZ4LIB_VISIBILITY # if defined(__GNUC__) && (__GNUC__ >= 4) # define LZ4LIB_VISIBILITY __attribute__ ((visibility ("hidden"))) # else # define LZ4LIB_VISIBILITY # endif #endif #define LZ4LIB_API LZ4LIB_VISIBILITY /*------ Version ------*/ #define LZ4_QUOTE(str) #str #define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) #define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /*-************************************ * Tuning parameter **************************************/ /*! * LZ4_MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio. * Reduced memory usage may improve speed, thanks to better cache locality. * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #ifndef LZ4_MEMORY_USAGE # define LZ4_MEMORY_USAGE 14 #endif /*-************************************ * Simple Functions **************************************/ /*! LZ4_compress_default() : * Compresses 'srcSize' bytes from buffer 'src' * into already allocated 'dst' buffer of size 'dstCapacity'. * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). * It also runs faster, so it's a recommended setting. * If the function cannot compress 'src' into a more limited 'dst' budget, * compression stops *immediately*, and the function result is zero. * In which case, 'dst' content is undefined (invalid). * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. * dstCapacity : size of buffer 'dst' (which must be already allocated) * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) * or 0 if compression fails * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). */ LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); /*! LZ4_decompress_safe() : * compressedSize : is the exact complete size of the compressed block. * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) * If destination buffer is not large enough, decoding will stop and output an error code (negative value). * If the source stream is detected malformed, the function will stop decoding and return a negative result. * Note 1 : This function is protected against malicious data packets : * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, * even if the compressed block is maliciously modified to order the decoder to do these actions. * In such case, the decoder stops immediately, and considers the compressed block malformed. * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. * The implementation is free to send / store / derive this information in whichever way is most beneficial. * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. */ LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); /*-************************************ * Advanced Functions **************************************/ #define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ #define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) /*! LZ4_compressBound() : Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) This function is primarily useful for memory allocation purposes (destination buffer size). Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) inputSize : max supported value is LZ4_MAX_INPUT_SIZE return : maximum output size in a "worst case" scenario or 0, if input size is incorrect (too large or negative) */ LZ4LIB_API int LZ4_compressBound(int inputSize); /*! LZ4_compress_fast_extState() : * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. * Use LZ4_sizeofState() to know how much memory must be allocated, * and allocate it on 8-bytes boundaries (using `malloc()` typically). * Then, provide this buffer as `void* state` to compression function. */ LZ4LIB_API int LZ4_sizeofState(void); LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); /*-********************************************* * Streaming Compression Functions ***********************************************/ typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ /*-********************************************** * Streaming Decompression Functions * Bufferless synchronous API ************************************************/ typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ #endif /* LZ4_H_2983827168210 */ /*^************************************* * !!!!!! STATIC LINKING ONLY !!!!!! ***************************************/ /*-**************************************************************************** * Experimental section * * Symbols declared in this section must be considered unstable. Their * signatures or semantics may change, or they may be removed altogether in the * future. They are therefore only safe to depend on when the caller is * statically linked against the library. * * To protect against unsafe usage, not only are the declarations guarded, * the definitions are hidden by default * when building LZ4 as a shared/dynamic library. * * In order to access these declarations, * define LZ4_STATIC_LINKING_ONLY in your application * before including LZ4's headers. * * In order to make their implementations accessible dynamically, you must * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. ******************************************************************************/ #ifdef LZ4_STATIC_LINKING_ONLY #ifndef LZ4_STATIC_3504398509 #define LZ4_STATIC_3504398509 #define LZ4LIB_STATIC_API LZ4LIB_API #ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ # define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ #endif #endif /* LZ4_STATIC_3504398509 */ #endif /* LZ4_STATIC_LINKING_ONLY */ #ifndef LZ4_H_98237428734687 #define LZ4_H_98237428734687 /*-************************************************************ * PRIVATE DEFINITIONS ************************************************************** * Do not use these definitions directly. * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. * Accessing members will expose code to API and/or ABI break in future versions of the library. **************************************************************/ #define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) #define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) #define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) #include typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; struct LZ4_stream_t_internal { uint32_t hashTable[LZ4_HASH_SIZE_U32]; uint32_t currentOffset; uint16_t dirty; uint16_t tableType; const uint8_t* dictionary; const LZ4_stream_t_internal* dictCtx; uint32_t dictSize; }; typedef struct { const uint8_t* externalDict; size_t extDictSize; const uint8_t* prefixEnd; size_t prefixSize; } LZ4_streamDecode_t_internal; #else typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; struct LZ4_stream_t_internal { unsigned int hashTable[LZ4_HASH_SIZE_U32]; unsigned int currentOffset; unsigned short dirty; unsigned short tableType; const unsigned char* dictionary; const LZ4_stream_t_internal* dictCtx; unsigned int dictSize; }; typedef struct { const unsigned char* externalDict; const unsigned char* prefixEnd; size_t extDictSize; size_t prefixSize; } LZ4_streamDecode_t_internal; #endif /*! LZ4_stream_t : * information structure to track an LZ4 stream. * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. * The structure definition can be convenient for static allocation * (on stack, or as part of larger structure). * Init this structure with LZ4_initStream() before first use. * note : only use this definition in association with static linking ! * this definition is not API/ABI safe, and may change in a future version. */ #define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ ) #define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long)) union LZ4_stream_u { unsigned long long table[LZ4_STREAMSIZE_U64]; LZ4_stream_t_internal internal_donotuse; } ; /* previously typedef'd to LZ4_stream_t */ /*! LZ4_initStream() : v1.9.0+ * An LZ4_stream_t structure must be initialized at least once. * This is automatically done when invoking LZ4_createStream(), * but it's not when the structure is simply declared on stack (for example). * * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. * It can also initialize any arbitrary buffer of sufficient size, * and will @return a pointer of proper type upon initialization. * * Note : initialization fails if size and alignment conditions are not respected. * In which case, the function will @return NULL. * Note2: An LZ4_stream_t structure guarantees correct alignment and size. * Note3: Before v1.9.0, use LZ4_resetStream() instead */ LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); /*! LZ4_streamDecode_t : * information structure to track an LZ4 stream during decompression. * init this structure using LZ4_setStreamDecode() before first use. * note : only use in association with static linking ! * this definition is not API/ABI safe, * and may change in a future version ! */ #define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ ) #define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) union LZ4_streamDecode_u { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; LZ4_streamDecode_t_internal internal_donotuse; } ; /* previously typedef'd to LZ4_streamDecode_t */ #endif /* LZ4_H_98237428734687 */ #if defined (__cplusplus) } #endif squashfs-tools-ng-1.1.3/lib/lz4/lz4hc.c000066400000000000000000001572151410627516300176000ustar00rootroot00000000000000/* LZ4 HC - High Compression Mode of LZ4 Copyright (C) 2011-2017, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : https://github.com/lz4/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ /* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ /* ************************************* * Tuning Parameter ***************************************/ /*! HEAPMODE : * Select how default compression function will allocate workplace memory, * in stack (0:fastest), or in heap (1:requires malloc()). * Since workplace is rather large, heap mode is recommended. */ #ifndef LZ4HC_HEAPMODE # define LZ4HC_HEAPMODE 1 #endif /*=== Dependency ===*/ #define LZ4_HC_STATIC_LINKING_ONLY #include "lz4hc.h" /*=== Common LZ4 definitions ===*/ #if defined(__GNUC__) # pragma GCC diagnostic ignored "-Wunused-function" #endif #if defined (__clang__) # pragma clang diagnostic ignored "-Wunused-function" #endif /*=== Enums ===*/ typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; #define LZ4_COMMONDEFS_ONLY #ifndef LZ4_SRC_INCLUDED #include "lz4.c" /* LZ4_count, constants, mem */ #endif /*=== Constants ===*/ #define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) #define LZ4_OPT_NUM (1<<12) /*=== Macros ===*/ #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) #define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG)) #define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */ #define DELTANEXTU16(table, pos) table[(U16)(pos)] /* faster */ /* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */ #define UPDATABLE(ip, op, anchor) &ip, &op, &anchor static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); } /************************************** * HC Compression **************************************/ static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) { MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable)); MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); } static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) { uptrval startingOffset = (uptrval)(hc4->end - hc4->base); if (startingOffset > 1 GB) { LZ4HC_clearTables(hc4); startingOffset = 0; } startingOffset += 64 KB; hc4->nextToUpdate = (U32) startingOffset; hc4->base = start - startingOffset; hc4->end = start; hc4->dictBase = start - startingOffset; hc4->dictLimit = (U32) startingOffset; hc4->lowLimit = (U32) startingOffset; } /* Update chains up to ip (excluded) */ LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) { U16* const chainTable = hc4->chainTable; U32* const hashTable = hc4->hashTable; const BYTE* const base = hc4->base; U32 const target = (U32)(ip - base); U32 idx = hc4->nextToUpdate; while (idx < target) { U32 const h = LZ4HC_hashPtr(base+idx); size_t delta = idx - hashTable[h]; if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX; DELTANEXTU16(chainTable, idx) = (U16)delta; hashTable[h] = idx; idx++; } hc4->nextToUpdate = target; } /** LZ4HC_countBack() : * @return : negative value, nb of common bytes before ip/match */ LZ4_FORCE_INLINE int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, const BYTE* const iMin, const BYTE* const mMin) { int back = 0; int const min = (int)MAX(iMin - ip, mMin - match); assert(min <= 0); assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31)); assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31)); while ( (back > min) && (ip[back-1] == match[back-1]) ) back--; return back; } #if defined(_MSC_VER) # define LZ4HC_rotl32(x,r) _rotl(x,r) #else # define LZ4HC_rotl32(x,r) ((x << r) | (x >> (32 - r))) #endif static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern) { size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3; if (bitsToRotate == 0) return pattern; return LZ4HC_rotl32(pattern, (int)bitsToRotate); } /* LZ4HC_countPattern() : * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */ static unsigned LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) { const BYTE* const iStart = ip; reg_t const pattern = (sizeof(pattern)==8) ? (reg_t)pattern32 + (((reg_t)pattern32) << 32) : pattern32; while (likely(ip < iEnd-(sizeof(pattern)-1))) { reg_t const diff = LZ4_read_ARCH(ip) ^ pattern; if (!diff) { ip+=sizeof(pattern); continue; } ip += LZ4_NbCommonBytes(diff); return (unsigned)(ip - iStart); } if (LZ4_isLittleEndian()) { reg_t patternByte = pattern; while ((ip>= 8; } } else { /* big endian */ U32 bitOffset = (sizeof(pattern)*8) - 8; while (ip < iEnd) { BYTE const byte = (BYTE)(pattern >> bitOffset); if (*ip != byte) break; ip ++; bitOffset -= 8; } } return (unsigned)(ip - iStart); } /* LZ4HC_reverseCountPattern() : * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) * read using natural platform endianess */ static unsigned LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) { const BYTE* const iStart = ip; while (likely(ip >= iLow+4)) { if (LZ4_read32(ip-4) != pattern) break; ip -= 4; } { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianess */ while (likely(ip>iLow)) { if (ip[-1] != *bytePtr) break; ip--; bytePtr--; } } return (unsigned)(iStart - ip); } /* LZ4HC_protectDictEnd() : * Checks if the match is in the last 3 bytes of the dictionary, so reading the * 4 byte MINMATCH would overflow. * @returns true if the match index is okay. */ static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex) { return ((U32)((dictLimit - 1) - matchIndex) >= 3); } typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e; typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; LZ4_FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch ( LZ4HC_CCtx_internal* hc4, const BYTE* const ip, const BYTE* const iLowLimit, const BYTE* const iHighLimit, int longest, const BYTE** matchpos, const BYTE** startpos, const int maxNbAttempts, const int patternAnalysis, const int chainSwap, const dictCtx_directive dict, const HCfavor_e favorDecSpeed) { U16* const chainTable = hc4->chainTable; U32* const HashTable = hc4->hashTable; const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx; const BYTE* const base = hc4->base; const U32 dictLimit = hc4->dictLimit; const BYTE* const lowPrefixPtr = base + dictLimit; const U32 ipIndex = (U32)(ip - base); const U32 lowestMatchIndex = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX; const BYTE* const dictBase = hc4->dictBase; int const lookBackLength = (int)(ip-iLowLimit); int nbAttempts = maxNbAttempts; U32 matchChainPos = 0; U32 const pattern = LZ4_read32(ip); U32 matchIndex; repeat_state_e repeat = rep_untested; size_t srcPatternLength = 0; DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch"); /* First Match */ LZ4HC_Insert(hc4, ip); matchIndex = HashTable[LZ4HC_hashPtr(ip)]; DEBUGLOG(7, "First match at index %u / %u (lowestMatchIndex)", matchIndex, lowestMatchIndex); while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) { int matchLength=0; nbAttempts--; assert(matchIndex < ipIndex); if (favorDecSpeed && (ipIndex - matchIndex < 8)) { /* do nothing */ } else if (matchIndex >= dictLimit) { /* within current Prefix */ const BYTE* const matchPtr = base + matchIndex; assert(matchPtr >= lowPrefixPtr); assert(matchPtr < ip); assert(longest >= 1); if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) { if (LZ4_read32(matchPtr) == pattern) { int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, lowPrefixPtr) : 0; matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit); matchLength -= back; if (matchLength > longest) { longest = matchLength; *matchpos = matchPtr + back; *startpos = ip + back; } } } } else { /* lowestMatchIndex <= matchIndex < dictLimit */ const BYTE* const matchPtr = dictBase + matchIndex; if (LZ4_read32(matchPtr) == pattern) { const BYTE* const dictStart = dictBase + hc4->lowLimit; int back = 0; const BYTE* vLimit = ip + (dictLimit - matchIndex); if (vLimit > iHighLimit) vLimit = iHighLimit; matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) matchLength += LZ4_count(ip+matchLength, lowPrefixPtr, iHighLimit); back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0; matchLength -= back; if (matchLength > longest) { longest = matchLength; *matchpos = base + matchIndex + back; /* virtual pos, relative to ip, to retrieve offset */ *startpos = ip + back; } } } if (chainSwap && matchLength==longest) { /* better match => select a better chain */ assert(lookBackLength==0); /* search forward only */ if (matchIndex + (U32)longest <= ipIndex) { int const kTrigger = 4; U32 distanceToNextMatch = 1; int const end = longest - MINMATCH + 1; int step = 1; int accel = 1 << kTrigger; int pos; for (pos = 0; pos < end; pos += step) { U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos); step = (accel++ >> kTrigger); if (candidateDist > distanceToNextMatch) { distanceToNextMatch = candidateDist; matchChainPos = (U32)pos; accel = 1 << kTrigger; } } if (distanceToNextMatch > 1) { if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ matchIndex -= distanceToNextMatch; continue; } } } { U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex); if (patternAnalysis && distNextMatch==1 && matchChainPos==0) { U32 const matchCandidateIdx = matchIndex-1; /* may be a repeated pattern */ if (repeat == rep_untested) { if ( ((pattern & 0xFFFF) == (pattern >> 16)) & ((pattern & 0xFF) == (pattern >> 24)) ) { repeat = rep_confirmed; srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern); } else { repeat = rep_not; } } if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex) && LZ4HC_protectDictEnd(dictLimit, matchCandidateIdx) ) { const int extDict = matchCandidateIdx < dictLimit; const BYTE* const matchPtr = (extDict ? dictBase : base) + matchCandidateIdx; if (LZ4_read32(matchPtr) == pattern) { /* good candidate */ const BYTE* const dictStart = dictBase + hc4->lowLimit; const BYTE* const iLimit = extDict ? dictBase + dictLimit : iHighLimit; size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern); if (extDict && matchPtr + forwardPatternLength == iLimit) { U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern); forwardPatternLength += LZ4HC_countPattern(lowPrefixPtr, iHighLimit, rotatedPattern); } { const BYTE* const lowestMatchPtr = extDict ? dictStart : lowPrefixPtr; size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern); size_t currentSegmentLength; if (!extDict && matchPtr - backLength == lowPrefixPtr && hc4->lowLimit < dictLimit) { U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern); backLength += LZ4HC_reverseCountPattern(dictBase + dictLimit, dictStart, rotatedPattern); } /* Limit backLength not go further than lowestMatchIndex */ backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex); assert(matchCandidateIdx - backLength >= lowestMatchIndex); currentSegmentLength = backLength + forwardPatternLength; /* Adjust to end of pattern if the source pattern fits, otherwise the beginning of the pattern */ if ( (currentSegmentLength >= srcPatternLength) /* current pattern segment large enough to contain full srcPatternLength */ && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */ U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength; /* best position, full pattern, might be followed by more match */ if (LZ4HC_protectDictEnd(dictLimit, newMatchIndex)) matchIndex = newMatchIndex; else { /* Can only happen if started in the prefix */ assert(newMatchIndex >= dictLimit - 3 && newMatchIndex < dictLimit && !extDict); matchIndex = dictLimit; } } else { U32 const newMatchIndex = matchCandidateIdx - (U32)backLength; /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */ if (!LZ4HC_protectDictEnd(dictLimit, newMatchIndex)) { assert(newMatchIndex >= dictLimit - 3 && newMatchIndex < dictLimit && !extDict); matchIndex = dictLimit; } else { matchIndex = newMatchIndex; if (lookBackLength==0) { /* no back possible */ size_t const maxML = MIN(currentSegmentLength, srcPatternLength); if ((size_t)longest < maxML) { assert(base + matchIndex < ip); if (ip - (base+matchIndex) > LZ4_DISTANCE_MAX) break; assert(maxML < 2 GB); longest = (int)maxML; *matchpos = base + matchIndex; /* virtual pos, relative to ip, to retrieve offset */ *startpos = ip; } { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex); if (distToNextPattern > matchIndex) break; /* avoid overflow */ matchIndex -= distToNextPattern; } } } } } continue; } } } } /* PA optimization */ /* follow current chain */ matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos); } /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */ if ( dict == usingDictCtxHc && nbAttempts && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) { size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->base); U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; assert(dictEndOffset <= 1 GB); matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset; while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { const BYTE* const matchPtr = dictCtx->base + dictMatchIndex; if (LZ4_read32(matchPtr) == pattern) { int mlt; int back = 0; const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex); if (vLimit > iHighLimit) vLimit = iHighLimit; mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->base + dictCtx->dictLimit) : 0; mlt -= back; if (mlt > longest) { longest = mlt; *matchpos = base + matchIndex + back; *startpos = ip + back; } } { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex); dictMatchIndex -= nextOffset; matchIndex -= nextOffset; } } } return longest; } LZ4_FORCE_INLINE int LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ const BYTE* const ip, const BYTE* const iLimit, const BYTE** matchpos, const int maxNbAttempts, const int patternAnalysis, const dictCtx_directive dict) { const BYTE* uselessPtr = ip; /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), * but this won't be the case here, as we define iLowLimit==ip, * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, matchpos, &uselessPtr, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); } /* LZ4HC_encodeSequence() : * @return : 0 if ok, * 1 if buffer issue detected */ LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( const BYTE** ip, BYTE** op, const BYTE** anchor, int matchLength, const BYTE* const match, limitedOutput_directive limit, BYTE* oend) { size_t length; BYTE* const token = (*op)++; #if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) static const BYTE* start = NULL; static U32 totalCost = 0; U32 const pos = (start==NULL) ? 0 : (U32)(*anchor - start); U32 const ll = (U32)(*ip - *anchor); U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; U32 const cost = 1 + llAdd + ll + 2 + mlAdd; if (start==NULL) start = *anchor; /* only works for single segment */ /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */ DEBUGLOG(6, "pos:%7u -- literals:%3u, match:%4i, offset:%5u, cost:%3u + %u", pos, (U32)(*ip - *anchor), matchLength, (U32)(*ip-match), cost, totalCost); totalCost += cost; #endif /* Encode Literal length */ length = (size_t)(*ip - *anchor); if ((limit) && ((*op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) return 1; /* Check output limit */ if (length >= RUN_MASK) { size_t len = length - RUN_MASK; *token = (RUN_MASK << ML_BITS); for(; len >= 255 ; len -= 255) *(*op)++ = 255; *(*op)++ = (BYTE)len; } else { *token = (BYTE)(length << ML_BITS); } /* Copy Literals */ LZ4_wildCopy8(*op, *anchor, (*op) + length); *op += length; /* Encode Offset */ assert( (*ip - match) <= LZ4_DISTANCE_MAX ); /* note : consider providing offset as a value, rather than as a pointer difference */ LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2; /* Encode MatchLength */ assert(matchLength >= MINMATCH); length = (size_t)matchLength - MINMATCH; if ((limit) && (*op + (length / 255) + (1 + LASTLITERALS) > oend)) return 1; /* Check output limit */ if (length >= ML_MASK) { *token += ML_MASK; length -= ML_MASK; for(; length >= 510 ; length -= 510) { *(*op)++ = 255; *(*op)++ = 255; } if (length >= 255) { length -= 255; *(*op)++ = 255; } *(*op)++ = (BYTE)length; } else { *token += (BYTE)(length); } /* Prepare next loop */ *ip += matchLength; *anchor = *ip; return 0; } LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( LZ4HC_CCtx_internal* const ctx, const char* const source, char* const dest, int* srcSizePtr, int const maxOutputSize, unsigned maxNbAttempts, const limitedOutput_directive limit, const dictCtx_directive dict ) { const int inputSize = *srcSizePtr; const int patternAnalysis = (maxNbAttempts > 128); /* levels 9+ */ const BYTE* ip = (const BYTE*) source; const BYTE* anchor = ip; const BYTE* const iend = ip + inputSize; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = (iend - LASTLITERALS); BYTE* optr = (BYTE*) dest; BYTE* op = (BYTE*) dest; BYTE* oend = op + maxOutputSize; int ml0, ml, ml2, ml3; const BYTE* start0; const BYTE* ref0; const BYTE* ref = NULL; const BYTE* start2 = NULL; const BYTE* ref2 = NULL; const BYTE* start3 = NULL; const BYTE* ref3 = NULL; /* init */ *srcSizePtr = 0; if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ /* Main Loop */ while (ip <= mflimit) { ml = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, &ref, maxNbAttempts, patternAnalysis, dict); if (ml encode ML1 */ optr = op; if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; continue; } if (start0 < ip) { /* first match was skipped at least once */ if (start2 < ip + ml0) { /* squeezing ML1 between ML0(original ML1) and ML2 */ ip = start0; ref = ref0; ml = ml0; /* restore initial ML1 */ } } /* Here, start0==ip */ if ((start2 - ip) < 3) { /* First Match too small : removed */ ml = ml2; ip = start2; ref =ref2; goto _Search2; } _Search3: /* At this stage, we have : * ml2 > ml1, and * ip1+3 <= ip2 (usually < ip1+ml1) */ if ((start2 - ip) < OPTIMAL_ML) { int correction; int new_ml = ml; if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH; correction = new_ml - (int)(start2 - ip); if (correction > 0) { start2 += correction; ref2 += correction; ml2 -= correction; } } /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */ if (start2 + ml2 <= mflimit) { ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); } else { ml3 = ml2; } if (ml3 == ml2) { /* No better match => encode ML1 and ML2 */ /* ip & ref are known; Now for ml */ if (start2 < ip+ml) ml = (int)(start2 - ip); /* Now, encode 2 sequences */ optr = op; if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; ip = start2; optr = op; if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml2, ref2, limit, oend)) goto _dest_overflow; continue; } if (start3 < ip+ml+3) { /* Not enough space for match 2 : remove it */ if (start3 >= (ip+ml)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */ if (start2 < ip+ml) { int correction = (int)(ip+ml - start2); start2 += correction; ref2 += correction; ml2 -= correction; if (ml2 < MINMATCH) { start2 = start3; ref2 = ref3; ml2 = ml3; } } optr = op; if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; ip = start3; ref = ref3; ml = ml3; start0 = start2; ref0 = ref2; ml0 = ml2; goto _Search2; } start2 = start3; ref2 = ref3; ml2 = ml3; goto _Search3; } /* * OK, now we have 3 ascending matches; * let's write the first one ML1. * ip & ref are known; Now decide ml. */ if (start2 < ip+ml) { if ((start2 - ip) < OPTIMAL_ML) { int correction; if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH; correction = ml - (int)(start2 - ip); if (correction > 0) { start2 += correction; ref2 += correction; ml2 -= correction; } } else { ml = (int)(start2 - ip); } } optr = op; if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; /* ML2 becomes ML1 */ ip = start2; ref = ref2; ml = ml2; /* ML3 becomes ML2 */ start2 = start3; ref2 = ref3; ml2 = ml3; /* let's find a new ML3 */ goto _Search3; } _last_literals: /* Encode Last Literals */ { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255; size_t const totalSize = 1 + litLength + lastRunSize; if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ if (limit && (op + totalSize > oend)) { if (limit == limitedOutput) return 0; /* Check output limit */ /* adapt lastRunSize to fill 'dest' */ lastRunSize = (size_t)(oend - op) - 1; litLength = (lastRunSize + 255 - RUN_MASK) / 255; lastRunSize -= litLength; } ip = anchor + lastRunSize; if (lastRunSize >= RUN_MASK) { size_t accumulator = lastRunSize - RUN_MASK; *op++ = (RUN_MASK << ML_BITS); for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRunSize << ML_BITS); } memcpy(op, anchor, lastRunSize); op += lastRunSize; } /* End */ *srcSizePtr = (int) (((const char*)ip) - source); return (int) (((char*)op)-dest); _dest_overflow: if (limit == fillOutput) { op = optr; /* restore correct out pointer */ goto _last_literals; } return 0; } static int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx, const char* const source, char* dst, int* srcSizePtr, int dstCapacity, int const nbSearches, size_t sufficient_len, const limitedOutput_directive limit, int const fullUpdate, const dictCtx_directive dict, HCfavor_e favorDecSpeed); LZ4_FORCE_INLINE int LZ4HC_compress_generic_internal ( LZ4HC_CCtx_internal* const ctx, const char* const src, char* const dst, int* const srcSizePtr, int const dstCapacity, int cLevel, const limitedOutput_directive limit, const dictCtx_directive dict ) { typedef enum { lz4hc, lz4opt } lz4hc_strat_e; typedef struct { lz4hc_strat_e strat; U32 nbSearches; U32 targetLength; } cParams_t; static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = { { lz4hc, 2, 16 }, /* 0, unused */ { lz4hc, 2, 16 }, /* 1, unused */ { lz4hc, 2, 16 }, /* 2, unused */ { lz4hc, 4, 16 }, /* 3 */ { lz4hc, 8, 16 }, /* 4 */ { lz4hc, 16, 16 }, /* 5 */ { lz4hc, 32, 16 }, /* 6 */ { lz4hc, 64, 16 }, /* 7 */ { lz4hc, 128, 16 }, /* 8 */ { lz4hc, 256, 16 }, /* 9 */ { lz4opt, 96, 64 }, /*10==LZ4HC_CLEVEL_OPT_MIN*/ { lz4opt, 512,128 }, /*11 */ { lz4opt,16384,LZ4_OPT_NUM }, /* 12==LZ4HC_CLEVEL_MAX */ }; DEBUGLOG(4, "LZ4HC_compress_generic(ctx=%p, src=%p, srcSize=%d)", ctx, src, *srcSizePtr); if (limit == fillOutput && dstCapacity < 1) return 0; /* Impossible to store anything */ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size (too large or negative) */ ctx->end += *srcSizePtr; if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT; /* note : convention is different from lz4frame, maybe something to review */ cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel); { cParams_t const cParam = clTable[cLevel]; HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio; int result; if (cParam.strat == lz4hc) { result = LZ4HC_compress_hashChain(ctx, src, dst, srcSizePtr, dstCapacity, cParam.nbSearches, limit, dict); } else { assert(cParam.strat == lz4opt); result = LZ4HC_compress_optimal(ctx, src, dst, srcSizePtr, dstCapacity, (int)cParam.nbSearches, cParam.targetLength, limit, cLevel == LZ4HC_CLEVEL_MAX, /* ultra mode */ dict, favor); } if (result <= 0) ctx->dirty = 1; return result; } } static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock); static int LZ4HC_compress_generic_noDictCtx ( LZ4HC_CCtx_internal* const ctx, const char* const src, char* const dst, int* const srcSizePtr, int const dstCapacity, int cLevel, limitedOutput_directive limit ) { assert(ctx->dictCtx == NULL); return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx); } static int LZ4HC_compress_generic_dictCtx ( LZ4HC_CCtx_internal* const ctx, const char* const src, char* const dst, int* const srcSizePtr, int const dstCapacity, int cLevel, limitedOutput_directive limit ) { const size_t position = (size_t)(ctx->end - ctx->base) - ctx->lowLimit; assert(ctx->dictCtx != NULL); if (position >= 64 KB) { ctx->dictCtx = NULL; return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); } else if (position == 0 && *srcSizePtr > 4 KB) { memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal)); LZ4HC_setExternalDict(ctx, (const BYTE *)src); ctx->compressionLevel = (short)cLevel; return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); } else { return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc); } } static int LZ4HC_compress_generic ( LZ4HC_CCtx_internal* const ctx, const char* const src, char* const dst, int* const srcSizePtr, int const dstCapacity, int cLevel, limitedOutput_directive limit ) { if (ctx->dictCtx == NULL) { return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); } else { return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); } } int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : * it reports an aligment of 8-bytes, * while actually aligning LZ4_streamHC_t on 4 bytes. */ static size_t LZ4_streamHC_t_alignment(void) { struct { char c; LZ4_streamHC_t t; } t_a; return sizeof(t_a) - sizeof(t_a.t); } #endif /* state is presumed correctly initialized, * in which case its size and alignment have already been validate */ static int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) { LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : * it reports an aligment of 8-bytes, * while actually aligning LZ4_streamHC_t on 4 bytes. */ assert(((size_t)state & (LZ4_streamHC_t_alignment() - 1)) == 0); /* check alignment */ #endif if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */ LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); LZ4HC_init_internal (ctx, (const BYTE*)src); if (dstCapacity < LZ4_compressBound(srcSize)) return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput); else return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited); } int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) { LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); if (ctx==NULL) return 0; /* init failure */ return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel); } int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) { #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t)); #else LZ4_streamHC_t state; LZ4_streamHC_t* const statePtr = &state; #endif int const cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 FREEMEM(statePtr); #endif return cSize; } /* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */ int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel) { LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); if (ctx==NULL) return 0; /* init failure */ LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source); LZ4_setCompressionLevel(ctx, cLevel); return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput); } /************************************** * Streaming Functions **************************************/ LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) { LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; if (buffer == NULL) return NULL; if (size < sizeof(LZ4_streamHC_t)) return NULL; #ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : * it reports an aligment of 8-bytes, * while actually aligning LZ4_streamHC_t on 4 bytes. */ if (((size_t)buffer) & (LZ4_streamHC_t_alignment() - 1)) return NULL; /* alignment check */ #endif /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */ LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= LZ4_STREAMHCSIZE); DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", LZ4_streamHCPtr, (unsigned)size); /* end-base will trigger a clearTable on starting compression */ LZ4_streamHCPtr->internal_donotuse.end = (const BYTE *)(ptrdiff_t)-1; LZ4_streamHCPtr->internal_donotuse.base = NULL; LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL; LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = 0; LZ4_streamHCPtr->internal_donotuse.dirty = 0; LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT); return LZ4_streamHCPtr; } /* just a stub */ void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) { DEBUGLOG(4, "LZ4_resetStreamHC_fast(%p, %d)", LZ4_streamHCPtr, compressionLevel); if (LZ4_streamHCPtr->internal_donotuse.dirty) { LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); } else { /* preserve end - base : can trigger clearTable's threshold */ LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.base; LZ4_streamHCPtr->internal_donotuse.base = NULL; LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL; } LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); } void LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) { DEBUGLOG(5, "LZ4_setCompressionLevel(%p, %d)", LZ4_streamHCPtr, compressionLevel); if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT; if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX; LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel; } /* compression */ static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) { DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock); if (ctxPtr->end >= ctxPtr->base + ctxPtr->dictLimit + 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ ctxPtr->lowLimit = ctxPtr->dictLimit; ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base); ctxPtr->dictBase = ctxPtr->base; ctxPtr->base = newBlock - ctxPtr->dictLimit; ctxPtr->end = newBlock; ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ /* cannot reference an extDict and a dictCtx at the same time */ ctxPtr->dictCtx = NULL; } /* ================================================ * LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX]) * ===============================================*/ typedef struct { int price; int off; int mlen; int litlen; } LZ4HC_optimal_t; /* price in bytes */ LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen) { int price = litlen; assert(litlen >= 0); if (litlen >= (int)RUN_MASK) price += 1 + ((litlen-(int)RUN_MASK) / 255); return price; } /* requires mlen >= MINMATCH */ LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen) { int price = 1 + 2 ; /* token + 16-bit offset */ assert(litlen >= 0); assert(mlen >= MINMATCH); price += LZ4HC_literalsPrice(litlen); if (mlen >= (int)(ML_MASK+MINMATCH)) price += 1 + ((mlen-(int)(ML_MASK+MINMATCH)) / 255); return price; } typedef struct { int off; int len; } LZ4HC_match_t; LZ4_FORCE_INLINE LZ4HC_match_t LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx, const BYTE* ip, const BYTE* const iHighLimit, int minLen, int nbSearches, const dictCtx_directive dict, const HCfavor_e favorDecSpeed) { LZ4HC_match_t match = { 0 , 0 }; const BYTE* matchPtr = NULL; /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), * but this won't be the case here, as we define iLowLimit==ip, * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ int matchLength = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, &matchPtr, &ip, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); if (matchLength <= minLen) return match; if (favorDecSpeed) { if ((matchLength>18) & (matchLength<=36)) matchLength=18; /* favor shortcut */ } match.len = matchLength; match.off = (int)(ip-matchPtr); return match; } static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, const char* const source, char* dst, int* srcSizePtr, int dstCapacity, int const nbSearches, size_t sufficient_len, const limitedOutput_directive limit, int const fullUpdate, const dictCtx_directive dict, const HCfavor_e favorDecSpeed) { #define TRAILING_LITERALS 3 LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS]; /* ~64 KB, which is a bit large for stack... */ const BYTE* ip = (const BYTE*) source; const BYTE* anchor = ip; const BYTE* const iend = ip + *srcSizePtr; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = iend - LASTLITERALS; BYTE* op = (BYTE*) dst; BYTE* opSaved = (BYTE*) dst; BYTE* oend = op + dstCapacity; /* init */ DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity); *srcSizePtr = 0; if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1; /* Main Loop */ assert(ip - anchor < LZ4_MAX_INPUT_SIZE); while (ip <= mflimit) { int const llen = (int)(ip - anchor); int best_mlen, best_off; int cur, last_match_pos = 0; LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); if (firstMatch.len==0) { ip++; continue; } if ((size_t)firstMatch.len > sufficient_len) { /* good enough solution : immediate encoding */ int const firstML = firstMatch.len; const BYTE* const matchPos = ip - firstMatch.off; opSaved = op; if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, matchPos, limit, oend) ) /* updates ip, op and anchor */ goto _dest_overflow; continue; } /* set prices for first positions (literals) */ { int rPos; for (rPos = 0 ; rPos < MINMATCH ; rPos++) { int const cost = LZ4HC_literalsPrice(llen + rPos); opt[rPos].mlen = 1; opt[rPos].off = 0; opt[rPos].litlen = llen + rPos; opt[rPos].price = cost; DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", rPos, cost, opt[rPos].litlen); } } /* set prices using initial match */ { int mlen = MINMATCH; int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ int const offset = firstMatch.off; assert(matchML < LZ4_OPT_NUM); for ( ; mlen <= matchML ; mlen++) { int const cost = LZ4HC_sequencePrice(llen, mlen); opt[mlen].mlen = mlen; opt[mlen].off = offset; opt[mlen].litlen = llen; opt[mlen].price = cost; DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i) -- initial setup", mlen, cost, mlen); } } last_match_pos = firstMatch.len; { int addLit; for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { opt[last_match_pos+addLit].mlen = 1; /* literal */ opt[last_match_pos+addLit].off = 0; opt[last_match_pos+addLit].litlen = addLit; opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); } } /* check further positions */ for (cur = 1; cur < last_match_pos; cur++) { const BYTE* const curPtr = ip + cur; LZ4HC_match_t newMatch; if (curPtr > mflimit) break; DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u", cur, opt[cur].price, opt[cur+1].price, cur+1); if (fullUpdate) { /* not useful to search here if next position has same (or lower) cost */ if ( (opt[cur+1].price <= opt[cur].price) /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */ && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) ) continue; } else { /* not useful to search here if next position has same (or lower) cost */ if (opt[cur+1].price <= opt[cur].price) continue; } DEBUGLOG(7, "search at rPos:%u", cur); if (fullUpdate) newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); else /* only test matches of minimum length; slightly faster, but misses a few bytes */ newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches, dict, favorDecSpeed); if (!newMatch.len) continue; if ( ((size_t)newMatch.len > sufficient_len) || (newMatch.len + cur >= LZ4_OPT_NUM) ) { /* immediate encoding */ best_mlen = newMatch.len; best_off = newMatch.off; last_match_pos = cur + 1; goto encode; } /* before match : set price with literals at beginning */ { int const baseLitlen = opt[cur].litlen; int litlen; for (litlen = 1; litlen < MINMATCH; litlen++) { int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen); int const pos = cur + litlen; if (price < opt[pos].price) { opt[pos].mlen = 1; /* literal */ opt[pos].off = 0; opt[pos].litlen = baseLitlen+litlen; opt[pos].price = price; DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", pos, price, opt[pos].litlen); } } } /* set prices using match at position = cur */ { int const matchML = newMatch.len; int ml = MINMATCH; assert(cur + newMatch.len < LZ4_OPT_NUM); for ( ; ml <= matchML ; ml++) { int const pos = cur + ml; int const offset = newMatch.off; int price; int ll; DEBUGLOG(7, "testing price rPos %i (last_match_pos=%i)", pos, last_match_pos); if (opt[cur].mlen == 1) { ll = opt[cur].litlen; price = ((cur > ll) ? opt[cur - ll].price : 0) + LZ4HC_sequencePrice(ll, ml); } else { ll = 0; price = opt[cur].price + LZ4HC_sequencePrice(0, ml); } assert((U32)favorDecSpeed <= 1); if (pos > last_match_pos+TRAILING_LITERALS || price <= opt[pos].price - (int)favorDecSpeed) { DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)", pos, price, ml); assert(pos < LZ4_OPT_NUM); if ( (ml == matchML) /* last pos of last match */ && (last_match_pos < pos) ) last_match_pos = pos; opt[pos].mlen = ml; opt[pos].off = offset; opt[pos].litlen = ll; opt[pos].price = price; } } } /* complete following positions with literals */ { int addLit; for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { opt[last_match_pos+addLit].mlen = 1; /* literal */ opt[last_match_pos+addLit].off = 0; opt[last_match_pos+addLit].litlen = addLit; opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); } } } /* for (cur = 1; cur <= last_match_pos; cur++) */ assert(last_match_pos < LZ4_OPT_NUM + TRAILING_LITERALS); best_mlen = opt[last_match_pos].mlen; best_off = opt[last_match_pos].off; cur = last_match_pos - best_mlen; encode: /* cur, last_match_pos, best_mlen, best_off must be set */ assert(cur < LZ4_OPT_NUM); assert(last_match_pos >= 1); /* == 1 when only one candidate */ DEBUGLOG(6, "reverse traversal, looking for shortest path (last_match_pos=%i)", last_match_pos); { int candidate_pos = cur; int selected_matchLength = best_mlen; int selected_offset = best_off; while (1) { /* from end to beginning */ int const next_matchLength = opt[candidate_pos].mlen; /* can be 1, means literal */ int const next_offset = opt[candidate_pos].off; DEBUGLOG(7, "pos %i: sequence length %i", candidate_pos, selected_matchLength); opt[candidate_pos].mlen = selected_matchLength; opt[candidate_pos].off = selected_offset; selected_matchLength = next_matchLength; selected_offset = next_offset; if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */ assert(next_matchLength > 0); /* can be 1, means literal */ candidate_pos -= next_matchLength; } } /* encode all recorded sequences in order */ { int rPos = 0; /* relative position (to ip) */ while (rPos < last_match_pos) { int const ml = opt[rPos].mlen; int const offset = opt[rPos].off; if (ml == 1) { ip++; rPos++; continue; } /* literal; note: can end up with several literals, in which case, skip them */ rPos += ml; assert(ml >= MINMATCH); assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX)); opSaved = op; if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ip - offset, limit, oend) ) /* updates ip, op and anchor */ goto _dest_overflow; } } } /* while (ip <= mflimit) */ _last_literals: /* Encode Last Literals */ { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255; size_t const totalSize = 1 + litLength + lastRunSize; if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ if (limit && (op + totalSize > oend)) { if (limit == limitedOutput) return 0; /* Check output limit */ /* adapt lastRunSize to fill 'dst' */ lastRunSize = (size_t)(oend - op) - 1; litLength = (lastRunSize + 255 - RUN_MASK) / 255; lastRunSize -= litLength; } ip = anchor + lastRunSize; if (lastRunSize >= RUN_MASK) { size_t accumulator = lastRunSize - RUN_MASK; *op++ = (RUN_MASK << ML_BITS); for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRunSize << ML_BITS); } memcpy(op, anchor, lastRunSize); op += lastRunSize; } /* End */ *srcSizePtr = (int) (((const char*)ip) - source); return (int) ((char*)op-dst); _dest_overflow: if (limit == fillOutput) { op = opSaved; /* restore correct out pointer */ goto _last_literals; } return 0; } squashfs-tools-ng-1.1.3/lib/lz4/lz4hc.h000066400000000000000000000317761410627516300176100ustar00rootroot00000000000000/* LZ4 HC - High Compression Mode of LZ4 Header File Copyright (C) 2011-2017, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : https://github.com/lz4/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ #ifndef LZ4_HC_H_19834876238432 #define LZ4_HC_H_19834876238432 #if defined (__cplusplus) extern "C" { #endif /* --- Dependency --- */ /* note : lz4hc requires lz4.h/lz4.c for compilation */ #include "lz4.h" /* stddef, LZ4LIB_API, LZ4_DEPRECATED */ /* --- Useful constants --- */ #define LZ4HC_CLEVEL_MIN 3 #define LZ4HC_CLEVEL_DEFAULT 9 #define LZ4HC_CLEVEL_OPT_MIN 10 #define LZ4HC_CLEVEL_MAX 12 /*-************************************ * Block Compression **************************************/ /*! LZ4_compress_HC() : * Compress data from `src` into `dst`, using the powerful but slower "HC" algorithm. * `dst` must be already allocated. * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h") * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h") * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work. * Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX. * @return : the number of bytes written into 'dst' * or 0 if compression fails. */ LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel); /* Note : * Decompression functions are provided within "lz4.h" (BSD license) */ /*! LZ4_compress_HC_extStateHC() : * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`. * `state` size is provided by LZ4_sizeofStateHC(). * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly). */ LZ4LIB_API int LZ4_sizeofStateHC(void); LZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel); /*! LZ4_compress_HC_destSize() : v1.9.0+ * Will compress as much data as possible from `src` * to fit into `targetDstSize` budget. * Result is provided in 2 parts : * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) * or 0 if compression fails. * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src` */ LZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int compressionLevel); /*-************************************ * Streaming Compression * Bufferless synchronous API **************************************/ typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */ /* These functions compress data in successive blocks of any size, using previous blocks as dictionary, to improve compression ratio. One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks. There is an exception for ring buffers, which can be smaller than 64 KB. Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue(). Before starting compression, state must be allocated and properly initialized. LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT. Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream) or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental). LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once, which is automatically the case when state is created using LZ4_createStreamHC(). After reset, a first "fictional block" can be designated as initial dictionary, using LZ4_loadDictHC() (Optional). Invoke LZ4_compress_HC_continue() to compress each successive block. The number of blocks is unlimited. Previous input blocks, including initial dictionary when present, must remain accessible and unmodified during compression. It's allowed to update compression level anytime between blocks, using LZ4_setCompressionLevel() (experimental). 'dst' buffer should be sized to handle worst case scenarios (see LZ4_compressBound(), it ensures compression success). In case of failure, the API does not guarantee recovery, so the state _must_ be reset. To ensure compression success whenever `dst` buffer size cannot be made >= LZ4_compressBound(), consider using LZ4_compress_HC_continue_destSize(). Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks, it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC(). Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB) After completing a streaming compression, it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state, just by resetting it, using LZ4_resetStreamHC_fast(). */ LZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel); /* v1.9.0+ */ /*^********************************************** * !!!!!! STATIC LINKING ONLY !!!!!! ***********************************************/ /*-****************************************************************** * PRIVATE DEFINITIONS : * Do not use these definitions directly. * They are merely exposed to allow static allocation of `LZ4_streamHC_t`. * Declare an `LZ4_streamHC_t` directly, rather than any type below. * Even then, only do so in the context of static linking, as definitions may change between versions. ********************************************************************/ #define LZ4HC_DICTIONARY_LOGSIZE 16 #define LZ4HC_MAXD (1<= 199901L) /* C99 */) #include typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; struct LZ4HC_CCtx_internal { uint32_t hashTable[LZ4HC_HASHTABLESIZE]; uint16_t chainTable[LZ4HC_MAXD]; const uint8_t* end; /* next block here to continue on current prefix */ const uint8_t* base; /* All index relative to this position */ const uint8_t* dictBase; /* alternate base for extDict */ uint32_t dictLimit; /* below that point, need extDict */ uint32_t lowLimit; /* below that point, no more dict */ uint32_t nextToUpdate; /* index from which to continue dictionary update */ short compressionLevel; int8_t favorDecSpeed; /* favor decompression speed if this flag set, otherwise, favor compression ratio */ int8_t dirty; /* stream has to be fully reset if this flag is set */ const LZ4HC_CCtx_internal* dictCtx; }; #else typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; struct LZ4HC_CCtx_internal { unsigned int hashTable[LZ4HC_HASHTABLESIZE]; unsigned short chainTable[LZ4HC_MAXD]; const unsigned char* end; /* next block here to continue on current prefix */ const unsigned char* base; /* All index relative to this position */ const unsigned char* dictBase; /* alternate base for extDict */ unsigned int dictLimit; /* below that point, need extDict */ unsigned int lowLimit; /* below that point, no more dict */ unsigned int nextToUpdate; /* index from which to continue dictionary update */ short compressionLevel; char favorDecSpeed; /* favor decompression speed if this flag set, otherwise, favor compression ratio */ char dirty; /* stream has to be fully reset if this flag is set */ const LZ4HC_CCtx_internal* dictCtx; }; #endif /* Do not use these definitions directly ! * Declare or allocate an LZ4_streamHC_t instead. */ #define LZ4_STREAMHCSIZE (4*LZ4HC_HASHTABLESIZE + 2*LZ4HC_MAXD + 56 + ((sizeof(void*)==16) ? 56 : 0) /* AS400*/ ) /* 262200 or 262256*/ #define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t)) union LZ4_streamHC_u { size_t table[LZ4_STREAMHCSIZE_SIZET]; LZ4HC_CCtx_internal internal_donotuse; }; /* previously typedef'd to LZ4_streamHC_t */ /* LZ4_streamHC_t : * This structure allows static allocation of LZ4 HC streaming state. * This can be used to allocate statically, on state, or as part of a larger structure. * * Such state **must** be initialized using LZ4_initStreamHC() before first use. * * Note that invoking LZ4_initStreamHC() is not required when * the state was created using LZ4_createStreamHC() (which is recommended). * Using the normal builder, a newly created state is automatically initialized. * * Static allocation shall only be used in combination with static linking. */ /* LZ4_initStreamHC() : v1.9.0+ * Required before first use of a statically allocated LZ4_streamHC_t. * Before v1.9.0 : use LZ4_resetStreamHC() instead */ LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size); #if defined (__cplusplus) } #endif #endif /* LZ4_HC_H_19834876238432 */ /*-************************************************** * !!!!! STATIC LINKING ONLY !!!!! * Following definitions are considered experimental. * They should not be linked from DLL, * as there is no guarantee of API stability yet. * Prototypes will be promoted to "stable" status * after successfull usage in real-life scenarios. ***************************************************/ #ifdef LZ4_HC_STATIC_LINKING_ONLY /* protection macro */ #ifndef LZ4_HC_SLO_098092834 #define LZ4_HC_SLO_098092834 #define LZ4_STATIC_LINKING_ONLY /* LZ4LIB_STATIC_API */ #include "lz4.h" #if defined (__cplusplus) extern "C" { #endif /*! LZ4_setCompressionLevel() : v1.8.0+ (experimental) * It's possible to change compression level * between successive invocations of LZ4_compress_HC_continue*() * for dynamic adaptation. */ LZ4LIB_STATIC_API void LZ4_setCompressionLevel( LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); /*! LZ4_resetStreamHC_fast() : v1.9.0+ * When an LZ4_streamHC_t is known to be in a internally coherent state, * it can often be prepared for a new compression with almost no work, only * sometimes falling back to the full, expensive reset that is always required * when the stream is in an indeterminate state (i.e., the reset performed by * LZ4_resetStreamHC()). * * LZ4_streamHCs are guaranteed to be in a valid state when: * - returned from LZ4_createStreamHC() * - reset by LZ4_resetStreamHC() * - memset(stream, 0, sizeof(LZ4_streamHC_t)) * - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast() * - the stream was in a valid state and was then used in any compression call * that returned success * - the stream was in an indeterminate state and was used in a compression * call that fully reset the state (LZ4_compress_HC_extStateHC()) and that * returned success * * Note: * A stream that was last used in a compression call that returned an error * may be passed to this function. However, it will be fully reset, which will * clear any existing history and settings from the context. */ LZ4LIB_STATIC_API void LZ4_resetStreamHC_fast( LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); #if defined (__cplusplus) } #endif #endif /* LZ4_HC_SLO_098092834 */ #endif /* LZ4_HC_STATIC_LINKING_ONLY */ squashfs-tools-ng-1.1.3/lib/sqfs/000077500000000000000000000000001410627516300166405ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/sqfs/Makemodule.am000066400000000000000000000102671410627516300212500ustar00rootroot00000000000000LIBSQFS_HEARDS = include/sqfs/meta_writer.h \ include/sqfs/meta_reader.h include/sqfs/id_table.h \ include/sqfs/compressor.h include/sqfs/block_processor.h \ include/sqfs/super.h include/sqfs/inode.h \ include/sqfs/dir.h include/sqfs/xattr.h \ include/sqfs/table.h include/sqfs/predef.h \ include/sqfs/error.h include/sqfs/dir_reader.h \ include/sqfs/dir_writer.h include/sqfs/io.h \ include/sqfs/data_reader.h include/sqfs/block.h \ include/sqfs/xattr_reader.h include/sqfs/xattr_writer.h \ include/sqfs/frag_table.h include/sqfs/block_writer.h libsquashfs_la_SOURCES = $(LIBSQFS_HEARDS) lib/sqfs/id_table.c lib/sqfs/super.c libsquashfs_la_SOURCES += lib/sqfs/readdir.c lib/sqfs/xattr/xattr.c libsquashfs_la_SOURCES += lib/sqfs/write_table.c lib/sqfs/meta_writer.c libsquashfs_la_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c libsquashfs_la_SOURCES += lib/sqfs/read_inode.c lib/sqfs/write_inode.c libsquashfs_la_SOURCES += lib/sqfs/dir_writer.c lib/sqfs/xattr/xattr_reader.c libsquashfs_la_SOURCES += lib/sqfs/read_table.c lib/sqfs/comp/compressor.c libsquashfs_la_SOURCES += lib/sqfs/comp/internal.h libsquashfs_la_SOURCES += lib/sqfs/dir_reader.c lib/sqfs/read_tree.c libsquashfs_la_SOURCES += lib/sqfs/inode.c lib/sqfs/xattr/xattr_writer.c libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_flush.c libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_record.c libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer.h libsquashfs_la_SOURCES += lib/sqfs/write_super.c lib/sqfs/data_reader.c libsquashfs_la_SOURCES += lib/sqfs/block_processor/internal.h libsquashfs_la_SOURCES += lib/sqfs/block_processor/frontend.c libsquashfs_la_SOURCES += lib/sqfs/block_processor/block_processor.c libsquashfs_la_SOURCES += lib/sqfs/block_processor/backend.c libsquashfs_la_SOURCES += lib/sqfs/frag_table.c include/sqfs/frag_table.h libsquashfs_la_SOURCES += lib/sqfs/block_writer.c include/sqfs/block_writer.h libsquashfs_la_SOURCES += lib/sqfs/misc.c libsquashfs_la_CPPFLAGS = $(AM_CPPFLAGS) libsquashfs_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBSQUASHFS_SO_VERSION) libsquashfs_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) libsquashfs_la_CFLAGS += $(XZ_CFLAGS) $(LZ4_CFLAGS) libsquashfs_la_CFLAGS += $(ZSTD_CFLAGS) $(PTHREAD_CFLAGS) libsquashfs_la_LIBADD = $(XZ_LIBS) $(ZLIB_LIBS) $(LZ4_LIBS) libsquashfs_la_LIBADD += $(ZSTD_LIBS) $(PTHREAD_LIBS) # directly "import" stuff from libutil libsquashfs_la_SOURCES += lib/util/str_table.c lib/util/alloc.c libsquashfs_la_SOURCES += lib/util/xxhash.c libsquashfs_la_SOURCES += lib/util/hash_table.c include/hash_table.h libsquashfs_la_SOURCES += lib/util/rbtree.c include/rbtree.h libsquashfs_la_SOURCES += lib/util/array.c include/array.h libsquashfs_la_SOURCES += lib/util/is_memory_zero.c libsquashfs_la_SOURCES += include/threadpool.h if CUSTOM_ALLOC libsquashfs_la_SOURCES += lib/util/mempool.c include/mempool.h endif if WINDOWS libsquashfs_la_SOURCES += lib/sqfs/win32/io_file.c libsquashfs_la_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 libsquashfs_la_CFLAGS += -Wc,-static-libgcc libsquashfs_la_LDFLAGS += -no-undefined -avoid-version else libsquashfs_la_SOURCES += lib/sqfs/unix/io_file.c endif if HAVE_PTHREAD libsquashfs_la_SOURCES += lib/util/threadpool.c else if WINDOWS libsquashfs_la_SOURCES += lib/util/threadpool.c else libsquashfs_la_SOURCES += lib/util/threadpool_serial.c libsquashfs_la_CPPFLAGS += -DNO_THREAD_IMPL endif endif if WITH_GZIP libsquashfs_la_SOURCES += lib/sqfs/comp/gzip.c libsquashfs_la_CPPFLAGS += -DWITH_GZIP if WITH_OWN_ZLIB libsquashfs_la_CPPFLAGS += -I$(top_srcdir)/lib/zlib libsquashfs_la_LIBADD += libz.la endif endif if WITH_XZ libsquashfs_la_SOURCES += lib/sqfs/comp/xz.c libsquashfs_la_SOURCES += lib/sqfs/comp/lzma.c libsquashfs_la_CPPFLAGS += -DWITH_XZ endif if WITH_LZ4 libsquashfs_la_SOURCES += lib/sqfs/comp/lz4.c libsquashfs_la_CPPFLAGS += -DWITH_LZ4 if WITH_OWN_LZ4 libsquashfs_la_CPPFLAGS += -I$(top_srcdir)/lib/lz4 libsquashfs_la_LIBADD += liblz4.la endif endif if WITH_ZSTD libsquashfs_la_SOURCES += lib/sqfs/comp/zstd.c libsquashfs_la_CPPFLAGS += -DWITH_ZSTD endif sqfsincludedir = $(includedir)/sqfs sqfsinclude_HEADERS = $(LIBSQFS_HEARDS) lib_LTLIBRARIES += libsquashfs.la pkgconfig_DATA += lib/sqfs/libsquashfs1.pc squashfs-tools-ng-1.1.3/lib/sqfs/block_processor/000077500000000000000000000000001410627516300220315ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/sqfs/block_processor/backend.c000066400000000000000000000165201410627516300235700ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * backend.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "internal.h" static int set_block_size(sqfs_inode_generic_t **inode, sqfs_u32 index, sqfs_u32 size) { size_t min_size = (index + 1) * sizeof(sqfs_u32); size_t avail = (*inode)->payload_bytes_available; sqfs_inode_generic_t *new; size_t newsz; if (avail < min_size) { newsz = avail ? avail : (sizeof(sqfs_u32) * 4); while (newsz < min_size) newsz *= 2; if (SZ_ADD_OV(newsz, sizeof(**inode), &newsz)) return SQFS_ERROR_OVERFLOW; if (sizeof(size_t) > sizeof(sqfs_u32)) { if ((newsz - sizeof(**inode)) > 0x0FFFFFFFFUL) return SQFS_ERROR_OVERFLOW; } new = realloc((*inode), newsz); if (new == NULL) return SQFS_ERROR_ALLOC; (*inode) = new; (*inode)->payload_bytes_available = newsz - sizeof(**inode); } (*inode)->extra[index] = size; if (min_size >= (*inode)->payload_bytes_used) (*inode)->payload_bytes_used = min_size; return 0; } static void release_old_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) { blk->next = proc->free_list; proc->free_list = blk; proc->backlog -= 1; } static int process_completed_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) { sqfs_u64 location; sqfs_u32 size; int err; if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { sqfs_block_t *it = proc->fblk_in_flight, *prev = NULL; while (it != NULL && it->index != blk->index) { prev = it; it = it->next; } if (it != NULL) { if (prev == NULL) { proc->fblk_in_flight = it->next; } else { prev->next = it->next; } free(it); } } err = proc->wr->write_data_block(proc->wr, blk->user, blk->size, blk->checksum, blk->flags & ~BLK_FLAG_INTERNAL, blk->data, &location); if (err) goto out; proc->stats.output_bytes_generated += blk->size; if (blk->flags & SQFS_BLK_IS_SPARSE) { if (blk->inode != NULL) { sqfs_inode_make_extended(*(blk->inode)); (*(blk->inode))->data.file_ext.sparse += blk->size; err = set_block_size(blk->inode, blk->index, 0); if (err) goto out; } proc->stats.sparse_block_count += 1; } else if (blk->size != 0) { size = blk->size; if (!(blk->flags & SQFS_BLK_IS_COMPRESSED)) size |= 1 << 24; if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { if (proc->frag_tbl != NULL) { err = sqfs_frag_table_set(proc->frag_tbl, blk->index, location, size); if (err) goto out; } proc->stats.frag_block_count += 1; } else { if (blk->inode != NULL) { err = set_block_size(blk->inode, blk->index, size); if (err) goto out; } proc->stats.data_block_count += 1; } } if (blk->flags & SQFS_BLK_LAST_BLOCK && blk->inode != NULL) sqfs_inode_set_file_block_start(*(blk->inode), location); out: release_old_block(proc, blk); return err; } static int process_completed_fragment(sqfs_block_processor_t *proc, sqfs_block_t *frag) { chunk_info_t *chunk = NULL, search; struct hash_entry *entry; sqfs_u32 index, offset; int err; if (frag->flags & SQFS_BLK_IS_SPARSE) { if (frag->inode != NULL) { sqfs_inode_make_extended(*(frag->inode)); set_block_size(frag->inode, frag->index, 0); (*(frag->inode))->data.file_ext.sparse += frag->size; } proc->stats.sparse_block_count += 1; release_old_block(proc, frag); return 0; } proc->stats.total_frag_count += 1; if (!(frag->flags & SQFS_BLK_DONT_DEDUPLICATE)) { search.hash = frag->checksum; search.size = frag->size; proc->current_frag = frag; proc->fblk_lookup_error = 0; entry = hash_table_search_pre_hashed(proc->frag_ht, search.hash, &search); proc->current_frag = NULL; if (proc->fblk_lookup_error != 0) { err = proc->fblk_lookup_error; goto fail; } if (entry != NULL) { if (frag->inode != NULL) { chunk = entry->data; sqfs_inode_set_frag_location(*(frag->inode), chunk->index, chunk->offset); } release_old_block(proc, frag); return 0; } } if (proc->frag_block != NULL) { size_t size = proc->frag_block->size + frag->size; if (size > proc->max_block_size) { proc->frag_block->io_seq_num = proc->io_seq_num++; err = enqueue_block(proc, proc->frag_block); proc->frag_block = NULL; if (err) goto fail; } } if (proc->frag_block == NULL) { if (proc->frag_tbl == NULL) { index = 0; } else { err = sqfs_frag_table_append(proc->frag_tbl, 0, 0, &index); if (err) goto fail; } offset = 0; proc->frag_block = frag; proc->frag_block->index = index; proc->frag_block->flags &= (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN); proc->frag_block->flags |= SQFS_BLK_FRAGMENT_BLOCK; } else { index = proc->frag_block->index; offset = proc->frag_block->size; memcpy(proc->frag_block->data + proc->frag_block->size, frag->data, frag->size); proc->frag_block->size += frag->size; proc->frag_block->flags |= (frag->flags & (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN)); } if (proc->frag_tbl != NULL) { err = SQFS_ERROR_ALLOC; chunk = calloc(1, sizeof(*chunk)); if (chunk == NULL) goto fail; chunk->index = index; chunk->offset = offset; chunk->size = frag->size; chunk->hash = frag->checksum; proc->current_frag = frag; proc->fblk_lookup_error = 0; entry = hash_table_insert_pre_hashed(proc->frag_ht, chunk->hash, chunk, chunk); proc->current_frag = NULL; if (proc->fblk_lookup_error != 0) { err = proc->fblk_lookup_error; goto fail; } if (entry == NULL) goto fail; } if (frag->inode != NULL) sqfs_inode_set_frag_location(*(frag->inode), index, offset); if (frag != proc->frag_block) release_old_block(proc, frag); proc->stats.actual_frag_count += 1; return 0; fail: free(chunk); if (frag != proc->frag_block) release_old_block(proc, frag); return err; } static void store_io_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) { sqfs_block_t *prev = NULL, *it = proc->io_queue; while (it != NULL && (it->io_seq_num < blk->io_seq_num)) { prev = it; it = it->next; } if (prev == NULL) { proc->io_queue = blk; } else { prev->next = blk; } blk->next = it; } int dequeue_block(sqfs_block_processor_t *proc) { size_t backlog_old = proc->backlog; sqfs_block_t *blk; int status; do { while (proc->io_queue != NULL) { if (proc->io_queue->io_seq_num != proc->io_deq_seq_num) break; blk = proc->io_queue; proc->io_queue = blk->next; proc->io_deq_seq_num += 1; status = process_completed_block(proc, blk); if (status != 0) return status; } if (proc->backlog < backlog_old) break; if ((proc->backlog == 1) && (proc->frag_block != NULL || proc->blk_current != NULL)) { break; } if ((proc->backlog == 2) && proc->frag_block != NULL && proc->blk_current != NULL) { break; } blk = proc->pool->dequeue(proc->pool); if (blk == NULL) { status = proc->pool->get_status(proc->pool); return status ? status : SQFS_ERROR_INTERNAL; } if (blk->flags & SQFS_BLK_IS_FRAGMENT) { status = process_completed_fragment(proc, blk); if (status != 0) return status; } else { if (!(blk->flags & SQFS_BLK_FRAGMENT_BLOCK) || (blk->flags & BLK_FLAG_MANUAL_SUBMISSION)) { blk->io_seq_num = proc->io_seq_num++; } store_io_block(proc, blk); } } while (proc->backlog >= backlog_old); return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/block_processor/block_processor.c000066400000000000000000000174171410627516300254000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * block_processor.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "internal.h" static int process_block(void *userptr, void *workitem) { worker_data_t *worker = userptr; sqfs_block_t *block = workitem; sqfs_s32 ret; if (block->size == 0) return 0; if (!(block->flags & SQFS_BLK_IGNORE_SPARSE) && is_memory_zero(block->data, block->size)) { block->flags |= SQFS_BLK_IS_SPARSE; return 0; } if (block->flags & SQFS_BLK_DONT_HASH) { block->checksum = 0; } else { block->checksum = xxh32(block->data, block->size); } if (block->flags & (SQFS_BLK_IS_FRAGMENT | SQFS_BLK_DONT_COMPRESS)) return 0; ret = worker->cmp->do_block(worker->cmp, block->data, block->size, worker->scratch, worker->scratch_size); if (ret < 0) return ret; if (ret > 0) { memcpy(block->data, worker->scratch, ret); block->size = ret; block->flags |= SQFS_BLK_IS_COMPRESSED; } return 0; } static int load_frag_block(sqfs_block_processor_t *proc, sqfs_u32 index) { sqfs_fragment_t info; size_t size; int ret; if (proc->cached_frag_blk == NULL) { size = sizeof(*proc->cached_frag_blk); proc->cached_frag_blk = alloc_flex(size, 1, proc->max_block_size); if (proc->cached_frag_blk == NULL) return SQFS_ERROR_ALLOC; } else { if (proc->cached_frag_blk->index == index) return 0; } ret = sqfs_frag_table_lookup(proc->frag_tbl, index, &info); if (ret != 0) return ret; size = SQFS_ON_DISK_BLOCK_SIZE(info.size); if (size >= proc->max_block_size) return SQFS_ERROR_CORRUPTED; if (SQFS_IS_BLOCK_COMPRESSED(info.size)) { ret = proc->file->read_at(proc->file, info.start_offset, proc->scratch, size); if (ret != 0) return ret; ret = proc->uncmp->do_block(proc->uncmp, proc->scratch, size, proc->cached_frag_blk->data, proc->max_block_size); if (ret <= 0) return ret ? ret : SQFS_ERROR_OVERFLOW; size = ret; } else { ret = proc->file->read_at(proc->file, info.start_offset, proc->cached_frag_blk->data, size); if (ret != 0) return ret; } proc->cached_frag_blk->size = size; proc->cached_frag_blk->index = index; return 0; } static bool chunk_info_equals(void *user, const void *k, const void *c) { const chunk_info_t *key = k, *cmp = c; sqfs_block_processor_t *proc = user; sqfs_block_t *it; int ret; if (key->size != cmp->size || key->hash != cmp->hash) return false; if (proc->uncmp == NULL || proc->file == NULL) return true; if (proc->current_frag == NULL || proc->frag_tbl == NULL) return true; if (proc->fblk_lookup_error != 0) return false; for (it = proc->fblk_in_flight; it != NULL; it = it->next) { if (it->index == cmp->index) break; } if (it == NULL && proc->frag_block != NULL) { if (proc->frag_block->index == cmp->index) it = proc->frag_block; } if (it == NULL) { ret = load_frag_block(proc, cmp->index); if (ret != 0) { proc->fblk_lookup_error = ret; return false; } it = proc->cached_frag_blk; } if (cmp->offset >= it->size || (it->size - cmp->offset) < cmp->size) { proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; return false; } if (cmp->size != proc->current_frag->size) { proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; return false; } return memcmp(it->data + cmp->offset, proc->current_frag->data, cmp->size) == 0; } static void ht_delete_function(struct hash_entry *entry) { free(entry->data); } static void free_block_list(sqfs_block_t *list) { while (list != NULL) { sqfs_block_t *it = list; list = it->next; free(it); } } static void block_processor_destroy(sqfs_object_t *base) { sqfs_block_processor_t *proc = (sqfs_block_processor_t *)base; free(proc->frag_block); free(proc->blk_current); free(proc->cached_frag_blk); free_block_list(proc->free_list); free_block_list(proc->io_queue); free_block_list(proc->fblk_in_flight); if (proc->frag_ht != NULL) hash_table_destroy(proc->frag_ht, ht_delete_function); /* XXX: shut down the pool first before cleaning up the worker data */ if (proc->pool != NULL) proc->pool->destroy(proc->pool); while (proc->workers != NULL) { worker_data_t *worker = proc->workers; proc->workers = worker->next; sqfs_destroy(worker->cmp); free(worker); } free(proc); } int sqfs_block_processor_sync(sqfs_block_processor_t *proc) { int ret; for (;;) { if (proc->backlog == 0) break; if ((proc->backlog == 1) && (proc->frag_block != NULL || proc->blk_current != NULL)) { break; } if ((proc->backlog == 2) && proc->frag_block != NULL && proc->blk_current != NULL) { break; } ret = dequeue_block(proc); if (ret != 0) return ret; } return 0; } int sqfs_block_processor_finish(sqfs_block_processor_t *proc) { sqfs_block_t *blk; int status; status = sqfs_block_processor_sync(proc); if (status != 0) return status; if (proc->frag_block != NULL) { blk = proc->frag_block; blk->next = NULL; proc->frag_block = NULL; blk->io_seq_num = proc->io_seq_num++; status = enqueue_block(proc, blk); if (status != 0) return status; status = sqfs_block_processor_sync(proc); } return status; } const sqfs_block_processor_stats_t *sqfs_block_processor_get_stats(const sqfs_block_processor_t *proc) { return &proc->stats; } int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, sqfs_block_processor_t **out) { size_t i, count, scratch_size = 0; sqfs_block_processor_t *proc; int ret; if (desc->size != sizeof(sqfs_block_processor_desc_t)) return SQFS_ERROR_ARG_INVALID; if (desc->file != NULL && desc->uncmp != NULL) scratch_size = desc->max_block_size; proc = alloc_flex(sizeof(*proc), 1, scratch_size); if (proc == NULL) return SQFS_ERROR_ALLOC; proc->max_backlog = desc->max_backlog; proc->max_block_size = desc->max_block_size; proc->frag_tbl = desc->tbl; proc->wr = desc->wr; proc->file = desc->file; proc->uncmp = desc->uncmp; proc->stats.size = sizeof(proc->stats); ((sqfs_object_t *)proc)->destroy = block_processor_destroy; /* we need at least one current data block + one fragment block */ if (proc->max_backlog < 3) proc->max_backlog = 3; /* create the thread pool */ proc->pool = thread_pool_create(desc->num_workers, process_block); if (proc->pool == NULL) { free(proc); return SQFS_ERROR_INTERNAL; } /* create the worker compressors & scratch buffer */ count = proc->pool->get_worker_count(proc->pool); for (i = 0; i < count; ++i) { worker_data_t *worker = alloc_flex(sizeof(*worker), 1, desc->max_block_size); if (worker == NULL) { ret = SQFS_ERROR_ALLOC; goto fail_pool; } worker->scratch_size = desc->max_block_size; worker->next = proc->workers; proc->workers = worker; worker->cmp = sqfs_copy(desc->cmp); if (worker->cmp == NULL) { ret = SQFS_ERROR_ALLOC; goto fail_pool; } proc->pool->set_worker_ptr(proc->pool, i, worker); } /* create the fragment hash table */ proc->frag_ht = hash_table_create(NULL, chunk_info_equals); if (proc->frag_ht == NULL) { ret = SQFS_ERROR_ALLOC; goto fail_pool; } proc->frag_ht->user = proc; *out = proc; return 0; fail_pool: block_processor_destroy((sqfs_object_t *)proc); return ret; } sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, sqfs_compressor_t *cmp, unsigned int num_workers, size_t max_backlog, sqfs_block_writer_t *wr, sqfs_frag_table_t *tbl) { sqfs_block_processor_desc_t desc; sqfs_block_processor_t *out; memset(&desc, 0, sizeof(desc)); desc.size = sizeof(desc); desc.max_block_size = max_block_size; desc.num_workers = num_workers; desc.max_backlog = max_backlog; desc.cmp = cmp; desc.wr = wr; desc.tbl = tbl; if (sqfs_block_processor_create_ex(&desc, &out) != 0) return NULL; return out; } squashfs-tools-ng-1.1.3/lib/sqfs/block_processor/frontend.c000066400000000000000000000120021410627516300240070ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * frontend.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "internal.h" static int get_new_block(sqfs_block_processor_t *proc, sqfs_block_t **out) { sqfs_block_t *blk; while (proc->backlog >= proc->max_backlog) { int ret = dequeue_block(proc); if (ret != 0) return ret; } if (proc->free_list != NULL) { blk = proc->free_list; proc->free_list = blk->next; } else { blk = malloc(sizeof(*blk) + proc->max_block_size); if (blk == NULL) return SQFS_ERROR_ALLOC; } memset(blk, 0, sizeof(*blk)); *out = blk; proc->backlog += 1; return 0; } static int add_sentinel_block(sqfs_block_processor_t *proc) { sqfs_block_t *blk; int ret; ret = get_new_block(proc, &blk); if (ret != 0) return ret; blk->inode = proc->inode; blk->flags = proc->blk_flags | SQFS_BLK_LAST_BLOCK; return enqueue_block(proc, blk); } int enqueue_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) { int status; if ((blk->flags & SQFS_BLK_FRAGMENT_BLOCK) && proc->file != NULL && proc->uncmp != NULL) { sqfs_block_t *copy = alloc_flex(sizeof(*copy), 1, blk->size); if (copy == NULL) return SQFS_ERROR_ALLOC; copy->size = blk->size; copy->index = blk->index; memcpy(copy->data, blk->data, blk->size); copy->next = proc->fblk_in_flight; proc->fblk_in_flight = copy; } if (proc->pool->submit(proc->pool, blk) != 0) { status = proc->pool->get_status(proc->pool); if (status == 0) status = SQFS_ERROR_ALLOC; blk->next = proc->free_list; proc->free_list = blk; return status; } return 0; } int sqfs_block_processor_begin_file(sqfs_block_processor_t *proc, sqfs_inode_generic_t **inode, void *user, sqfs_u32 flags) { if (proc->begin_called) return SQFS_ERROR_SEQUENCE; if (flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) return SQFS_ERROR_UNSUPPORTED; if (inode != NULL) { (*inode) = calloc(1, sizeof(sqfs_inode_generic_t)); if ((*inode) == NULL) return SQFS_ERROR_ALLOC; (*inode)->base.type = SQFS_INODE_FILE; sqfs_inode_set_frag_location(*inode, 0xFFFFFFFF, 0xFFFFFFFF); } proc->begin_called = true; proc->inode = inode; proc->blk_flags = flags | SQFS_BLK_FIRST_BLOCK; proc->blk_index = 0; proc->user = user; return 0; } int sqfs_block_processor_append(sqfs_block_processor_t *proc, const void *data, size_t size) { sqfs_block_t *new; sqfs_u64 filesize; size_t diff; int err; if (!proc->begin_called) return SQFS_ERROR_SEQUENCE; if (proc->inode != NULL) { sqfs_inode_get_file_size(*(proc->inode), &filesize); sqfs_inode_set_file_size(*(proc->inode), filesize + size); } while (size > 0) { if (proc->blk_current == NULL) { err = get_new_block(proc, &new); if (err != 0) return err; proc->blk_current = new; proc->blk_current->flags = proc->blk_flags; proc->blk_current->inode = proc->inode; proc->blk_current->user = proc->user; proc->blk_current->index = proc->blk_index++; proc->blk_flags &= ~SQFS_BLK_FIRST_BLOCK; } diff = proc->max_block_size - proc->blk_current->size; if (diff == 0) { err = enqueue_block(proc, proc->blk_current); proc->blk_current = NULL; if (err) return err; continue; } if (diff > size) diff = size; memcpy(proc->blk_current->data + proc->blk_current->size, data, diff); size -= diff; proc->blk_current->size += diff; data = (const char *)data + diff; proc->stats.input_bytes_read += diff; } if (proc->blk_current->size == proc->max_block_size) { err = enqueue_block(proc, proc->blk_current); proc->blk_current = NULL; if (err) return err; } return 0; } int sqfs_block_processor_end_file(sqfs_block_processor_t *proc) { int err; if (!proc->begin_called) return SQFS_ERROR_SEQUENCE; if (proc->blk_current == NULL) { if (!(proc->blk_flags & SQFS_BLK_FIRST_BLOCK)) { err = add_sentinel_block(proc); if (err) return err; } } else { if (proc->blk_flags & SQFS_BLK_DONT_FRAGMENT) { proc->blk_current->flags |= SQFS_BLK_LAST_BLOCK; } else { if (!(proc->blk_current->flags & SQFS_BLK_FIRST_BLOCK)) { err = add_sentinel_block(proc); if (err) return err; } proc->blk_current->flags |= SQFS_BLK_IS_FRAGMENT; } err = enqueue_block(proc, proc->blk_current); proc->blk_current = NULL; if (err) return err; } proc->begin_called = false; proc->inode = NULL; proc->user = NULL; proc->blk_flags = 0; return 0; } int sqfs_block_processor_submit_block(sqfs_block_processor_t *proc, void *user, sqfs_u32 flags, const void *data, size_t size) { sqfs_block_t *blk; int ret; if (proc->begin_called) return SQFS_ERROR_SEQUENCE; if (size > proc->max_block_size) return SQFS_ERROR_OVERFLOW; if (flags & ~SQFS_BLK_FLAGS_ALL) return SQFS_ERROR_UNSUPPORTED; ret = get_new_block(proc, &blk); if (ret != 0) return ret; blk->flags = flags | BLK_FLAG_MANUAL_SUBMISSION; blk->user = user; blk->size = size; memcpy(blk->data, data, size); return enqueue_block(proc, blk); } squashfs-tools-ng-1.1.3/lib/sqfs/block_processor/internal.h000066400000000000000000000041551410627516300240230ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * internal.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef INTERNAL_H #define INTERNAL_H #include "config.h" #include "sqfs/block_processor.h" #include "sqfs/block_writer.h" #include "sqfs/frag_table.h" #include "sqfs/compressor.h" #include "sqfs/inode.h" #include "sqfs/table.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "hash_table.h" #include "threadpool.h" #include "util.h" #include #include typedef struct { sqfs_u32 index; sqfs_u32 offset; sqfs_u32 size; sqfs_u32 hash; } chunk_info_t; enum { BLK_FLAG_MANUAL_SUBMISSION = 0x10000000, BLK_FLAG_INTERNAL = 0x10000000, }; typedef struct sqfs_block_t { struct sqfs_block_t *next; sqfs_inode_generic_t **inode; sqfs_u32 io_seq_num; sqfs_u32 flags; sqfs_u32 size; sqfs_u32 checksum; /* For data blocks: index within the inode. For fragment fragment blocks: fragment table index. */ sqfs_u32 index; /* User data pointer */ void *user; sqfs_u8 data[]; } sqfs_block_t; typedef struct worker_data_t { struct worker_data_t *next; sqfs_compressor_t *cmp; size_t scratch_size; sqfs_u8 scratch[]; } worker_data_t; struct sqfs_block_processor_t { sqfs_object_t obj; sqfs_frag_table_t *frag_tbl; sqfs_block_t *frag_block; sqfs_block_writer_t *wr; sqfs_block_processor_stats_t stats; sqfs_inode_generic_t **inode; sqfs_block_t *blk_current; sqfs_u32 blk_flags; sqfs_u32 blk_index; void *user; struct hash_table *frag_ht; sqfs_block_t *free_list; size_t max_block_size; size_t max_backlog; size_t backlog; bool begin_called; sqfs_file_t *file; sqfs_compressor_t *uncmp; thread_pool_t *pool; worker_data_t *workers; sqfs_block_t *io_queue; sqfs_u32 io_seq_num; sqfs_u32 io_deq_seq_num; sqfs_block_t *current_frag; sqfs_block_t *cached_frag_blk; sqfs_block_t *fblk_in_flight; int fblk_lookup_error; sqfs_u8 scratch[]; }; SQFS_INTERNAL int enqueue_block(sqfs_block_processor_t *proc, sqfs_block_t *blk); SQFS_INTERNAL int dequeue_block(sqfs_block_processor_t *proc); #endif /* INTERNAL_H */ squashfs-tools-ng-1.1.3/lib/sqfs/block_writer.c000066400000000000000000000141121410627516300214710ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * block_writer.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/block_writer.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" #include #include #define MK_BLK_HASH(chksum, size) \ (((sqfs_u64)(size) << 32) | (sqfs_u64)(chksum)) #define SIZE_FROM_HASH(hash) ((hash >> 32) & ((1 << 24) - 1)) #define INIT_BLOCK_COUNT (128) #define SCRATCH_SIZE (8192) typedef struct { sqfs_u64 offset; sqfs_u64 hash; } blk_info_t; typedef struct { sqfs_block_writer_t base; sqfs_file_t *file; size_t num_blocks; size_t max_blocks; blk_info_t *blocks; size_t devblksz; sqfs_u64 blocks_written; sqfs_u64 data_area_start; sqfs_u64 start; size_t file_start; sqfs_u32 flags; sqfs_u8 scratch[]; } block_writer_default_t; static int store_block_location(block_writer_default_t *wr, sqfs_u64 offset, sqfs_u32 size, sqfs_u32 chksum) { blk_info_t *new; size_t new_sz; if (wr->num_blocks == wr->max_blocks) { new_sz = wr->max_blocks * 2; new = realloc(wr->blocks, sizeof(wr->blocks[0]) * new_sz); if (new == NULL) return SQFS_ERROR_ALLOC; wr->blocks = new; wr->max_blocks = new_sz; } wr->blocks[wr->num_blocks].offset = offset; wr->blocks[wr->num_blocks].hash = MK_BLK_HASH(chksum, size); wr->num_blocks += 1; return 0; } static int compare_blocks(block_writer_default_t *wr, sqfs_u64 loc_a, sqfs_u64 loc_b, size_t size) { sqfs_u8 *ptr_a = wr->scratch, *ptr_b = ptr_a + SCRATCH_SIZE / 2; size_t diff; int ret; while (size > 0) { diff = SCRATCH_SIZE / 2; diff = diff > size ? size : diff; ret = wr->file->read_at(wr->file, loc_a, ptr_a, diff); if (ret != 0) return ret; ret = wr->file->read_at(wr->file, loc_b, ptr_b, diff); if (ret != 0) return ret; if (memcmp(ptr_a, ptr_b, diff) != 0) return 1; size -= diff; loc_a += diff; loc_b += diff; } return 0; } static int deduplicate_blocks(block_writer_default_t *wr, size_t count, size_t *out) { sqfs_u64 loc_a, loc_b; size_t i, j, sz; int ret; for (i = 0; i < wr->file_start; ++i) { for (j = 0; j < count; ++j) { if (wr->blocks[i + j].hash == 0) break; if (wr->blocks[i + j].hash != wr->blocks[wr->file_start + j].hash) break; } if (j != count) continue; if (wr->flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) break; for (j = 0; j < count; ++j) { sz = SIZE_FROM_HASH(wr->blocks[i + j].hash); loc_a = wr->blocks[i + j].offset; loc_b = wr->blocks[wr->file_start + j].offset; ret = compare_blocks(wr, loc_a, loc_b, sz); if (ret < 0) return ret; if (ret > 0) break; } if (j == count) break; } *out = i; return 0; } static int align_file(block_writer_default_t *wr) { void *padding; sqfs_u64 size; size_t diff; int ret; size = wr->file->get_size(wr->file); diff = size % wr->devblksz; if (diff == 0) return 0; padding = calloc(1, diff); if (padding == 0) return SQFS_ERROR_ALLOC; ret = wr->file->write_at(wr->file, size, padding, diff); free(padding); if (ret) return ret; return store_block_location(wr, size, 0, 0); } static void block_writer_destroy(sqfs_object_t *wr) { free(((block_writer_default_t *)wr)->blocks); free(wr); } static int write_data_block(sqfs_block_writer_t *base, void *user, sqfs_u32 size, sqfs_u32 checksum, sqfs_u32 flags, const sqfs_u8 *data, sqfs_u64 *location) { block_writer_default_t *wr = (block_writer_default_t *)base; size_t start, count; sqfs_u64 offset; sqfs_u32 out; int err; (void)user; if (flags & (SQFS_BLK_FIRST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { if (flags & SQFS_BLK_ALIGN) { err = align_file(wr); if (err) return err; } if (flags & SQFS_BLK_FIRST_BLOCK) { wr->start = wr->file->get_size(wr->file); wr->file_start = wr->num_blocks; } } offset = wr->file->get_size(wr->file); *location = offset; if (size != 0 && !(flags & SQFS_BLK_IS_SPARSE)) { out = size; if (!(flags & SQFS_BLK_IS_COMPRESSED)) out |= 1 << 24; err = store_block_location(wr, offset, out, checksum); if (err) return err; err = wr->file->write_at(wr->file, offset, data, size); if (err) return err; wr->blocks_written = wr->num_blocks; } if (flags & SQFS_BLK_ALIGN) { if (flags & (SQFS_BLK_LAST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { err = align_file(wr); if (err) return err; } } if (flags & SQFS_BLK_LAST_BLOCK) { count = wr->num_blocks - wr->file_start; if (count == 0) { *location = 0; } else if (!(flags & SQFS_BLK_DONT_DEDUPLICATE)) { err = deduplicate_blocks(wr, count, &start); if (err) return err; offset = wr->blocks[start].offset; *location = offset; if (start >= wr->file_start) return 0; offset = start + count; if (offset >= wr->file_start) { count = wr->num_blocks - offset; wr->num_blocks = offset; } else { wr->num_blocks = wr->file_start; } err = wr->file->truncate(wr->file, wr->start); if (err) return err; } wr->blocks_written = wr->num_blocks; } return 0; } static sqfs_u64 get_block_count(const sqfs_block_writer_t *wr) { return ((const block_writer_default_t *)wr)->blocks_written; } sqfs_block_writer_t *sqfs_block_writer_create(sqfs_file_t *file, size_t devblksz, sqfs_u32 flags) { block_writer_default_t *wr; if (flags & ~SQFS_BLOCK_WRITER_ALL_FLAGS) return NULL; if (flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) { wr = calloc(1, sizeof(*wr)); } else { wr = alloc_flex(sizeof(*wr), 1, SCRATCH_SIZE); } if (wr == NULL) return NULL; ((sqfs_block_writer_t *)wr)->write_data_block = write_data_block; ((sqfs_block_writer_t *)wr)->get_block_count = get_block_count; ((sqfs_object_t *)wr)->destroy = block_writer_destroy; wr->flags = flags; wr->file = file; wr->devblksz = devblksz; wr->max_blocks = INIT_BLOCK_COUNT; wr->data_area_start = wr->file->get_size(wr->file); wr->blocks = alloc_array(sizeof(wr->blocks[0]), wr->max_blocks); if (wr->blocks == NULL) { free(wr); return NULL; } return (sqfs_block_writer_t *)wr; } squashfs-tools-ng-1.1.3/lib/sqfs/comp/000077500000000000000000000000001410627516300175765ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/sqfs/comp/compressor.c000066400000000000000000000121331410627516300221360ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * compressor.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include #include #include "internal.h" typedef int (*compressor_fun_t)(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { #ifdef WITH_GZIP [SQFS_COMP_GZIP] = gzip_compressor_create, #endif #ifdef WITH_XZ [SQFS_COMP_XZ] = xz_compressor_create, [SQFS_COMP_LZMA] = lzma_compressor_create, #endif #ifdef WITH_LZ4 [SQFS_COMP_LZ4] = lz4_compressor_create, #endif #ifdef WITH_ZSTD [SQFS_COMP_ZSTD] = zstd_compressor_create, #endif }; static const char *names[] = { [SQFS_COMP_GZIP] = "gzip", [SQFS_COMP_LZMA] = "lzma", [SQFS_COMP_LZO] = "lzo", [SQFS_COMP_XZ] = "xz", [SQFS_COMP_LZ4] = "lz4", [SQFS_COMP_ZSTD] = "zstd", }; int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size) { sqfs_u8 buffer[64]; sqfs_u16 header; int ret; /* XXX: options for all known compressors should fit into this */ if (size >= (sizeof(buffer) - sizeof(header))) return SQFS_ERROR_INTERNAL; header = htole16(0x8000 | size); memcpy(buffer, &header, sizeof(header)); memcpy(buffer + sizeof(header), data, size); ret = file->write_at(file, sizeof(sqfs_super_t), buffer, sizeof(header) + size); if (ret) return ret; return sizeof(header) + size; } int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size) { sqfs_u8 buffer[64]; sqfs_u16 header; int ret; /* XXX: options for all known compressors should fit into this */ if (size >= (sizeof(buffer) - sizeof(header))) return SQFS_ERROR_INTERNAL; ret = file->read_at(file, sizeof(sqfs_super_t), buffer, sizeof(header) + size); if (ret) return ret; memcpy(&header, buffer, sizeof(header)); if (le16toh(header) != (0x8000 | size)) return SQFS_ERROR_CORRUPTED; memcpy(data, buffer + 2, size); return 0; } int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { sqfs_u8 padd0[sizeof(cfg->opt)]; int ret; /* check compressor ID */ if (cfg == NULL) return SQFS_ERROR_ARG_INVALID; if (cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX) return SQFS_ERROR_UNSUPPORTED; if (compressors[cfg->id] == NULL) return SQFS_ERROR_UNSUPPORTED; /* make sure the padding bytes are cleared, so we could theoretically turn them into option fields in the future and remain compatible */ memset(padd0, 0, sizeof(padd0)); switch (cfg->id) { case SQFS_COMP_XZ: ret = memcmp(cfg->opt.xz.padd0, padd0, sizeof(cfg->opt.xz.padd0)); break; case SQFS_COMP_LZMA: ret = memcmp(cfg->opt.lzma.padd0, padd0, sizeof(cfg->opt.lzma.padd0)); break; case SQFS_COMP_LZO: ret = memcmp(cfg->opt.lzo.padd0, padd0, sizeof(cfg->opt.lzo.padd0)); break; case SQFS_COMP_GZIP: ret = memcmp(cfg->opt.gzip.padd0, padd0, sizeof(cfg->opt.gzip.padd0)); break; default: ret = memcmp(cfg->opt.padd0, padd0, sizeof(cfg->opt.padd0)); break; } if (ret != 0) return SQFS_ERROR_ARG_INVALID; return compressors[cfg->id](cfg, out); } const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id) { if (id < 0 || (size_t)id >= sizeof(names) / sizeof(names[0])) return NULL; return names[id]; } int sqfs_compressor_id_from_name(const char *name) { size_t i; for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { if (names[i] != NULL && strcmp(names[i], name) == 0) return i; } return SQFS_ERROR_UNSUPPORTED; } int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, SQFS_COMPRESSOR id, size_t block_size, sqfs_u16 flags) { sqfs_u32 flag_mask = SQFS_COMP_FLAG_GENERIC_ALL; memset(cfg, 0, sizeof(*cfg)); switch (id) { case SQFS_COMP_GZIP: flag_mask |= SQFS_COMP_FLAG_GZIP_ALL; cfg->level = SQFS_GZIP_DEFAULT_LEVEL; cfg->opt.gzip.window_size = SQFS_GZIP_DEFAULT_WINDOW; break; case SQFS_COMP_LZO: cfg->opt.lzo.algorithm = SQFS_LZO_DEFAULT_ALG; cfg->level = SQFS_LZO_DEFAULT_LEVEL; break; case SQFS_COMP_ZSTD: cfg->level = SQFS_ZSTD_DEFAULT_LEVEL; break; case SQFS_COMP_XZ: flag_mask |= SQFS_COMP_FLAG_XZ_ALL; cfg->level = SQFS_XZ_DEFAULT_LEVEL; cfg->opt.xz.dict_size = block_size; cfg->opt.xz.lc = SQFS_XZ_DEFAULT_LC; cfg->opt.xz.lp = SQFS_XZ_DEFAULT_LP; cfg->opt.xz.pb = SQFS_XZ_DEFAULT_PB; if (block_size < SQFS_XZ_MIN_DICT_SIZE) cfg->opt.xz.dict_size = SQFS_XZ_MIN_DICT_SIZE; break; case SQFS_COMP_LZMA: flag_mask |= SQFS_COMP_FLAG_LZMA_ALL; cfg->level = SQFS_LZMA_DEFAULT_LEVEL; cfg->opt.lzma.dict_size = block_size; cfg->opt.lzma.lc = SQFS_LZMA_DEFAULT_LC; cfg->opt.lzma.lp = SQFS_LZMA_DEFAULT_LP; cfg->opt.lzma.pb = SQFS_LZMA_DEFAULT_PB; if (block_size < SQFS_LZMA_MIN_DICT_SIZE) cfg->opt.lzma.dict_size = SQFS_LZMA_MIN_DICT_SIZE; break; case SQFS_COMP_LZ4: flag_mask |= SQFS_COMP_FLAG_LZ4_ALL; break; default: return SQFS_ERROR_UNSUPPORTED; } if (flags & ~flag_mask) { memset(cfg, 0, sizeof(*cfg)); return SQFS_ERROR_UNSUPPORTED; } cfg->id = id; cfg->flags = flags; cfg->block_size = block_size; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/comp/gzip.c000066400000000000000000000154661410627516300207270ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * gzip.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include #include #include #include #include #include "internal.h" typedef struct { sqfs_u32 level; sqfs_u16 window; sqfs_u16 strategies; } gzip_options_t; typedef struct { sqfs_compressor_t base; z_stream strm; bool compress; size_t block_size; gzip_options_t opt; } gzip_compressor_t; static void gzip_destroy(sqfs_object_t *base) { gzip_compressor_t *gzip = (gzip_compressor_t *)base; if (gzip->compress) { deflateEnd(&gzip->strm); } else { inflateEnd(&gzip->strm); } free(gzip); } static void gzip_get_configuration(const sqfs_compressor_t *base, sqfs_compressor_config_t *cfg) { const gzip_compressor_t *gzip = (const gzip_compressor_t *)base; memset(cfg, 0, sizeof(*cfg)); cfg->id = SQFS_COMP_GZIP; cfg->flags = gzip->opt.strategies; cfg->block_size = gzip->block_size; cfg->level = gzip->opt.level; cfg->opt.gzip.window_size = gzip->opt.window; if (!gzip->compress) cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; } static int gzip_write_options(sqfs_compressor_t *base, sqfs_file_t *file) { gzip_compressor_t *gzip = (gzip_compressor_t *)base; gzip_options_t opt; if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL && gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW && gzip->opt.strategies == 0) { return 0; } opt.level = htole32(gzip->opt.level); opt.window = htole16(gzip->opt.window); opt.strategies = htole16(gzip->opt.strategies); return sqfs_generic_write_options(file, &opt, sizeof(opt)); } static int gzip_read_options(sqfs_compressor_t *base, sqfs_file_t *file) { gzip_compressor_t *gzip = (gzip_compressor_t *)base; gzip_options_t opt; int ret; ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); if (ret) return ret; gzip->opt.level = le32toh(opt.level); gzip->opt.window = le16toh(opt.window); gzip->opt.strategies = le16toh(opt.strategies); if (gzip->opt.level < 1 || gzip->opt.level > 9) return SQFS_ERROR_UNSUPPORTED; if (gzip->opt.window < 8 || gzip->opt.window > 15) return SQFS_ERROR_UNSUPPORTED; if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) return SQFS_ERROR_UNSUPPORTED; return 0; } static int flag_to_zlib_strategy(int flag) { switch (flag) { case SQFS_COMP_FLAG_GZIP_DEFAULT: return Z_DEFAULT_STRATEGY; case SQFS_COMP_FLAG_GZIP_FILTERED: return Z_FILTERED; case SQFS_COMP_FLAG_GZIP_HUFFMAN: return Z_HUFFMAN_ONLY; case SQFS_COMP_FLAG_GZIP_RLE: return Z_RLE; case SQFS_COMP_FLAG_GZIP_FIXED: return Z_FIXED; default: break; } return 0; } static int find_strategy(gzip_compressor_t *gzip, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { int ret, strategy, selected = Z_DEFAULT_STRATEGY; size_t i, length, minlength = 0; for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) { if ((gzip->opt.strategies & i) == 0) continue; ret = deflateReset(&gzip->strm); if (ret != Z_OK) return SQFS_ERROR_COMPRESSOR; strategy = flag_to_zlib_strategy(i); gzip->strm.next_in = (z_const Bytef *)in; gzip->strm.avail_in = size; gzip->strm.next_out = out; gzip->strm.avail_out = outsize; ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); if (ret != Z_OK) return SQFS_ERROR_COMPRESSOR; ret = deflate(&gzip->strm, Z_FINISH); if (ret == Z_STREAM_END) { length = gzip->strm.total_out; if (minlength == 0 || length < minlength) { minlength = length; selected = strategy; } } else if (ret != Z_OK && ret != Z_BUF_ERROR) { return SQFS_ERROR_COMPRESSOR; } } return selected; } static sqfs_s32 gzip_do_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { gzip_compressor_t *gzip = (gzip_compressor_t *)base; int ret, strategy = 0; size_t written; if (size >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; if (gzip->compress && gzip->opt.strategies != 0) { strategy = find_strategy(gzip, in, size, out, outsize); if (strategy < 0) return strategy; } if (gzip->compress) { ret = deflateReset(&gzip->strm); } else { ret = inflateReset(&gzip->strm); } if (ret != Z_OK) return SQFS_ERROR_COMPRESSOR; gzip->strm.next_in = (const void *)in; gzip->strm.avail_in = size; gzip->strm.next_out = out; gzip->strm.avail_out = outsize; if (gzip->compress && gzip->opt.strategies != 0) { ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); if (ret != Z_OK) return SQFS_ERROR_COMPRESSOR; } if (gzip->compress) { ret = deflate(&gzip->strm, Z_FINISH); } else { ret = inflate(&gzip->strm, Z_FINISH); } if (ret == Z_STREAM_END) { written = gzip->strm.total_out; if (gzip->compress && written >= size) return 0; return written; } if (ret != Z_OK && ret != Z_BUF_ERROR) return SQFS_ERROR_COMPRESSOR; return 0; } static sqfs_object_t *gzip_create_copy(const sqfs_object_t *cmp) { gzip_compressor_t *gzip = malloc(sizeof(*gzip)); int ret; if (gzip == NULL) return NULL; memcpy(gzip, cmp, sizeof(*gzip)); memset(&gzip->strm, 0, sizeof(gzip->strm)); if (gzip->compress) { ret = deflateInit2(&gzip->strm, gzip->opt.level, Z_DEFLATED, gzip->opt.window, 8, Z_DEFAULT_STRATEGY); } else { ret = inflateInit(&gzip->strm); } if (ret != Z_OK) { free(gzip); return NULL; } return (sqfs_object_t *)gzip; } int gzip_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { gzip_compressor_t *gzip; sqfs_compressor_t *base; int ret; if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL | SQFS_COMP_FLAG_GENERIC_ALL)) { return SQFS_ERROR_UNSUPPORTED; } if (cfg->level < SQFS_GZIP_MIN_LEVEL || cfg->level > SQFS_GZIP_MAX_LEVEL) { return SQFS_ERROR_UNSUPPORTED; } if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW || cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) { return SQFS_ERROR_UNSUPPORTED; } gzip = calloc(1, sizeof(*gzip)); base = (sqfs_compressor_t *)gzip; if (gzip == NULL) return SQFS_ERROR_ALLOC; gzip->opt.level = cfg->level; gzip->opt.window = cfg->opt.gzip.window_size; gzip->opt.strategies = cfg->flags & SQFS_COMP_FLAG_GZIP_ALL; gzip->compress = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) == 0; gzip->block_size = cfg->block_size; base->get_configuration = gzip_get_configuration; base->do_block = gzip_do_block; base->write_options = gzip_write_options; base->read_options = gzip_read_options; ((sqfs_object_t *)base)->copy = gzip_create_copy; ((sqfs_object_t *)base)->destroy = gzip_destroy; if (gzip->compress) { ret = deflateInit2(&gzip->strm, cfg->level, Z_DEFLATED, cfg->opt.gzip.window_size, 8, Z_DEFAULT_STRATEGY); } else { ret = inflateInit(&gzip->strm); } if (ret != Z_OK) { free(gzip); return SQFS_ERROR_COMPRESSOR; } *out = base; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/comp/internal.h000066400000000000000000000021261410627516300215640ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * internal.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef INTERNAL_H #define INTERNAL_H #include "config.h" #include "sqfs/predef.h" #include "sqfs/compressor.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" SQFS_INTERNAL int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size); SQFS_INTERNAL int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size); SQFS_INTERNAL int xz_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); SQFS_INTERNAL int gzip_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); SQFS_INTERNAL int lz4_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); SQFS_INTERNAL int zstd_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); SQFS_INTERNAL int lzma_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out); #endif /* INTERNAL_H */ squashfs-tools-ng-1.1.3/lib/sqfs/comp/lz4.c000066400000000000000000000072701410627516300204610ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * lz4.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include #include #include #include #include #include "internal.h" typedef struct { sqfs_compressor_t base; size_t block_size; bool high_compression; } lz4_compressor_t; typedef struct { sqfs_u32 version; sqfs_u32 flags; } lz4_options; #define LZ4LEGACY 1 /* old verions of liblz4 don't have this */ #ifndef LZ4HC_CLEVEL_MAX #define LZ4HC_CLEVEL_MAX 12 #endif static int lz4_write_options(sqfs_compressor_t *base, sqfs_file_t *file) { lz4_compressor_t *lz4 = (lz4_compressor_t *)base; lz4_options opt = { .version = htole32(LZ4LEGACY), .flags = htole32(lz4->high_compression ? SQFS_COMP_FLAG_LZ4_HC : 0), }; return sqfs_generic_write_options(file, &opt, sizeof(opt)); } static int lz4_read_options(sqfs_compressor_t *base, sqfs_file_t *file) { lz4_options opt; int ret; (void)base; ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); if (ret) return ret; opt.version = le32toh(opt.version); opt.flags = le32toh(opt.flags); if (opt.version != LZ4LEGACY) return SQFS_ERROR_UNSUPPORTED; return 0; } static sqfs_s32 lz4_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { lz4_compressor_t *lz4 = (lz4_compressor_t *)base; int ret; if (size >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; if (lz4->high_compression) { ret = LZ4_compress_HC((const void *)in, (void *)out, size, outsize, LZ4HC_CLEVEL_MAX); } else { ret = LZ4_compress_default((const void *)in, (void *)out, size, outsize); } if (ret < 0) return SQFS_ERROR_COMPRESSOR; return ret; } static sqfs_s32 lz4_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { int ret; (void)base; if (outsize >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; ret = LZ4_decompress_safe((const void *)in, (void *)out, size, outsize); if (ret < 0) return SQFS_ERROR_COMPRESSOR; return ret; } static void lz4_get_configuration(const sqfs_compressor_t *base, sqfs_compressor_config_t *cfg) { const lz4_compressor_t *lz4 = (const lz4_compressor_t *)base; memset(cfg, 0, sizeof(*cfg)); cfg->id = SQFS_COMP_LZ4; cfg->block_size = lz4->block_size; if (lz4->high_compression) cfg->flags |= SQFS_COMP_FLAG_LZ4_HC; if (base->do_block == lz4_uncomp_block) cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; } static sqfs_object_t *lz4_create_copy(const sqfs_object_t *cmp) { lz4_compressor_t *lz4 = malloc(sizeof(*lz4)); if (lz4 == NULL) return NULL; memcpy(lz4, cmp, sizeof(*lz4)); return (sqfs_object_t *)lz4; } static void lz4_destroy(sqfs_object_t *base) { free(base); } int lz4_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { sqfs_compressor_t *base; lz4_compressor_t *lz4; if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL | SQFS_COMP_FLAG_GENERIC_ALL)) { return SQFS_ERROR_UNSUPPORTED; } if (cfg->level != 0) return SQFS_ERROR_UNSUPPORTED; lz4 = calloc(1, sizeof(*lz4)); base = (sqfs_compressor_t *)lz4; if (lz4 == NULL) return SQFS_ERROR_ALLOC; lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0; lz4->block_size = cfg->block_size; base->get_configuration = lz4_get_configuration; base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? lz4_uncomp_block : lz4_comp_block; base->write_options = lz4_write_options; base->read_options = lz4_read_options; ((sqfs_object_t *)base)->copy = lz4_create_copy; ((sqfs_object_t *)base)->destroy = lz4_destroy; *out = base; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/comp/lzma.c000066400000000000000000000151141410627516300207070ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * lzma.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include #include #include #include #include "internal.h" #define LZMA_SIZE_OFFSET (5) #define LZMA_SIZE_BYTES (8) #define LZMA_HEADER_SIZE (13) #define MEMLIMIT (64 * 1024 * 1024) typedef struct { sqfs_compressor_t base; size_t block_size; size_t dict_size; sqfs_u32 flags; sqfs_u8 level; sqfs_u8 lc; sqfs_u8 lp; sqfs_u8 pb; } lzma_compressor_t; static int lzma_write_options(sqfs_compressor_t *base, sqfs_file_t *file) { (void)base; (void)file; return 0; } static int lzma_read_options(sqfs_compressor_t *base, sqfs_file_t *file) { (void)base; (void)file; return SQFS_ERROR_UNSUPPORTED; } static sqfs_s32 try_compress(lzma_compressor_t *lzma, sqfs_u32 preset, const sqfs_u8 *in, size_t size, sqfs_u8 *out, size_t outsize) { lzma_stream strm = LZMA_STREAM_INIT; lzma_options_lzma opt; int ret; lzma_lzma_preset(&opt, preset); opt.dict_size = lzma->block_size; opt.lc = lzma->lc; opt.lp = lzma->lp; opt.pb = lzma->pb; if (lzma_alone_encoder(&strm, &opt) != LZMA_OK) { lzma_end(&strm); return SQFS_ERROR_COMPRESSOR; } strm.next_out = out; strm.avail_out = outsize; strm.next_in = in; strm.avail_in = size; ret = lzma_code(&strm, LZMA_FINISH); lzma_end(&strm); if (ret != LZMA_STREAM_END) return ret == LZMA_OK ? 0 : SQFS_ERROR_COMPRESSOR; if (strm.total_out > size) return 0; out[LZMA_SIZE_OFFSET ] = size & 0xFF; out[LZMA_SIZE_OFFSET + 1] = (size >> 8) & 0xFF; out[LZMA_SIZE_OFFSET + 2] = (size >> 16) & 0xFF; out[LZMA_SIZE_OFFSET + 3] = (size >> 24) & 0xFF; out[LZMA_SIZE_OFFSET + 4] = 0; out[LZMA_SIZE_OFFSET + 5] = 0; out[LZMA_SIZE_OFFSET + 6] = 0; out[LZMA_SIZE_OFFSET + 7] = 0; return strm.total_out; } static sqfs_s32 lzma_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { lzma_compressor_t *lzma = (lzma_compressor_t *)base; sqfs_s32 ret, smallest; sqfs_u32 preset; if (outsize < LZMA_HEADER_SIZE || size >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; preset = lzma->level; ret = try_compress(lzma, preset, in, size, out, outsize); if (ret < 0 || !(lzma->flags & SQFS_COMP_FLAG_LZMA_EXTREME)) return ret; preset |= LZMA_PRESET_EXTREME; smallest = ret; ret = try_compress(lzma, preset, in, size, out, outsize); if (ret < 0 || (ret > 0 && (smallest == 0 || ret < smallest))) return ret; preset &= ~LZMA_PRESET_EXTREME; return smallest == 0 ? 0 : try_compress(lzma, preset, in, size, out, outsize); } static sqfs_s32 lzma_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { sqfs_u8 lzma_header[LZMA_HEADER_SIZE]; lzma_stream strm = LZMA_STREAM_INIT; size_t hdrsize; int ret; (void)base; if (size >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; if (size < sizeof(lzma_header)) return SQFS_ERROR_CORRUPTED; hdrsize = (size_t)in[LZMA_SIZE_OFFSET] | ((size_t)in[LZMA_SIZE_OFFSET + 1] << 8) | ((size_t)in[LZMA_SIZE_OFFSET + 2] << 16) | ((size_t)in[LZMA_SIZE_OFFSET + 3] << 24); if (hdrsize > outsize) return 0; if (lzma_alone_decoder(&strm, MEMLIMIT) != LZMA_OK) { lzma_end(&strm); return SQFS_ERROR_COMPRESSOR; } memcpy(lzma_header, in, sizeof(lzma_header)); memset(lzma_header + LZMA_SIZE_OFFSET, 0xFF, LZMA_SIZE_BYTES); strm.next_out = out; strm.avail_out = outsize; strm.next_in = lzma_header; strm.avail_in = sizeof(lzma_header); ret = lzma_code(&strm, LZMA_RUN); if (ret != LZMA_OK || strm.avail_in != 0) { lzma_end(&strm); return SQFS_ERROR_COMPRESSOR; } strm.next_in = in + sizeof(lzma_header); strm.avail_in = size - sizeof(lzma_header); ret = lzma_code(&strm, LZMA_FINISH); lzma_end(&strm); if (ret != LZMA_STREAM_END && ret != LZMA_OK) return SQFS_ERROR_COMPRESSOR; if (ret == LZMA_OK) { if (strm.total_out < hdrsize || strm.avail_in != 0) return 0; } return hdrsize; } static void lzma_get_configuration(const sqfs_compressor_t *base, sqfs_compressor_config_t *cfg) { const lzma_compressor_t *lzma = (const lzma_compressor_t *)base; memset(cfg, 0, sizeof(*cfg)); cfg->id = SQFS_COMP_LZMA; cfg->block_size = lzma->block_size; cfg->flags = lzma->flags; cfg->level = lzma->level; cfg->opt.lzma.dict_size = lzma->dict_size; cfg->opt.lzma.lc = lzma->lc; cfg->opt.lzma.lp = lzma->lp; cfg->opt.lzma.pb = lzma->pb; } static sqfs_object_t *lzma_create_copy(const sqfs_object_t *cmp) { lzma_compressor_t *copy = malloc(sizeof(*copy)); if (copy != NULL) memcpy(copy, cmp, sizeof(*copy)); return (sqfs_object_t *)copy; } static void lzma_destroy(sqfs_object_t *base) { free(base); } int lzma_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { sqfs_compressor_t *base; lzma_compressor_t *lzma; sqfs_u32 mask; mask = SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_LZMA_ALL; if (cfg->flags & ~mask) return SQFS_ERROR_UNSUPPORTED; /* XXX: values are unsigned and minimum is 0 */ if (cfg->level > SQFS_LZMA_MAX_LEVEL) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.lc > SQFS_LZMA_MAX_LC) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.lp > SQFS_LZMA_MAX_LP) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.pb > SQFS_LZMA_MAX_PB) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.lc + cfg->opt.lzma.lp > 4) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.dict_size == 0) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.dict_size < SQFS_LZMA_MIN_DICT_SIZE) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.dict_size > SQFS_LZMA_MAX_DICT_SIZE) return SQFS_ERROR_UNSUPPORTED; mask = cfg->opt.lzma.dict_size; mask &= mask - 1; if (mask != 0) { if ((mask & (mask - 1)) != 0) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.lzma.dict_size != (mask | mask >> 1)) return SQFS_ERROR_UNSUPPORTED; } lzma = calloc(1, sizeof(*lzma)); base = (sqfs_compressor_t *)lzma; if (lzma == NULL) return SQFS_ERROR_ALLOC; lzma->block_size = cfg->block_size; lzma->flags = cfg->flags; lzma->level = cfg->level; lzma->dict_size = cfg->opt.lzma.dict_size; lzma->lc = cfg->opt.lzma.lc; lzma->lp = cfg->opt.lzma.lp; lzma->pb = cfg->opt.lzma.pb; base->get_configuration = lzma_get_configuration; base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? lzma_uncomp_block : lzma_comp_block; base->write_options = lzma_write_options; base->read_options = lzma_read_options; ((sqfs_object_t *)base)->copy = lzma_create_copy; ((sqfs_object_t *)base)->destroy = lzma_destroy; *out = base; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/comp/xz.c000066400000000000000000000160471410627516300204130ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xz.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include #include #include #include #include "internal.h" typedef struct { sqfs_compressor_t base; size_t block_size; size_t dict_size; sqfs_u8 level; sqfs_u8 lc; sqfs_u8 lp; sqfs_u8 pb; int flags; } xz_compressor_t; typedef struct { sqfs_u32 dict_size; sqfs_u32 flags; } xz_options_t; static bool is_dict_size_valid(size_t size) { size_t x = size & (size - 1); if (x == 0) return true; return size == (x | (x >> 1)); } static int xz_write_options(sqfs_compressor_t *base, sqfs_file_t *file) { xz_compressor_t *xz = (xz_compressor_t *)base; xz_options_t opt; sqfs_u32 flags; if (xz->flags == 0 && xz->dict_size == xz->block_size) return 0; flags = xz->flags & SQFS_COMP_FLAG_XZ_ALL; flags &= ~SQFS_COMP_FLAG_XZ_EXTREME; opt.dict_size = htole32(xz->dict_size); opt.flags = htole32(flags); return sqfs_generic_write_options(file, &opt, sizeof(opt)); } static int xz_read_options(sqfs_compressor_t *base, sqfs_file_t *file) { xz_compressor_t *xz = (xz_compressor_t *)base; xz_options_t opt; int ret; ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); if (ret) return ret; opt.dict_size = le32toh(opt.dict_size); opt.flags = le32toh(opt.flags); if (!is_dict_size_valid(opt.dict_size)) return SQFS_ERROR_CORRUPTED; if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) return SQFS_ERROR_UNSUPPORTED; xz->flags = opt.flags; xz->dict_size = opt.dict_size; return 0; } static sqfs_s32 compress(xz_compressor_t *xz, lzma_vli filter, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize, sqfs_u32 presets) { lzma_filter filters[5]; lzma_options_lzma opt; size_t written = 0; lzma_ret ret; int i = 0; if (lzma_lzma_preset(&opt, presets)) return SQFS_ERROR_COMPRESSOR; opt.lc = xz->lc; opt.lp = xz->lp; opt.pb = xz->pb; opt.dict_size = xz->dict_size; if (filter != LZMA_VLI_UNKNOWN) { filters[i].id = filter; filters[i].options = NULL; ++i; } filters[i].id = LZMA_FILTER_LZMA2; filters[i].options = &opt; ++i; filters[i].id = LZMA_VLI_UNKNOWN; filters[i].options = NULL; ++i; ret = lzma_stream_buffer_encode(filters, LZMA_CHECK_CRC32, NULL, in, size, out, &written, outsize); if (ret == LZMA_OK) return (written >= size) ? 0 : written; if (ret != LZMA_BUF_ERROR) return SQFS_ERROR_COMPRESSOR; return 0; } static lzma_vli flag_to_vli(int flag) { switch (flag) { case SQFS_COMP_FLAG_XZ_X86: return LZMA_FILTER_X86; case SQFS_COMP_FLAG_XZ_POWERPC: return LZMA_FILTER_POWERPC; case SQFS_COMP_FLAG_XZ_IA64: return LZMA_FILTER_IA64; case SQFS_COMP_FLAG_XZ_ARM: return LZMA_FILTER_ARM; case SQFS_COMP_FLAG_XZ_ARMTHUMB: return LZMA_FILTER_ARMTHUMB; case SQFS_COMP_FLAG_XZ_SPARC: return LZMA_FILTER_SPARC; default: break; } return LZMA_VLI_UNKNOWN; } static sqfs_s32 xz_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { xz_compressor_t *xz = (xz_compressor_t *)base; lzma_vli filter, selected = LZMA_VLI_UNKNOWN; sqfs_s32 ret, smallest; bool extreme; size_t i; if (size >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, xz->level); if (ret < 0 || xz->flags == 0) return ret; smallest = ret; extreme = false; if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, xz->level | LZMA_PRESET_EXTREME); if (ret > 0 && (smallest == 0 || ret < smallest)) { smallest = ret; extreme = true; } } for (i = 1; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { if ((i & SQFS_COMP_FLAG_XZ_EXTREME) || (xz->flags & i) == 0) continue; filter = flag_to_vli(i); ret = compress(xz, filter, in, size, out, outsize, xz->level); if (ret > 0 && (smallest == 0 || ret < smallest)) { smallest = ret; selected = filter; extreme = false; } if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { ret = compress(xz, filter, in, size, out, outsize, xz->level | LZMA_PRESET_EXTREME); if (ret > 0 && (smallest == 0 || ret < smallest)) { smallest = ret; selected = filter; extreme = true; } } } if (smallest == 0) return 0; return compress(xz, selected, in, size, out, outsize, xz->level | (extreme ? LZMA_PRESET_EXTREME : 0)); } static sqfs_s32 xz_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { sqfs_u64 memlimit = 65 * 1024 * 1024; size_t dest_pos = 0; size_t src_pos = 0; lzma_ret ret; (void)base; if (outsize >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; ret = lzma_stream_buffer_decode(&memlimit, 0, NULL, in, &src_pos, size, out, &dest_pos, outsize); if (ret == LZMA_OK && size == src_pos) return dest_pos; return SQFS_ERROR_COMPRESSOR; } static void xz_get_configuration(const sqfs_compressor_t *base, sqfs_compressor_config_t *cfg) { const xz_compressor_t *xz = (const xz_compressor_t *)base; memset(cfg, 0, sizeof(*cfg)); cfg->id = SQFS_COMP_XZ; cfg->flags = xz->flags; cfg->block_size = xz->block_size; cfg->level = xz->level; cfg->opt.xz.dict_size = xz->dict_size; cfg->opt.xz.lc = xz->lc; cfg->opt.xz.lp = xz->lp; cfg->opt.xz.pb = xz->pb; if (base->do_block == xz_uncomp_block) cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; } static sqfs_object_t *xz_create_copy(const sqfs_object_t *cmp) { xz_compressor_t *xz = malloc(sizeof(*xz)); if (xz == NULL) return NULL; memcpy(xz, cmp, sizeof(*xz)); return (sqfs_object_t *)xz; } static void xz_destroy(sqfs_object_t *base) { free(base); } int xz_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { sqfs_compressor_t *base; xz_compressor_t *xz; if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_XZ_ALL)) { return SQFS_ERROR_UNSUPPORTED; } if (!is_dict_size_valid(cfg->opt.xz.dict_size)) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.xz.lc + cfg->opt.xz.lp > 4) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.xz.pb > SQFS_XZ_MAX_PB) return SQFS_ERROR_UNSUPPORTED; if (cfg->level > SQFS_XZ_MAX_LEVEL) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.xz.dict_size < SQFS_XZ_MIN_DICT_SIZE) return SQFS_ERROR_UNSUPPORTED; if (cfg->opt.xz.dict_size > SQFS_XZ_MAX_DICT_SIZE) return SQFS_ERROR_UNSUPPORTED; xz = calloc(1, sizeof(*xz)); base = (sqfs_compressor_t *)xz; if (xz == NULL) return SQFS_ERROR_ALLOC; xz->flags = cfg->flags; xz->dict_size = cfg->opt.xz.dict_size; xz->block_size = cfg->block_size; xz->lc = cfg->opt.xz.lc; xz->lp = cfg->opt.xz.lp; xz->pb = cfg->opt.xz.pb; xz->level = cfg->level; base->get_configuration = xz_get_configuration; base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? xz_uncomp_block : xz_comp_block; base->write_options = xz_write_options; base->read_options = xz_read_options; ((sqfs_object_t *)base)->copy = xz_create_copy; ((sqfs_object_t *)base)->destroy = xz_destroy; *out = base; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/comp/zstd.c000066400000000000000000000072111410627516300207270ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * zstd.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include #include #include #include #include #include "internal.h" typedef struct { sqfs_compressor_t base; size_t block_size; ZSTD_CCtx *zctx; int level; } zstd_compressor_t; typedef struct { sqfs_u32 level; } zstd_options_t; static int zstd_write_options(sqfs_compressor_t *base, sqfs_file_t *file) { zstd_compressor_t *zstd = (zstd_compressor_t *)base; zstd_options_t opt; if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL) return 0; opt.level = htole32(zstd->level); return sqfs_generic_write_options(file, &opt, sizeof(opt)); } static int zstd_read_options(sqfs_compressor_t *base, sqfs_file_t *file) { zstd_options_t opt; int ret; (void)base; ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); if (ret) return ret; opt.level = le32toh(opt.level); return 0; } static sqfs_s32 zstd_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { zstd_compressor_t *zstd = (zstd_compressor_t *)base; size_t ret; if (size >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; ret = ZSTD_compressCCtx(zstd->zctx, out, outsize, in, size, zstd->level); if (ZSTD_isError(ret)) { if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) return 0; return SQFS_ERROR_COMPRESSOR; } return ret < size ? ret : 0; } static sqfs_s32 zstd_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { size_t ret; (void)base; if (outsize >= 0x7FFFFFFF) return SQFS_ERROR_ARG_INVALID; ret = ZSTD_decompress(out, outsize, in, size); if (ZSTD_isError(ret)) return SQFS_ERROR_COMPRESSOR; return ret; } static void zstd_get_configuration(const sqfs_compressor_t *base, sqfs_compressor_config_t *cfg) { const zstd_compressor_t *zstd = (const zstd_compressor_t *)base; memset(cfg, 0, sizeof(*cfg)); cfg->id = SQFS_COMP_ZSTD; cfg->block_size = zstd->block_size; cfg->level = zstd->level; if (base->do_block == zstd_uncomp_block) cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; } static sqfs_object_t *zstd_create_copy(const sqfs_object_t *cmp) { zstd_compressor_t *zstd = malloc(sizeof(*zstd)); if (zstd == NULL) return NULL; memcpy(zstd, cmp, sizeof(*zstd)); zstd->zctx = ZSTD_createCCtx(); if (zstd->zctx == NULL) { free(zstd); return NULL; } return (sqfs_object_t *)zstd; } static void zstd_destroy(sqfs_object_t *base) { zstd_compressor_t *zstd = (zstd_compressor_t *)base; ZSTD_freeCCtx(zstd->zctx); free(zstd); } int zstd_compressor_create(const sqfs_compressor_config_t *cfg, sqfs_compressor_t **out) { zstd_compressor_t *zstd; sqfs_compressor_t *base; if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) return SQFS_ERROR_UNSUPPORTED; if (cfg->level < 1 || cfg->level > (unsigned int)ZSTD_maxCLevel()) return SQFS_ERROR_UNSUPPORTED; zstd = calloc(1, sizeof(*zstd)); base = (sqfs_compressor_t *)zstd; if (zstd == NULL) return SQFS_ERROR_ALLOC; zstd->block_size = cfg->block_size; zstd->level = cfg->level; zstd->zctx = ZSTD_createCCtx(); if (zstd->zctx == NULL) { free(zstd); return SQFS_ERROR_COMPRESSOR; } base->get_configuration = zstd_get_configuration; base->do_block = cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS ? zstd_uncomp_block : zstd_comp_block; base->write_options = zstd_write_options; base->read_options = zstd_read_options; ((sqfs_object_t *)base)->copy = zstd_create_copy; ((sqfs_object_t *)base)->destroy = zstd_destroy; *out = base; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/data_reader.c000066400000000000000000000200221410627516300212330ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * data_reader.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/data_reader.h" #include "sqfs/compressor.h" #include "sqfs/frag_table.h" #include "sqfs/block.h" #include "sqfs/error.h" #include "sqfs/table.h" #include "sqfs/inode.h" #include "sqfs/io.h" #include "util.h" #include #include struct sqfs_data_reader_t { sqfs_object_t obj; sqfs_frag_table_t *frag_tbl; sqfs_compressor_t *cmp; sqfs_file_t *file; sqfs_u8 *data_block; size_t data_blk_size; sqfs_u64 current_block; sqfs_u8 *frag_block; size_t frag_blk_size; sqfs_u32 current_frag_index; sqfs_u32 block_size; sqfs_u8 scratch[]; }; static int get_block(sqfs_data_reader_t *data, sqfs_u64 off, sqfs_u32 size, sqfs_u32 max_size, size_t *out_sz, sqfs_u8 **out) { sqfs_u32 on_disk_size; sqfs_s32 ret; int err; *out = alloc_array(1, max_size); *out_sz = max_size; if (*out == NULL) { err = SQFS_ERROR_ALLOC; goto fail; } if (SQFS_IS_SPARSE_BLOCK(size)) return 0; on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size); if (on_disk_size > max_size) { err = SQFS_ERROR_OVERFLOW; goto fail; } if (SQFS_IS_BLOCK_COMPRESSED(size)) { err = data->file->read_at(data->file, off, data->scratch, on_disk_size); if (err) goto fail; ret = data->cmp->do_block(data->cmp, data->scratch, on_disk_size, *out, max_size); if (ret <= 0) { err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; goto fail; } *out_sz = ret; } else { err = data->file->read_at(data->file, off, *out, on_disk_size); if (err) goto fail; *out_sz = on_disk_size; } return 0; fail: free(*out); *out = NULL; *out_sz = 0; return err; } static int precache_data_block(sqfs_data_reader_t *data, sqfs_u64 location, sqfs_u32 size) { if (data->data_block != NULL && data->current_block == location) return 0; free(data->data_block); data->current_block = location; return get_block(data, location, size, data->block_size, &data->data_blk_size, &data->data_block); } static int precache_fragment_block(sqfs_data_reader_t *data, size_t idx) { sqfs_fragment_t ent; int ret; if (data->frag_block != NULL && idx == data->current_frag_index) return 0; ret = sqfs_frag_table_lookup(data->frag_tbl, idx, &ent); if (ret != 0) return ret; free(data->frag_block); data->current_frag_index = idx; return get_block(data, ent.start_offset, ent.size, data->block_size, &data->frag_blk_size, &data->frag_block); } static void data_reader_destroy(sqfs_object_t *obj) { sqfs_data_reader_t *data = (sqfs_data_reader_t *)obj; sqfs_destroy(data->frag_tbl); free(data->data_block); free(data->frag_block); free(data); } static sqfs_object_t *data_reader_copy(const sqfs_object_t *obj) { const sqfs_data_reader_t *data = (const sqfs_data_reader_t *)obj; sqfs_data_reader_t *copy; copy = alloc_flex(sizeof(*data), 1, data->block_size); if (copy == NULL) return NULL; memcpy(copy, data, sizeof(*data) + data->block_size); copy->frag_tbl = sqfs_copy(data->frag_tbl); if (copy->frag_tbl == NULL) goto fail_ftbl; if (data->data_block != NULL) { copy->data_block = malloc(data->data_blk_size); if (copy->data_block == NULL) goto fail_dblk; memcpy(copy->data_block, data->data_block, data->data_blk_size); } if (copy->frag_block != NULL) { copy->frag_block = malloc(copy->frag_blk_size); if (copy->frag_block == NULL) goto fail_fblk; memcpy(copy->frag_block, data->frag_block, data->frag_blk_size); } /* XXX: file and cmp aren't deep-copied becaues data doesn't own them either. */ return (sqfs_object_t *)copy; fail_fblk: free(copy->data_block); fail_dblk: sqfs_destroy(copy->frag_tbl); fail_ftbl: free(copy); return NULL; } sqfs_data_reader_t *sqfs_data_reader_create(sqfs_file_t *file, size_t block_size, sqfs_compressor_t *cmp, sqfs_u32 flags) { sqfs_data_reader_t *data; if (flags != 0) return NULL; data = alloc_flex(sizeof(*data), 1, block_size); if (data == NULL) return NULL; data->frag_tbl = sqfs_frag_table_create(0); if (data->frag_tbl == NULL) { free(data); return NULL; } ((sqfs_object_t *)data)->destroy = data_reader_destroy; ((sqfs_object_t *)data)->copy = data_reader_copy; data->file = file; data->block_size = block_size; data->cmp = cmp; return data; } int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data, const sqfs_super_t *super) { int ret; free(data->frag_block); data->frag_block = NULL; data->current_frag_index = 0; ret = sqfs_frag_table_read(data->frag_tbl, data->file, super, data->cmp); if (ret != 0) return ret; data->current_frag_index = sqfs_frag_table_get_size(data->frag_tbl); return 0; } int sqfs_data_reader_get_block(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, size_t index, size_t *size, sqfs_u8 **out) { size_t i, unpacked_size; sqfs_u64 off, filesz; sqfs_inode_get_file_block_start(inode, &off); sqfs_inode_get_file_size(inode, &filesz); if (index >= sqfs_inode_get_file_block_count(inode)) return SQFS_ERROR_OUT_OF_BOUNDS; for (i = 0; i < index; ++i) { off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); filesz -= data->block_size; } unpacked_size = filesz < data->block_size ? filesz : data->block_size; return get_block(data, off, inode->extra[index], unpacked_size, size, out); } int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, size_t *size, sqfs_u8 **out) { sqfs_u32 frag_idx, frag_off, frag_sz; size_t block_count; sqfs_u64 filesz; int err; sqfs_inode_get_file_size(inode, &filesz); sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); *size = 0; *out = NULL; block_count = sqfs_inode_get_file_block_count(inode); if (block_count * data->block_size >= filesz) return 0; frag_sz = filesz % data->block_size; err = precache_fragment_block(data, frag_idx); if (err) return err; if (frag_off + frag_sz > data->block_size) return SQFS_ERROR_OUT_OF_BOUNDS; *out = alloc_array(1, frag_sz); if (*out == NULL) return SQFS_ERROR_ALLOC; *size = frag_sz; memcpy(*out, (char *)data->frag_block + frag_off, frag_sz); return 0; } sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, sqfs_u64 offset, void *buffer, sqfs_u32 size) { sqfs_u32 frag_idx, frag_off, diff, total = 0; size_t i, block_count; sqfs_u64 off, filesz; char *ptr; int err; if (size >= 0x7FFFFFFF) size = 0x7FFFFFFE; /* work out file location and size */ sqfs_inode_get_file_size(inode, &filesz); sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); sqfs_inode_get_file_block_start(inode, &off); block_count = sqfs_inode_get_file_block_count(inode); if (offset >= filesz) return 0; if ((filesz - offset) < (sqfs_u64)size) size = filesz - offset; if (size == 0) return 0; /* find location of the first block */ for (i = 0; offset > data->block_size && i < block_count; ++i) { off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); offset -= data->block_size; } /* copy data from blocks */ while (i < block_count && size > 0) { diff = data->block_size - offset; if (size < diff) diff = size; if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { memset(buffer, 0, diff); } else { err = precache_data_block(data, off, inode->extra[i]); if (err) return err; memcpy(buffer, (char *)data->data_block + offset, diff); off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); } ++i; offset = 0; size -= diff; total += diff; buffer = (char *)buffer + diff; } /* copy from fragment */ if (size > 0) { err = precache_fragment_block(data, frag_idx); if (err) return err; if ((frag_off + offset) >= data->frag_blk_size) return SQFS_ERROR_OUT_OF_BOUNDS; if ((data->frag_blk_size - (frag_off + offset)) < size) return SQFS_ERROR_OUT_OF_BOUNDS; ptr = (char *)data->frag_block + frag_off + offset; memcpy(buffer, ptr, size); total += size; } return total; } squashfs-tools-ng-1.1.3/lib/sqfs/dir_reader.c000066400000000000000000000150301410627516300211030ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * fs_reader.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_reader.h" #include "sqfs/dir_reader.h" #include "sqfs/compressor.h" #include "sqfs/super.h" #include "sqfs/inode.h" #include "sqfs/error.h" #include "sqfs/dir.h" #include "util.h" #include #include struct sqfs_dir_reader_t { sqfs_object_t base; sqfs_meta_reader_t *meta_dir; sqfs_meta_reader_t *meta_inode; const sqfs_super_t *super; sqfs_dir_header_t hdr; sqfs_u64 dir_block_start; size_t entries; size_t size; size_t start_size; sqfs_u16 dir_offset; sqfs_u16 inode_offset; }; static void dir_reader_destroy(sqfs_object_t *obj) { sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; sqfs_destroy(rd->meta_inode); sqfs_destroy(rd->meta_dir); free(rd); } static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) { const sqfs_dir_reader_t *rd = (const sqfs_dir_reader_t *)obj; sqfs_dir_reader_t *copy = malloc(sizeof(*copy)); if (copy == NULL) return NULL; memcpy(copy, rd, sizeof(*copy)); copy->meta_inode = sqfs_copy(rd->meta_inode); if (copy->meta_inode == NULL) goto fail_mino; copy->meta_dir = sqfs_copy(rd->meta_dir); if (copy->meta_dir == NULL) goto fail_mdir; return (sqfs_object_t *)copy; fail_mdir: sqfs_destroy(copy->meta_inode); fail_mino: free(copy); return NULL; } sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, sqfs_compressor_t *cmp, sqfs_file_t *file, sqfs_u32 flags) { sqfs_dir_reader_t *rd; sqfs_u64 start, limit; if (flags != 0) return NULL; rd = calloc(1, sizeof(*rd)); if (rd == NULL) return NULL; start = super->inode_table_start; limit = super->directory_table_start; rd->meta_inode = sqfs_meta_reader_create(file, cmp, start, limit); if (rd->meta_inode == NULL) { free(rd); return NULL; } start = super->directory_table_start; limit = super->id_table_start; if (super->fragment_table_start < limit) limit = super->fragment_table_start; if (super->export_table_start < limit) limit = super->export_table_start; rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit); if (rd->meta_dir == NULL) { sqfs_destroy(rd->meta_inode); free(rd); return NULL; } ((sqfs_object_t *)rd)->destroy = dir_reader_destroy; ((sqfs_object_t *)rd)->copy = dir_reader_copy; rd->super = super; return rd; } int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *inode, sqfs_u32 flags) { sqfs_u64 block_start; size_t size, offset; if (flags != 0) return SQFS_ERROR_UNSUPPORTED; if (inode->base.type == SQFS_INODE_DIR) { size = inode->data.dir.size; offset = inode->data.dir.offset; block_start = inode->data.dir.start_block; } else if (inode->base.type == SQFS_INODE_EXT_DIR) { size = inode->data.dir_ext.size; offset = inode->data.dir_ext.offset; block_start = inode->data.dir_ext.start_block; } else { return SQFS_ERROR_NOT_DIR; } memset(&rd->hdr, 0, sizeof(rd->hdr)); rd->size = size; rd->entries = 0; if (rd->size <= sizeof(rd->hdr)) return 0; block_start += rd->super->directory_table_start; rd->dir_block_start = block_start; rd->dir_offset = offset; rd->start_size = size; return sqfs_meta_reader_seek(rd->meta_dir, block_start, offset); } int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) { sqfs_dir_entry_t *ent; size_t count; int err; if (!rd->entries) { if (rd->size < sizeof(rd->hdr)) return 1; err = sqfs_meta_reader_read_dir_header(rd->meta_dir, &rd->hdr); if (err) return err; rd->size -= sizeof(rd->hdr); rd->entries = rd->hdr.count + 1; } err = sqfs_meta_reader_read_dir_ent(rd->meta_dir, &ent); if (err) return err; count = sizeof(*ent) + strlen((const char *)ent->name); if (count > rd->size) { rd->size = 0; rd->entries = 0; } else { rd->size -= count; rd->entries -= 1; } rd->inode_offset = ent->offset; *out = ent; return 0; } int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) { memset(&rd->hdr, 0, sizeof(rd->hdr)); rd->size = rd->start_size; rd->entries = 0; return sqfs_meta_reader_seek(rd->meta_dir, rd->dir_block_start, rd->dir_offset); } int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) { sqfs_dir_entry_t *ent; int ret; if (rd->size != rd->start_size) { ret = sqfs_dir_reader_rewind(rd); if (ret) return ret; } do { ret = sqfs_dir_reader_read(rd, &ent); if (ret < 0) return ret; if (ret > 0) return SQFS_ERROR_NO_ENTRY; ret = strcmp((const char *)ent->name, name); free(ent); } while (ret < 0); return ret == 0 ? 0 : SQFS_ERROR_NO_ENTRY; } int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, sqfs_inode_generic_t **inode) { sqfs_u64 block_start; block_start = rd->hdr.start_block; return sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, block_start, rd->inode_offset, inode); } int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, sqfs_inode_generic_t **inode) { sqfs_u64 block_start = rd->super->root_inode_ref >> 16; sqfs_u16 offset = rd->super->root_inode_ref & 0xFFFF; return sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, block_start, offset, inode); } int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *start, const char *path, sqfs_inode_generic_t **out) { sqfs_inode_generic_t *inode; sqfs_dir_entry_t *ent; const char *ptr; int ret = 0; if (start == NULL) { ret = sqfs_dir_reader_get_root_inode(rd, &inode); } else { inode = alloc_flex(sizeof(*inode), 1, start->payload_bytes_used); if (inode == NULL) { ret = SQFS_ERROR_ALLOC; } else { memcpy(inode, start, sizeof(*start) + start->payload_bytes_used); } } if (ret) return ret; while (*path != '\0') { if (*path == '/') { while (*path == '/') ++path; continue; } ret = sqfs_dir_reader_open_dir(rd, inode, 0); free(inode); if (ret) return ret; ptr = strchr(path, '/'); if (ptr == NULL) { if (ptr == NULL) { for (ptr = path; *ptr != '\0'; ++ptr) ; } } do { ret = sqfs_dir_reader_read(rd, &ent); if (ret < 0) return ret; if (ret == 0) { ret = strncmp((const char *)ent->name, path, ptr - path); if (ret == 0) ret = ent->name[ptr - path]; free(ent); } } while (ret < 0); if (ret > 0) return SQFS_ERROR_NO_ENTRY; ret = sqfs_dir_reader_get_inode(rd, &inode); if (ret) return ret; path = ptr; } *out = inode; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/dir_writer.c000066400000000000000000000233341410627516300211630ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * dir_writer.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_writer.h" #include "sqfs/dir_writer.h" #include "sqfs/super.h" #include "sqfs/table.h" #include "sqfs/inode.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/dir.h" #include "array.h" #include "util.h" #include #include typedef struct dir_entry_t { struct dir_entry_t *next; sqfs_u64 inode_ref; sqfs_u32 inode_num; sqfs_u16 type; size_t name_len; char name[]; } dir_entry_t; typedef struct index_ent_t { struct index_ent_t *next; dir_entry_t *ent; sqfs_u64 block; sqfs_u32 index; } index_ent_t; struct sqfs_dir_writer_t { sqfs_object_t base; dir_entry_t *list; dir_entry_t *list_end; index_ent_t *idx; index_ent_t *idx_end; sqfs_u64 dir_ref; size_t dir_size; size_t ent_count; sqfs_meta_writer_t *dm; array_t export_tbl; }; static int get_type(sqfs_u16 mode) { switch (mode & S_IFMT) { case S_IFSOCK: return SQFS_INODE_SOCKET; case S_IFIFO: return SQFS_INODE_FIFO; case S_IFLNK: return SQFS_INODE_SLINK; case S_IFBLK: return SQFS_INODE_BDEV; case S_IFCHR: return SQFS_INODE_CDEV; case S_IFDIR: return SQFS_INODE_DIR; case S_IFREG: return SQFS_INODE_FILE; default: break; } return SQFS_ERROR_UNSUPPORTED; } static void writer_reset(sqfs_dir_writer_t *writer) { dir_entry_t *ent; index_ent_t *idx; while (writer->idx != NULL) { idx = writer->idx; writer->idx = idx->next; free(idx); } while (writer->list != NULL) { ent = writer->list; writer->list = ent->next; free(ent); } writer->list_end = NULL; writer->idx_end = NULL; writer->dir_ref = 0; writer->dir_size = 0; writer->ent_count = 0; } static int add_export_table_entry(sqfs_dir_writer_t *writer, sqfs_u32 inum, sqfs_u64 iref) { sqfs_u64 *ptr; int ret; if (writer->export_tbl.data == NULL) return 0; if (inum < 1) return SQFS_ERROR_ARG_INVALID; ret = array_set_capacity(&writer->export_tbl, inum); if (ret != 0) return ret; ptr = (sqfs_u64 *)writer->export_tbl.data; if ((inum - 1) >= writer->export_tbl.used) { memset(ptr + writer->export_tbl.used, 0xFF, (inum - writer->export_tbl.used) * sizeof(*ptr)); writer->export_tbl.used = inum; } ptr[inum - 1] = iref; return 0; } static void dir_writer_destroy(sqfs_object_t *obj) { sqfs_dir_writer_t *writer = (sqfs_dir_writer_t *)obj; writer_reset(writer); array_cleanup(&writer->export_tbl); free(writer); } sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, sqfs_u32 flags) { sqfs_dir_writer_t *writer; if (flags & ~SQFS_DIR_WRITER_CREATE_ALL_FLAGS) return NULL; writer = calloc(1, sizeof(*writer)); if (writer == NULL) return NULL; if (flags & SQFS_DIR_WRITER_CREATE_EXPORT_TABLE) { if (array_init(&writer->export_tbl, sizeof(sqfs_u64), 512)) { free(writer); return NULL; } memset(writer->export_tbl.data, 0xFF, writer->export_tbl.size * writer->export_tbl.count); } ((sqfs_object_t *)writer)->destroy = dir_writer_destroy; writer->dm = dm; return writer; } int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer, sqfs_u32 flags) { sqfs_u32 offset; sqfs_u64 block; if (flags != 0) return SQFS_ERROR_UNSUPPORTED; writer_reset(writer); sqfs_meta_writer_get_position(writer->dm, &block, &offset); writer->dir_ref = (block << 16) | offset; return 0; } int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, sqfs_u32 inode_num, sqfs_u64 inode_ref, sqfs_u16 mode) { dir_entry_t *ent; int type, err; type = get_type(mode); if (type < 0) return type; if (name[0] == '\0' || inode_num < 1) return SQFS_ERROR_ARG_INVALID; err = add_export_table_entry(writer, inode_num, inode_ref); if (err) return err; ent = alloc_flex(sizeof(*ent), 1, strlen(name)); if (ent == NULL) return SQFS_ERROR_ALLOC; ent->inode_ref = inode_ref; ent->inode_num = inode_num; ent->type = type; ent->name_len = strlen(name); memcpy(ent->name, name, ent->name_len); if (writer->list_end == NULL) { writer->list = writer->list_end = ent; } else { writer->list_end->next = ent; writer->list_end = ent; } writer->ent_count += 1; return 0; } static size_t get_conseq_entry_count(sqfs_u32 offset, dir_entry_t *head) { size_t size, count = 0; dir_entry_t *it; sqfs_s32 diff; size = (offset + sizeof(sqfs_dir_header_t)) % SQFS_META_BLOCK_SIZE; for (it = head; it != NULL; it = it->next) { if ((it->inode_ref >> 16) != (head->inode_ref >> 16)) break; diff = it->inode_num - head->inode_num; if (diff > 32767 || diff < -32767) break; size += sizeof(sqfs_dir_entry_t) + it->name_len; if (count > 0 && size > SQFS_META_BLOCK_SIZE) break; count += 1; if (count == SQFS_MAX_DIR_ENT) break; } return count; } static int add_header(sqfs_dir_writer_t *writer, size_t count, dir_entry_t *ref, sqfs_u64 block) { sqfs_dir_header_t hdr; index_ent_t *idx; int err; hdr.count = htole32(count - 1); hdr.start_block = htole32(ref->inode_ref >> 16); hdr.inode_number = htole32(ref->inode_num); err = sqfs_meta_writer_append(writer->dm, &hdr, sizeof(hdr)); if (err) return err; idx = calloc(1, sizeof(*idx)); if (idx == NULL) return SQFS_ERROR_ALLOC; idx->ent = ref; idx->block = block; idx->index = writer->dir_size; if (writer->idx_end == NULL) { writer->idx = writer->idx_end = idx; } else { writer->idx_end->next = idx; writer->idx_end = idx; } writer->dir_size += sizeof(hdr); return 0; } int sqfs_dir_writer_end(sqfs_dir_writer_t *writer) { dir_entry_t *it, *first; sqfs_dir_entry_t ent; sqfs_u16 *diff_u16; size_t i, count; sqfs_u32 offset; sqfs_u64 block; int err; for (it = writer->list; it != NULL; ) { sqfs_meta_writer_get_position(writer->dm, &block, &offset); count = get_conseq_entry_count(offset, it); err = add_header(writer, count, it, block); if (err) return err; first = it; for (i = 0; i < count; ++i) { ent.offset = htole16(it->inode_ref & 0x0000FFFF); ent.inode_diff = it->inode_num - first->inode_num; ent.type = htole16(it->type); ent.size = htole16(it->name_len - 1); diff_u16 = (sqfs_u16 *)&ent.inode_diff; *diff_u16 = htole16(*diff_u16); err = sqfs_meta_writer_append(writer->dm, &ent, sizeof(ent)); if (err) return err; err = sqfs_meta_writer_append(writer->dm, it->name, it->name_len); if (err) return err; writer->dir_size += sizeof(ent) + it->name_len; it = it->next; } } return 0; } size_t sqfs_dir_writer_get_size(const sqfs_dir_writer_t *writer) { return writer->dir_size; } sqfs_u64 sqfs_dir_writer_get_dir_reference(const sqfs_dir_writer_t *writer) { return writer->dir_ref; } size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer) { size_t index_size = 0; index_ent_t *idx; for (idx = writer->idx; idx != NULL; idx = idx->next) index_size += sizeof(sqfs_dir_index_t) + idx->ent->name_len; return index_size; } size_t sqfs_dir_writer_get_entry_count(const sqfs_dir_writer_t *writer) { return writer->ent_count; } sqfs_inode_generic_t *sqfs_dir_writer_create_inode(const sqfs_dir_writer_t *writer, size_t hlinks, sqfs_u32 xattr, sqfs_u32 parent_ino) { sqfs_inode_generic_t *inode; sqfs_dir_index_t ent; sqfs_u64 start_block; sqfs_u16 block_offset; size_t index_size; index_ent_t *idx; sqfs_u8 *ptr; index_size = 0; for (idx = writer->idx; idx != NULL; idx = idx->next) index_size += sizeof(ent) + idx->ent->name_len; inode = alloc_flex(sizeof(*inode), 1, index_size); if (inode == NULL) return NULL; inode->payload_bytes_available = index_size; start_block = writer->dir_ref >> 16; block_offset = writer->dir_ref & 0xFFFF; if (xattr != 0xFFFFFFFF || start_block > 0xFFFFFFFFUL || writer->dir_size > (0xFFFF - 3)) { inode->base.type = SQFS_INODE_EXT_DIR; } else { inode->base.type = SQFS_INODE_DIR; } if (inode->base.type == SQFS_INODE_DIR) { inode->data.dir.start_block = start_block; inode->data.dir.nlink = writer->ent_count + hlinks + 2; inode->data.dir.size = writer->dir_size + 3; inode->data.dir.offset = block_offset; inode->data.dir.parent_inode = parent_ino; } else { inode->data.dir_ext.nlink = writer->ent_count + hlinks + 2; inode->data.dir_ext.size = writer->dir_size + 3; inode->data.dir_ext.start_block = start_block; inode->data.dir_ext.parent_inode = parent_ino; inode->data.dir_ext.offset = block_offset; inode->data.dir_ext.xattr_idx = xattr; inode->data.dir_ext.inodex_count = 0; inode->payload_bytes_used = 0; for (idx = writer->idx; idx != NULL; idx = idx->next) { memset(&ent, 0, sizeof(ent)); ent.start_block = idx->block; ent.index = idx->index; ent.size = idx->ent->name_len - 1; ptr = (sqfs_u8 *)inode->extra + inode->payload_bytes_used; memcpy(ptr, &ent, sizeof(ent)); memcpy(ptr + sizeof(ent), idx->ent->name, idx->ent->name_len); inode->data.dir_ext.inodex_count += 1; inode->payload_bytes_used += sizeof(ent); inode->payload_bytes_used += idx->ent->name_len; } } return inode; } int sqfs_dir_writer_write_export_table(sqfs_dir_writer_t *writer, sqfs_file_t *file, sqfs_compressor_t *cmp, sqfs_u32 root_inode_num, sqfs_u64 root_inode_ref, sqfs_super_t *super) { sqfs_u64 start; size_t size; int ret; ret = add_export_table_entry(writer, root_inode_num, root_inode_ref); if (ret) return 0; if (writer->export_tbl.data == NULL) return 0; size = writer->export_tbl.size * writer->export_tbl.used; ret = sqfs_write_table(file, cmp, writer->export_tbl.data, size, &start); if (ret) return ret; super->export_table_start = start; super->flags |= SQFS_FLAG_EXPORTABLE; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/frag_table.c000066400000000000000000000111431410627516300210720ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * frag_table.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/frag_table.h" #include "sqfs/super.h" #include "sqfs/table.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "compat.h" #include "array.h" #include #include struct sqfs_frag_table_t { sqfs_object_t base; array_t table; }; static void frag_table_destroy(sqfs_object_t *obj) { sqfs_frag_table_t *tbl = (sqfs_frag_table_t *)obj; array_cleanup(&tbl->table); free(tbl); } static sqfs_object_t *frag_table_copy(const sqfs_object_t *obj) { const sqfs_frag_table_t *tbl = (const sqfs_frag_table_t *)obj; sqfs_frag_table_t *copy = calloc(1, sizeof(*copy)); if (copy == NULL) return NULL; if (array_init_copy(©->table, &tbl->table)) { free(copy); return NULL; } return (sqfs_object_t *)copy; } sqfs_frag_table_t *sqfs_frag_table_create(sqfs_u32 flags) { sqfs_frag_table_t *tbl; if (flags != 0) return NULL; tbl = calloc(1, sizeof(*tbl)); if (tbl == NULL) return NULL; array_init(&tbl->table, sizeof(sqfs_fragment_t), 0); ((sqfs_object_t *)tbl)->copy = frag_table_copy; ((sqfs_object_t *)tbl)->destroy = frag_table_destroy; return tbl; } int sqfs_frag_table_read(sqfs_frag_table_t *tbl, sqfs_file_t *file, const sqfs_super_t *super, sqfs_compressor_t *cmp) { sqfs_u64 location, lower, upper; void *raw = NULL; size_t size; int err; array_cleanup(&tbl->table); tbl->table.size = sizeof(sqfs_fragment_t); if (super->flags & SQFS_FLAG_NO_FRAGMENTS) return 0; if (super->fragment_table_start == 0xFFFFFFFFFFFFFFFFUL) return 0; if (super->fragment_entry_count == 0) return 0; if (super->fragment_table_start >= super->bytes_used) return SQFS_ERROR_OUT_OF_BOUNDS; /* location must be after inode & directory table, but before the ID table */ if (super->fragment_table_start < super->directory_table_start) return SQFS_ERROR_CORRUPTED; if (super->fragment_table_start >= super->id_table_start) return SQFS_ERROR_CORRUPTED; location = super->fragment_table_start; lower = super->directory_table_start; upper = super->id_table_start; if (super->export_table_start < super->id_table_start) upper = super->export_table_start; if (SZ_MUL_OV(super->fragment_entry_count, sizeof(sqfs_fragment_t), &size)) { return SQFS_ERROR_OVERFLOW; } err = sqfs_read_table(file, cmp, size, location, lower, upper, &raw); if (err) { free(raw); return err; } tbl->table.data = raw; tbl->table.count = super->fragment_entry_count; tbl->table.used = super->fragment_entry_count; return 0; } int sqfs_frag_table_write(sqfs_frag_table_t *tbl, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp) { size_t i; int err; if (tbl->table.used == 0) { super->fragment_table_start = 0xFFFFFFFFFFFFFFFF; super->flags |= SQFS_FLAG_NO_FRAGMENTS; super->flags &= ~SQFS_FLAG_ALWAYS_FRAGMENTS; super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; return 0; } err = sqfs_write_table(file, cmp, tbl->table.data, tbl->table.size * tbl->table.used, &super->fragment_table_start); if (err) return err; super->fragment_entry_count = tbl->table.used; super->flags &= ~SQFS_FLAG_NO_FRAGMENTS; super->flags |= SQFS_FLAG_ALWAYS_FRAGMENTS; super->flags |= SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; for (i = 0; i < tbl->table.used; ++i) { sqfs_u32 sz = ((sqfs_fragment_t *)tbl->table.data)[i].size; if (SQFS_IS_BLOCK_COMPRESSED(le32toh(sz))) { super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; break; } } return 0; } int sqfs_frag_table_lookup(sqfs_frag_table_t *tbl, sqfs_u32 index, sqfs_fragment_t *out) { sqfs_fragment_t *frag = array_get(&tbl->table, index); if (frag == NULL) return SQFS_ERROR_OUT_OF_BOUNDS; out->start_offset = le64toh(frag->start_offset); out->size = le32toh(frag->size); out->pad0 = le32toh(frag->pad0); return 0; } int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, sqfs_u32 size, sqfs_u32 *index) { sqfs_fragment_t frag; if (index != NULL) *index = tbl->table.used; memset(&frag, 0, sizeof(frag)); frag.start_offset = htole64(location); frag.size = htole32(size); return array_append(&tbl->table, &frag); } int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, sqfs_u64 location, sqfs_u32 size) { sqfs_fragment_t frag; memset(&frag, 0, sizeof(frag)); frag.start_offset = htole64(location); frag.size = htole32(size); return array_set(&tbl->table, index, &frag); } size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl) { return tbl->table.used; } squashfs-tools-ng-1.1.3/lib/sqfs/id_table.c000066400000000000000000000067011410627516300205530ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * id_table.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/id_table.h" #include "sqfs/super.h" #include "sqfs/table.h" #include "sqfs/error.h" #include "compat.h" #include "array.h" #include #include struct sqfs_id_table_t { sqfs_object_t base; array_t ids; }; static void id_table_destroy(sqfs_object_t *obj) { sqfs_id_table_t *tbl = (sqfs_id_table_t *)obj; array_cleanup(&tbl->ids); free(tbl); } static sqfs_object_t *id_table_copy(const sqfs_object_t *obj) { const sqfs_id_table_t *tbl = (const sqfs_id_table_t *)obj; sqfs_id_table_t *copy = calloc(1, sizeof(*copy)); if (copy == NULL) return NULL; if (array_init_copy(©->ids, &tbl->ids) != 0) { free(copy); return NULL; } return (sqfs_object_t *)copy; } sqfs_id_table_t *sqfs_id_table_create(sqfs_u32 flags) { sqfs_id_table_t *tbl; if (flags != 0) return NULL; tbl = calloc(1, sizeof(sqfs_id_table_t)); if (tbl != NULL) { array_init(&tbl->ids, sizeof(sqfs_u32), 0); ((sqfs_object_t *)tbl)->destroy = id_table_destroy; ((sqfs_object_t *)tbl)->copy = id_table_copy; } return tbl; } int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, sqfs_u32 id, sqfs_u16 *out) { size_t i; for (i = 0; i < tbl->ids.used; ++i) { if (((sqfs_u32 *)tbl->ids.data)[i] == id) { *out = i; return 0; } } if (tbl->ids.used == 0x10000) return SQFS_ERROR_OVERFLOW; *out = tbl->ids.used; return array_append(&tbl->ids, &id); } int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, sqfs_u16 index, sqfs_u32 *out) { if (index >= tbl->ids.used) return SQFS_ERROR_OUT_OF_BOUNDS; *out = ((sqfs_u32 *)tbl->ids.data)[index]; return 0; } int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, const sqfs_super_t *super, sqfs_compressor_t *cmp) { sqfs_u64 upper_limit, lower_limit; void *raw_ids; size_t i; int ret; if (!super->id_count || super->id_table_start >= super->bytes_used) return SQFS_ERROR_CORRUPTED; upper_limit = super->id_table_start; lower_limit = super->directory_table_start; if (super->fragment_table_start > lower_limit && super->fragment_table_start < upper_limit) { lower_limit = super->fragment_table_start; } if (super->export_table_start > lower_limit && super->export_table_start < upper_limit) { lower_limit = super->export_table_start; } array_cleanup(&tbl->ids); tbl->ids.size = sizeof(sqfs_u32); ret = sqfs_read_table(file, cmp, super->id_count * sizeof(sqfs_u32), super->id_table_start, lower_limit, upper_limit, &raw_ids); if (ret) return ret; for (i = 0; i < super->id_count; ++i) ((sqfs_u32 *)raw_ids)[i] = le32toh(((sqfs_u32 *)raw_ids)[i]); tbl->ids.data = raw_ids; tbl->ids.used = super->id_count; tbl->ids.count = super->id_count; return 0; } int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp) { sqfs_u64 start; size_t i; int ret; for (i = 0; i < tbl->ids.used; ++i) { ((sqfs_u32 *)tbl->ids.data)[i] = htole32(((sqfs_u32 *)tbl->ids.data)[i]); } super->id_count = tbl->ids.used; ret = sqfs_write_table(file, cmp, tbl->ids.data, sizeof(sqfs_u32) * tbl->ids.used, &start); super->id_table_start = start; for (i = 0; i < tbl->ids.used; ++i) { ((sqfs_u32 *)tbl->ids.data)[i] = le32toh(((sqfs_u32 *)tbl->ids.data)[i]); } return ret; } squashfs-tools-ng-1.1.3/lib/sqfs/inode.c000066400000000000000000000220041410627516300201000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * inode.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/inode.h" #include "sqfs/error.h" #include "sqfs/dir.h" #include #include #include "util.h" static int inverse_type[] = { [SQFS_INODE_DIR] = SQFS_INODE_EXT_DIR, [SQFS_INODE_FILE] = SQFS_INODE_EXT_FILE, [SQFS_INODE_SLINK] = SQFS_INODE_EXT_SLINK, [SQFS_INODE_BDEV] = SQFS_INODE_EXT_BDEV, [SQFS_INODE_CDEV] = SQFS_INODE_EXT_CDEV, [SQFS_INODE_FIFO] = SQFS_INODE_EXT_FIFO, [SQFS_INODE_SOCKET] = SQFS_INODE_EXT_SOCKET, [SQFS_INODE_EXT_DIR] = SQFS_INODE_DIR, [SQFS_INODE_EXT_FILE] = SQFS_INODE_FILE, [SQFS_INODE_EXT_SLINK] = SQFS_INODE_SLINK, [SQFS_INODE_EXT_BDEV] = SQFS_INODE_BDEV, [SQFS_INODE_EXT_CDEV] = SQFS_INODE_CDEV, [SQFS_INODE_EXT_FIFO] = SQFS_INODE_FIFO, [SQFS_INODE_EXT_SOCKET] = SQFS_INODE_SOCKET, }; int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, sqfs_u32 *out) { switch (inode->base.type) { case SQFS_INODE_DIR: case SQFS_INODE_FILE: case SQFS_INODE_SLINK: case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: *out = 0xFFFFFFFF; break; case SQFS_INODE_EXT_DIR: *out = inode->data.dir_ext.xattr_idx; break; case SQFS_INODE_EXT_FILE: *out = inode->data.file_ext.xattr_idx; break; case SQFS_INODE_EXT_SLINK: *out = inode->data.slink_ext.xattr_idx; break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: *out = inode->data.dev_ext.xattr_idx; break; case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: *out = inode->data.ipc_ext.xattr_idx; break; default: return SQFS_ERROR_CORRUPTED; } return 0; } int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index) { int err; if (index != 0xFFFFFFFF) { err = sqfs_inode_make_extended(inode); if (err) return err; } switch (inode->base.type) { case SQFS_INODE_DIR: case SQFS_INODE_FILE: case SQFS_INODE_SLINK: case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: break; case SQFS_INODE_EXT_DIR: inode->data.dir_ext.xattr_idx = index; break; case SQFS_INODE_EXT_FILE: inode->data.file_ext.xattr_idx = index; break; case SQFS_INODE_EXT_SLINK: inode->data.slink_ext.xattr_idx = index; break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: inode->data.dev_ext.xattr_idx = index; break; case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: inode->data.ipc_ext.xattr_idx = index; break; default: return SQFS_ERROR_CORRUPTED; } if (index == 0xFFFFFFFF) { err = sqfs_inode_make_basic(inode); if (err) return err; } return 0; } int sqfs_inode_make_extended(sqfs_inode_generic_t *inode) { switch (inode->base.type) { case SQFS_INODE_DIR: { sqfs_inode_dir_ext_t temp = { .nlink = inode->data.dir.nlink, .size = inode->data.dir.size, .start_block = inode->data.dir.start_block, .parent_inode = inode->data.dir.parent_inode, .inodex_count = 0, .offset = inode->data.dir.offset, .xattr_idx = 0xFFFFFFFF, }; inode->data.dir_ext = temp; break; } case SQFS_INODE_FILE: { sqfs_inode_file_ext_t temp = { .blocks_start = inode->data.file.blocks_start, .file_size = inode->data.file.file_size, .sparse = 0, .nlink = 1, .fragment_idx = inode->data.file.fragment_index, .fragment_offset = inode->data.file.fragment_offset, .xattr_idx = 0xFFFFFFFF, }; inode->data.file_ext = temp; break; } case SQFS_INODE_SLINK: inode->data.slink_ext.xattr_idx = 0xFFFFFFFF; break; case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; break; case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; break; case SQFS_INODE_EXT_DIR: case SQFS_INODE_EXT_FILE: case SQFS_INODE_EXT_SLINK: case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: return 0; default: return SQFS_ERROR_CORRUPTED; } inode->base.type = inverse_type[inode->base.type]; return 0; } int sqfs_inode_make_basic(sqfs_inode_generic_t *inode) { sqfs_u32 xattr; int err; err = sqfs_inode_get_xattr_index(inode, &xattr); if (err != 0 || xattr != 0xFFFFFFFF) return err; switch (inode->base.type) { case SQFS_INODE_DIR: case SQFS_INODE_FILE: case SQFS_INODE_SLINK: case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: return 0; case SQFS_INODE_EXT_DIR: { sqfs_inode_dir_t temp = { .start_block = inode->data.dir_ext.start_block, .nlink = inode->data.dir_ext.nlink, .size = inode->data.dir_ext.size, .offset = inode->data.dir_ext.offset, .parent_inode = inode->data.dir_ext.parent_inode, }; if (inode->data.dir_ext.size > 0x0FFFF) return 0; inode->data.dir = temp; break; } case SQFS_INODE_EXT_FILE: { sqfs_inode_file_t temp = { .blocks_start = inode->data.file_ext.blocks_start, .fragment_index = inode->data.file_ext.fragment_idx, .fragment_offset = inode->data.file_ext.fragment_offset, .file_size = inode->data.file_ext.file_size, }; if (inode->data.file_ext.blocks_start > 0x0FFFFFFFFUL) return 0; if (inode->data.file_ext.file_size > 0x0FFFFFFFFUL) return 0; if (inode->data.file_ext.sparse > 0) return 0; if (inode->data.file_ext.nlink > 1) return 0; inode->data.file = temp; break; } case SQFS_INODE_EXT_SLINK: case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: break; default: return SQFS_ERROR_CORRUPTED; } inode->base.type = inverse_type[inode->base.type]; return 0; } int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size) { if (inode->base.type == SQFS_INODE_EXT_FILE) { inode->data.file_ext.file_size = size; if (size < 0x0FFFFFFFFUL) sqfs_inode_make_basic(inode); } else if (inode->base.type == SQFS_INODE_FILE) { if (size > 0x0FFFFFFFFUL) { sqfs_inode_make_extended(inode); inode->data.file_ext.file_size = size; } else { inode->data.file.file_size = size; } } else { return SQFS_ERROR_NOT_FILE; } return 0; } int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, sqfs_u32 index, sqfs_u32 offset) { if (inode->base.type == SQFS_INODE_EXT_FILE) { inode->data.file_ext.fragment_idx = index; inode->data.file_ext.fragment_offset = offset; } else if (inode->base.type == SQFS_INODE_FILE) { inode->data.file.fragment_index = index; inode->data.file.fragment_offset = offset; } else { return SQFS_ERROR_NOT_FILE; } return 0; } int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, sqfs_u64 location) { if (inode->base.type == SQFS_INODE_EXT_FILE) { inode->data.file_ext.blocks_start = location; if (location < 0x0FFFFFFFFUL) sqfs_inode_make_basic(inode); } else if (inode->base.type == SQFS_INODE_FILE) { if (location > 0x0FFFFFFFFUL) { sqfs_inode_make_extended(inode); inode->data.file_ext.blocks_start = location; } else { inode->data.file.blocks_start = location; } } else { return SQFS_ERROR_NOT_FILE; } return 0; } int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size) { if (inode->base.type == SQFS_INODE_EXT_FILE) { *size = inode->data.file_ext.file_size; } else if (inode->base.type == SQFS_INODE_FILE) { *size = inode->data.file.file_size; } else { return SQFS_ERROR_NOT_FILE; } return 0; } int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, sqfs_u32 *index, sqfs_u32 *offset) { if (inode->base.type == SQFS_INODE_EXT_FILE) { *index = inode->data.file_ext.fragment_idx; *offset = inode->data.file_ext.fragment_offset; } else if (inode->base.type == SQFS_INODE_FILE) { *index = inode->data.file.fragment_index; *offset = inode->data.file.fragment_offset; } else { return SQFS_ERROR_NOT_FILE; } return 0; } int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, sqfs_u64 *location) { if (inode->base.type == SQFS_INODE_EXT_FILE) { *location = inode->data.file_ext.blocks_start; } else if (inode->base.type == SQFS_INODE_FILE) { *location = inode->data.file.blocks_start; } else { return SQFS_ERROR_NOT_FILE; } return 0; } int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, sqfs_dir_index_t **out, size_t index) { sqfs_dir_index_t ent; const char *ptr; size_t offset; if (inode->base.type != SQFS_INODE_EXT_DIR) { if (inode->base.type == SQFS_INODE_DIR) return SQFS_ERROR_OUT_OF_BOUNDS; return SQFS_ERROR_NOT_DIR; } offset = 0; ptr = (const char *)inode->extra; for (;;) { if (offset >= inode->payload_bytes_used) return SQFS_ERROR_OUT_OF_BOUNDS; if (index == 0) break; memcpy(&ent, ptr + offset, sizeof(ent)); offset += sizeof(ent) + ent.size + 1; index -= 1; } memcpy(&ent, ptr + offset, sizeof(ent)); *out = alloc_flex(sizeof(ent), 1, ent.size + 2); if (*out == NULL) return SQFS_ERROR_ALLOC; memcpy(*out, &ent, sizeof(ent)); memcpy((*out)->name, ptr + offset + sizeof(ent), ent.size + 1); return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/libsquashfs1.pc.in000066400000000000000000000004511410627516300221760ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libsquashfs Version: @PACKAGE_VERSION@ Description: A library for working with SquashFS file systems. Cflags: -I${includedir} Libs: -L${libdir} -lsquashfs @PTHREAD_LIBS@ Requires.private: @LIBSQFS_DEP_MOD@ squashfs-tools-ng-1.1.3/lib/sqfs/meta_reader.c000066400000000000000000000073121410627516300212570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * meta_reader.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_reader.h" #include "sqfs/compressor.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" #include #include struct sqfs_meta_reader_t { sqfs_object_t base; sqfs_u64 start; sqfs_u64 limit; size_t data_used; /* The location of the current block in the image */ sqfs_u64 block_offset; /* The location of the next block after the current one */ sqfs_u64 next_block; /* A byte offset into the uncompressed data of the current block */ size_t offset; /* The underlying file descriptor to read from */ sqfs_file_t *file; /* A pointer to the compressor to use for extracting data */ sqfs_compressor_t *cmp; /* The raw data read from the input file */ sqfs_u8 data[SQFS_META_BLOCK_SIZE]; /* The uncompressed data read from the input file */ sqfs_u8 scratch[SQFS_META_BLOCK_SIZE]; }; static void meta_reader_destroy(sqfs_object_t *m) { free(m); } static sqfs_object_t *meta_reader_copy(const sqfs_object_t *obj) { const sqfs_meta_reader_t *m = (const sqfs_meta_reader_t *)obj; sqfs_meta_reader_t *copy = malloc(sizeof(*copy)); if (copy != NULL) { memcpy(copy, m, sizeof(*m)); } /* XXX: cmp and file aren't deep-copied because m doesn't own them either. */ return (sqfs_object_t *)copy; } sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, sqfs_compressor_t *cmp, sqfs_u64 start, sqfs_u64 limit) { sqfs_meta_reader_t *m = calloc(1, sizeof(*m)); if (m == NULL) return NULL; ((sqfs_object_t *)m)->copy = meta_reader_copy; ((sqfs_object_t *)m)->destroy = meta_reader_destroy; m->block_offset = 0xFFFFFFFFFFFFFFFFUL; m->start = start; m->limit = limit; m->file = file; m->cmp = cmp; return m; } int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, sqfs_u64 block_start, size_t offset) { bool compressed; sqfs_u16 header; sqfs_u32 size; sqfs_s32 ret; int err; if (block_start < m->start || block_start >= m->limit) return SQFS_ERROR_OUT_OF_BOUNDS; if (block_start == m->block_offset) { if (offset >= m->data_used) return SQFS_ERROR_OUT_OF_BOUNDS; m->offset = offset; return 0; } err = m->file->read_at(m->file, block_start, &header, 2); if (err) return err; header = le16toh(header); compressed = (header & 0x8000) == 0; size = header & 0x7FFF; if (size > sizeof(m->data)) return SQFS_ERROR_CORRUPTED; if ((block_start + 2 + size) > m->limit) return SQFS_ERROR_OUT_OF_BOUNDS; err = m->file->read_at(m->file, block_start + 2, m->data, size); if (err) return err; if (compressed) { ret = m->cmp->do_block(m->cmp, m->data, size, m->scratch, sizeof(m->scratch)); if (ret < 0) return ret; memcpy(m->data, m->scratch, ret); m->data_used = ret; } else { m->data_used = size; } if (offset >= m->data_used) return SQFS_ERROR_OUT_OF_BOUNDS; m->block_offset = block_start; m->next_block = block_start + size + 2; m->offset = offset; return 0; } void sqfs_meta_reader_get_position(const sqfs_meta_reader_t *m, sqfs_u64 *block_start, size_t *offset) { *block_start = m->block_offset; *offset = m->offset; } int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size) { size_t diff; int ret; while (size != 0) { diff = m->data_used - m->offset; if (diff == 0) { ret = sqfs_meta_reader_seek(m, m->next_block, 0); if (ret) return ret; diff = m->data_used; } if (diff > size) diff = size; memcpy(data, m->data + m->offset, diff); m->offset += diff; data = (char *)data + diff; size -= diff; } return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/meta_writer.c000066400000000000000000000077041410627516300213360ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * meta_writer.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_writer.h" #include "sqfs/compressor.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" #include #include typedef struct meta_block_t { struct meta_block_t *next; /* possibly compressed data with 2 byte header */ sqfs_u8 data[SQFS_META_BLOCK_SIZE + 2]; } meta_block_t; struct sqfs_meta_writer_t { sqfs_object_t base; /* A byte offset into the uncompressed data of the current block */ size_t offset; /* The location of the current block in the file */ size_t block_offset; /* The underlying file descriptor to write to */ sqfs_file_t *file; /* A pointer to the compressor to use for compressing the data */ sqfs_compressor_t *cmp; /* The raw data chunk that data is appended to */ sqfs_u8 data[SQFS_META_BLOCK_SIZE]; sqfs_u32 flags; meta_block_t *list; meta_block_t *list_end; }; static int write_block(sqfs_file_t *file, meta_block_t *outblk) { sqfs_u16 header; size_t count; sqfs_u64 off; memcpy(&header, outblk->data, sizeof(header)); count = le16toh(header) & 0x7FFF; off = file->get_size(file); return file->write_at(file, off, outblk->data, count + 2); } static void meta_writer_destroy(sqfs_object_t *obj) { sqfs_meta_writer_t *m = (sqfs_meta_writer_t *)obj; meta_block_t *blk; while (m->list != NULL) { blk = m->list; m->list = blk->next; free(blk); } free(m); } sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, sqfs_compressor_t *cmp, sqfs_u32 flags) { sqfs_meta_writer_t *m; if (flags & ~SQFS_META_WRITER_ALL_FLAGS) return NULL; m = calloc(1, sizeof(*m)); if (m == NULL) return NULL; ((sqfs_object_t *)m)->destroy = meta_writer_destroy; m->cmp = cmp; m->file = file; m->flags = flags; return m; } int sqfs_meta_writer_flush(sqfs_meta_writer_t *m) { meta_block_t *outblk; sqfs_u16 header; sqfs_u32 count; sqfs_s32 ret; if (m->offset == 0) return 0; outblk = calloc(1, sizeof(*outblk)); if (outblk == NULL) return SQFS_ERROR_ALLOC; ret = m->cmp->do_block(m->cmp, m->data, m->offset, outblk->data + 2, sizeof(outblk->data) - 2); if (ret < 0) { free(outblk); return ret; } if (ret > 0) { header = htole16(ret); count = ret + 2; } else { header = htole16(m->offset | 0x8000); memcpy(outblk->data + 2, m->data, m->offset); count = m->offset + 2; } memcpy(outblk->data, &header, sizeof(header)); ret = 0; if (m->flags & SQFS_META_WRITER_KEEP_IN_MEMORY) { if (m->list == NULL) { m->list = outblk; } else { m->list_end->next = outblk; } m->list_end = outblk; } else { ret = write_block(m->file, outblk); free(outblk); } memset(m->data, 0, sizeof(m->data)); m->offset = 0; m->block_offset += count; return ret; } int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, size_t size) { size_t diff; int ret; while (size != 0) { diff = sizeof(m->data) - m->offset; if (diff == 0) { ret = sqfs_meta_writer_flush(m); if (ret) return ret; diff = sizeof(m->data); } if (diff > size) diff = size; memcpy(m->data + m->offset, data, diff); m->offset += diff; size -= diff; data = (const char *)data + diff; } if (m->offset == sizeof(m->data)) return sqfs_meta_writer_flush(m); return 0; } void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, sqfs_u64 *block_start, sqfs_u32 *offset) { *block_start = m->block_offset; *offset = m->offset; } void sqfs_meta_writer_reset(sqfs_meta_writer_t *m) { m->block_offset = 0; m->offset = 0; } int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m) { meta_block_t *blk; int ret; while (m->list != NULL) { blk = m->list; ret = write_block(m->file, blk); if (ret) return ret; m->list = blk->next; free(blk); } m->list_end = NULL; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/misc.c000066400000000000000000000004161410627516300177400ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * misc.c * * Copyright (C) 2021 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/predef.h" #include void sqfs_free(void *ptr) { free(ptr); } squashfs-tools-ng-1.1.3/lib/sqfs/read_inode.c000066400000000000000000000220131410627516300210730ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * read_inode.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_reader.h" #include "sqfs/error.h" #include "sqfs/super.h" #include "sqfs/inode.h" #include "sqfs/dir.h" #include "util.h" #include #include #include #define SWAB16(x) x = le16toh(x) #define SWAB32(x) x = le32toh(x) #define SWAB64(x) x = le64toh(x) static int set_mode(sqfs_inode_t *inode) { inode->mode &= ~S_IFMT; switch (inode->type) { case SQFS_INODE_SOCKET: case SQFS_INODE_EXT_SOCKET: inode->mode |= S_IFSOCK; break; case SQFS_INODE_SLINK: case SQFS_INODE_EXT_SLINK: inode->mode |= S_IFLNK; break; case SQFS_INODE_FILE: case SQFS_INODE_EXT_FILE: inode->mode |= S_IFREG; break; case SQFS_INODE_BDEV: case SQFS_INODE_EXT_BDEV: inode->mode |= S_IFBLK; break; case SQFS_INODE_DIR: case SQFS_INODE_EXT_DIR: inode->mode |= S_IFDIR; break; case SQFS_INODE_CDEV: case SQFS_INODE_EXT_CDEV: inode->mode |= S_IFCHR; break; case SQFS_INODE_FIFO: case SQFS_INODE_EXT_FIFO: inode->mode |= S_IFIFO; break; default: return SQFS_ERROR_UNSUPPORTED; } return 0; } static sqfs_u64 get_block_count(sqfs_u64 size, sqfs_u64 block_size, sqfs_u32 frag_index, sqfs_u32 frag_offset) { sqfs_u64 count = size / block_size; if ((size % block_size) != 0 && (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) { ++count; } return count; } static int read_inode_file(sqfs_meta_reader_t *ir, sqfs_inode_t *base, size_t block_size, sqfs_inode_generic_t **result) { sqfs_inode_generic_t *out; sqfs_inode_file_t file; sqfs_u64 i, count; int err; err = sqfs_meta_reader_read(ir, &file, sizeof(file)); if (err) return err; SWAB32(file.blocks_start); SWAB32(file.fragment_index); SWAB32(file.fragment_offset); SWAB32(file.file_size); count = get_block_count(file.file_size, block_size, file.fragment_index, file.fragment_offset); out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); if (out == NULL) return SQFS_ERROR_ALLOC; out->base = *base; out->data.file = file; out->payload_bytes_available = count * sizeof(sqfs_u32); out->payload_bytes_used = count * sizeof(sqfs_u32); err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); if (err) { free(out); return err; } for (i = 0; i < count; ++i) SWAB32(out->extra[i]); *result = out; return 0; } static int read_inode_file_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, size_t block_size, sqfs_inode_generic_t **result) { sqfs_inode_file_ext_t file; sqfs_inode_generic_t *out; sqfs_u64 i, count; int err; err = sqfs_meta_reader_read(ir, &file, sizeof(file)); if (err) return err; SWAB64(file.blocks_start); SWAB64(file.file_size); SWAB64(file.sparse); SWAB32(file.nlink); SWAB32(file.fragment_idx); SWAB32(file.fragment_offset); SWAB32(file.xattr_idx); count = get_block_count(file.file_size, block_size, file.fragment_idx, file.fragment_offset); out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); if (out == NULL) { return errno == EOVERFLOW ? SQFS_ERROR_OVERFLOW : SQFS_ERROR_ALLOC; } out->base = *base; out->data.file_ext = file; out->payload_bytes_available = count * sizeof(sqfs_u32); out->payload_bytes_used = count * sizeof(sqfs_u32); err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); if (err) { free(out); return err; } for (i = 0; i < count; ++i) SWAB32(out->extra[i]); *result = out; return 0; } static int read_inode_slink(sqfs_meta_reader_t *ir, sqfs_inode_t *base, sqfs_inode_generic_t **result) { sqfs_inode_generic_t *out; sqfs_inode_slink_t slink; size_t size; int err; err = sqfs_meta_reader_read(ir, &slink, sizeof(slink)); if (err) return err; SWAB32(slink.nlink); SWAB32(slink.target_size); if (SZ_ADD_OV(slink.target_size, 1, &size) || SZ_ADD_OV(sizeof(*out), size, &size)) { return SQFS_ERROR_OVERFLOW; } out = calloc(1, size); if (out == NULL) return SQFS_ERROR_ALLOC; out->payload_bytes_available = size - sizeof(*out); out->payload_bytes_used = size - sizeof(*out) - 1; out->base = *base; out->data.slink = slink; err = sqfs_meta_reader_read(ir, (void *)out->extra, slink.target_size); if (err) { free(out); return err; } *result = out; return 0; } static int read_inode_slink_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, sqfs_inode_generic_t **result) { sqfs_u32 xattr; int err; err = read_inode_slink(ir, base, result); if (err) return err; err = sqfs_meta_reader_read(ir, &xattr, sizeof(xattr)); if (err) { free(*result); return err; } (*result)->data.slink_ext.xattr_idx = le32toh(xattr); return 0; } static int read_inode_dir_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, sqfs_inode_generic_t **result) { size_t i, new_sz, index_max, index_used; sqfs_inode_generic_t *out, *new; sqfs_inode_dir_ext_t dir; sqfs_dir_index_t ent; int err; err = sqfs_meta_reader_read(ir, &dir, sizeof(dir)); if (err) return err; SWAB32(dir.nlink); SWAB32(dir.size); SWAB32(dir.start_block); SWAB32(dir.parent_inode); SWAB16(dir.inodex_count); SWAB16(dir.offset); SWAB32(dir.xattr_idx); index_max = dir.size ? 128 : 0; index_used = 0; out = alloc_flex(sizeof(*out), 1, index_max); if (out == NULL) return SQFS_ERROR_ALLOC; out->base = *base; out->data.dir_ext = dir; if (dir.size == 0) { *result = out; return 0; } for (i = 0; i < dir.inodex_count; ++i) { err = sqfs_meta_reader_read(ir, &ent, sizeof(ent)); if (err) { free(out); return err; } SWAB32(ent.start_block); SWAB32(ent.index); SWAB32(ent.size); new_sz = index_max; while (sizeof(ent) + ent.size + 1 > new_sz - index_used) { if (SZ_MUL_OV(new_sz, 2, &new_sz)) { free(out); return SQFS_ERROR_OVERFLOW; } } if (new_sz > index_max) { new = realloc(out, sizeof(*out) + new_sz); if (new == NULL) { free(out); return SQFS_ERROR_ALLOC; } out = new; index_max = new_sz; } memcpy((char *)out->extra + index_used, &ent, sizeof(ent)); index_used += sizeof(ent); err = sqfs_meta_reader_read(ir, (char *)out->extra + index_used, ent.size + 1); if (err) { free(out); return err; } index_used += ent.size + 1; } out->payload_bytes_used = index_used; out->payload_bytes_available = index_used; *result = out; return 0; } int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, const sqfs_super_t *super, sqfs_u64 block_start, size_t offset, sqfs_inode_generic_t **result) { sqfs_inode_generic_t *out; sqfs_inode_t inode; int err; /* read base inode */ block_start += super->inode_table_start; err = sqfs_meta_reader_seek(ir, block_start, offset); if (err) return err; err = sqfs_meta_reader_read(ir, &inode, sizeof(inode)); if (err) return err; SWAB16(inode.type); SWAB16(inode.mode); SWAB16(inode.uid_idx); SWAB16(inode.gid_idx); SWAB32(inode.mod_time); SWAB32(inode.inode_number); err = set_mode(&inode); if (err) return err; /* inode types where the size is variable */ switch (inode.type) { case SQFS_INODE_FILE: return read_inode_file(ir, &inode, super->block_size, result); case SQFS_INODE_SLINK: return read_inode_slink(ir, &inode, result); case SQFS_INODE_EXT_FILE: return read_inode_file_ext(ir, &inode, super->block_size, result); case SQFS_INODE_EXT_SLINK: return read_inode_slink_ext(ir, &inode, result); case SQFS_INODE_EXT_DIR: return read_inode_dir_ext(ir, &inode, result); default: break; } /* everything else */ out = calloc(1, sizeof(*out)); if (out == NULL) return SQFS_ERROR_ALLOC; out->base = inode; switch (inode.type) { case SQFS_INODE_DIR: err = sqfs_meta_reader_read(ir, &out->data.dir, sizeof(out->data.dir)); if (err) goto fail_free; SWAB32(out->data.dir.start_block); SWAB32(out->data.dir.nlink); SWAB16(out->data.dir.size); SWAB16(out->data.dir.offset); SWAB32(out->data.dir.parent_inode); break; case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: err = sqfs_meta_reader_read(ir, &out->data.dev, sizeof(out->data.dev)); if (err) goto fail_free; SWAB32(out->data.dev.nlink); SWAB32(out->data.dev.devno); break; case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: err = sqfs_meta_reader_read(ir, &out->data.ipc, sizeof(out->data.ipc)); if (err) goto fail_free; SWAB32(out->data.ipc.nlink); break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: err = sqfs_meta_reader_read(ir, &out->data.dev_ext, sizeof(out->data.dev_ext)); if (err) goto fail_free; SWAB32(out->data.dev_ext.nlink); SWAB32(out->data.dev_ext.devno); SWAB32(out->data.dev_ext.xattr_idx); break; case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: err = sqfs_meta_reader_read(ir, &out->data.ipc_ext, sizeof(out->data.ipc_ext)); if (err) goto fail_free; SWAB32(out->data.ipc_ext.nlink); SWAB32(out->data.ipc_ext.xattr_idx); break; default: err = SQFS_ERROR_UNSUPPORTED; goto fail_free; } *result = out; return 0; fail_free: free(out); return err; } squashfs-tools-ng-1.1.3/lib/sqfs/read_super.c000066400000000000000000000045301410627516300211370ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * read_super.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/super.h" #include "sqfs/error.h" #include "sqfs/io.h" #include "util.h" #include int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file) { size_t block_size = 0; sqfs_super_t temp; int i, ret; ret = file->read_at(file, 0, &temp, sizeof(temp)); if (ret) return ret; temp.magic = le32toh(temp.magic); temp.inode_count = le32toh(temp.inode_count); temp.modification_time = le32toh(temp.modification_time); temp.block_size = le32toh(temp.block_size); temp.fragment_entry_count = le32toh(temp.fragment_entry_count); temp.compression_id = le16toh(temp.compression_id); temp.block_log = le16toh(temp.block_log); temp.flags = le16toh(temp.flags); temp.id_count = le16toh(temp.id_count); temp.version_major = le16toh(temp.version_major); temp.version_minor = le16toh(temp.version_minor); temp.root_inode_ref = le64toh(temp.root_inode_ref); temp.bytes_used = le64toh(temp.bytes_used); temp.id_table_start = le64toh(temp.id_table_start); temp.xattr_id_table_start = le64toh(temp.xattr_id_table_start); temp.inode_table_start = le64toh(temp.inode_table_start); temp.directory_table_start = le64toh(temp.directory_table_start); temp.fragment_table_start = le64toh(temp.fragment_table_start); temp.export_table_start = le64toh(temp.export_table_start); if (temp.magic != SQFS_MAGIC) return SFQS_ERROR_SUPER_MAGIC; if ((temp.version_major != SQFS_VERSION_MAJOR) || (temp.version_minor != SQFS_VERSION_MINOR)) return SFQS_ERROR_SUPER_VERSION; if ((temp.block_size - 1) & temp.block_size) return SQFS_ERROR_SUPER_BLOCK_SIZE; if (temp.block_size < SQFS_MIN_BLOCK_SIZE) return SQFS_ERROR_SUPER_BLOCK_SIZE; if (temp.block_size > SQFS_MAX_BLOCK_SIZE) return SQFS_ERROR_SUPER_BLOCK_SIZE; if (temp.block_log < 12 || temp.block_log > 20) return SQFS_ERROR_CORRUPTED; block_size = 1; for (i = 0; i < temp.block_log; ++i) block_size <<= 1; if (temp.block_size != block_size) return SQFS_ERROR_CORRUPTED; if (temp.compression_id < SQFS_COMP_MIN || temp.compression_id > SQFS_COMP_MAX) return SQFS_ERROR_UNSUPPORTED; if (temp.id_count == 0) return SQFS_ERROR_CORRUPTED; memcpy(super, &temp, sizeof(temp)); return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/read_table.c000066400000000000000000000034041410627516300210670ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * read_table.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_reader.h" #include "sqfs/error.h" #include "sqfs/table.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" #include int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, size_t table_size, sqfs_u64 location, sqfs_u64 lower_limit, sqfs_u64 upper_limit, void **out) { size_t diff, block_count, blk_idx = 0; sqfs_u64 start, *locations; sqfs_meta_reader_t *m; void *data, *ptr; int err; data = malloc(table_size); if (data == NULL) return SQFS_ERROR_ALLOC; /* restore list from image */ block_count = table_size / SQFS_META_BLOCK_SIZE; if ((table_size % SQFS_META_BLOCK_SIZE) != 0) ++block_count; locations = alloc_array(sizeof(sqfs_u64), block_count); if (locations == NULL) { err = SQFS_ERROR_ALLOC; goto fail_data; } err = file->read_at(file, location, locations, sizeof(sqfs_u64) * block_count); if (err) goto fail_idx; /* Read the actual data */ m = sqfs_meta_reader_create(file, cmp, lower_limit, upper_limit); if (m == NULL) { err = SQFS_ERROR_ALLOC; goto fail_idx; } ptr = data; while (table_size > 0) { start = le64toh(locations[blk_idx++]); err = sqfs_meta_reader_seek(m, start, 0); if (err) goto fail; diff = SQFS_META_BLOCK_SIZE; if (diff > table_size) diff = table_size; err = sqfs_meta_reader_read(m, ptr, diff); if (err) goto fail; ptr = (char *)ptr + diff; table_size -= diff; } sqfs_destroy(m); free(locations); *out = data; return 0; fail: sqfs_destroy(m); fail_idx: free(locations); fail_data: free(data); *out = NULL; return err; } squashfs-tools-ng-1.1.3/lib/sqfs/read_tree.c000066400000000000000000000130371410627516300207420ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * read_tree.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_reader.h" #include "sqfs/dir_reader.h" #include "sqfs/compressor.h" #include "sqfs/id_table.h" #include "sqfs/super.h" #include "sqfs/inode.h" #include "sqfs/error.h" #include "sqfs/dir.h" #include "util.h" #include #include static int should_skip(int type, unsigned int flags) { switch (type) { case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: case SQFS_INODE_EXT_CDEV: case SQFS_INODE_EXT_BDEV: return (flags & SQFS_TREE_NO_DEVICES); case SQFS_INODE_SLINK: case SQFS_INODE_EXT_SLINK: return (flags & SQFS_TREE_NO_SLINKS); case SQFS_INODE_SOCKET: case SQFS_INODE_EXT_SOCKET: return(flags & SQFS_TREE_NO_SOCKETS); case SQFS_INODE_FIFO: case SQFS_INODE_EXT_FIFO: return (flags & SQFS_TREE_NO_FIFO); default: break; } return 0; } static bool would_be_own_parent(sqfs_tree_node_t *parent, sqfs_tree_node_t *n) { sqfs_u32 inum = n->inode->base.inode_number; while (parent != NULL) { if (parent->inode->base.inode_number == inum) return true; parent = parent->parent; } return false; } static sqfs_tree_node_t *create_node(sqfs_inode_generic_t *inode, const char *name) { sqfs_tree_node_t *n; n = alloc_flex(sizeof(*n), 1, strlen(name) + 1); if (n == NULL) return NULL; n->inode = inode; strcpy((char *)n->name, name); return n; } static int fill_dir(sqfs_dir_reader_t *dr, sqfs_tree_node_t *root, unsigned int flags) { sqfs_tree_node_t *n, *prev, **tail; sqfs_inode_generic_t *inode; sqfs_dir_entry_t *ent; int err; tail = &root->children; for (;;) { err = sqfs_dir_reader_read(dr, &ent); if (err > 0) break; if (err < 0) return err; if (should_skip(ent->type, flags)) { free(ent); continue; } err = sqfs_dir_reader_get_inode(dr, &inode); if (err) { free(ent); return err; } n = create_node(inode, (const char *)ent->name); free(ent); if (n == NULL) { free(inode); return SQFS_ERROR_ALLOC; } if (would_be_own_parent(root, n)) { free(n); free(inode); return SQFS_ERROR_LINK_LOOP; } *tail = n; tail = &n->next; n->parent = root; } n = root->children; prev = NULL; while (n != NULL) { if (n->inode->base.type == SQFS_INODE_DIR || n->inode->base.type == SQFS_INODE_EXT_DIR) { if (!(flags & SQFS_TREE_NO_RECURSE)) { err = sqfs_dir_reader_open_dir(dr, n->inode, 0); if (err) return err; err = fill_dir(dr, n, flags); if (err) return err; } if (n->children == NULL && (flags & SQFS_TREE_NO_EMPTY)) { free(n->inode); if (prev == NULL) { root->children = root->children->next; free(n); n = root->children; } else { prev->next = n->next; free(n); n = prev->next; } continue; } } prev = n; n = n->next; } return 0; } static int resolve_ids(sqfs_tree_node_t *root, const sqfs_id_table_t *idtbl) { sqfs_tree_node_t *it; int err; for (it = root->children; it != NULL; it = it->next) resolve_ids(it, idtbl); err = sqfs_id_table_index_to_id(idtbl, root->inode->base.uid_idx, &root->uid); if (err) return err; return sqfs_id_table_index_to_id(idtbl, root->inode->base.gid_idx, &root->gid); } void sqfs_dir_tree_destroy(sqfs_tree_node_t *root) { sqfs_tree_node_t *it; while (root->children != NULL) { it = root->children; root->children = it->next; sqfs_dir_tree_destroy(it); } free(root->inode); free(root); } int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd, const sqfs_id_table_t *idtbl, const char *path, unsigned int flags, sqfs_tree_node_t **out) { sqfs_tree_node_t *root, *tail, *new; sqfs_inode_generic_t *inode; sqfs_dir_entry_t *ent; const char *ptr; int ret; if (flags & ~SQFS_TREE_ALL_FLAGS) return SQFS_ERROR_UNSUPPORTED; ret = sqfs_dir_reader_get_root_inode(rd, &inode); if (ret) return ret; root = tail = create_node(inode, ""); if (root == NULL) { free(inode); return SQFS_ERROR_ALLOC; } inode = NULL; while (path != NULL && *path != '\0') { if (*path == '/') { while (*path == '/') ++path; continue; } ret = sqfs_dir_reader_open_dir(rd, tail->inode, 0); if (ret) goto fail; ptr = strchr(path, '/'); if (ptr == NULL) { if (ptr == NULL) { for (ptr = path; *ptr != '\0'; ++ptr) ; } } for (;;) { ret = sqfs_dir_reader_read(rd, &ent); if (ret < 0) goto fail; if (ret > 0) { ret = SQFS_ERROR_NO_ENTRY; goto fail; } ret = strncmp((const char *)ent->name, path, ptr - path); if (ret == 0 && ent->name[ptr - path] == '\0') break; free(ent); } ret = sqfs_dir_reader_get_inode(rd, &inode); if (ret) { free(ent); goto fail; } new = create_node(inode, (const char *)ent->name); free(ent); if (new == NULL) { free(inode); ret = SQFS_ERROR_ALLOC; goto fail; } inode = NULL; path = ptr; if (flags & SQFS_TREE_STORE_PARENTS) { tail->children = new; new->parent = tail; tail = new; } else { sqfs_dir_tree_destroy(root); root = tail = new; } } if (tail->inode->base.type == SQFS_INODE_DIR || tail->inode->base.type == SQFS_INODE_EXT_DIR) { ret = sqfs_dir_reader_open_dir(rd, tail->inode, 0); if (ret) goto fail; ret = fill_dir(rd, tail, flags); if (ret) goto fail; } ret = resolve_ids(root, idtbl); if (ret) goto fail; *out = root; return 0; fail: sqfs_dir_tree_destroy(root); return ret; } squashfs-tools-ng-1.1.3/lib/sqfs/readdir.c000066400000000000000000000025151410627516300204210ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * readdir.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_reader.h" #include "sqfs/error.h" #include "sqfs/dir.h" #include "compat.h" #include #include int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, sqfs_dir_header_t *hdr) { int err = sqfs_meta_reader_read(m, hdr, sizeof(*hdr)); if (err) return err; hdr->count = le32toh(hdr->count); hdr->start_block = le32toh(hdr->start_block); hdr->inode_number = le32toh(hdr->inode_number); if (hdr->count > (SQFS_MAX_DIR_ENT - 1)) return SQFS_ERROR_CORRUPTED; return 0; } int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, sqfs_dir_entry_t **result) { sqfs_dir_entry_t ent, *out; sqfs_u16 *diff_u16; int err; err = sqfs_meta_reader_read(m, &ent, sizeof(ent)); if (err) return err; diff_u16 = (sqfs_u16 *)&ent.inode_diff; *diff_u16 = le16toh(*diff_u16); ent.offset = le16toh(ent.offset); ent.type = le16toh(ent.type); ent.size = le16toh(ent.size); out = calloc(1, sizeof(*out) + ent.size + 2); if (out == NULL) return SQFS_ERROR_ALLOC; *out = ent; err = sqfs_meta_reader_read(m, out->name, ent.size + 1); if (err) { free(out); return err; } *result = out; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/super.c000066400000000000000000000025701410627516300201460ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * super.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/super.h" #include "sqfs/error.h" #include int sqfs_super_init(sqfs_super_t *super, size_t block_size, sqfs_u32 mtime, SQFS_COMPRESSOR compressor) { unsigned int i; if (block_size & (block_size - 1)) return SQFS_ERROR_SUPER_BLOCK_SIZE; if (block_size < SQFS_MIN_BLOCK_SIZE) return SQFS_ERROR_SUPER_BLOCK_SIZE; if (block_size > SQFS_MAX_BLOCK_SIZE) return SQFS_ERROR_SUPER_BLOCK_SIZE; memset(super, 0, sizeof(*super)); super->magic = SQFS_MAGIC; super->modification_time = mtime; super->block_size = block_size; super->compression_id = compressor; super->flags = SQFS_FLAG_NO_FRAGMENTS | SQFS_FLAG_NO_XATTRS; super->flags |= SQFS_FLAG_NO_DUPLICATES; super->version_major = SQFS_VERSION_MAJOR; super->version_minor = SQFS_VERSION_MINOR; super->bytes_used = sizeof(*super); super->id_table_start = 0xFFFFFFFFFFFFFFFFUL; super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; super->inode_table_start = 0xFFFFFFFFFFFFFFFFUL; super->directory_table_start = 0xFFFFFFFFFFFFFFFFUL; super->fragment_table_start = 0xFFFFFFFFFFFFFFFFUL; super->export_table_start = 0xFFFFFFFFFFFFFFFFUL; for (i = block_size; i != 0x01; i >>= 1) super->block_log += 1; return 0; } squashfs-tools-ng-1.1.3/lib/sqfs/unix/000077500000000000000000000000001410627516300176235ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/sqfs/unix/io_file.c000066400000000000000000000066671410627516300214140ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * io_file.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/io.h" #include "sqfs/error.h" #include #include #include #include #include #include typedef struct { sqfs_file_t base; bool readonly; sqfs_u64 size; int fd; } sqfs_file_stdio_t; static void stdio_destroy(sqfs_object_t *base) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; close(file->fd); free(file); } static sqfs_object_t *stdio_copy(const sqfs_object_t *base) { const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; sqfs_file_stdio_t *copy; int err; if (!file->readonly) { errno = ENOTSUP; return NULL; } copy = calloc(1, sizeof(*copy)); if (copy == NULL) return NULL; memcpy(copy, file, sizeof(*file)); copy->fd = dup(file->fd); if (copy->fd < 0) { err = errno; free(copy); copy = NULL; errno = err; } return (sqfs_object_t *)copy; } static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, void *buffer, size_t size) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; ssize_t ret; while (size > 0) { ret = pread(file->fd, buffer, size, offset); if (ret < 0) { if (errno == EINTR) continue; return SQFS_ERROR_IO; } if (ret == 0) return SQFS_ERROR_OUT_OF_BOUNDS; buffer = (char *)buffer + ret; size -= ret; offset += ret; } return 0; } static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, const void *buffer, size_t size) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; ssize_t ret; while (size > 0) { ret = pwrite(file->fd, buffer, size, offset); if (ret < 0) { if (errno == EINTR) continue; return SQFS_ERROR_IO; } if (ret == 0) return SQFS_ERROR_OUT_OF_BOUNDS; buffer = (const char *)buffer + ret; size -= ret; offset += ret; } if (offset >= file->size) file->size = offset; return 0; } static sqfs_u64 stdio_get_size(const sqfs_file_t *base) { const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; return file->size; } static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; if (ftruncate(file->fd, size)) return SQFS_ERROR_IO; file->size = size; return 0; } sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) { sqfs_file_stdio_t *file; int open_mode, temp; sqfs_file_t *base; struct stat sb; if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { errno = EINVAL; return NULL; } file = calloc(1, sizeof(*file)); base = (sqfs_file_t *)file; if (file == NULL) return NULL; if (flags & SQFS_FILE_OPEN_READ_ONLY) { file->readonly = true; open_mode = O_RDONLY; } else { file->readonly = false; open_mode = O_CREAT | O_RDWR; if (flags & SQFS_FILE_OPEN_OVERWRITE) { open_mode |= O_TRUNC; } else { open_mode |= O_EXCL; } } file->fd = open(filename, open_mode, 0644); if (file->fd < 0) { temp = errno; free(file); errno = temp; return NULL; } if (fstat(file->fd, &sb)) { temp = errno; close(file->fd); free(file); errno = temp; return NULL; } file->size = sb.st_size; base->read_at = stdio_read_at; base->write_at = stdio_write_at; base->get_size = stdio_get_size; base->truncate = stdio_truncate; ((sqfs_object_t *)base)->copy = stdio_copy; ((sqfs_object_t *)base)->destroy = stdio_destroy; return base; } squashfs-tools-ng-1.1.3/lib/sqfs/win32/000077500000000000000000000000001410627516300176025ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/sqfs/win32/io_file.c000066400000000000000000000100541410627516300213540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * io_file.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/io.h" #include "sqfs/error.h" #include #define WIN32_LEAN_AND_MEAN #include typedef struct { sqfs_file_t base; bool readonly; sqfs_u64 size; HANDLE fd; } sqfs_file_stdio_t; static void stdio_destroy(sqfs_object_t *base) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; CloseHandle(file->fd); free(file); } static sqfs_object_t *stdio_copy(const sqfs_object_t *base) { const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; sqfs_file_stdio_t *copy; BOOL ret; if (!file->readonly) { SetLastError(ERROR_NOT_SUPPORTED); return NULL; } copy = calloc(1, sizeof(*copy)); if (copy == NULL) return NULL; memcpy(copy, file, sizeof(*file)); ret = DuplicateHandle(GetCurrentProcess(), file->fd, GetCurrentProcess(), ©->fd, 0, FALSE, DUPLICATE_SAME_ACCESS); if (!ret) { free(copy); return NULL; } return (sqfs_object_t *)copy; } static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, void *buffer, size_t size) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; DWORD actually_read; LARGE_INTEGER pos; if (offset >= file->size) return SQFS_ERROR_OUT_OF_BOUNDS; if (size == 0) return 0; if ((offset + size - 1) >= file->size) return SQFS_ERROR_OUT_OF_BOUNDS; pos.QuadPart = offset; if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) return SQFS_ERROR_IO; while (size > 0) { if (!ReadFile(file->fd, buffer, size, &actually_read, NULL)) return SQFS_ERROR_IO; size -= actually_read; buffer = (char *)buffer + actually_read; } return 0; } static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, const void *buffer, size_t size) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; DWORD actually_read; LARGE_INTEGER pos; if (size == 0) return 0; pos.QuadPart = offset; if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) return SQFS_ERROR_IO; while (size > 0) { if (!WriteFile(file->fd, buffer, size, &actually_read, NULL)) return SQFS_ERROR_IO; size -= actually_read; buffer = (char *)buffer + actually_read; offset += actually_read; if (offset > file->size) file->size = offset; } return 0; } static sqfs_u64 stdio_get_size(const sqfs_file_t *base) { const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; return file->size; } static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) { sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; LARGE_INTEGER pos; pos.QuadPart = size; if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) return SQFS_ERROR_IO; if (!SetEndOfFile(file->fd)) return SQFS_ERROR_IO; file->size = size; return 0; } sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) { int access_flags, creation_mode, share_mode; sqfs_file_stdio_t *file; LARGE_INTEGER size; sqfs_file_t *base; if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) return NULL; file = calloc(1, sizeof(*file)); base = (sqfs_file_t *)file; if (file == NULL) return NULL; if (flags & SQFS_FILE_OPEN_READ_ONLY) { file->readonly = true; access_flags = GENERIC_READ; creation_mode = OPEN_EXISTING; share_mode = FILE_SHARE_READ; } else { file->readonly = false; access_flags = GENERIC_READ | GENERIC_WRITE; share_mode = 0; if (flags & SQFS_FILE_OPEN_OVERWRITE) { creation_mode = CREATE_ALWAYS; } else { creation_mode = CREATE_NEW; } } file->fd = CreateFile(filename, access_flags, share_mode, NULL, creation_mode, FILE_ATTRIBUTE_NORMAL, NULL); if (file->fd == INVALID_HANDLE_VALUE) { free(file); return NULL; } if (!GetFileSizeEx(file->fd, &size)) { free(file); return NULL; } file->size = size.QuadPart; base->read_at = stdio_read_at; base->write_at = stdio_write_at; base->get_size = stdio_get_size; base->truncate = stdio_truncate; ((sqfs_object_t *)base)->destroy = stdio_destroy; ((sqfs_object_t *)base)->copy = stdio_copy; return base; } squashfs-tools-ng-1.1.3/lib/sqfs/write_inode.c000066400000000000000000000134321410627516300213170ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * write_inode.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_writer.h" #include "sqfs/error.h" #include "sqfs/inode.h" #include "sqfs/dir.h" #include "compat.h" #include #include #if defined(_WIN32) || defined(__WINDOWS__) # include # ifdef _MSC_VER # define alloca _alloca # endif #elif defined(HAVE_ALLOCA_H) # include #endif static int write_block_sizes(sqfs_meta_writer_t *ir, const sqfs_inode_generic_t *n) { sqfs_u32 *sizes; size_t i; if (n->payload_bytes_used < sizeof(sizes[0])) return 0; if ((n->payload_bytes_used % sizeof(sizes[0])) != 0) return SQFS_ERROR_CORRUPTED; sizes = alloca(n->payload_bytes_used); for (i = 0; i < (n->payload_bytes_used / sizeof(sizes[0])); ++i) sizes[i] = htole32(n->extra[i]); return sqfs_meta_writer_append(ir, sizes, n->payload_bytes_used); } static int write_dir_index(sqfs_meta_writer_t *ir, const sqfs_u8 *data, size_t count) { sqfs_dir_index_t ent; size_t len; int err; while (count > sizeof(ent)) { memcpy(&ent, data, sizeof(ent)); data += sizeof(ent); count -= sizeof(ent); len = ent.size + 1; if (len > count) return SQFS_ERROR_CORRUPTED; ent.start_block = htole32(ent.start_block); ent.index = htole32(ent.index); ent.size = htole32(ent.size); err = sqfs_meta_writer_append(ir, &ent, sizeof(ent)); if (err) return err; err = sqfs_meta_writer_append(ir, data, len); if (err) return err; data += len; count -= len; } return 0; } int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *ir, const sqfs_inode_generic_t *n) { sqfs_inode_t base; int ret; base.type = htole16(n->base.type); base.mode = htole16(n->base.mode & ~SQFS_INODE_MODE_MASK); base.uid_idx = htole16(n->base.uid_idx); base.gid_idx = htole16(n->base.gid_idx); base.mod_time = htole32(n->base.mod_time); base.inode_number = htole32(n->base.inode_number); ret = sqfs_meta_writer_append(ir, &base, sizeof(base)); if (ret) return ret; switch (n->base.type) { case SQFS_INODE_DIR: { sqfs_inode_dir_t dir = { .start_block = htole32(n->data.dir.start_block), .nlink = htole32(n->data.dir.nlink), .size = htole16(n->data.dir.size), .offset = htole16(n->data.dir.offset), .parent_inode = htole32(n->data.dir.parent_inode), }; return sqfs_meta_writer_append(ir, &dir, sizeof(dir)); } case SQFS_INODE_FILE: { sqfs_inode_file_t file = { .blocks_start = htole32(n->data.file.blocks_start), .fragment_index = htole32(n->data.file.fragment_index), .fragment_offset = htole32(n->data.file.fragment_offset), .file_size = htole32(n->data.file.file_size), }; ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); if (ret) return ret; return write_block_sizes(ir, n); } case SQFS_INODE_SLINK: { sqfs_inode_slink_t slink = { .nlink = htole32(n->data.slink.nlink), .target_size = htole32(n->data.slink.target_size), }; ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); if (ret) return ret; return sqfs_meta_writer_append(ir, n->extra, n->data.slink.target_size); } case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: { sqfs_inode_dev_t dev = { .nlink = htole32(n->data.dev.nlink), .devno = htole32(n->data.dev.devno), }; return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); } case SQFS_INODE_FIFO: case SQFS_INODE_SOCKET: { sqfs_inode_ipc_t ipc = { .nlink = htole32(n->data.ipc.nlink), }; return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); } case SQFS_INODE_EXT_DIR: { sqfs_inode_dir_ext_t dir = { .nlink = htole32(n->data.dir_ext.nlink), .size = htole32(n->data.dir_ext.size), .start_block = htole32(n->data.dir_ext.start_block), .parent_inode = htole32(n->data.dir_ext.parent_inode), .inodex_count = htole16(n->data.dir_ext.inodex_count), .offset = htole16(n->data.dir_ext.offset), .xattr_idx = htole32(n->data.dir_ext.xattr_idx), }; ret = sqfs_meta_writer_append(ir, &dir, sizeof(dir)); if (ret) return ret; return write_dir_index(ir, (const sqfs_u8 *)n->extra, n->payload_bytes_used); } case SQFS_INODE_EXT_FILE: { sqfs_inode_file_ext_t file = { .blocks_start = htole64(n->data.file_ext.blocks_start), .file_size = htole64(n->data.file_ext.file_size), .sparse = htole64(n->data.file_ext.sparse), .nlink = htole32(n->data.file_ext.nlink), .fragment_idx = htole32(n->data.file_ext.fragment_idx), .fragment_offset = htole32(n->data.file_ext.fragment_offset), .xattr_idx = htole32(n->data.file_ext.xattr_idx), }; ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); if (ret) return ret; return write_block_sizes(ir, n); } case SQFS_INODE_EXT_SLINK: { sqfs_inode_slink_t slink = { .nlink = htole32(n->data.slink_ext.nlink), .target_size = htole32(n->data.slink_ext.target_size), }; sqfs_u32 xattr = htole32(n->data.slink_ext.xattr_idx); ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); if (ret) return ret; ret = sqfs_meta_writer_append(ir, n->extra, n->data.slink_ext.target_size); if (ret) return ret; return sqfs_meta_writer_append(ir, &xattr, sizeof(xattr)); } case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: { sqfs_inode_dev_ext_t dev = { .nlink = htole32(n->data.dev_ext.nlink), .devno = htole32(n->data.dev_ext.devno), .xattr_idx = htole32(n->data.dev_ext.xattr_idx), }; return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); } case SQFS_INODE_EXT_FIFO: case SQFS_INODE_EXT_SOCKET: { sqfs_inode_ipc_ext_t ipc = { .nlink = htole32(n->data.ipc_ext.nlink), .xattr_idx = htole32(n->data.ipc_ext.xattr_idx), }; return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); } default: break; } return SQFS_ERROR_UNSUPPORTED; } squashfs-tools-ng-1.1.3/lib/sqfs/write_super.c000066400000000000000000000026301410627516300213550ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * write_super.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/super.h" #include "sqfs/io.h" #include "compat.h" int sqfs_super_write(const sqfs_super_t *super, sqfs_file_t *file) { sqfs_super_t copy; copy.magic = htole32(super->magic); copy.inode_count = htole32(super->inode_count); copy.modification_time = htole32(super->modification_time); copy.block_size = htole32(super->block_size); copy.fragment_entry_count = htole32(super->fragment_entry_count); copy.compression_id = htole16(super->compression_id); copy.block_log = htole16(super->block_log); copy.flags = htole16(super->flags); copy.id_count = htole16(super->id_count); copy.version_major = htole16(super->version_major); copy.version_minor = htole16(super->version_minor); copy.root_inode_ref = htole64(super->root_inode_ref); copy.bytes_used = htole64(super->bytes_used); copy.id_table_start = htole64(super->id_table_start); copy.xattr_id_table_start = htole64(super->xattr_id_table_start); copy.inode_table_start = htole64(super->inode_table_start); copy.directory_table_start = htole64(super->directory_table_start); copy.fragment_table_start = htole64(super->fragment_table_start); copy.export_table_start = htole64(super->export_table_start); return file->write_at(file, 0, ©, sizeof(copy)); } squashfs-tools-ng-1.1.3/lib/sqfs/write_table.c000066400000000000000000000031361410627516300213100ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * write_table.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/meta_writer.h" #include "sqfs/error.h" #include "sqfs/super.h" #include "sqfs/table.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" #include int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, const void *data, size_t table_size, sqfs_u64 *start) { size_t block_count, list_size, diff, blkidx = 0; sqfs_u64 off, *locations; sqfs_meta_writer_t *m; int ret; block_count = table_size / SQFS_META_BLOCK_SIZE; if ((table_size % SQFS_META_BLOCK_SIZE) != 0) ++block_count; locations = alloc_array(sizeof(sqfs_u64), block_count); if (locations == NULL) return SQFS_ERROR_ALLOC; /* Write actual data */ m = sqfs_meta_writer_create(file, cmp, 0); if (m == NULL) { ret = SQFS_ERROR_ALLOC; goto out_idx; } while (table_size > 0) { locations[blkidx++] = htole64(file->get_size(file)); diff = SQFS_META_BLOCK_SIZE; if (diff > table_size) diff = table_size; ret = sqfs_meta_writer_append(m, data, diff); if (ret) goto out; data = (const char *)data + diff; table_size -= diff; } ret = sqfs_meta_writer_flush(m); if (ret) goto out; /* write location list */ *start = file->get_size(file); list_size = sizeof(sqfs_u64) * block_count; off = file->get_size(file); ret = file->write_at(file, off, locations, list_size); if (ret) goto out; /* cleanup */ ret = 0; out: sqfs_destroy(m); out_idx: free(locations); return ret; } squashfs-tools-ng-1.1.3/lib/sqfs/xattr/000077500000000000000000000000001410627516300200025ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/sqfs/xattr/xattr.c000066400000000000000000000017611410627516300213150ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * write_xattr.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/xattr.h" #include "sqfs/error.h" #include static const struct { const char *prefix; SQFS_XATTR_TYPE type; } xattr_types[] = { { "user.", SQFS_XATTR_USER }, { "trusted.", SQFS_XATTR_TRUSTED }, { "security.", SQFS_XATTR_SECURITY }, }; int sqfs_get_xattr_prefix_id(const char *key) { size_t i, len; for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { len = strlen(xattr_types[i].prefix); if (strncmp(key, xattr_types[i].prefix, len) == 0 && strlen(key) > len) { return xattr_types[i].type; } } return SQFS_ERROR_UNSUPPORTED; } const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) { size_t i; for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { if (xattr_types[i].type == id) return xattr_types[i].prefix; } return NULL; } squashfs-tools-ng-1.1.3/lib/sqfs/xattr/xattr_reader.c000066400000000000000000000171051410627516300226360ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_reader.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/xattr_reader.h" #include "sqfs/meta_reader.h" #include "sqfs/super.h" #include "sqfs/xattr.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "util.h" #include #include #include struct sqfs_xattr_reader_t { sqfs_object_t base; sqfs_u64 xattr_start; sqfs_u64 xattr_end; size_t num_id_blocks; size_t num_ids; sqfs_u64 *id_block_starts; sqfs_meta_reader_t *idrd; sqfs_meta_reader_t *kvrd; }; static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) { const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); if (copy == NULL) return NULL; memcpy(copy, xr, sizeof(*xr)); if (xr->kvrd != NULL) { copy->kvrd = sqfs_copy(xr->kvrd); if (copy->kvrd == NULL) goto fail_kvrd; } if (xr->idrd != NULL) { copy->idrd = sqfs_copy(xr->idrd); if (copy->idrd == NULL) goto fail_idrd; } if (xr->id_block_starts != NULL) { copy->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); if (copy->id_block_starts == NULL) goto fail_idblk; memcpy(copy->id_block_starts, xr->id_block_starts, sizeof(sqfs_u64) * xr->num_id_blocks); } return (sqfs_object_t *)copy; fail_idblk: if (copy->idrd != NULL) sqfs_destroy(copy->idrd); fail_idrd: if (copy->kvrd != NULL) sqfs_destroy(copy->kvrd); fail_kvrd: free(copy); return NULL; } static void xattr_reader_destroy(sqfs_object_t *obj) { sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; if (xr->kvrd != NULL) sqfs_destroy(xr->kvrd); if (xr->idrd != NULL) sqfs_destroy(xr->idrd); free(xr->id_block_starts); free(xr); } int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, sqfs_file_t *file, sqfs_compressor_t *cmp) { sqfs_xattr_id_table_t idtbl; size_t i; int err; /* sanity check */ if (super->flags & SQFS_FLAG_NO_XATTRS) return 0; if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) return 0; if (super->xattr_id_table_start >= super->bytes_used) return SQFS_ERROR_OUT_OF_BOUNDS; /* cleanup pre-existing data */ if (xr->idrd != NULL) { sqfs_destroy(xr->idrd); xr->idrd = NULL; } if (xr->kvrd != NULL) { sqfs_destroy(xr->kvrd); xr->kvrd = NULL; } free(xr->id_block_starts); xr->id_block_starts = NULL; /* read the locations table */ err = file->read_at(file, super->xattr_id_table_start, &idtbl, sizeof(idtbl)); if (err) return err; xr->xattr_start = le64toh(idtbl.xattr_table_start); xr->num_ids = le32toh(idtbl.xattr_ids); xr->num_id_blocks = (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) xr->num_id_blocks += 1; xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); if (xr->id_block_starts == NULL) { if (errno == EOVERFLOW) return SQFS_ERROR_OVERFLOW; return SQFS_ERROR_ALLOC; } err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), xr->id_block_starts, sizeof(sqfs_u64) * xr->num_id_blocks); if (err) goto fail_blocks; for (i = 0; i < xr->num_id_blocks; ++i) { xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); if (xr->id_block_starts[i] > super->bytes_used) { err = SQFS_ERROR_OUT_OF_BOUNDS; goto fail_blocks; } } /* create the meta data readers */ xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, super->bytes_used); if (xr->idrd == NULL) goto fail_blocks; xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, super->bytes_used); if (xr->kvrd == NULL) goto fail_idrd; xr->xattr_end = super->bytes_used; return 0; fail_idrd: sqfs_destroy(xr->idrd); xr->idrd = NULL; fail_blocks: free(xr->id_block_starts); xr->id_block_starts = NULL; return err; } int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, sqfs_xattr_entry_t **key_out) { sqfs_xattr_entry_t key, *out; const char *prefix; size_t plen, total; int ret; ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); if (ret) return ret; key.type = le16toh(key.type); key.size = le16toh(key.size); prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); if (prefix == NULL) return SQFS_ERROR_UNSUPPORTED; plen = strlen(prefix); if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || SZ_ADD_OV(sizeof(*out), total, &total)) { return SQFS_ERROR_OVERFLOW; } out = calloc(1, total); if (out == NULL) return SQFS_ERROR_ALLOC; *out = key; memcpy(out->key, prefix, plen); ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); if (ret) { free(out); return ret; } *key_out = out; return 0; } int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, const sqfs_xattr_entry_t *key, sqfs_xattr_value_t **val_out) { size_t offset, new_offset, size; sqfs_xattr_value_t value, *out; sqfs_u64 ref, start, new_start; int ret; ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); if (ret) return ret; if (key->type & SQFS_XATTR_FLAG_OOL) { ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); if (ret) return ret; sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); new_start = xr->xattr_start + (ref >> 16); if (new_start >= xr->xattr_end) return SQFS_ERROR_OUT_OF_BOUNDS; new_offset = ref & 0xFFFF; if (new_offset >= SQFS_META_BLOCK_SIZE) return SQFS_ERROR_OUT_OF_BOUNDS; ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); if (ret) return ret; ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); if (ret) return ret; } value.size = le32toh(value.size); if (SZ_ADD_OV(sizeof(*out), value.size, &size) || SZ_ADD_OV(size, 1, &size)) { return SQFS_ERROR_OVERFLOW; } out = calloc(1, size); if (out == NULL) return SQFS_ERROR_ALLOC; *out = value; ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); if (ret) goto fail; if (key->type & SQFS_XATTR_FLAG_OOL) { ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); if (ret) goto fail; } *val_out = out; return 0; fail: free(out); return ret; } int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, const sqfs_xattr_id_t *desc) { sqfs_u32 offset = desc->xattr & 0xFFFF; sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); return sqfs_meta_reader_seek(xr->kvrd, block, offset); } int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, sqfs_xattr_id_t *desc) { size_t block, offset; int ret; memset(desc, 0, sizeof(*desc)); if (idx == 0xFFFFFFFF) return 0; if (xr->kvrd == NULL || xr->idrd == NULL) return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; if (idx >= xr->num_ids) return SQFS_ERROR_OUT_OF_BOUNDS; offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], offset); if (ret) return ret; ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); if (ret) return ret; desc->xattr = le64toh(desc->xattr); desc->count = le32toh(desc->count); desc->size = le32toh(desc->size); return 0; } sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) { sqfs_xattr_reader_t *xr; if (flags != 0) return NULL; xr = calloc(1, sizeof(*xr)); if (xr == NULL) return NULL; ((sqfs_object_t *)xr)->copy = xattr_reader_copy; ((sqfs_object_t *)xr)->destroy = xattr_reader_destroy; return xr; } squashfs-tools-ng-1.1.3/lib/sqfs/xattr/xattr_writer.c000066400000000000000000000055731410627516300227160ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_writer.c * * Copyright (C) 2019 David Oberhollenzer */ #include "xattr_writer.h" static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) { const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; sqfs_xattr_writer_t *copy; kv_block_desc_t *it; copy = calloc(1, sizeof(*copy)); if (copy == NULL) return NULL; memcpy(copy, xwr, sizeof(*xwr)); if (str_table_copy(©->keys, &xwr->keys)) goto fail_keys; if (str_table_copy(©->values, &xwr->values)) goto fail_values; if (array_init_copy(©->kv_pairs, &xwr->kv_pairs)) goto fail_pairs; if (rbtree_copy(&xwr->kv_block_tree, ©->kv_block_tree) != 0) goto fail_tree; for (it = xwr->kv_block_first; it != NULL; it = it->next) { rbtree_node_t *n = rbtree_lookup(©->kv_block_tree, it); if (copy->kv_block_last == NULL) { copy->kv_block_first = rbtree_node_key(n); copy->kv_block_last = copy->kv_block_first; } else { copy->kv_block_last->next = rbtree_node_key(n); copy->kv_block_last = copy->kv_block_last->next; } copy->kv_block_last->next = NULL; } return (sqfs_object_t *)copy; fail_tree: array_cleanup(©->kv_pairs); fail_pairs: str_table_cleanup(©->values); fail_values: str_table_cleanup(©->keys); fail_keys: free(copy); return NULL; } static void xattr_writer_destroy(sqfs_object_t *obj) { sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; rbtree_cleanup(&xwr->kv_block_tree); array_cleanup(&xwr->kv_pairs); str_table_cleanup(&xwr->values); str_table_cleanup(&xwr->keys); free(xwr); } static int block_compare(const void *context, const void *lhs, const void *rhs) { const sqfs_xattr_writer_t *xwr = context; const kv_block_desc_t *l = lhs, *r = rhs; if (l->count != r->count) return l->count < r->count ? -1 : 1; if (l->start == r->start) return 0; return memcmp((sqfs_u64 *)xwr->kv_pairs.data + l->start, (sqfs_u64 *)xwr->kv_pairs.data + r->start, l->count * xwr->kv_pairs.size); } sqfs_xattr_writer_t *sqfs_xattr_writer_create(sqfs_u32 flags) { sqfs_xattr_writer_t *xwr; if (flags != 0) return NULL; xwr = calloc(1, sizeof(*xwr)); if (xwr == NULL) return NULL; if (str_table_init(&xwr->keys)) goto fail_keys; if (str_table_init(&xwr->values)) goto fail_values; if (array_init(&xwr->kv_pairs, sizeof(sqfs_u64), XATTR_INITIAL_PAIR_CAP)) { goto fail_pairs; } if (rbtree_init(&xwr->kv_block_tree, sizeof(kv_block_desc_t), sizeof(sqfs_u32), block_compare)) { goto fail_tree; } xwr->kv_block_tree.key_context = xwr; ((sqfs_object_t *)xwr)->copy = xattr_writer_copy; ((sqfs_object_t *)xwr)->destroy = xattr_writer_destroy; return xwr; fail_tree: array_cleanup(&xwr->kv_pairs); fail_pairs: str_table_cleanup(&xwr->values); fail_values: str_table_cleanup(&xwr->keys); fail_keys: free(xwr); return NULL; } squashfs-tools-ng-1.1.3/lib/sqfs/xattr/xattr_writer.h000066400000000000000000000022651410627516300227160ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_writer.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef XATTR_WRITER_H #define XATTR_WRITER_H #define SQFS_BUILDING_DLL #include "config.h" #include "sqfs/xattr_writer.h" #include "sqfs/meta_writer.h" #include "sqfs/super.h" #include "sqfs/xattr.h" #include "sqfs/error.h" #include "sqfs/block.h" #include "sqfs/io.h" #include "str_table.h" #include "rbtree.h" #include "array.h" #include "util.h" #include #include #include #define XATTR_INITIAL_PAIR_CAP 128 #define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) #define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) #define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) typedef struct kv_block_desc_t { struct kv_block_desc_t *next; size_t start; size_t count; sqfs_u64 start_ref; size_t size_bytes; } kv_block_desc_t; struct sqfs_xattr_writer_t { sqfs_object_t base; str_table_t keys; str_table_t values; array_t kv_pairs; size_t kv_start; rbtree_t kv_block_tree; kv_block_desc_t *kv_block_first; kv_block_desc_t *kv_block_last; size_t num_blocks; }; #endif /* XATTR_WRITER_H */ squashfs-tools-ng-1.1.3/lib/sqfs/xattr/xattr_writer_flush.c000066400000000000000000000171271410627516300241150ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_writer_flush.c * * Copyright (C) 2019 David Oberhollenzer */ #include "xattr_writer.h" static const char *hexmap = "0123456789ABCDEF"; static void *from_base32(const char *input, size_t *size_out) { sqfs_u8 lo, hi, *out, *ptr; size_t len; len = strlen(input); *size_out = len / 2; out = malloc(*size_out); if (out == NULL) return NULL; ptr = out; while (*input != '\0') { lo = strchr(hexmap, *(input++)) - hexmap; hi = strchr(hexmap, *(input++)) - hexmap; *(ptr++) = lo | (hi << 4); } return out; } static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, bool value_is_ool) { sqfs_xattr_entry_t kent; int type, err; size_t len; type = sqfs_get_xattr_prefix_id(key); assert(type >= 0); key = strchr(key, '.'); assert(key != NULL); ++key; len = strlen(key); if (value_is_ool) type |= SQFS_XATTR_FLAG_OOL; memset(&kent, 0, sizeof(kent)); kent.type = htole16(type); kent.size = htole16(len); err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); if (err) return err; err = sqfs_meta_writer_append(mw, key, len); if (err) return err; return sizeof(kent) + len; } static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, sqfs_u64 *value_ref_out) { sqfs_xattr_value_t vent; sqfs_u32 offset; sqfs_u64 block; size_t size; void *value; int err; value = from_base32(value_str, &size); if (value == NULL) return SQFS_ERROR_ALLOC; memset(&vent, 0, sizeof(vent)); vent.size = htole32(size); sqfs_meta_writer_get_position(mw, &block, &offset); *value_ref_out = (block << 16) | (offset & 0xFFFF); err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); if (err) goto fail; err = sqfs_meta_writer_append(mw, value, size); if (err) goto fail; free(value); return sizeof(vent) + size; fail: free(value); return err; } static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) { sqfs_xattr_value_t vent; sqfs_u64 ref; int err; memset(&vent, 0, sizeof(vent)); vent.size = htole32(sizeof(location)); ref = htole64(location); err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); if (err) return err; err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); if (err) return err; return sizeof(vent) + sizeof(ref); } static bool should_store_ool(const char *val_str, size_t refcount) { if (refcount < 2) return false; /* Storing in line needs this many bytes: refcount * len Storing out-of-line needs this many: len + (refcount - 1) * 8 Out-of-line prefereable iff refcount > 1 and: refcount * len > len + (refcount - 1) * 8 => refcount * len - len > (refcount - 1) * 8 => (refcount - 1) * len > (refcount - 1) * 8 => len > 8 */ return (strlen(val_str) / 2) > sizeof(sqfs_u64); } static int write_block_pairs(const sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw, const kv_block_desc_t *blk, sqfs_u64 *ool_locations) { const char *key_str, *value_str; sqfs_s32 diff, total = 0; size_t i, refcount; sqfs_u64 ref; for (i = 0; i < blk->count; ++i) { sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[blk->start + i]; sqfs_u32 key_idx = GET_KEY(ent); sqfs_u32 val_idx = GET_VALUE(ent); key_str = str_table_get_string(&xwr->keys, key_idx); value_str = str_table_get_string(&xwr->values, val_idx); if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { diff = write_key(mw, key_str, false); if (diff < 0) return diff; total += diff; diff = write_value(mw, value_str, &ref); if (diff < 0) return diff; total += diff; refcount = str_table_get_ref_count(&xwr->values, val_idx); if (should_store_ool(value_str, refcount)) ool_locations[val_idx] = ref; } else { diff = write_key(mw, key_str, true); if (diff < 0) return diff; total += diff; diff = write_value_ool(mw, ool_locations[val_idx]); if (diff < 0) return diff; total += diff; } } return total; } static int write_kv_pairs(const sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw) { sqfs_u64 block, *ool_locations; kv_block_desc_t *blk; sqfs_u32 offset; sqfs_s32 size; size_t i; ool_locations = alloc_array(sizeof(ool_locations[0]), str_table_count(&xwr->values)); if (ool_locations == NULL) return SQFS_ERROR_ALLOC; for (i = 0; i < str_table_count(&xwr->values); ++i) ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { sqfs_meta_writer_get_position(mw, &block, &offset); blk->start_ref = (block << 16) | (offset & 0xFFFF); size = write_block_pairs(xwr, mw, blk, ool_locations); if (size < 0) { free(ool_locations); return size; } blk->size_bytes = size; } free(ool_locations); return sqfs_meta_writer_flush(mw); } static int write_id_table(const sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw, sqfs_u64 *locations) { sqfs_xattr_id_t id_ent; kv_block_desc_t *blk; sqfs_u32 offset; sqfs_u64 block; size_t i = 0; int err; locations[i++] = 0; for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { memset(&id_ent, 0, sizeof(id_ent)); id_ent.xattr = htole64(blk->start_ref); id_ent.count = htole32(blk->count); id_ent.size = htole32(blk->size_bytes); err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); if (err) return err; sqfs_meta_writer_get_position(mw, &block, &offset); if (block != locations[i - 1]) locations[i++] = block; } return sqfs_meta_writer_flush(mw); } static int write_location_table(const sqfs_xattr_writer_t *xwr, sqfs_u64 kv_start, sqfs_file_t *file, const sqfs_super_t *super, sqfs_u64 *locations, size_t loc_count) { sqfs_xattr_id_table_t idtbl; int err; memset(&idtbl, 0, sizeof(idtbl)); idtbl.xattr_table_start = htole64(kv_start); idtbl.xattr_ids = htole32(xwr->num_blocks); err = file->write_at(file, super->xattr_id_table_start, &idtbl, sizeof(idtbl)); if (err) return err; return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), locations, sizeof(locations[0]) * loc_count); } static int alloc_location_table(const sqfs_xattr_writer_t *xwr, sqfs_u64 **tbl_out, size_t *szout) { sqfs_u64 *locations; size_t size, count; if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) return SQFS_ERROR_OVERFLOW; count = size / SQFS_META_BLOCK_SIZE; if (size % SQFS_META_BLOCK_SIZE) ++count; locations = alloc_array(sizeof(sqfs_u64), count); if (locations == NULL) return SQFS_ERROR_ALLOC; *tbl_out = locations; *szout = count; return 0; } int sqfs_xattr_writer_flush(const sqfs_xattr_writer_t *xwr, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp) { sqfs_u64 *locations = NULL, kv_start, id_start; sqfs_meta_writer_t *mw; size_t i, count; int err; if (xwr->kv_pairs.used == 0 || xwr->num_blocks == 0) { super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; super->flags |= SQFS_FLAG_NO_XATTRS; return 0; } mw = sqfs_meta_writer_create(file, cmp, 0); if (mw == NULL) return SQFS_ERROR_ALLOC; kv_start = file->get_size(file); err = write_kv_pairs(xwr, mw); if (err) goto out; sqfs_meta_writer_reset(mw); id_start = file->get_size(file); err = alloc_location_table(xwr, &locations, &count); if (err) goto out; err = write_id_table(xwr, mw, locations); if (err) goto out; super->xattr_id_table_start = file->get_size(file); super->flags &= ~SQFS_FLAG_NO_XATTRS; for (i = 0; i < count; ++i) locations[i] = htole64(locations[i] + id_start); err = write_location_table(xwr, kv_start, file, super, locations, count); out: free(locations); sqfs_destroy(mw); return err; } squashfs-tools-ng-1.1.3/lib/sqfs/xattr/xattr_writer_record.c000066400000000000000000000060611410627516300242450ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * xattr_writer_record.c * * Copyright (C) 2019 David Oberhollenzer */ #include "xattr_writer.h" static const char *hexmap = "0123456789ABCDEF"; static char *to_base32(const void *input, size_t size) { const sqfs_u8 *in = input; char *out, *ptr; size_t i; out = malloc(2 * size + 1); if (out == NULL) return NULL; ptr = out; for (i = 0; i < size; ++i) { *(ptr++) = hexmap[ in[i] & 0x0F]; *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; } *ptr = '\0'; return out; } static int compare_u64(const void *a, const void *b) { sqfs_u64 lhs = *((const sqfs_u64 *)a); sqfs_u64 rhs = *((const sqfs_u64 *)b); return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); } int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr, sqfs_u32 flags) { if (flags != 0) return SQFS_ERROR_UNSUPPORTED; xwr->kv_start = xwr->kv_pairs.used; return 0; } int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, const void *value, size_t size) { size_t i, key_index, old_value_index, value_index; sqfs_u64 kv_pair; char *value_str; int err; if (sqfs_get_xattr_prefix_id(key) < 0) return SQFS_ERROR_UNSUPPORTED; err = str_table_get_index(&xwr->keys, key, &key_index); if (err) return err; value_str = to_base32(value, size); if (value_str == NULL) return SQFS_ERROR_ALLOC; err = str_table_get_index(&xwr->values, value_str, &value_index); free(value_str); if (err) return err; str_table_add_ref(&xwr->values, value_index); if (sizeof(size_t) > sizeof(sqfs_u32)) { if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) return SQFS_ERROR_OVERFLOW; } kv_pair = MK_PAIR(key_index, value_index); for (i = xwr->kv_start; i < xwr->kv_pairs.used; ++i) { sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[i]; if (ent == kv_pair) return 0; if (GET_KEY(ent) == key_index) { old_value_index = GET_VALUE(ent); str_table_del_ref(&xwr->values, old_value_index); ((sqfs_u64 *)xwr->kv_pairs.data)[i] = kv_pair; return 0; } } return array_append(&xwr->kv_pairs, &kv_pair); } int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) { kv_block_desc_t blk; rbtree_node_t *n; sqfs_u32 index; int ret; memset(&blk, 0, sizeof(blk)); blk.start = xwr->kv_start; blk.count = xwr->kv_pairs.used - xwr->kv_start; if (blk.count == 0) { *out = 0xFFFFFFFF; return 0; } array_sort_range(&xwr->kv_pairs, blk.start, blk.count, compare_u64); n = rbtree_lookup(&xwr->kv_block_tree, &blk); if (n != NULL) { index = *((sqfs_u32 *)rbtree_node_value(n)); xwr->kv_pairs.used = xwr->kv_start; } else { index = xwr->num_blocks; ret = rbtree_insert(&xwr->kv_block_tree, &blk, &index); if (ret != 0) return ret; xwr->num_blocks += 1; n = rbtree_lookup(&xwr->kv_block_tree, &blk); if (xwr->kv_block_last == NULL) { xwr->kv_block_first = rbtree_node_key(n); xwr->kv_block_last = xwr->kv_block_first; } else { xwr->kv_block_last->next = rbtree_node_key(n); xwr->kv_block_last = xwr->kv_block_last->next; } } *out = index; return 0; } squashfs-tools-ng-1.1.3/lib/tar/000077500000000000000000000000001410627516300164525ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/tar/Makemodule.am000066400000000000000000000010561410627516300210560ustar00rootroot00000000000000libtar_a_SOURCES = lib/tar/read_header.c lib/tar/write_header.c libtar_a_SOURCES += lib/tar/number.c lib/tar/checksum.c lib/tar/cleanup.c libtar_a_SOURCES += lib/tar/read_sparse_map.c lib/tar/read_sparse_map_old.c libtar_a_SOURCES += lib/tar/base64.c lib/tar/urldecode.c lib/tar/internal.h libtar_a_SOURCES += lib/tar/padd_file.c lib/tar/record_to_memory.c libtar_a_SOURCES += lib/tar/pax_header.c lib/tar/read_sparse_map_new.c libtar_a_SOURCES += include/tar.h libtar_a_CFLAGS = $(AM_CFLAGS) libtar_a_CPPFLAGS = $(AM_CPPFLAGS) noinst_LIBRARIES += libtar.a squashfs-tools-ng-1.1.3/lib/tar/base64.c000066400000000000000000000020311410627516300176760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * base64.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static sqfs_u8 convert(char in) { if (isupper(in)) return in - 'A'; if (islower(in)) return in - 'a' + 26; if (isdigit(in)) return in - '0' + 52; if (in == '+') return 62; if (in == '/' || in == '-') return 63; return 0; } size_t base64_decode(sqfs_u8 *out, const char *in, size_t len) { sqfs_u8 *start = out; while (len > 0) { unsigned int diff = 0, value = 0; while (diff < 4 && len > 0) { if (*in == '=' || *in == '_' || *in == '\0') { len = 0; } else { value = (value << 6) | convert(*(in++)); --len; ++diff; } } if (diff < 2) break; value <<= 6 * (4 - diff); switch (diff) { case 4: out[2] = value & 0xff; /* fall-through */ case 3: out[1] = (value >> 8) & 0xff; /* fall-through */ default: out[0] = (value >> 16) & 0xff; } out += (diff * 3) / 4; } *out = '\0'; return out - start; } squashfs-tools-ng-1.1.3/lib/tar/checksum.c000066400000000000000000000022111410627516300204140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * checksum.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static unsigned int get_checksum(const tar_header_t *hdr) { const unsigned char *header_start = (const unsigned char *)hdr; const unsigned char *chksum_start = (const unsigned char *)hdr->chksum; const unsigned char *header_end = header_start + sizeof(*hdr); const unsigned char *chksum_end = chksum_start + sizeof(hdr->chksum); const unsigned char *p; unsigned int chksum = 0; for (p = header_start; p < chksum_start; p++) chksum += *p; for (; p < chksum_end; p++) chksum += ' '; for (; p < header_end; p++) chksum += *p; return chksum; } void update_checksum(tar_header_t *hdr) { unsigned int chksum = get_checksum(hdr); sprintf(hdr->chksum, "%06o", chksum); hdr->chksum[6] = '\0'; hdr->chksum[7] = ' '; } bool is_checksum_valid(const tar_header_t *hdr) { unsigned int calculated_chksum = get_checksum(hdr); sqfs_u64 read_chksum; if (read_octal(hdr->chksum, sizeof(hdr->chksum), &read_chksum)) return 0; return read_chksum == calculated_chksum; } squashfs-tools-ng-1.1.3/lib/tar/cleanup.c000066400000000000000000000012221410627516300202420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * cleanup.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" void free_sparse_list(sparse_map_t *sparse) { sparse_map_t *old; while (sparse != NULL) { old = sparse; sparse = sparse->next; free(old); } } void free_xattr_list(tar_xattr_t *list) { tar_xattr_t *old; while (list != NULL) { old = list; list = list->next; free(old); } } void clear_header(tar_header_decoded_t *hdr) { free_xattr_list(hdr->xattr); free_sparse_list(hdr->sparse); free(hdr->name); free(hdr->link_target); memset(hdr, 0, sizeof(*hdr)); } squashfs-tools-ng-1.1.3/lib/tar/internal.h000066400000000000000000000031201410627516300204330ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * internal.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef INTERNAL_H #define INTERNAL_H #include "config.h" #include "tar.h" #include #include #include #include #include enum { PAX_SIZE = 0x001, PAX_UID = 0x002, PAX_GID = 0x004, PAX_DEV_MAJ = 0x008, PAX_DEV_MIN = 0x010, PAX_NAME = 0x020, PAX_SLINK_TARGET = 0x040, PAX_MTIME = 0x100, PAX_SPARSE_SIZE = 0x400, PAX_SPARSE_GNU_1_X = 0x800, }; enum { ETV_UNKNOWN = 0, ETV_V7_UNIX, ETV_PRE_POSIX, ETV_POSIX, }; #define TAR_MAX_SYMLINK_LEN (65536) #define TAR_MAX_PATH_LEN (65536) #define TAR_MAX_PAX_LEN (65536) #define TAR_MAX_SPARSE_ENT (65536) int read_octal(const char *str, int digits, sqfs_u64 *out); int read_binary(const char *str, int digits, sqfs_u64 *out); int read_number(const char *str, int digits, sqfs_u64 *out); int pax_read_decimal(const char *str, sqfs_u64 *out); void update_checksum(tar_header_t *hdr); bool is_checksum_valid(const tar_header_t *hdr); sparse_map_t *read_sparse_map(const char *line); sparse_map_t *read_gnu_old_sparse(istream_t *fp, tar_header_t *hdr); sparse_map_t *read_gnu_new_sparse(istream_t *fp, tar_header_decoded_t *out); void free_sparse_list(sparse_map_t *sparse); size_t base64_decode(sqfs_u8 *out, const char *in, size_t len); void urldecode(char *str); char *record_to_memory(istream_t *fp, size_t size); int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax, tar_header_decoded_t *out); #endif /* INTERNAL_H */ squashfs-tools-ng-1.1.3/lib/tar/number.c000066400000000000000000000032261410627516300201110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * number.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" int read_octal(const char *str, int digits, sqfs_u64 *out) { sqfs_u64 result = 0; while (digits > 0 && isspace(*str)) { ++str; --digits; } while (digits > 0 && *str >= '0' && *str <= '7') { if (result > 0x1FFFFFFFFFFFFFFFUL) { fputs("numeric overflow parsing tar header\n", stderr); return -1; } result = (result << 3) | (*(str++) - '0'); --digits; } *out = result; return 0; } int read_binary(const char *str, int digits, sqfs_u64 *out) { sqfs_u64 x, ov, result = 0; bool first = true; while (digits > 0) { x = *((const unsigned char *)str++); --digits; if (first) { first = false; if (x == 0xFF) { result = 0xFFFFFFFFFFFFFFFFUL; } else { x &= 0x7F; result = 0; if (digits > 7 && x != 0) goto fail_ov; } } ov = (result >> 56) & 0xFF; if (ov != 0 && ov != 0xFF) goto fail_ov; result = (result << 8) | x; } *out = result; return 0; fail_ov: fputs("numeric overflow parsing tar header\n", stderr); return -1; } int read_number(const char *str, int digits, sqfs_u64 *out) { if (*((const unsigned char *)str) & 0x80) return read_binary(str, digits, out); return read_octal(str, digits, out); } int pax_read_decimal(const char *str, sqfs_u64 *out) { sqfs_u64 result = 0; while (*str >= '0' && *str <= '9') { if (result > 0xFFFFFFFFFFFFFFFFUL / 10) { fputs("numeric overflow parsing pax header\n", stderr); return -1; } result = (result * 10) + (*(str++) - '0'); } *out = result; return 0; } squashfs-tools-ng-1.1.3/lib/tar/padd_file.c000066400000000000000000000012221410627516300205220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * padd_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include #include int padd_file(ostream_t *fp, sqfs_u64 size) { size_t padd_sz = size % TAR_RECORD_SIZE; int status = -1; sqfs_u8 *buffer; if (padd_sz == 0) return 0; padd_sz = TAR_RECORD_SIZE - padd_sz; buffer = calloc(1, padd_sz); if (buffer == NULL) goto fail_errno; if (ostream_append(fp, buffer, padd_sz)) goto out; status = 0; out: free(buffer); return status; fail_errno: perror("padding output file to block size"); goto out; } squashfs-tools-ng-1.1.3/lib/tar/pax_header.c000066400000000000000000000114241410627516300207200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * pax_header.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static tar_xattr_t *mkxattr(const char *key, size_t keylen, const char *value, size_t valuelen) { tar_xattr_t *xattr; xattr = calloc(1, sizeof(*xattr) + keylen + 1 + valuelen + 1); if (xattr == NULL) return NULL; xattr->key = xattr->data; xattr->value = (sqfs_u8 *)xattr->data + keylen + 1; xattr->value_len = valuelen; memcpy(xattr->key, key, keylen); memcpy(xattr->value, value, valuelen); return xattr; } int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax, tar_header_decoded_t *out) { char *buffer, *line, *key, *ptr, *value, *end; sparse_map_t *sparse_last = NULL, *sparse; sqfs_u64 field, offset = 0, num_bytes = 0; tar_xattr_t *xattr; long len; buffer = record_to_memory(fp, entsize); if (buffer == NULL) return -1; end = buffer + entsize; for (line = buffer; line < end; line += len) { len = strtol(line, &ptr, 10); if (ptr == line || !isspace(*ptr) || len <= 0) goto fail_malformed; if (len > (end - line)) goto fail_ov; line[len - 1] = '\0'; while (ptr < end && isspace(*ptr)) ++ptr; if (ptr >= end || (ptr - line) >= len) goto fail_malformed; if (!strncmp(ptr, "uid=", 4)) { if (pax_read_decimal(ptr + 4, &field)) goto fail; out->sb.st_uid = field; *set_by_pax |= PAX_UID; } else if (!strncmp(ptr, "gid=", 4)) { if (pax_read_decimal(ptr + 4, &field)) goto fail; out->sb.st_gid = field; *set_by_pax |= PAX_GID; } else if (!strncmp(ptr, "path=", 5)) { free(out->name); out->name = strdup(ptr + 5); if (out->name == NULL) goto fail_errno; *set_by_pax |= PAX_NAME; } else if (!strncmp(ptr, "size=", 5)) { if (pax_read_decimal(ptr + 5, &out->record_size)) goto fail; *set_by_pax |= PAX_SIZE; } else if (!strncmp(ptr, "linkpath=", 9)) { free(out->link_target); out->link_target = strdup(ptr + 9); if (out->link_target == NULL) goto fail_errno; *set_by_pax |= PAX_SLINK_TARGET; } else if (!strncmp(ptr, "mtime=", 6)) { if (ptr[6] == '-') { if (pax_read_decimal(ptr + 7, &field)) goto fail; out->mtime = -((sqfs_s64)field); } else { if (pax_read_decimal(ptr + 6, &field)) goto fail; out->mtime = field; } *set_by_pax |= PAX_MTIME; } else if (!strncmp(ptr, "GNU.sparse.name=", 16)) { free(out->name); out->name = strdup(ptr + 16); if (out->name == NULL) goto fail_errno; *set_by_pax |= PAX_NAME; } else if (!strncmp(ptr, "GNU.sparse.map=", 15)) { free_sparse_list(out->sparse); sparse_last = NULL; out->sparse = read_sparse_map(ptr + 15); if (out->sparse == NULL) goto fail; } else if (!strncmp(ptr, "GNU.sparse.size=", 16)) { if (pax_read_decimal(ptr + 16, &out->actual_size)) goto fail; *set_by_pax |= PAX_SPARSE_SIZE; } else if (!strncmp(ptr, "GNU.sparse.realsize=", 20)) { if (pax_read_decimal(ptr + 20, &out->actual_size)) goto fail; *set_by_pax |= PAX_SPARSE_SIZE; } else if (!strncmp(ptr, "GNU.sparse.major=", 17) || !strncmp(ptr, "GNU.sparse.minor=", 17)) { *set_by_pax |= PAX_SPARSE_GNU_1_X; } else if (!strncmp(ptr, "GNU.sparse.offset=", 18)) { if (pax_read_decimal(ptr + 18, &offset)) goto fail; } else if (!strncmp(ptr, "GNU.sparse.numbytes=", 20)) { if (pax_read_decimal(ptr + 20, &num_bytes)) goto fail; sparse = calloc(1, sizeof(*sparse)); if (sparse == NULL) goto fail_errno; sparse->offset = offset; sparse->count = num_bytes; if (sparse_last == NULL) { free_sparse_list(out->sparse); out->sparse = sparse_last = sparse; } else { sparse_last->next = sparse; sparse_last = sparse; } } else if (!strncmp(ptr, "SCHILY.xattr.", 13)) { key = ptr + 13; ptr = strrchr(key, '='); if (ptr == NULL || ptr == key) continue; value = ptr + 1; xattr = mkxattr(key, ptr - key, value, len - (value - line) - 1); if (xattr == NULL) goto fail_errno; xattr->next = out->xattr; out->xattr = xattr; } else if (!strncmp(ptr, "LIBARCHIVE.xattr.", 17)) { key = ptr + 17; ptr = strrchr(key, '='); if (ptr == NULL || ptr == key) continue; value = ptr + 1; xattr = mkxattr(key, ptr - key, value, strlen(value)); if (xattr == NULL) goto fail_errno; urldecode(xattr->key); xattr->value_len = base64_decode(xattr->value, value, xattr->value_len); xattr->next = out->xattr; out->xattr = xattr; } } free(buffer); return 0; fail_malformed: fputs("Found a malformed PAX header.\n", stderr); goto fail; fail_ov: fputs("Numeric overflow in PAX header.\n", stderr); goto fail; fail_errno: perror("reading pax header"); goto fail; fail: free(buffer); return -1; } squashfs-tools-ng-1.1.3/lib/tar/read_header.c000066400000000000000000000163731410627516300210530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_header.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static bool is_zero_block(const tar_header_t *hdr) { const unsigned char *ptr = (const unsigned char *)hdr; return ptr[0] == '\0' && memcmp(ptr, ptr + 1, sizeof(*hdr) - 1) == 0; } static int check_version(const tar_header_t *hdr) { char buffer[sizeof(hdr->magic) + sizeof(hdr->version)]; memset(buffer, '\0', sizeof(buffer)); if (memcmp(hdr->magic, buffer, sizeof(hdr->magic)) == 0 && memcmp(hdr->version, buffer, sizeof(hdr->version)) == 0) return ETV_V7_UNIX; if (memcmp(hdr->magic, TAR_MAGIC, sizeof(hdr->magic)) == 0 && memcmp(hdr->version, TAR_VERSION, sizeof(hdr->version)) == 0) return ETV_POSIX; if (memcmp(hdr->magic, TAR_MAGIC_OLD, sizeof(hdr->magic)) == 0 && memcmp(hdr->version, TAR_VERSION_OLD, sizeof(hdr->version)) == 0) return ETV_PRE_POSIX; return ETV_UNKNOWN; } static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, tar_header_decoded_t *out, int version) { size_t len1, len2; sqfs_u64 field; if (!(set_by_pax & PAX_NAME)) { if (hdr->tail.posix.prefix[0] != '\0' && version == ETV_POSIX) { len1 = strnlen(hdr->name, sizeof(hdr->name)); len2 = strnlen(hdr->tail.posix.prefix, sizeof(hdr->tail.posix.prefix)); out->name = malloc(len1 + 1 + len2 + 1); if (out->name != NULL) { memcpy(out->name, hdr->tail.posix.prefix, len2); out->name[len2] = '/'; memcpy(out->name + len2 + 1, hdr->name, len1); out->name[len1 + 1 + len2] = '\0'; } } else { out->name = strndup(hdr->name, sizeof(hdr->name)); } if (out->name == NULL) { perror("decoding filename"); return -1; } } if (!(set_by_pax & PAX_SIZE)) { if (read_number(hdr->size, sizeof(hdr->size), &out->record_size)) return -1; } if (!(set_by_pax & PAX_UID)) { if (read_number(hdr->uid, sizeof(hdr->uid), &field)) return -1; out->sb.st_uid = field; } if (!(set_by_pax & PAX_GID)) { if (read_number(hdr->gid, sizeof(hdr->gid), &field)) return -1; out->sb.st_gid = field; } if (!(set_by_pax & PAX_DEV_MAJ)) { if (read_number(hdr->devmajor, sizeof(hdr->devmajor), &field)) return -1; out->sb.st_rdev = makedev(field, minor(out->sb.st_rdev)); } if (!(set_by_pax & PAX_DEV_MIN)) { if (read_number(hdr->devminor, sizeof(hdr->devminor), &field)) return -1; out->sb.st_rdev = makedev(major(out->sb.st_rdev), field); } if (!(set_by_pax & PAX_MTIME)) { if (read_number(hdr->mtime, sizeof(hdr->mtime), &field)) return -1; if (field & 0x8000000000000000UL) { field = ~field + 1; out->mtime = -((sqfs_s64)field); } else { out->mtime = field; } } if (read_octal(hdr->mode, sizeof(hdr->mode), &field)) return -1; out->sb.st_mode = field & 07777; if (hdr->typeflag == TAR_TYPE_LINK || hdr->typeflag == TAR_TYPE_SLINK) { if (!(set_by_pax & PAX_SLINK_TARGET)) { out->link_target = strndup(hdr->linkname, sizeof(hdr->linkname)); if (out->link_target == NULL) { perror("decoding symlink target"); return -1; } } } out->unknown_record = false; switch (hdr->typeflag) { case '\0': case TAR_TYPE_FILE: case TAR_TYPE_GNU_SPARSE: out->sb.st_mode |= S_IFREG; break; case TAR_TYPE_LINK: out->is_hard_link = true; break; case TAR_TYPE_SLINK: out->sb.st_mode = S_IFLNK | 0777; break; case TAR_TYPE_CHARDEV: out->sb.st_mode |= S_IFCHR; break; case TAR_TYPE_BLOCKDEV: out->sb.st_mode |= S_IFBLK; break; case TAR_TYPE_DIR: out->sb.st_mode |= S_IFDIR; break; case TAR_TYPE_FIFO: out->sb.st_mode |= S_IFIFO; break; default: out->unknown_record = true; break; } if (sizeof(time_t) * CHAR_BIT < 64) { if (out->mtime > (sqfs_s64)INT32_MAX) { out->sb.st_mtime = INT32_MAX; } else if (out->mtime < (sqfs_s64)INT32_MIN) { out->sb.st_mtime = INT32_MIN; } else { out->sb.st_mtime = out->mtime; } } else { out->sb.st_mtime = out->mtime; } return 0; } int read_header(istream_t *fp, tar_header_decoded_t *out) { unsigned int set_by_pax = 0; bool prev_was_zero = false; sqfs_u64 pax_size; tar_header_t hdr; int version, ret; memset(out, 0, sizeof(*out)); for (;;) { ret = istream_read(fp, &hdr, sizeof(hdr)); if (ret < 0) goto fail; if ((size_t)ret < sizeof(hdr)) goto out_eof; if (is_zero_block(&hdr)) { if (prev_was_zero) goto out_eof; prev_was_zero = true; continue; } prev_was_zero = false; version = check_version(&hdr); if (version == ETV_UNKNOWN) goto fail_magic; if (!is_checksum_valid(&hdr)) goto fail_chksum; switch (hdr.typeflag) { case TAR_TYPE_GNU_SLINK: if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) goto fail; if (pax_size < 1 || pax_size > TAR_MAX_SYMLINK_LEN) goto fail_slink_len; free(out->link_target); out->link_target = record_to_memory(fp, pax_size); if (out->link_target == NULL) goto fail; set_by_pax |= PAX_SLINK_TARGET; continue; case TAR_TYPE_GNU_PATH: if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) goto fail; if (pax_size < 1 || pax_size > TAR_MAX_PATH_LEN) goto fail_path_len; free(out->name); out->name = record_to_memory(fp, pax_size); if (out->name == NULL) goto fail; set_by_pax |= PAX_NAME; continue; case TAR_TYPE_PAX_GLOBAL: if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) goto fail; skip_entry(fp, pax_size); continue; case TAR_TYPE_PAX: clear_header(out); if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) goto fail; if (pax_size < 1 || pax_size > TAR_MAX_PAX_LEN) goto fail_pax_len; set_by_pax = 0; if (read_pax_header(fp, pax_size, &set_by_pax, out)) goto fail; continue; case TAR_TYPE_GNU_SPARSE: free_sparse_list(out->sparse); out->sparse = read_gnu_old_sparse(fp, &hdr); if (out->sparse == NULL) goto fail; if (read_number(hdr.tail.gnu.realsize, sizeof(hdr.tail.gnu.realsize), &out->actual_size)) goto fail; break; default: break; } break; } if (decode_header(&hdr, set_by_pax, out, version)) goto fail; if (set_by_pax & PAX_SPARSE_GNU_1_X) { free_sparse_list(out->sparse); out->sparse = read_gnu_new_sparse(fp, out); if (out->sparse == NULL) goto fail; } if (out->sparse != NULL) { out->sb.st_size = out->actual_size; } else { out->sb.st_size = out->record_size; out->actual_size = out->record_size; } return 0; out_eof: clear_header(out); return 1; fail_slink_len: fprintf(stderr, "rejecting GNU symlink header with size %lu\n", (unsigned long)pax_size); goto fail; fail_path_len: fprintf(stderr, "rejecting GNU long path header with size %lu\n", (unsigned long)pax_size); goto fail; fail_pax_len: fprintf(stderr, "rejecting PAX header with size %lu\n", (unsigned long)pax_size); goto fail; fail_magic: fputs("input is not a ustar tar archive!\n", stderr); goto fail; fail_chksum: fputs("invalid tar header checksum!\n", stderr); goto fail; fail: clear_header(out); return -1; } int skip_padding(istream_t *fp, sqfs_u64 size) { size_t tail = size % 512; return tail ? istream_skip(fp, 512 - tail) : 0; } int skip_entry(istream_t *fp, sqfs_u64 size) { size_t tail = size % 512; return istream_skip(fp, tail ? (size + 512 - tail) : size); } squashfs-tools-ng-1.1.3/lib/tar/read_sparse_map.c000066400000000000000000000017201410627516300217430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_sparse_map.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" sparse_map_t *read_sparse_map(const char *line) { sparse_map_t *last = NULL, *list = NULL, *ent = NULL; do { ent = calloc(1, sizeof(*ent)); if (ent == NULL) goto fail_errno; if (pax_read_decimal(line, &ent->offset)) goto fail_format; while (isdigit(*line)) ++line; if (*(line++) != ',') goto fail_format; if (pax_read_decimal(line, &ent->count)) goto fail_format; while (isdigit(*line)) ++line; if (last == NULL) { list = last = ent; } else { last->next = ent; last = ent; } } while (*(line++) == ','); return list; fail_errno: perror("parsing GNU pax sparse file record"); goto fail; fail_format: fputs("malformed GNU pax sparse file record\n", stderr); goto fail; fail: free_sparse_list(list); free(ent); return NULL; } squashfs-tools-ng-1.1.3/lib/tar/read_sparse_map_new.c000066400000000000000000000040511410627516300226140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_sparse_map_new.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static int decode(const char *str, size_t len, size_t *out) { size_t count = 0; *out = 0; while (count < len && isdigit(*str)) { if (SZ_MUL_OV(*out, 10, out)) return -1; if (SZ_ADD_OV(*out, (*(str++) - '0'), out)) return -1; ++count; } if (count == 0 || count == len) return 0; return (*str == '\n') ? ((int)count + 1) : -1; } sparse_map_t *read_gnu_new_sparse(istream_t *fp, tar_header_decoded_t *out) { sparse_map_t *last = NULL, *list = NULL, *ent = NULL; size_t i, count, value; char buffer[1024]; int diff, ret; if (out->record_size < 512) goto fail_format; ret = istream_read(fp, buffer, 512); if (ret < 0) goto fail; if (ret < 512) goto fail_format; diff = decode(buffer, 512, &count); if (diff <= 0) goto fail_format; out->record_size -= 512; if (count == 0 || count > TAR_MAX_SPARSE_ENT) goto fail_format; for (i = 0; i < (count * 2); ++i) { ret = decode(buffer + diff, 512 - diff, &value); if (ret < 0) goto fail_format; if (ret > 0) { diff += ret; } else { if (out->record_size < 512) goto fail_format; ret = istream_read(fp, buffer + 512, 512); if (ret < 0) goto fail; if (ret < 512) goto fail_format; ret = decode(buffer + diff, 1024 - diff, &value); if (ret <= 0) goto fail_format; memcpy(buffer, buffer + 512, 512); diff = diff + ret - 512; out->record_size -= 512; } if ((i & 0x01) == 0) { ent = calloc(1, sizeof(*ent)); if (ent == NULL) goto fail_errno; if (list == NULL) { list = last = ent; } else { last->next = ent; last = ent; } ent->offset = value; } else { ent->count = value; } } return list; fail_errno: perror("parsing GNU 1.0 style sparse file record"); goto fail; fail_format: fputs("Malformed GNU 1.0 style sparse file map.\n", stderr); goto fail; fail: free_sparse_list(list); return NULL; } squashfs-tools-ng-1.1.3/lib/tar/read_sparse_map_old.c000066400000000000000000000037061410627516300226070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_sparse_map_old.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" sparse_map_t *read_gnu_old_sparse(istream_t *fp, tar_header_t *hdr) { sparse_map_t *list = NULL, *end = NULL, *node; gnu_sparse_t sph; sqfs_u64 off, sz; int i, ret; for (i = 0; i < 4; ++i) { if (!isdigit(hdr->tail.gnu.sparse[i].offset[0])) break; if (!isdigit(hdr->tail.gnu.sparse[i].numbytes[0])) break; if (read_octal(hdr->tail.gnu.sparse[i].offset, sizeof(hdr->tail.gnu.sparse[i].offset), &off)) goto fail; if (read_octal(hdr->tail.gnu.sparse[i].numbytes, sizeof(hdr->tail.gnu.sparse[i].numbytes), &sz)) goto fail; node = calloc(1, sizeof(*node)); if (node == NULL) goto fail_errno; node->offset = off; node->count = sz; if (list == NULL) { list = end = node; } else { end->next = node; end = node; } } if (hdr->tail.gnu.isextended == 0) return list; do { ret = istream_read(fp, &sph, sizeof(sph)); if (ret < 0) goto fail; if ((size_t)ret < sizeof(sph)) { fputs("reading GNU sparse header: " "unexpected end-of-file\n", stderr); goto fail; } for (i = 0; i < 21; ++i) { if (!isdigit(sph.sparse[i].offset[0])) break; if (!isdigit(sph.sparse[i].numbytes[0])) break; if (read_octal(sph.sparse[i].offset, sizeof(sph.sparse[i].offset), &off)) goto fail; if (read_octal(sph.sparse[i].numbytes, sizeof(sph.sparse[i].numbytes), &sz)) goto fail; node = calloc(1, sizeof(*node)); if (node == NULL) goto fail_errno; node->offset = off; node->count = sz; if (list == NULL) { list = end = node; } else { end->next = node; end = node; } } } while (sph.isextended != 0); return list; fail_errno: perror("parsing GNU sparse header"); goto fail; fail: free_sparse_list(list); return NULL; } squashfs-tools-ng-1.1.3/lib/tar/record_to_memory.c000066400000000000000000000012671410627516300221740ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * record_to_memory.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "internal.h" char *record_to_memory(istream_t *fp, size_t size) { char *buffer = malloc(size + 1); int ret; if (buffer == NULL) goto fail_errno; ret = istream_read(fp, buffer, size); if (ret < 0) goto fail; if ((size_t)ret < size) { fputs("Reading tar record: unexpected end-of-file.\n", stderr); goto fail; } if (skip_padding(fp, size)) goto fail; buffer[size] = '\0'; return buffer; fail_errno: perror("reading tar record"); goto fail; fail: free(buffer); return NULL; } squashfs-tools-ng-1.1.3/lib/tar/urldecode.c000066400000000000000000000011311410627516300205600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * urldecode.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static int xdigit(int x) { if (isupper(x)) return x - 'A' + 0x0A; if (islower(x)) return x - 'a' + 0x0A; return x - '0'; } void urldecode(char *str) { unsigned char *out = (unsigned char *)str; char *in = str; int x; while (*in != '\0') { x = *(in++); if (x == '%' && isxdigit(in[0]) && isxdigit(in[1])) { x = xdigit(*(in++)) << 4; x |= xdigit(*(in++)); } *(out++) = x; } *out = '\0'; } squashfs-tools-ng-1.1.3/lib/tar/write_header.c000066400000000000000000000152711410627516300212660ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * write_header.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" static void write_binary(char *dst, sqfs_u64 value, int digits) { memset(dst, 0, digits); while (digits > 0) { ((unsigned char *)dst)[digits - 1] = value & 0xFF; --digits; value >>= 8; } ((unsigned char *)dst)[0] |= 0x80; } static void write_number(char *dst, sqfs_u64 value, int digits) { sqfs_u64 mask = 0; char buffer[64]; int i; for (i = 0; i < (digits - 1); ++i) mask = (mask << 3) | 7; if (value <= mask) { sprintf(buffer, "%0*lo ", digits - 1, (unsigned long)value); memcpy(dst, buffer, digits); } else if (value <= ((mask << 3) | 7)) { sprintf(buffer, "%0*lo", digits, (unsigned long)value); memcpy(dst, buffer, digits); } else { write_binary(dst, value, digits); } } static void write_number_signed(char *dst, sqfs_s64 value, int digits) { sqfs_u64 neg; if (value < 0) { neg = -value; write_binary(dst, ~neg + 1, digits); } else { write_number(dst, value, digits); } } static int write_header(ostream_t *fp, const struct stat *sb, const char *name, const char *slink_target, int type) { int maj = 0, min = 0; sqfs_u64 size = 0; tar_header_t hdr; if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) { maj = major(sb->st_rdev); min = minor(sb->st_rdev); } if (S_ISREG(sb->st_mode)) size = sb->st_size; memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.name, name, sizeof(hdr.name) - 1); write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); write_number(hdr.size, size, sizeof(hdr.size)); write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); hdr.typeflag = type; if (slink_target != NULL) memcpy(hdr.linkname, slink_target, sb->st_size); memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); sprintf(hdr.uname, "%u", sb->st_uid); sprintf(hdr.gname, "%u", sb->st_gid); write_number(hdr.devmajor, maj, sizeof(hdr.devmajor)); write_number(hdr.devminor, min, sizeof(hdr.devminor)); update_checksum(&hdr); return ostream_append(fp, &hdr, sizeof(hdr)); } static int write_gnu_header(ostream_t *fp, const struct stat *orig, const char *payload, size_t payload_len, int type, const char *name) { struct stat sb; sb = *orig; sb.st_mode = S_IFREG | 0644; sb.st_size = payload_len; if (write_header(fp, &sb, name, NULL, type)) return -1; if (ostream_append(fp, payload, payload_len)) return -1; return padd_file(fp, payload_len); } static size_t num_digits(size_t num) { size_t i = 1; while (num >= 10) { num /= 10; ++i; } return i; } static size_t prefix_digit_len(size_t len) { size_t old_ndigit, ndigit = 0; do { old_ndigit = ndigit; ndigit = num_digits(len + ndigit); } while (old_ndigit != ndigit); return ndigit; } static int write_schily_xattr(ostream_t *fp, const struct stat *orig, const char *name, const tar_xattr_t *xattr) { static const char *prefix = "SCHILY.xattr."; size_t len, total_size = 0; const tar_xattr_t *it; struct stat sb; for (it = xattr; it != NULL; it = it->next) { len = strlen(prefix) + strlen(it->key) + it->value_len + 3; total_size += len + prefix_digit_len(len); } sb = *orig; sb.st_mode = S_IFREG | 0644; sb.st_size = total_size; if (write_header(fp, &sb, name, NULL, TAR_TYPE_PAX)) return -1; for (it = xattr; it != NULL; it = it->next) { len = strlen(prefix) + strlen(it->key) + it->value_len + 3; len += prefix_digit_len(len); if (ostream_printf(fp, PRI_SZ " %s%s=", len, prefix, it->key) < 0) { return -1; } if (ostream_append(fp, it->value, it->value_len)) return -1; if (ostream_append(fp, "\n", 1)) return -1; } return padd_file(fp, total_size); } int write_tar_header(ostream_t *fp, const struct stat *sb, const char *name, const char *slink_target, const tar_xattr_t *xattr, unsigned int counter) { const char *reason; char buffer[64]; int type; if (xattr != NULL) { sprintf(buffer, "pax/xattr%u", counter); if (write_schily_xattr(fp, sb, buffer, xattr)) return -1; } if (!S_ISLNK(sb->st_mode)) slink_target = NULL; if (S_ISLNK(sb->st_mode) && sb->st_size >= 100) { sprintf(buffer, "gnu/target%u", counter); if (write_gnu_header(fp, sb, slink_target, sb->st_size, TAR_TYPE_GNU_SLINK, buffer)) return -1; slink_target = NULL; } if (strlen(name) >= 100) { sprintf(buffer, "gnu/name%u", counter); if (write_gnu_header(fp, sb, name, strlen(name), TAR_TYPE_GNU_PATH, buffer)) { return -1; } sprintf(buffer, "gnu/data%u", counter); name = buffer; } switch (sb->st_mode & S_IFMT) { case S_IFCHR: type = TAR_TYPE_CHARDEV; break; case S_IFBLK: type = TAR_TYPE_BLOCKDEV; break; case S_IFLNK: type = TAR_TYPE_SLINK; break; case S_IFREG: type = TAR_TYPE_FILE; break; case S_IFDIR: type = TAR_TYPE_DIR; break; case S_IFIFO: type = TAR_TYPE_FIFO; break; case S_IFSOCK: reason = "cannot pack socket"; goto out_skip; default: reason = "unknown type"; goto out_skip; } return write_header(fp, sb, name, slink_target, type); out_skip: fprintf(stderr, "WARNING: %s: %s\n", name, reason); return 1; } int write_hard_link(ostream_t *fp, const struct stat *sb, const char *name, const char *target, unsigned int counter) { tar_header_t hdr; char buffer[64]; size_t len; memset(&hdr, 0, sizeof(hdr)); len = strlen(target); if (len >= 100) { sprintf(buffer, "gnu/target%u", counter); if (write_gnu_header(fp, sb, target, len, TAR_TYPE_GNU_SLINK, buffer)) return -1; sprintf(hdr.linkname, "hardlink_%u", counter); } else { memcpy(hdr.linkname, target, len); } len = strlen(name); if (len >= 100) { sprintf(buffer, "gnu/name%u", counter); if (write_gnu_header(fp, sb, name, len, TAR_TYPE_GNU_PATH, buffer)) { return -1; } sprintf(hdr.name, "gnu/data%u", counter); } else { memcpy(hdr.name, name, len); } write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); write_number(hdr.size, 0, sizeof(hdr.size)); write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); hdr.typeflag = TAR_TYPE_LINK; memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); sprintf(hdr.uname, "%u", sb->st_uid); sprintf(hdr.gname, "%u", sb->st_gid); write_number(hdr.devmajor, 0, sizeof(hdr.devmajor)); write_number(hdr.devminor, 0, sizeof(hdr.devminor)); update_checksum(&hdr); return ostream_append(fp, &hdr, sizeof(hdr)); } squashfs-tools-ng-1.1.3/lib/util/000077500000000000000000000000001410627516300166415ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/util/Makemodule.am000066400000000000000000000017621410627516300212510ustar00rootroot00000000000000libutil_a_SOURCES = include/util.h include/str_table.h include/hash_table.h libutil_a_SOURCES += lib/util/str_table.c lib/util/alloc.c libutil_a_SOURCES += lib/util/rbtree.c include/rbtree.h libutil_a_SOURCES += lib/util/array.c include/array.h libutil_a_SOURCES += lib/util/xxhash.c lib/util/hash_table.c libutil_a_SOURCES += lib/util/fast_urem_by_const.h libutil_a_SOURCES += include/threadpool.h libutil_a_SOURCES += include/w32threadwrap.h libutil_a_SOURCES += lib/util/threadpool_serial.c libutil_a_SOURCES += lib/util/is_memory_zero.c libutil_a_CFLAGS = $(AM_CFLAGS) libutil_a_CPPFLAGS = $(AM_CPPFLAGS) if WINDOWS libutil_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 endif if HAVE_PTHREAD libutil_a_SOURCES += lib/util/threadpool.c libutil_a_CFLAGS += $(PTHREAD_CFLAGS) else if WINDOWS libutil_a_SOURCES += lib/util/threadpool.c else libutil_a_CPPFLAGS += -DNO_THREAD_IMPL endif endif if CUSTOM_ALLOC libutil_a_SOURCES += lib/util/mempool.c include/mempool.h endif noinst_LIBRARIES += libutil.a squashfs-tools-ng-1.1.3/lib/util/alloc.c000066400000000000000000000011651410627516300201020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * alloc.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util.h" #include #include void *alloc_flex(size_t base_size, size_t item_size, size_t nmemb) { size_t size; if (SZ_MUL_OV(nmemb, item_size, &size) || SZ_ADD_OV(base_size, size, &size)) { errno = EOVERFLOW; return NULL; } return calloc(1, size); } void *alloc_array(size_t item_size, size_t nmemb) { size_t size; if (SZ_MUL_OV(nmemb, item_size, &size)) { errno = EOVERFLOW; return NULL; } return calloc(1, size); } squashfs-tools-ng-1.1.3/lib/util/array.c000066400000000000000000000041421410627516300201240ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * array.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "array.h" #include "sqfs/error.h" #include int array_init(array_t *array, size_t size, size_t capacity) { size_t total; memset(array, 0, sizeof(*array)); if (capacity > 0) { if (SZ_MUL_OV(size, capacity, &total)) return SQFS_ERROR_OVERFLOW; array->data = malloc(total); if (array->data == NULL) return SQFS_ERROR_ALLOC; } array->size = size; array->count = capacity; return 0; } int array_init_copy(array_t *array, const array_t *src) { int ret; ret = array_init(array, src->size, src->used); if (ret != 0) return ret; memcpy(array->data, src->data, src->used * src->size); array->used = src->used; return 0; } void array_cleanup(array_t *array) { free(array->data); memset(array, 0, sizeof(*array)); } int array_append(array_t *array, const void *data) { size_t new_sz, new_count; void *new; if (array->used == array->count) { if (array->count == 0) { new_count = 128; } else { if (SZ_MUL_OV(array->count, 2, &new_count)) return SQFS_ERROR_ALLOC; } if (SZ_MUL_OV(new_count, array->size, &new_sz)) return SQFS_ERROR_ALLOC; new = realloc(array->data, new_sz); if (new == NULL) return SQFS_ERROR_ALLOC; array->data = new; array->count = new_count; } memcpy((char *)array->data + array->size * array->used, data, array->size); array->used += 1; return 0; } int array_set_capacity(array_t *array, size_t capacity) { size_t new_sz, new_count; void *new; if (capacity <= array->count) return 0; if (array->count == 0) { new_count = 128; } else { if (SZ_MUL_OV(array->count, 2, &new_count)) return SQFS_ERROR_ALLOC; } while (new_count < capacity) { if (SZ_MUL_OV(new_count, 2, &new_count)) return SQFS_ERROR_ALLOC; } if (SZ_MUL_OV(new_count, array->size, &new_sz)) return SQFS_ERROR_ALLOC; new = realloc(array->data, new_sz); if (new == NULL) return SQFS_ERROR_ALLOC; array->data = new; array->count = new_count; return 0; } squashfs-tools-ng-1.1.3/lib/util/fast_urem_by_const.h000066400000000000000000000056151410627516300227060ustar00rootroot00000000000000/* * Copyright © 2010 Valve Software * * 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 (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "sqfs/predef.h" #include #include /* * Code for fast 32-bit unsigned remainder, based off of "Faster Remainder by * Direct Computation: Applications to Compilers and Software Libraries," * available at https://arxiv.org/pdf/1902.01961.pdf. * * util_fast_urem32(n, d, REMAINDER_MAGIC(d)) returns the same thing as * n % d for any unsigned n and d, however it compiles down to only a few * multiplications, so it should be faster than plain sqfs_u32 modulo if the * same divisor is used many times. */ #define REMAINDER_MAGIC(divisor) \ ((sqfs_u64) ~0ull / (divisor) + 1) /* * Get bits 64-96 of a 32x64-bit multiply. If __int128_t is available, we use * it, which usually compiles down to one instruction on 64-bit architectures. * Otherwise on 32-bit architectures we usually get four instructions (one * 32x32->64 multiply, one 32x32->32 multiply, and one 64-bit add). */ static inline sqfs_u32 _mul32by64_hi(sqfs_u32 a, sqfs_u64 b) { #if __SIZEOF_INT128__ == 16 return ((__uint128_t) b * a) >> 64; #else /* * Let b = b0 + 2^32 * b1. Then a * b = a * b0 + 2^32 * a * b1. We would * have to do a 96-bit addition to get the full result, except that only * one term has non-zero lower 32 bits, which means that to get the high 32 * bits, we only have to add the high 64 bits of each term. Unfortunately, * we have to do the 64-bit addition in case the low 32 bits overflow. */ sqfs_u32 b0 = (sqfs_u32) b; sqfs_u32 b1 = b >> 32; return ((((sqfs_u64) a * b0) >> 32) + (sqfs_u64) a * b1) >> 32; #endif } static inline sqfs_u32 util_fast_urem32(sqfs_u32 n, sqfs_u32 d, sqfs_u64 magic) { sqfs_u64 lowbits = magic * n; sqfs_u32 result = _mul32by64_hi(d, lowbits); assert(result == n % d); return result; } squashfs-tools-ng-1.1.3/lib/util/hash_table.c000066400000000000000000000314201410627516300210770ustar00rootroot00000000000000/* * Copyright © 2009,2012 Intel Corporation * Copyright © 1988-2004 Keith Packard and Bart Massey. * * 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 (including the next * paragraph) 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. * * Except as contained in this notice, the names of the authors * or their institutions shall not be used in advertising or * otherwise to promote the sale, use or other dealings in this * Software without prior written authorization from the * authors. * * Authors: * Eric Anholt * Keith Packard */ /** * Implements an open-addressing, linear-reprobing hash table. * * For more information, see: * * http://cgit.freedesktop.org/~anholt/hash_table/tree/README */ #include #include #include #include "fast_urem_by_const.h" #include "hash_table.h" # define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static const sqfs_u32 deleted_key_value; /** * From Knuth -- a good choice for hash/rehash values is p, p-2 where * p and p-2 are both prime. These tables are sized to have an extra 10% * free to avoid exponential performance degradation as the hash table fills */ static const struct { sqfs_u32 max_entries, size, rehash; sqfs_u64 size_magic, rehash_magic; } hash_sizes[] = { #define ENTRY(max_entries, size, rehash) \ { max_entries, size, rehash, \ REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) } ENTRY(2, 5, 3 ), ENTRY(4, 7, 5 ), ENTRY(8, 13, 11 ), ENTRY(16, 19, 17 ), ENTRY(32, 43, 41 ), ENTRY(64, 73, 71 ), ENTRY(128, 151, 149 ), ENTRY(256, 283, 281 ), ENTRY(512, 571, 569 ), ENTRY(1024, 1153, 1151 ), ENTRY(2048, 2269, 2267 ), ENTRY(4096, 4519, 4517 ), ENTRY(8192, 9013, 9011 ), ENTRY(16384, 18043, 18041 ), ENTRY(32768, 36109, 36107 ), ENTRY(65536, 72091, 72089 ), ENTRY(131072, 144409, 144407 ), ENTRY(262144, 288361, 288359 ), ENTRY(524288, 576883, 576881 ), ENTRY(1048576, 1153459, 1153457 ), ENTRY(2097152, 2307163, 2307161 ), ENTRY(4194304, 4613893, 4613891 ), ENTRY(8388608, 9227641, 9227639 ), ENTRY(16777216, 18455029, 18455027 ), ENTRY(33554432, 36911011, 36911009 ), ENTRY(67108864, 73819861, 73819859 ), ENTRY(134217728, 147639589, 147639587 ), ENTRY(268435456, 295279081, 295279079 ), ENTRY(536870912, 590559793, 590559791 ), ENTRY(1073741824, 1181116273, 1181116271 ), ENTRY(2147483648ul, 2362232233ul, 2362232231ul ) }; static inline bool key_pointer_is_reserved(const struct hash_table *ht, const void *key) { return key == NULL || key == ht->deleted_key; } static int entry_is_free(const struct hash_entry *entry) { return entry->key == NULL; } static int entry_is_deleted(const struct hash_table *ht, struct hash_entry *entry) { return entry->key == ht->deleted_key; } static int entry_is_present(const struct hash_table *ht, struct hash_entry *entry) { return entry->key != NULL && entry->key != ht->deleted_key; } static bool hash_table_init(struct hash_table *ht, sqfs_u32 (*key_hash_function)(void *user, const void *key), bool (*key_equals_function)(void *user, const void *a, const void *b)) { ht->size_index = 0; ht->size = hash_sizes[ht->size_index].size; ht->rehash = hash_sizes[ht->size_index].rehash; ht->size_magic = hash_sizes[ht->size_index].size_magic; ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; ht->max_entries = hash_sizes[ht->size_index].max_entries; ht->key_hash_function = key_hash_function; ht->key_equals_function = key_equals_function; ht->table = calloc(sizeof(struct hash_entry), ht->size); ht->entries = 0; ht->deleted_entries = 0; ht->deleted_key = &deleted_key_value; return ht->table != NULL; } struct hash_table * hash_table_create(sqfs_u32 (*key_hash_function)(void *user, const void *key), bool (*key_equals_function)(void *user, const void *a, const void *b)) { struct hash_table *ht; ht = malloc(sizeof(struct hash_table)); if (ht == NULL) return NULL; if (!hash_table_init(ht, key_hash_function, key_equals_function)) { free(ht); return NULL; } return ht; } struct hash_table * hash_table_clone(struct hash_table *src) { struct hash_table *ht; ht = malloc(sizeof(struct hash_table)); if (ht == NULL) return NULL; memcpy(ht, src, sizeof(struct hash_table)); ht->table = calloc(sizeof(struct hash_entry), ht->size); if (ht->table == NULL) { free(ht); return NULL; } memcpy(ht->table, src->table, ht->size * sizeof(struct hash_entry)); return ht; } /** * Frees the given hash table. */ void hash_table_destroy(struct hash_table *ht, void (*delete_function)(struct hash_entry *entry)) { if (!ht) return; if (delete_function) { hash_table_foreach(ht, entry) { delete_function(entry); } } free(ht->table); free(ht); } static struct hash_entry * hash_table_search(struct hash_table *ht, sqfs_u32 hash, const void *key) { assert(!key_pointer_is_reserved(ht, key)); sqfs_u32 size = ht->size; sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, ht->rehash_magic); sqfs_u32 hash_address = start_hash_address; do { struct hash_entry *entry = ht->table + hash_address; if (entry_is_free(entry)) { return NULL; } else if (entry_is_present(ht, entry) && entry->hash == hash) { if (ht->key_equals_function(ht->user, key, entry->key)) { return entry; } } hash_address += double_hash; if (hash_address >= size) hash_address -= size; } while (hash_address != start_hash_address); return NULL; } /** * Finds a hash table entry with the given key and hash of that key. * * Returns NULL if no entry is found. Note that the data pointer may be * modified by the user. */ struct hash_entry * hash_table_search_pre_hashed(struct hash_table *ht, sqfs_u32 hash, const void *key) { assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(ht->user, key)); return hash_table_search(ht, hash, key); } static void hash_table_insert_rehash(struct hash_table *ht, sqfs_u32 hash, const void *key, void *data) { sqfs_u32 size = ht->size; sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, ht->rehash_magic); sqfs_u32 hash_address = start_hash_address; do { struct hash_entry *entry = ht->table + hash_address; if (entry->key == NULL) { entry->hash = hash; entry->key = key; entry->data = data; return; } hash_address += double_hash; if (hash_address >= size) hash_address -= size; } while (true); } static void hash_table_rehash(struct hash_table *ht, unsigned new_size_index) { struct hash_table old_ht; struct hash_entry *table; if (new_size_index >= ARRAY_SIZE(hash_sizes)) return; table = calloc(sizeof(struct hash_entry), hash_sizes[new_size_index].size); if (table == NULL) return; old_ht = *ht; ht->table = table; ht->size_index = new_size_index; ht->size = hash_sizes[ht->size_index].size; ht->rehash = hash_sizes[ht->size_index].rehash; ht->size_magic = hash_sizes[ht->size_index].size_magic; ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; ht->max_entries = hash_sizes[ht->size_index].max_entries; ht->entries = 0; ht->deleted_entries = 0; hash_table_foreach(&old_ht, entry) { hash_table_insert_rehash(ht, entry->hash, entry->key, entry->data); } ht->entries = old_ht.entries; free(old_ht.table); } static struct hash_entry * hash_table_insert(struct hash_table *ht, sqfs_u32 hash, const void *key, void *data) { struct hash_entry *available_entry = NULL; assert(!key_pointer_is_reserved(ht, key)); if (ht->entries >= ht->max_entries) { hash_table_rehash(ht, ht->size_index + 1); } else if (ht->deleted_entries + ht->entries >= ht->max_entries) { hash_table_rehash(ht, ht->size_index); } sqfs_u32 size = ht->size; sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, ht->rehash_magic); sqfs_u32 hash_address = start_hash_address; do { struct hash_entry *entry = ht->table + hash_address; if (!entry_is_present(ht, entry)) { /* Stash the first available entry we find */ if (available_entry == NULL) available_entry = entry; if (entry_is_free(entry)) break; } /* Implement replacement when another insert happens * with a matching key. This is a relatively common * feature of hash tables, with the alternative * generally being "insert the new value as well, and * return it first when the key is searched for". * * Note that the hash table doesn't have a delete * callback. If freeing of old data pointers is * required to avoid memory leaks, perform a search * before inserting. */ if (!entry_is_deleted(ht, entry) && entry->hash == hash && ht->key_equals_function(ht->user, key, entry->key)) { entry->key = key; entry->data = data; return entry; } hash_address += double_hash; if (hash_address >= size) hash_address -= size; } while (hash_address != start_hash_address); if (available_entry) { if (entry_is_deleted(ht, available_entry)) ht->deleted_entries--; available_entry->hash = hash; available_entry->key = key; available_entry->data = data; ht->entries++; return available_entry; } /* We could hit here if a required resize failed. An unchecked-malloc * application could ignore this result. */ return NULL; } /** * Inserts the key with the given hash into the table. * * Note that insertion may rearrange the table on a resize or rehash, * so previously found hash_entries are no longer valid after this function. */ struct hash_entry * hash_table_insert_pre_hashed(struct hash_table *ht, sqfs_u32 hash, const void *key, void *data) { assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(ht->user, key)); return hash_table_insert(ht, hash, key, data); } /** * This function is an iterator over the hash table. * * Pass in NULL for the first entry, as in the start of a for loop. Note that * an iteration over the table is O(table_size) not O(entries). */ struct hash_entry * hash_table_next_entry(struct hash_table *ht, struct hash_entry *entry) { if (entry == NULL) entry = ht->table; else entry = entry + 1; for (; entry != ht->table + ht->size; entry++) { if (entry_is_present(ht, entry)) { return entry; } } return NULL; } squashfs-tools-ng-1.1.3/lib/util/is_memory_zero.c000066400000000000000000000016111410627516300220460ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * is_memory_zero.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util.h" #include #define U64THRESHOLD (128) static bool test_u8(const unsigned char *blob, size_t size) { while (size--) { if (*(blob++) != 0) return false; } return true; } bool is_memory_zero(const void *blob, size_t size) { const sqfs_u64 *u64ptr; size_t diff; if (size < U64THRESHOLD) return test_u8(blob, size); diff = (uintptr_t)blob % sizeof(sqfs_u64); if (diff != 0) { diff = sizeof(sqfs_u64) - diff; if (!test_u8(blob, diff)) return false; blob = (const char *)blob + diff; size -= diff; } u64ptr = blob; while (size >= sizeof(sqfs_u64)) { if (*(u64ptr++) != 0) return false; size -= sizeof(sqfs_u64); } return test_u8((const unsigned char *)u64ptr, size); } squashfs-tools-ng-1.1.3/lib/util/mempool.c000066400000000000000000000076551410627516300204720ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * mempool.c * * Copyright (C) 2021 David Oberhollenzer */ #include "mempool.h" #include #include #include #include #if defined(_WIN32) || defined(__WINDOWS__) #define WIN32_LEAN_AND_MEAN #include #else #include #endif #define DEF_POOL_SIZE (65536) #define MEM_ALIGN (8) typedef struct pool_t { struct pool_t *next; unsigned char *data; unsigned char *limit; unsigned int *bitmap; size_t obj_free; unsigned int blob[]; } pool_t; struct mem_pool_t { size_t obj_size; size_t pool_size; size_t bitmap_count; pool_t *pool_list; }; static size_t pool_size_from_bitmap_count(size_t count, size_t obj_size) { size_t size, byte_count, bit_count; size = sizeof(pool_t); if (size % sizeof(unsigned int)) size += sizeof(unsigned int) - size % sizeof(unsigned int); byte_count = count * sizeof(unsigned int); bit_count = byte_count * CHAR_BIT; size += byte_count; if (size % obj_size) size += obj_size - size % obj_size; size += bit_count * obj_size; return size; } static pool_t *create_pool(const mem_pool_t *mem) { unsigned char *ptr; pool_t *pool; #if defined(_WIN32) || defined(__WINDOWS__) pool = VirtualAlloc(NULL, mem->pool_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (pool == NULL) return NULL; #else pool = mmap(NULL, mem->pool_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (pool == MAP_FAILED) return NULL; #endif pool->bitmap = pool->blob; pool->obj_free = mem->bitmap_count * sizeof(unsigned int) * CHAR_BIT; ptr = (unsigned char *)(pool->bitmap + mem->bitmap_count); if (((uintptr_t)ptr) % mem->obj_size) { ptr += mem->obj_size; ptr -= ((uintptr_t)ptr) % mem->obj_size; } pool->data = ptr; pool->limit = pool->data + pool->obj_free * mem->obj_size - 1; memset(pool->bitmap, 0, mem->bitmap_count * sizeof(unsigned int)); return pool; } mem_pool_t *mem_pool_create(size_t obj_size) { mem_pool_t *mem = calloc(1, sizeof(*mem)); size_t count = 1, total; if (mem == NULL) return NULL; if (obj_size % MEM_ALIGN) obj_size += MEM_ALIGN - obj_size % MEM_ALIGN; for (;;) { total = pool_size_from_bitmap_count(count, obj_size); if (total > DEF_POOL_SIZE) break; ++count; } --count; mem->obj_size = obj_size; mem->pool_size = DEF_POOL_SIZE; mem->bitmap_count = count; return mem; } void mem_pool_destroy(mem_pool_t *mem) { while (mem->pool_list != NULL) { pool_t *pool = mem->pool_list; mem->pool_list = pool->next; #if defined(_WIN32) || defined(__WINDOWS__) VirtualFree(pool, mem->pool_size, MEM_RELEASE); #else munmap(pool, mem->pool_size); #endif } free(mem); } void *mem_pool_allocate(mem_pool_t *mem) { size_t idx, i, j; void *ptr = NULL; pool_t *it; for (it = mem->pool_list; it != NULL; it = it->next) { if (it->obj_free > 0) break; } if (it == NULL) { it = create_pool(mem); if (it == NULL) return NULL; it->next = mem->pool_list; mem->pool_list = it; } for (i = 0; i < mem->bitmap_count; ++i) { if (it->bitmap[i] < UINT_MAX) break; } for (j = 0; j < (sizeof(it->bitmap[i]) * CHAR_BIT); ++j) { if (!(it->bitmap[i] & (1UL << j))) break; } idx = i * sizeof(unsigned int) * CHAR_BIT + j; ptr = it->data + idx * mem->obj_size; it->bitmap[i] |= (1UL << j); it->obj_free -= 1; memset(ptr, 0, mem->obj_size); return ptr; } void mem_pool_free(mem_pool_t *mem, void *ptr) { size_t idx, i, j; pool_t *it; for (it = mem->pool_list; it != NULL; it = it->next) { if ((unsigned char *)ptr >= it->data && (unsigned char *)ptr < it->limit) { break; } } assert(it != NULL); idx = (size_t)((unsigned char *)ptr - it->data); assert((idx % mem->obj_size) == 0); idx /= mem->obj_size; i = idx / (sizeof(unsigned int) * CHAR_BIT); j = idx % (sizeof(unsigned int) * CHAR_BIT); assert((it->bitmap[i] & (1 << j)) != 0); it->bitmap[i] &= ~(1 << j); } squashfs-tools-ng-1.1.3/lib/util/rbtree.c000066400000000000000000000122531410627516300202730ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * rbtree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "sqfs/error.h" #include "rbtree.h" #include #include #define IS_RED(n) ((n) && (n)->is_red) #ifdef NO_CUSTOM_ALLOC static void destroy_nodes_dfs(rbtree_node_t *n) { rbtree_node_t *l, *r; if (n != NULL) { l = n->left; r = n->right; free(n); destroy_nodes_dfs(l); destroy_nodes_dfs(r); } } #else static void destroy_nodes_dfs(rbtree_node_t *n) { (void)n; } #endif static void flip_colors(rbtree_node_t *n) { n->is_red = !n->is_red; n->left->is_red = !n->left->is_red; n->right->is_red = !n->right->is_red; } static rbtree_node_t *rotate_right(rbtree_node_t *n) { rbtree_node_t *x; x = n->left; n->left = x->right; x->right = n; x->is_red = x->right->is_red; x->right->is_red = 1; return x; } static rbtree_node_t *rotate_left(rbtree_node_t *n) { rbtree_node_t *x; x = n->right; n->right = x->left; x->left = n; x->is_red = x->left->is_red; x->left->is_red = 1; return x; } static rbtree_node_t *subtree_balance(rbtree_node_t *n) { if (IS_RED(n->right) && !IS_RED(n->left)) n = rotate_left(n); if (IS_RED(n->left) && IS_RED(n->left->left)) n = rotate_right(n); if (IS_RED(n->left) && IS_RED(n->right)) flip_colors(n); return n; } static rbtree_node_t *subtree_insert(rbtree_t *tree, rbtree_node_t *root, rbtree_node_t *new) { if (root == NULL) return new; if (tree->key_compare(tree->key_context, new->data, root->data) < 0) { root->left = subtree_insert(tree, root->left, new); } else { root->right = subtree_insert(tree, root->right, new); } return subtree_balance(root); } static rbtree_node_t *mknode(rbtree_t *t, const void *key, const void *value) { rbtree_node_t *node; #ifdef NO_CUSTOM_ALLOC node = calloc(1, sizeof(*node) + t->key_size_padded + t->value_size); #else node = mem_pool_allocate(t->pool); #endif if (node == NULL) return NULL; node->value_offset = t->key_size_padded; node->is_red = 1; memcpy(node->data, key, t->key_size); memcpy(node->data + t->key_size_padded, value, t->value_size); return node; } static rbtree_node_t *copy_node(rbtree_t *nt, const rbtree_t *t, const rbtree_node_t *n) { rbtree_node_t *out; #ifdef NO_CUSTOM_ALLOC out = calloc(1, sizeof(*out) + t->key_size_padded + t->value_size); #else out = mem_pool_allocate(nt->pool); #endif if (out == NULL) return NULL; memcpy(out, n, sizeof(*n) + t->key_size_padded + t->value_size); out->left = NULL; out->right = NULL; if (n->left != NULL) { out->left = copy_node(nt, t, n->left); if (out->left == NULL) { destroy_nodes_dfs(out); return NULL; } } if (n->right != NULL) { out->right = copy_node(nt, t, n->right); if (out->right == NULL) { destroy_nodes_dfs(out); return NULL; } } return out; } int rbtree_init(rbtree_t *tree, size_t keysize, size_t valuesize, int(*key_compare)(const void *, const void *, const void *)) { size_t diff, size; memset(tree, 0, sizeof(*tree)); tree->key_compare = key_compare; tree->key_size = keysize; tree->key_size_padded = keysize; tree->value_size = valuesize; /* make sure the value always has pointer alignment */ diff = keysize % sizeof(void *); if (diff != 0) { diff = sizeof(void *) - diff; if (SZ_ADD_OV(tree->key_size_padded, diff, &tree->key_size_padded)) { return SQFS_ERROR_OVERFLOW; } } /* make sure the node can store the offset */ if (sizeof(size_t) > sizeof(sqfs_u32)) { if (tree->key_size_padded > 0x0FFFFFFFFUL) return SQFS_ERROR_OVERFLOW; } /* make sure the nodes fit in memory */ size = sizeof(rbtree_node_t); if (SZ_ADD_OV(size, tree->key_size_padded, &size)) return SQFS_ERROR_OVERFLOW; if (SZ_ADD_OV(size, tree->value_size, &size)) return SQFS_ERROR_OVERFLOW; #ifndef NO_CUSTOM_ALLOC /* initialize the underlying pool allocator */ tree->pool = mem_pool_create(size); if (tree->pool == NULL) return SQFS_ERROR_ALLOC; #endif return 0; } int rbtree_copy(const rbtree_t *tree, rbtree_t *out) { memcpy(out, tree, sizeof(*out)); out->root = NULL; #ifndef NO_CUSTOM_ALLOC out->pool = mem_pool_create(sizeof(rbtree_node_t) + tree->key_size_padded + tree->value_size); if (out->pool == NULL) return SQFS_ERROR_ALLOC; #endif if (tree->root != NULL) { out->root = copy_node(out, tree, tree->root); if (out->root == NULL) { memset(out, 0, sizeof(*out)); return SQFS_ERROR_ALLOC; } } return 0; } void rbtree_cleanup(rbtree_t *tree) { #ifdef NO_CUSTOM_ALLOC destroy_nodes_dfs(tree->root); #else mem_pool_destroy(tree->pool); #endif memset(tree, 0, sizeof(*tree)); } int rbtree_insert(rbtree_t *tree, const void *key, const void *value) { rbtree_node_t *node = mknode(tree, key, value); if (node == NULL) return SQFS_ERROR_ALLOC; tree->root = subtree_insert(tree, tree->root, node); tree->root->is_red = 0; return 0; } rbtree_node_t *rbtree_lookup(const rbtree_t *tree, const void *key) { rbtree_node_t *node = tree->root; int ret; while (node != NULL) { ret = tree->key_compare(tree->key_context, key, node->data); if (ret == 0) break; node = ret < 0 ? node->left : node->right; } return node; } squashfs-tools-ng-1.1.3/lib/util/str_table.c000066400000000000000000000073171410627516300207740ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * str_table.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include #include #include #include "sqfs/error.h" #include "str_table.h" #include "util.h" /* R5 hash function (borrowed from reiserfs) */ static sqfs_u32 strhash(const char *s) { const signed char *str = (const signed char *)s; sqfs_u32 a = 0; while (*str != '\0') { a += *str << 4; a += *str >> 4; a *= 11; str++; } return a; } static bool key_equals_function(void *user, const void *a, const void *b) { (void)user; return strcmp(a, b) == 0; } int str_table_init(str_table_t *table) { memset(table, 0, sizeof(*table)); if (array_init(&table->bucket_ptrs, sizeof(str_bucket_t *), 0)) goto fail_arr; table->ht = hash_table_create(NULL, key_equals_function); if (table->ht == NULL) goto fail_ht; return 0; fail_ht: array_cleanup(&table->bucket_ptrs); fail_arr: memset(table, 0, sizeof(*table)); return SQFS_ERROR_ALLOC; } int str_table_copy(str_table_t *dst, const str_table_t *src) { str_bucket_t *bucket, **array; int ret; ret = array_init_copy(&dst->bucket_ptrs, &src->bucket_ptrs); if (ret != 0) return ret; dst->ht = hash_table_clone(src->ht); if (dst->ht == NULL) { array_cleanup(&dst->bucket_ptrs); return SQFS_ERROR_ALLOC; } array = (str_bucket_t **)dst->bucket_ptrs.data; hash_table_foreach(dst->ht, ent) { bucket = alloc_flex(sizeof(*bucket), 1, strlen(ent->key) + 1); if (bucket == NULL) { str_table_cleanup(dst); return SQFS_ERROR_ALLOC; } memcpy(bucket, ent->data, sizeof(*bucket) + strlen(ent->key) + 1); ent->data = bucket; ent->key = bucket->string; array[bucket->index] = bucket; } return 0; } void str_table_cleanup(str_table_t *table) { hash_table_foreach(table->ht, ent) { free(ent->data); ent->data = NULL; ent->key = NULL; } hash_table_destroy(table->ht, NULL); array_cleanup(&table->bucket_ptrs); memset(table, 0, sizeof(*table)); } int str_table_get_index(str_table_t *table, const char *str, size_t *idx) { struct hash_entry *ent; str_bucket_t *new; sqfs_u32 hash; hash = strhash(str); ent = hash_table_search_pre_hashed(table->ht, hash, str); if (ent != NULL) { *idx = ((str_bucket_t *)ent->data)->index; return 0; } new = alloc_flex(sizeof(*new), 1, strlen(str) + 1); if (new == NULL) return SQFS_ERROR_ALLOC; new->index = table->next_index; strcpy(new->string, str); ent = hash_table_insert_pre_hashed(table->ht, hash, str, new); if (ent == NULL) { free(new); return SQFS_ERROR_ALLOC; } ent->key = new->string; if (array_append(&table->bucket_ptrs, &new) != 0) { free(new); ent->key = NULL; ent->data = NULL; return SQFS_ERROR_ALLOC; } *idx = table->next_index++; return 0; } static str_bucket_t *bucket_by_index(const str_table_t *table, size_t index) { if (index >= table->bucket_ptrs.used) return NULL; return ((str_bucket_t **)table->bucket_ptrs.data)[index]; } const char *str_table_get_string(const str_table_t *table, size_t index) { str_bucket_t *bucket = bucket_by_index(table, index); return bucket == NULL ? NULL : bucket->string; } void str_table_add_ref(str_table_t *table, size_t index) { str_bucket_t *bucket = bucket_by_index(table, index); if (bucket != NULL && bucket->refcount < ~((size_t)0)) bucket->refcount += 1; } void str_table_del_ref(str_table_t *table, size_t index) { str_bucket_t *bucket = bucket_by_index(table, index); if (bucket != NULL && bucket->refcount > 0) bucket->refcount -= 1; } size_t str_table_get_ref_count(const str_table_t *table, size_t index) { str_bucket_t *bucket = bucket_by_index(table, index); return bucket != NULL ? bucket->refcount : 0; } squashfs-tools-ng-1.1.3/lib/util/threadpool.c000066400000000000000000000174011410627516300211510ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool.c * * Copyright (C) 2021 David Oberhollenzer */ #include "threadpool.h" #include "util.h" #include #include #if defined(_WIN32) || defined(__WINDOWS__) #include "w32threadwrap.h" #define THREAD_FUN(funname, argname) DWORD WINAPI funname(LPVOID argname) #define THREAD_EXIT_SUCCESS (0) #else #include #include #define THREAD_FUN(funname, argname) void *funname(void *argname) #define THREAD_EXIT_SUCCESS NULL #endif typedef struct thread_pool_impl_t thread_pool_impl_t; typedef struct work_item_t { struct work_item_t *next; size_t ticket_number; void *data; } work_item_t; typedef struct { pthread_t thread; thread_pool_impl_t *pool; thread_pool_worker_t fun; void *user; } worker_t; struct thread_pool_impl_t { thread_pool_t base; pthread_mutex_t mtx; pthread_cond_t queue_cond; pthread_cond_t done_cond; size_t next_ticket; size_t next_dequeue_ticket; size_t item_count; work_item_t *queue; work_item_t *queue_last; work_item_t *done; work_item_t *safe_done; work_item_t *safe_done_last; work_item_t *recycle; int status; size_t num_workers; worker_t workers[]; }; /*****************************************************************************/ static void store_completed(thread_pool_impl_t *pool, work_item_t *done, int status) { work_item_t *it = pool->done, *prev = NULL; while (it != NULL) { if (it->ticket_number >= done->ticket_number) break; prev = it; it = it->next; } if (prev == NULL) { done->next = pool->done; pool->done = done; } else { done->next = prev->next; prev->next = done; } if (status != 0 && pool->status == 0) pool->status = status; pthread_cond_broadcast(&pool->done_cond); } static work_item_t *get_next_work_item(thread_pool_impl_t *pool) { work_item_t *item = NULL; while (pool->queue == NULL && pool->status == 0) pthread_cond_wait(&pool->queue_cond, &pool->mtx); if (pool->status == 0) { item = pool->queue; pool->queue = item->next; item->next = NULL; if (pool->queue == NULL) pool->queue_last = NULL; } return item; } static THREAD_FUN(worker_proc, arg) { work_item_t *item = NULL; worker_t *worker = arg; int status = 0; for (;;) { pthread_mutex_lock(&worker->pool->mtx); if (item != NULL) store_completed(worker->pool, item, status); item = get_next_work_item(worker->pool); pthread_mutex_unlock(&worker->pool->mtx); if (item == NULL) break; status = worker->fun(worker->user, item->data); } return THREAD_EXIT_SUCCESS; } /*****************************************************************************/ static work_item_t *try_dequeue_done(thread_pool_impl_t *pool) { work_item_t *out; if (pool->done == NULL) return NULL; if (pool->done->ticket_number != pool->next_dequeue_ticket) return NULL; out = pool->done; pool->done = out->next; out->next = NULL; pool->next_dequeue_ticket += 1; return out; } static void free_item_list(work_item_t *list) { while (list != NULL) { work_item_t *item = list; list = list->next; free(item); } } static void destroy(thread_pool_t *interface) { thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; size_t i; pthread_mutex_lock(&pool->mtx); pool->status = -1; pthread_cond_broadcast(&pool->queue_cond); pthread_mutex_unlock(&pool->mtx); for (i = 0; i < pool->num_workers; ++i) pthread_join(pool->workers[i].thread, NULL); pthread_cond_destroy(&pool->done_cond); pthread_cond_destroy(&pool->queue_cond); pthread_mutex_destroy(&pool->mtx); free_item_list(pool->queue); free_item_list(pool->done); free_item_list(pool->recycle); free_item_list(pool->safe_done); free(pool); } static size_t get_worker_count(thread_pool_t *interface) { thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; return pool->num_workers; } static void set_worker_ptr(thread_pool_t *interface, size_t idx, void *ptr) { thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; if (idx >= pool->num_workers) return; pthread_mutex_lock(&pool->mtx); pool->workers[idx].user = ptr; pthread_mutex_unlock(&pool->mtx); } static int submit(thread_pool_t *interface, void *ptr) { thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; work_item_t *item = NULL; int status; if (pool->recycle != NULL) { item = pool->recycle; pool->recycle = item->next; item->next = NULL; } if (item == NULL) { item = calloc(1, sizeof(*item)); if (item == NULL) return -1; } pthread_mutex_lock(&pool->mtx); status = pool->status; if (status == 0) { item->data = ptr; item->ticket_number = pool->next_ticket++; if (pool->queue_last == NULL) { pool->queue = item; } else { pool->queue_last->next = item; } pool->queue_last = item; pool->item_count += 1; } for (;;) { work_item_t *done = try_dequeue_done(pool); if (done == NULL) break; if (pool->safe_done_last == NULL) { pool->safe_done = done; } else { pool->safe_done_last->next = done; } pool->safe_done_last = done; } pthread_cond_broadcast(&pool->queue_cond); pthread_mutex_unlock(&pool->mtx); if (status != 0) { memset(item, 0, sizeof(*item)); item->next = pool->recycle; pool->recycle = item; } return status; } static void *dequeue(thread_pool_t *interface) { thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; work_item_t *out = NULL; void *ptr = NULL; if (pool->item_count == 0) return NULL; if (pool->safe_done != NULL) { out = pool->safe_done; pool->safe_done = pool->safe_done->next; if (pool->safe_done == NULL) pool->safe_done_last = NULL; } else { pthread_mutex_lock(&pool->mtx); for (;;) { out = try_dequeue_done(pool); if (out != NULL) break; pthread_cond_wait(&pool->done_cond, &pool->mtx); } pthread_mutex_unlock(&pool->mtx); } ptr = out->data; out->ticket_number = 0; out->data = NULL; out->next = pool->recycle; pool->recycle = out; pool->item_count -= 1; return ptr; } static int get_status(thread_pool_t *interface) { thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; int status; pthread_mutex_lock(&pool->mtx); status = pool->status; pthread_mutex_unlock(&pool->mtx); return status; } thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker) { thread_pool_impl_t *pool; thread_pool_t *interface; sigset_t set, oldset; size_t i, j; int ret; if (num_jobs < 1) num_jobs = 1; pool = alloc_flex(sizeof(*pool), sizeof(pool->workers[0]), num_jobs); if (pool == NULL) return NULL; if (pthread_mutex_init(&pool->mtx, NULL) != 0) goto fail_free; if (pthread_cond_init(&pool->queue_cond, NULL) != 0) goto fail_mtx; if (pthread_cond_init(&pool->done_cond, NULL) != 0) goto fail_qcond; sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, &oldset); pool->num_workers = num_jobs; for (i = 0; i < num_jobs; ++i) { pool->workers[i].fun = worker; pool->workers[i].pool = pool; ret = pthread_create(&pool->workers[i].thread, NULL, worker_proc, pool->workers + i); if (ret != 0) goto fail; } pthread_sigmask(SIG_SETMASK, &oldset, NULL); interface = (thread_pool_t *)pool; interface->destroy = destroy; interface->get_worker_count = get_worker_count; interface->set_worker_ptr = set_worker_ptr; interface->submit = submit; interface->dequeue = dequeue; interface->get_status = get_status; return interface; fail: pthread_mutex_lock(&pool->mtx); pool->status = -1; pthread_cond_broadcast(&pool->queue_cond); pthread_mutex_unlock(&pool->mtx); for (j = 0; j < i; ++j) pthread_join(pool->workers[j].thread, NULL); pthread_sigmask(SIG_SETMASK, &oldset, NULL); pthread_cond_destroy(&pool->done_cond); fail_qcond: pthread_cond_destroy(&pool->queue_cond); fail_mtx: pthread_mutex_destroy(&pool->mtx); fail_free: free(pool); return NULL; } squashfs-tools-ng-1.1.3/lib/util/threadpool_serial.c000066400000000000000000000056301410627516300225110ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool_serial.c * * Copyright (C) 2021 David Oberhollenzer */ #include "threadpool.h" #include "util.h" #include #include typedef struct work_item_t { struct work_item_t *next; void *data; } work_item_t; typedef struct { thread_pool_t base; work_item_t *queue; work_item_t *queue_last; work_item_t *recycle; thread_pool_worker_t fun; void *user; int status; } serial_impl_t; static void destroy(thread_pool_t *interface) { serial_impl_t *pool = (serial_impl_t *)interface; while (pool->queue != NULL) { work_item_t *item = pool->queue; pool->queue = item->next; free(item); } while (pool->recycle != NULL) { work_item_t *item = pool->recycle; pool->recycle = item->next; free(item); } free(pool); } static size_t get_worker_count(thread_pool_t *pool) { (void)pool; return 1; } static void set_worker_ptr(thread_pool_t *interface, size_t idx, void *ptr) { serial_impl_t *pool = (serial_impl_t *)interface; if (idx >= 1) return; pool->user = ptr; } static int submit(thread_pool_t *interface, void *ptr) { serial_impl_t *pool = (serial_impl_t *)interface; work_item_t *item = NULL; if (pool->status != 0) return pool->status; if (pool->recycle != NULL) { item = pool->recycle; pool->recycle = item->next; item->next = NULL; } if (item == NULL) { item = calloc(1, sizeof(*item)); if (item == NULL) return -1; } item->data = ptr; if (pool->queue_last == NULL) { pool->queue = item; } else { pool->queue_last->next = item; } pool->queue_last = item; return 0; } static void *dequeue(thread_pool_t *interface) { serial_impl_t *pool = (serial_impl_t *)interface; work_item_t *item; void *ptr; int ret; if (pool->queue == NULL) return NULL; item = pool->queue; pool->queue = item->next; if (pool->queue == NULL) pool->queue_last = NULL; ptr = item->data; memset(item, 0, sizeof(*item)); item->next = pool->recycle; pool->recycle = item; ret = pool->fun(pool->user, ptr); if (ret != 0 && pool->status == 0) pool->status = ret; return ptr; } static int get_status(thread_pool_t *interface) { serial_impl_t *pool = (serial_impl_t *)interface; return pool->status; } thread_pool_t *thread_pool_create_serial(thread_pool_worker_t worker) { serial_impl_t *pool = calloc(1, sizeof(*pool)); thread_pool_t *interface = (thread_pool_t *)pool; if (pool == NULL) return NULL; pool->fun = worker; interface = (thread_pool_t *)pool; interface->destroy = destroy; interface->get_worker_count = get_worker_count; interface->set_worker_ptr = set_worker_ptr; interface->submit = submit; interface->dequeue = dequeue; interface->get_status = get_status; return interface; } #ifdef NO_THREAD_IMPL thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker) { (void)num_jobs; return thread_pool_create_serial(worker); } #endif squashfs-tools-ng-1.1.3/lib/util/xxhash.c000066400000000000000000000067421410627516300203210ustar00rootroot00000000000000/* * xxHash - Extremely Fast Hash algorithm * Copyright (C) 2012-2016, Yann Collet. * * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- * This code has been lifted from Linux (the OS kernel) and adapted for use in * libsquashfs. For the original, unmodified and complete source, see below. * * You can contact the author at: * - xxHash homepage: http://cyan4973.github.io/xxHash/ * - xxHash source repository: https://github.com/Cyan4973/xxHash */ #include "config.h" #include "util.h" #include #define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) static const sqfs_u32 PRIME32_1 = 2654435761U; static const sqfs_u32 PRIME32_2 = 2246822519U; static const sqfs_u32 PRIME32_3 = 3266489917U; static const sqfs_u32 PRIME32_4 = 668265263U; static const sqfs_u32 PRIME32_5 = 374761393U; static sqfs_u32 xxh32_round(sqfs_u32 seed, sqfs_u32 input) { seed += input * PRIME32_2; seed = xxh_rotl32(seed, 13); seed *= PRIME32_1; return seed; } static sqfs_u32 XXH_readLE32(const sqfs_u8 *ptr) { sqfs_u32 value; memcpy(&value, ptr, sizeof(value)); return le32toh(value); } sqfs_u32 xxh32(const void *input, const size_t len) { const sqfs_u8 *p = (const sqfs_u8 *)input; const sqfs_u8 *b_end = p + len; sqfs_u32 h32; if (len >= 16) { const sqfs_u8 *const limit = b_end - 16; sqfs_u32 v1 = PRIME32_1 + PRIME32_2; sqfs_u32 v2 = PRIME32_2; sqfs_u32 v3 = 0; sqfs_u32 v4 = PRIME32_1; do { v1 = xxh32_round(v1, XXH_readLE32(p )); v2 = xxh32_round(v2, XXH_readLE32(p + 4)); v3 = xxh32_round(v3, XXH_readLE32(p + 8)); v4 = xxh32_round(v4, XXH_readLE32(p + 12)); p += 16; } while (p <= limit); h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) + xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18); } else { h32 = PRIME32_5; } h32 += (sqfs_u32)len; while (p + 4 <= b_end) { h32 += XXH_readLE32(p) * PRIME32_3; h32 = xxh_rotl32(h32, 17) * PRIME32_4; p += 4; } while (p < b_end) { h32 += (*p) * PRIME32_5; h32 = xxh_rotl32(h32, 11) * PRIME32_1; p++; } h32 ^= h32 >> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } squashfs-tools-ng-1.1.3/lib/zlib/000077500000000000000000000000001410627516300166245ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/lib/zlib/Makemodule.am000066400000000000000000000012001410627516300212170ustar00rootroot00000000000000if WITH_OWN_ZLIB libz_la_SOURCES = lib/zlib/adler32.c lib/zlib/inffixed.h lib/zlib/zconf.h libz_la_SOURCES += lib/zlib/deflate.c lib/zlib/deflate.h lib/zlib/inffast.c libz_la_SOURCES += lib/zlib/inffast.h lib/zlib/inflate.c lib/zlib/inflate.h libz_la_SOURCES += lib/zlib/inftrees.c lib/zlib/inftrees.h lib/zlib/trees.c libz_la_SOURCES += lib/zlib/zlib.h lib/zlib/trees.h lib/zlib/zutil.c libz_la_SOURCES += lib/zlib/zutil.h lib/zlib/crc32.c lib/zlib/crc32.h libz_la_CPPFLAGS = -I$(top_srcdir)/lib/zlib -DZLIB_CONST=1 libz_la_CPPFLAGS += -DNO_GZCOMPRESS=1 -DHAVE_MEMCPY=1 noinst_LTLIBRARIES += libz.la endif EXTRA_DIST += lib/zlib/README squashfs-tools-ng-1.1.3/lib/zlib/README000066400000000000000000000154241410627516300175120ustar00rootroot00000000000000Changes made for inclusion in libsquashfs: NOTE: THE SOURCE CODE IN THIS DIRECTORY IS NOT THE ORIGINAL ZLIB SOURCE CODE! A number of things were removed for inclusion in libsquashfs, part of the squashfs-tools-ng package. The original package that the source code in this directory is based on is the zlib-1.2.11 source tar ball obtained from http://zlib.net/ on October 16, 2017. An attempt has been made at removing the entire build system, samples, tests, documentation, 3rd party contributions and various utility functions. The following sub-directories were removed with all there contents: amiga/ contrib/ doc/ examples/ msdos/ nintendods/ old/ os400/ qnx/ test/ watcom/ win32/ The following source files were removed in their entirety: gzread.c gzclose.c gzwrite.c gzguts.h gzlib.c gzguts.h compress.c uncompr.c infback.c The following source files were modified (modifications marked with a comment in the source): zutil.c inflate.c deflate.c inftrees.c zconf.h zutil.h The following additional files were removed in their entirety: ChangeLog CMakeLists.txt configure FAQ INDEX Makefile Makefile.in make_vms.com treebuild.xml zconf.h.cmakein zconf.h.in zlib.3 zlib.3.pdf zlib.map zlib.pc.cmakein zlib.pc.in zlib2ansi The following additional files were modified: README A copy of the zlib license is included in licenses/zlib.txt in the root directory of the squashfs-tools-ng source package. Rest of the original README follows: ------------------------------------------------------------------------------- ZLIB DATA COMPRESSION LIBRARY zlib 1.2.11 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). All functions of the compression library are documented in the file zlib.h (volunteer to write man pages welcome, contact zlib@gzip.org). A usage example of the library is given in the file test/example.c which also tests that the library is working correctly. Another example is given in the file test/minigzip.c. The compression library itself is composed of all source files in the root directory. To compile all files and run the test program, follow the instructions given at the top of Makefile.in. In short "./configure; make test", and if that goes well, "make install" should work for most flavors of Unix. For Windows, use one of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use make_vms.com. Questions about zlib should be sent to , or to Gilles Vollant for the Windows DLL version. The zlib home page is http://zlib.net/ . Before reporting a problem, please check this site to verify that you have the latest version of zlib; otherwise get the latest version and check whether the problem still exists or not. PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at http://marknelson.us/1997/01/01/zlib-engine/ . The changes made in version 1.2.11 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . zlib is available in Java using the java.util.zip package, documented at http://java.sun.com/developer/technicalArticles/Programming/compression/ . A Perl interface to zlib written by Paul Marquess is available at CPAN (Comprehensive Perl Archive Network) sites, including http://search.cpan.org/~pmqs/IO-Compress-Zlib/ . A Python interface to zlib written by A.M. Kuchling is available in Python 1.5 and later versions, see http://docs.python.org/library/zlib.html . zlib is built into tcl: http://wiki.tcl.tk/4610 . An experimental package to read and write files in .zip format, written on top of zlib by Gilles Vollant , is available in the contrib/minizip directory of zlib. Notes for some targets: - For Windows DLL versions, please see win32/DLL_FAQ.txt - For 64-bit Irix, deflate.c must be compiled without any optimization. With -O, one libpng test fails. The test works in 32 bit mode (with the -n32 compiler flag). The compiler bug has been reported to SGI. - zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works when compiled with cc. - On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is necessary to get gzprintf working correctly. This is done by configure. - zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with other compilers. Use "make test" to check your compiler. - gzdopen is not supported on RISCOS or BEOS. - For PalmOs, see http://palmzlib.sourceforge.net/ Acknowledgments: The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. Copyright notice: (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu If you use the zlib library in a product, we would appreciate *not* receiving lengthy legal documents to sign. The sources are provided for free but without warranty of any kind. The library has been entirely written by Jean-loup Gailly and Mark Adler; it does not include third-party code. If you redistribute modified sources, we would appreciate that you include in the file ChangeLog history information documenting your changes. Please read the FAQ for more information on the distribution of modified source versions. squashfs-tools-ng-1.1.3/lib/zlib/adler32.c000066400000000000000000000121241410627516300202240ustar00rootroot00000000000000/* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-2011, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* use NO_DIVIDE if your processor does not do division in hardware -- try it both ways to see which is faster */ #ifdef NO_DIVIDE /* note that this assumes BASE is 65521, where 65536 % 65521 == 15 (thank you to John Reiser for pointing this out) */ # define CHOP(a) \ do { \ unsigned long tmp = a >> 16; \ a &= 0xffffUL; \ a += (tmp << 4) - tmp; \ } while (0) # define MOD28(a) \ do { \ CHOP(a); \ if (a >= BASE) a -= BASE; \ } while (0) # define MOD(a) \ do { \ CHOP(a); \ MOD28(a); \ } while (0) # define MOD63(a) \ do { /* this assumes a is not negative */ \ z_off64_t tmp = a >> 32; \ a &= 0xffffffffL; \ a += (tmp << 8) - (tmp << 5) + tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE # define MOD28(a) a %= BASE # define MOD63(a) a %= BASE #endif /* ========================================================================= */ uLong ZEXPORT adler32_z(adler, buf, len) uLong adler; const Bytef *buf; z_size_t len; { unsigned long sum2; unsigned n; /* split Adler-32 into component sums */ sum2 = (adler >> 16) & 0xffff; adler &= 0xffff; /* in case user likes doing a byte at a time, keep it fast */ if (len == 1) { adler += buf[0]; if (adler >= BASE) adler -= BASE; sum2 += adler; if (sum2 >= BASE) sum2 -= BASE; return adler | (sum2 << 16); } /* initial Adler-32 value (deferred check for len == 1 speed) */ if (buf == Z_NULL) return 1L; /* in case short lengths are provided, keep it somewhat fast */ if (len < 16) { while (len--) { adler += *buf++; sum2 += adler; } if (adler >= BASE) adler -= BASE; MOD28(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } /* do length NMAX blocks -- requires just one modulo operation */ while (len >= NMAX) { len -= NMAX; n = NMAX / 16; /* NMAX is divisible by 16 */ do { DO16(buf); /* 16 sums unrolled */ buf += 16; } while (--n); MOD(adler); MOD(sum2); } /* do remaining bytes (less than NMAX, still just one modulo) */ if (len) { /* avoid modulos if none remaining */ while (len >= 16) { len -= 16; DO16(buf); buf += 16; } while (len--) { adler += *buf++; sum2 += adler; } MOD(adler); MOD(sum2); } /* return recombined sums */ return adler | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32(adler, buf, len) uLong adler; const Bytef *buf; uInt len; { return adler32_z(adler, buf, len); } /* ========================================================================= */ local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { unsigned long sum1; unsigned long sum2; unsigned rem; /* for negative len, return invalid adler32 as a clue for debugging */ if (len2 < 0) return 0xffffffffUL; /* the derivation of this formula is left as an exercise for the reader */ MOD63(len2); /* assumes len2 >= 0 */ rem = (unsigned)len2; sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32_combine(adler1, adler2, len2) uLong adler1; uLong adler2; z_off_t len2; { return adler32_combine_(adler1, adler2, len2); } uLong ZEXPORT adler32_combine64(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { return adler32_combine_(adler1, adler2, len2); } squashfs-tools-ng-1.1.3/lib/zlib/crc32.c000066400000000000000000000333451410627516300177140ustar00rootroot00000000000000/* crc32.c -- compute the CRC-32 of a data stream * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing * tables for updating the shift register in one step with three exclusive-ors * instead of four steps with four exclusive-ors. This results in about a * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. */ /* @(#) $Id$ */ /* Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore protection on the static variables used to control the first-use generation of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. */ #ifdef MAKECRCH # include # ifndef DYNAMIC_CRC_TABLE # define DYNAMIC_CRC_TABLE # endif /* !DYNAMIC_CRC_TABLE */ #endif /* MAKECRCH */ #include "zutil.h" /* for STDC and FAR definitions */ /* Definitions for doing the crc four data bytes at a time. */ #if !defined(NOBYFOUR) && defined(Z_U4) # define BYFOUR #endif #ifdef BYFOUR local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, z_size_t)); local unsigned long crc32_big OF((unsigned long, const unsigned char FAR *, z_size_t)); # define TBLS 8 #else # define TBLS 1 #endif /* BYFOUR */ /* Local functions for crc concatenation */ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); #ifdef DYNAMIC_CRC_TABLE local volatile int crc_table_empty = 1; local z_crc_t FAR crc_table[TBLS][256]; local void make_crc_table OF((void)); #ifdef MAKECRCH local void write_table OF((FILE *, const z_crc_t FAR *)); #endif /* MAKECRCH */ /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. Polynomials over GF(2) are represented in binary, one bit per coefficient, with the lowest powers in the most significant bit. Then adding polynomials is just exclusive-or, and multiplying a polynomial by x is a right shift by one. If we call the above polynomial p, and represent a byte as the polynomial q, also with the lowest power in the most significant bit (so the byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, where a mod b means the remainder after dividing a by b. This calculation is done using the shift-register method of multiplying and taking the remainder. The register is initialized to zero, and for each incoming bit, x^32 is added mod p to the register if the bit is a one (where x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by x (which is shifting right by one and adding x^32 mod p if the bit shifted out is a one). We start with the highest power (least significant bit) of q and repeat for all eight bits of q. The first table is simply the CRC of all possible eight bit values. This is all the information needed to generate CRCs on data a byte at a time for all combinations of CRC register values and incoming bytes. The remaining tables allow for word-at-a-time CRC calculation for both big-endian and little- endian machines, where a word is four bytes. */ local void make_crc_table() { z_crc_t c; int n, k; z_crc_t poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static volatile int first = 1; /* flag to limit concurrent making */ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; /* See if another task is already doing this (not thread-safe, but better than nothing -- significantly reduces duration of vulnerability in case the advice about DYNAMIC_CRC_TABLE is ignored) */ if (first) { first = 0; /* make exclusive-or pattern from polynomial (0xedb88320UL) */ poly = 0; for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) poly |= (z_crc_t)1 << (31 - p[n]); /* generate a crc for every 8-bit value */ for (n = 0; n < 256; n++) { c = (z_crc_t)n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[0][n] = c; } #ifdef BYFOUR /* generate crc for each value followed by one, two, and three zeros, and then the byte reversal of those as well as the first table */ for (n = 0; n < 256; n++) { c = crc_table[0][n]; crc_table[4][n] = ZSWAP32(c); for (k = 1; k < 4; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; crc_table[k + 4][n] = ZSWAP32(c); } } #endif /* BYFOUR */ crc_table_empty = 0; } else { /* not first */ /* wait for the other guy to finish (not efficient, but rare) */ while (crc_table_empty) ; } #ifdef MAKECRCH /* write out CRC tables to crc32.h */ { FILE *out; out = fopen("crc32.h", "w"); if (out == NULL) return; fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); fprintf(out, "local const z_crc_t FAR "); fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); write_table(out, crc_table[0]); # ifdef BYFOUR fprintf(out, "#ifdef BYFOUR\n"); for (k = 1; k < 8; k++) { fprintf(out, " },\n {\n"); write_table(out, crc_table[k]); } fprintf(out, "#endif\n"); # endif /* BYFOUR */ fprintf(out, " }\n};\n"); fclose(out); } #endif /* MAKECRCH */ } #ifdef MAKECRCH local void write_table(out, table) FILE *out; const z_crc_t FAR *table; { int n; for (n = 0; n < 256; n++) fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", (unsigned long)(table[n]), n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); } #endif /* MAKECRCH */ #else /* !DYNAMIC_CRC_TABLE */ /* ======================================================================== * Tables of CRC-32s of all single-byte values, made by make_crc_table(). */ #include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ /* ========================================================================= * This function can be used by asm versions of crc32() */ const z_crc_t FAR * ZEXPORT get_crc_table() { #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ return (const z_crc_t FAR *)crc_table; } /* ========================================================================= */ #define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 /* ========================================================================= */ unsigned long ZEXPORT crc32_z(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; z_size_t len; { if (buf == Z_NULL) return 0UL; #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ #ifdef BYFOUR if (sizeof(void *) == sizeof(ptrdiff_t)) { z_crc_t endian; endian = 1; if (*((unsigned char *)(&endian))) return crc32_little(crc, buf, len); else return crc32_big(crc, buf, len); } #endif /* BYFOUR */ crc = crc ^ 0xffffffffUL; while (len >= 8) { DO8; len -= 8; } if (len) do { DO1; } while (--len); return crc ^ 0xffffffffUL; } /* ========================================================================= */ unsigned long ZEXPORT crc32(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; uInt len; { return crc32_z(crc, buf, len); } #ifdef BYFOUR /* This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit integer pointer type. This violates the strict aliasing rule, where a compiler can assume, for optimization purposes, that two pointers to fundamentally different types won't ever point to the same memory. This can manifest as a problem only if one of the pointers is written to. This code only reads from those pointers. So long as this code remains isolated in this compilation unit, there won't be a problem. For this reason, this code should not be copied and pasted into a compilation unit in which other code writes to the buffer that is passed to these routines. */ /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] #define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 /* ========================================================================= */ local unsigned long crc32_little(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = (z_crc_t)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOLIT32; len -= 32; } while (len >= 4) { DOLIT4; len -= 4; } buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); } while (--len); c = ~c; return (unsigned long)c; } /* ========================================================================= */ #define DOBIG4 c ^= *buf4++; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 /* ========================================================================= */ local unsigned long crc32_big(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = ZSWAP32((z_crc_t)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOBIG32; len -= 32; } while (len >= 4) { DOBIG4; len -= 4; } buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); } while (--len); c = ~c; return (unsigned long)(ZSWAP32(c)); } #endif /* BYFOUR */ #define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ /* ========================================================================= */ local unsigned long gf2_matrix_times(mat, vec) unsigned long *mat; unsigned long vec; { unsigned long sum; sum = 0; while (vec) { if (vec & 1) sum ^= *mat; vec >>= 1; mat++; } return sum; } /* ========================================================================= */ local void gf2_matrix_square(square, mat) unsigned long *square; unsigned long *mat; { int n; for (n = 0; n < GF2_DIM; n++) square[n] = gf2_matrix_times(mat, mat[n]); } /* ========================================================================= */ local uLong crc32_combine_(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { int n; unsigned long row; unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ /* degenerate case (also disallow negative lengths) */ if (len2 <= 0) return crc1; /* put operator for one zero bit in odd */ odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ row = 1; for (n = 1; n < GF2_DIM; n++) { odd[n] = row; row <<= 1; } /* put operator for two zero bits in even */ gf2_matrix_square(even, odd); /* put operator for four zero bits in odd */ gf2_matrix_square(odd, even); /* apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even) */ do { /* apply zeros operator for this bit of len2 */ gf2_matrix_square(even, odd); if (len2 & 1) crc1 = gf2_matrix_times(even, crc1); len2 >>= 1; /* if no more bits set, then done */ if (len2 == 0) break; /* another iteration of the loop with odd and even swapped */ gf2_matrix_square(odd, even); if (len2 & 1) crc1 = gf2_matrix_times(odd, crc1); len2 >>= 1; /* if no more bits set, then done */ } while (len2 != 0); /* return combined crc */ crc1 ^= crc2; return crc1; } /* ========================================================================= */ uLong ZEXPORT crc32_combine(crc1, crc2, len2) uLong crc1; uLong crc2; z_off_t len2; { return crc32_combine_(crc1, crc2, len2); } uLong ZEXPORT crc32_combine64(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { return crc32_combine_(crc1, crc2, len2); } squashfs-tools-ng-1.1.3/lib/zlib/crc32.h000066400000000000000000000735421410627516300177240ustar00rootroot00000000000000/* crc32.h -- tables for rapid CRC calculation * Generated automatically by crc32.c */ local const z_crc_t FAR crc_table[TBLS][256] = { { 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, 0x2d02ef8dUL #ifdef BYFOUR }, { 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, 0x9324fd72UL }, { 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, 0xbe9834edUL }, { 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, 0xde0506f1UL }, { 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, 0x8def022dUL }, { 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, 0x72fd2493UL }, { 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, 0xed3498beUL }, { 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, 0xf10605deUL #endif } }; squashfs-tools-ng-1.1.3/lib/zlib/deflate.c000066400000000000000000002322741410627516300204060ustar00rootroot00000000000000/* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". * Available in http://tools.ietf.org/html/rfc1951 * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* @(#) $Id$ */ #include "deflate.h" /* XXX: Not original zlib source code. The following 2 lines were commented out by David Oberhollenzer for use in in libsquashfs. const char deflate_copyright[] = " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; */ /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* =========================================================================== * Function prototypes. */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ finish_started, /* finish started, need only more output at next deflate */ finish_done /* finish done, accept no more input or output */ } block_state; typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ local int deflateStateCheck OF((z_streamp strm)); local void slide_hash OF((deflate_state *s)); local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); #ifndef FASTEST local block_state deflate_slow OF((deflate_state *s, int flush)); #endif local block_state deflate_rle OF((deflate_state *s, int flush)); local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV # pragma message("Assembler code may have bugs -- use at your own risk") void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif #ifdef ZLIB_DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif /* =========================================================================== * Local data */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; compress_func func; } config; #ifdef FASTEST local const config configuration_table[2] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ #else local const config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ /* 2 */ {4, 5, 16, 8, deflate_fast}, /* 3 */ {4, 6, 32, 32, deflate_fast}, /* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ /* 5 */ {8, 16, 32, 32, deflate_slow}, /* 6 */ {8, 16, 128, 128, deflate_slow}, /* 7 */ {8, 32, 128, 256, deflate_slow}, /* 8 */ {32, 128, 258, 1024, deflate_slow}, /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ #endif /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ /* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ #define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to UPDATE_HASH are made with consecutive input * characters, so that a running hash key can be computed from the previous * key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. * IN assertion: all calls to INSERT_STRING are made with consecutive input * characters and the first MIN_MATCH bytes of str are valid (except for * the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #else #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #endif /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ s->head[s->hash_size-1] = NIL; \ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* =========================================================================== * Slide the hash table when sliding the window down (could be avoided with 32 * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ local void slide_hash(s) deflate_state *s; { unsigned n, m; Posf *p; uInt wsize = s->w_size; n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m - wsize : NIL); } while (--n); n = wsize; #ifndef FASTEST p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m - wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); #endif } /* ========================================================================= */ int ZEXPORT deflateInit_(strm, level, version, stream_size) z_streamp strm; int level; const char *version; int stream_size; { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version, stream_size) z_streamp strm; int level; int method; int windowBits; int memLevel; int strategy; const char *version; int stream_size; { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; ushf *overlay; /* We overlay pending_buf and d_buf+l_buf. This works since the average * output size for (length,distance) codes is <= 24 bits. */ if (version == Z_NULL || version[0] != my_version[0] || stream_size != sizeof(z_stream)) { return Z_VERSION_ERROR; } if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } #ifdef GZIP else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->status = INIT_STATE; /* to pass state test in deflateReset() */ s->wrap = wrap; s->gzhead = Z_NULL; s->w_bits = (uInt)windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = (uInt)memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->high_water = 0; /* nothing written to s->window yet */ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); s->pending_buf = (uchf *) overlay; s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; strm->msg = ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s->level = level; s->strategy = strategy; s->method = (Byte)method; return deflateReset(strm); } /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ local int deflateStateCheck (strm) z_streamp strm; { deflate_state *s; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) return 1; s = strm->state; if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && #ifdef GZIP s->status != GZIP_STATE && #endif s->status != EXTRA_STATE && s->status != NAME_STATE && s->status != COMMENT_STATE && s->status != HCRC_STATE && s->status != BUSY_STATE && s->status != FINISH_STATE)) return 1; return 0; } /* ========================================================================= */ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { deflate_state *s; uInt str, n; int wrap; unsigned avail; z_const unsigned char *next; if (deflateStateCheck(strm) || dictionary == Z_NULL) return Z_STREAM_ERROR; s = strm->state; wrap = s->wrap; if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) return Z_STREAM_ERROR; /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap == 1) strm->adler = adler32(strm->adler, dictionary, dictLength); s->wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s->w_size) { if (wrap == 0) { /* already empty otherwise */ CLEAR_HASH(s); s->strstart = 0; s->block_start = 0L; s->insert = 0; } dictionary += dictLength - s->w_size; /* use the tail */ dictLength = s->w_size; } /* insert dictionary into window and hash */ avail = strm->avail_in; next = strm->next_in; strm->avail_in = dictLength; strm->next_in = (z_const Bytef *)dictionary; fill_window(s); while (s->lookahead >= MIN_MATCH) { str = s->strstart; n = s->lookahead - (MIN_MATCH-1); do { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; } while (--n); s->strstart = str; s->lookahead = MIN_MATCH-1; fill_window(s); } s->strstart += s->lookahead; s->block_start = (long)s->strstart; s->insert = s->lookahead; s->lookahead = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; strm->next_in = next; strm->avail_in = avail; s->wrap = wrap; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) z_streamp strm; Bytef *dictionary; uInt *dictLength; { deflate_state *s; uInt len; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; len = s->strstart + s->lookahead; if (len > s->w_size) len = s->w_size; if (dictionary != Z_NULL && len) zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); if (dictLength != Z_NULL) *dictLength = len; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; if (deflateStateCheck(strm)) { return Z_STREAM_ERROR; } strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } s->status = #ifdef GZIP s->wrap == 2 ? GZIP_STATE : #endif s->wrap ? INIT_STATE : BUSY_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); s->last_flush = Z_NO_FLUSH; _tr_init(s); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateReset (strm) z_streamp strm; { int ret; ret = deflateResetKeep(strm); if (ret == Z_OK) lm_init(strm->state); return ret; } /* ========================================================================= */ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; { if (deflateStateCheck(strm) || strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePending (strm, pending, bits) unsigned *pending; int *bits; z_streamp strm; { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; if (bits != Z_NULL) *bits = strm->state->bi_valid; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; int value; { deflate_state *s; int put; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; do { put = Buf_size - s->bi_valid; if (put > bits) put = bits; s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); s->bi_valid += put; _tr_flush_bits(s); value >>= put; bits -= put; } while (bits); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateParams(strm, level, strategy) z_streamp strm; int level; int strategy; { deflate_state *s; compress_func func; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && s->high_water) { /* Flush the last buffer: */ int err = deflate(strm, Z_BLOCK); if (err == Z_STREAM_ERROR) return err; if (strm->avail_out == 0) return Z_BUF_ERROR; } if (s->level != level) { if (s->level == 0 && s->matches != 0) { if (s->matches == 1) slide_hash(s); else CLEAR_HASH(s); s->matches = 0; } s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; s->nice_match = configuration_table[level].nice_length; s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) z_streamp strm; int good_length; int max_lazy; int nice_length; int max_chain; { deflate_state *s; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; s->good_match = (uInt)good_length; s->max_lazy_match = (uInt)max_lazy; s->nice_match = nice_length; s->max_chain_length = (uInt)max_chain; return Z_OK; } /* ========================================================================= * For the default windowBits of 15 and memLevel of 8, this function returns * a close to exact, as well as small, upper bound on the compressed size. * They are coded as constants here for a reason--if the #define's are * changed, then this function needs to be changed as well. The return * value for 15 and 8 only works for those exact settings. * * For any setting other than those defaults for windowBits and memLevel, * the value returned is a conservative worst case for the maximum expansion * resulting from using fixed blocks instead of stored blocks, which deflate * can emit on compressed data for some combinations of the parameters. * * This function could be more sophisticated to provide closer upper bounds for * every combination of windowBits and memLevel. But even the conservative * upper bound of about 14% expansion does not seem onerous for output buffer * allocation. */ uLong ZEXPORT deflateBound(strm, sourceLen) z_streamp strm; uLong sourceLen; { deflate_state *s; uLong complen, wraplen; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; /* if can't get parameters, return conservative bound plus zlib wrapper */ if (deflateStateCheck(strm)) return complen + 6; /* compute wrapper length */ s = strm->state; switch (s->wrap) { case 0: /* raw deflate */ wraplen = 0; break; case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; #ifdef GZIP case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ Bytef *str; if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; if (str != Z_NULL) do { wraplen++; } while (*str++); str = s->gzhead->comment; if (str != Z_NULL) do { wraplen++; } while (*str++); if (s->gzhead->hcrc) wraplen += 2; } break; #endif default: /* for compiler happiness */ wraplen = 6; } /* if not default parameters, return conservative bound */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) return complen + wraplen; /* default settings: return tight bound for that case */ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB (s, b) deflate_state *s; uInt b; { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. All deflate() output, except for * some deflate_stored() output, goes through this function so some * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; { unsigned len; deflate_state *s = strm->state; _tr_flush_bits(s); len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; s->pending_out += len; strm->total_out += len; strm->avail_out -= len; s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } /* =========================================================================== * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. */ #define HCRC_UPDATE(beg) \ do { \ if (s->gzhead->hcrc && s->pending > (beg)) \ strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ s->pending - (beg)); \ } while (0) /* ========================================================================= */ int ZEXPORT deflate (strm, flush) z_streamp strm; int flush; { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || (strm->avail_in != 0 && strm->next_in == Z_NULL) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); old_flush = s->last_flush; s->last_flush = flush; /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s->last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Write the header */ if (s->status == INIT_STATE) { /* zlib header */ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; uInt level_flags; if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) level_flags = 0; else if (s->level < 6) level_flags = 1; else if (s->level == 6) level_flags = 2; else level_flags = 3; header |= (level_flags << 6); if (s->strstart != 0) header |= PRESET_DICT; header += 31 - (header % 31); putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s->strstart != 0) { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } strm->adler = adler32(0L, Z_NULL, 0); s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } #ifdef GZIP if (s->status == GZIP_STATE) { /* gzip header */ strm->adler = crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } else { put_byte(s, (s->gzhead->text ? 1 : 0) + (s->gzhead->hcrc ? 2 : 0) + (s->gzhead->extra == Z_NULL ? 0 : 4) + (s->gzhead->name == Z_NULL ? 0 : 8) + (s->gzhead->comment == Z_NULL ? 0 : 16) ); put_byte(s, (Byte)(s->gzhead->time & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } if (s->gzhead->hcrc) strm->adler = crc32(strm->adler, s->pending_buf, s->pending); s->gzindex = 0; s->status = EXTRA_STATE; } } if (s->status == EXTRA_STATE) { if (s->gzhead->extra != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; while (s->pending + left > s->pending_buf_size) { uInt copy = s->pending_buf_size - s->pending; zmemcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, copy); s->pending = s->pending_buf_size; HCRC_UPDATE(beg); s->gzindex += copy; flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; left -= copy; } zmemcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, left); s->pending += left; HCRC_UPDATE(beg); s->gzindex = 0; } s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { HCRC_UPDATE(beg); flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); HCRC_UPDATE(beg); s->gzindex = 0; } s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { HCRC_UPDATE(beg); flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); HCRC_UPDATE(beg); } s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { if (s->pending + 2 > s->pending_buf_size) { flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); strm->adler = crc32(0L, Z_NULL, 0); } s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } #endif /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; bstate = s->level == 0 ? deflate_stored(s, flush) : s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s->strategy == Z_RLE ? deflate_rle(s, flush) : (*(configuration_table[s->level].func))(s, flush); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; } if (bstate == need_more || bstate == finish_started) { if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ if (s->lookahead == 0) { s->strstart = 0; s->block_start = 0L; s->insert = 0; } } } flush_pending(strm); if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; /* Write the trailer */ #ifdef GZIP if (s->wrap == 2) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); put_byte(s, (Byte)(strm->total_in & 0xff)); put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); } else #endif { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ return s->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int ZEXPORT deflateEnd (strm) z_streamp strm; { int status; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; status = strm->state->status; /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } /* ========================================================================= * Copy the source state to the destination state. * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ int ZEXPORT deflateCopy (dest, source) z_streamp dest; z_streamp source; { #ifdef MAXSEG_64K return Z_STREAM_ERROR; #else deflate_state *ds; deflate_state *ss; ushf *overlay; if (deflateStateCheck(source) || dest == Z_NULL) { return Z_STREAM_ERROR; } ss = source->state; zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); ds->pending_buf = (uchf *) overlay; if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { deflateEnd (dest); return Z_MEM_ERROR; } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; ds->bl_desc.dyn_tree = ds->bl_tree; return Z_OK; #endif /* MAXSEG_64K */ } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ local unsigned read_buf(strm, buf, size) z_streamp strm; Bytef *buf; unsigned size; { unsigned len = strm->avail_in; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; zmemcpy(buf, strm->next_in, len); if (strm->state->wrap == 1) { strm->adler = adler32(strm->adler, buf, len); } #ifdef GZIP else if (strm->state->wrap == 2) { strm->adler = crc32(strm->adler, buf, len); } #endif strm->next_in += len; strm->total_in += len; return len; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init (s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->insert = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; #ifndef FASTEST #ifdef ASMV match_init(); /* initialize the asm code */ #endif #endif } #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ #ifndef ASMV /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = (int)s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan+best_len-1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len-1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match+best_len-1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart+3, +5, ... up to strstart+257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window+strstart+257 */ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len-1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan+best_len-1); #else scan_end1 = scan[best_len-1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); if ((uInt)best_len <= s->lookahead) return (uInt)best_len; return s->lookahead; } #endif /* ASMV */ #else /* FASTEST */ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ register Bytef *strend = s->window + s->strstart + MAX_MATCH; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Return failure if the match length is less than 2: */ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match += 2; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); if (len < MIN_MATCH) return MIN_MATCH - 1; s->match_start = cur_match; return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; } #endif /* FASTEST */ #ifdef ZLIB_DEBUG #define EQUAL 0 /* result of memcmp for equal strings */ /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(s, start, match, length) deflate_state *s; IPos start, match; int length; { /* check that the match is indeed a match */ if (zmemcmp(s->window + match, s->window + start, length) != EQUAL) { fprintf(stderr, " start %u, match %u, length %d\n", start, match, length); do { fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); } while (--length != 0); z_error("invalid match"); } if (z_verbose > 1) { fprintf(stderr,"\\[%d,%d]", start-match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* ZLIB_DEBUG */ /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(s) deflate_state *s; { unsigned n; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (sizeof(int) <= 2) { if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if * strstart == 0 && lookahead == 1 (input done a byte at time) */ more--; } } /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s->strstart >= wsize+MAX_DIST(s)) { zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; slide_hash(s); more += wsize; } if (s->strm->avail_in == 0) break; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead + s->insert >= MIN_MATCH) { uInt str = s->strstart - s->insert; s->ins_h = s->window[str]; UPDATE_HASH(s, s->ins_h, s->window[str + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif while (s->insert) { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; s->insert--; if (s->lookahead + s->insert < MIN_MATCH) break; } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ if (s->high_water < s->window_size) { ulg curr = s->strstart + (ulg)(s->lookahead); ulg init; if (s->high_water < curr) { /* Previous high water mark below current data -- zero WIN_INIT * bytes or up to end of window, whichever is less. */ init = s->window_size - curr; if (init > WIN_INIT) init = WIN_INIT; zmemzero(s->window + curr, (unsigned)init); s->high_water = curr + init; } else if (s->high_water < (ulg)curr + WIN_INIT) { /* High water mark at or above current data, but below current data * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up * to end of window, whichever is less. */ init = (ulg)curr + WIN_INIT - s->high_water; if (init > s->window_size - s->high_water) init = s->window_size - s->high_water; zmemzero(s->window + s->high_water, (unsigned)init); s->high_water += init; } } Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "not enough room for search"); } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, last) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ (last)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, last) { \ FLUSH_BLOCK_ONLY(s, last); \ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } /* Maximum stored block length in deflate format (not including header). */ #define MAX_STORED 65535 /* Minimum of a and b. */ #define MIN(a, b) ((a) > (b) ? (b) : (a)) /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * * In case deflateParams() is used to later switch to a non-zero compression * level, s->matches (otherwise unused when storing) keeps track of the number * of hash table slides to perform. If s->matches is 1, then one hash table * slide will be done when switching. If s->matches is 2, the maximum value * allowed here, then the hash table will be cleared, since two or more slides * is the same as a clear. * * deflate_stored() is written to minimize the number of times an input byte is * copied. It is most efficient with large input and output buffers, which * maximizes the opportunites to have a single copy from next_in to next_out. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. */ unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); /* Copy as many min_block or larger stored blocks directly to next_out as * possible. If flushing, copy the remaining available input to next_out as * stored blocks, if there is enough space. */ unsigned len, left, have, last = 0; unsigned used = s->strm->avail_in; do { /* Set len to the maximum size block that we can copy directly with the * available input data and output space. Set left to how much of that * would be copied from what's left in the window. */ len = MAX_STORED; /* maximum deflate stored block length */ have = (s->bi_valid + 42) >> 3; /* number of header bytes */ if (s->strm->avail_out < have) /* need room for header */ break; /* maximum stored block length that will fit in avail_out: */ have = s->strm->avail_out - have; left = s->strstart - s->block_start; /* bytes left in window */ if (len > (ulg)left + s->strm->avail_in) len = left + s->strm->avail_in; /* limit len to the input */ if (len > have) len = have; /* limit len to the output */ /* If the stored block would be less than min_block in length, or if * unable to copy all of the available input when flushing, then try * copying to the window and the pending buffer instead. Also don't * write an empty block when flushing -- deflate() does that. */ if (len < min_block && ((len == 0 && flush != Z_FINISH) || flush == Z_NO_FLUSH || len != left + s->strm->avail_in)) break; /* Make a dummy stored block in pending to get the header bytes, * including any pending bits. This also updates the debugging counts. */ last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; _tr_stored_block(s, (char *)0, 0L, last); /* Replace the lengths in the dummy stored block with len. */ s->pending_buf[s->pending - 4] = len; s->pending_buf[s->pending - 3] = len >> 8; s->pending_buf[s->pending - 2] = ~len; s->pending_buf[s->pending - 1] = ~len >> 8; /* Write the stored block header bytes. */ flush_pending(s->strm); #ifdef ZLIB_DEBUG /* Update debugging counts for the data about to be copied. */ s->compressed_len += len << 3; s->bits_sent += len << 3; #endif /* Copy uncompressed bytes from the window to next_out. */ if (left) { if (left > len) left = len; zmemcpy(s->strm->next_out, s->window + s->block_start, left); s->strm->next_out += left; s->strm->avail_out -= left; s->strm->total_out += left; s->block_start += left; len -= left; } /* Copy uncompressed bytes directly from next_in to next_out, updating * the check value. */ if (len) { read_buf(s->strm, s->strm->next_out, len); s->strm->next_out += len; s->strm->avail_out -= len; s->strm->total_out += len; } } while (last == 0); /* Update the sliding window with the last s->w_size bytes of the copied * data, or append all of the copied data to the existing window if less * than s->w_size bytes were copied. Also update the number of bytes to * insert in the hash tables, in the event that deflateParams() switches to * a non-zero compression level. */ used -= s->strm->avail_in; /* number of input bytes directly copied */ if (used) { /* If any input was used, then no unused input remains in the window, * therefore s->block_start == s->strstart. */ if (used >= s->w_size) { /* supplant the previous history */ s->matches = 2; /* clear hash */ zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); s->strstart = s->w_size; } else { if (s->window_size - s->strstart <= used) { /* Slide the window down. */ s->strstart -= s->w_size; zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ } zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); s->strstart += used; } s->block_start = s->strstart; s->insert += MIN(used, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; /* If the last block was written to next_out, then done. */ if (last) return finish_done; /* If flushing and all input has been consumed, then done. */ if (flush != Z_NO_FLUSH && flush != Z_FINISH && s->strm->avail_in == 0 && (long)s->strstart == s->block_start) return block_done; /* Fill the window with any remaining input. */ have = s->window_size - s->strstart - 1; if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { /* Slide the window down. */ s->block_start -= s->w_size; s->strstart -= s->w_size; zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ have += s->w_size; /* more space now */ } if (have > s->strm->avail_in) have = s->strm->avail_in; if (have) { read_buf(s->strm, s->window + s->strstart, have); s->strstart += have; } if (s->high_water < s->strstart) s->high_water = s->strstart; /* There was not enough avail_out to write a complete worthy or flushed * stored block to next_out. Write a stored block to pending instead, if we * have enough input for a worthy block, or if flushing and there is enough * room for the remaining input as a stored block in the pending buffer. */ have = (s->bi_valid + 42) >> 3; /* number of header bytes */ /* maximum stored block length that will fit in pending: */ have = MIN(s->pending_buf_size - have, MAX_STORED); min_block = MIN(have, s->w_size); left = s->strstart - s->block_start; if (left >= min_block || ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && s->strm->avail_in == 0 && left <= have)) { len = MIN(left, have); last = flush == Z_FINISH && s->strm->avail_in == 0 && len == left ? 1 : 0; _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); s->block_start += len; flush_pending(s->strm); } /* We've done all we can with the available input and output. */ return last ? finish_started : need_more; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local block_state deflate_fast(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); _tr_tally_dist(s, s->strstart - s->match_start, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ #ifndef FASTEST if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else #endif { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } #ifndef FASTEST /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local block_state deflate_slow(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED #if TOO_FAR <= 32767 || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR) #endif )) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart-1, s->prev_match, s->prev_length); _tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length-1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); if (bflush) { FLUSH_BLOCK_ONLY(s, 0); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return need_more; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); s->match_available = 0; } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } #endif /* FASTEST */ /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ local block_state deflate_rle(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s->lookahead <= MAX_MATCH) { fill_window(s); if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* See how many times the previous byte repeats */ s->match_length = 0; if (s->lookahead >= MIN_MATCH && s->strstart > 0) { scan = s->window + s->strstart - 1; prev = *scan; if (prev == *++scan && prev == *++scan && prev == *++scan) { strend = s->window + s->strstart + MAX_MATCH; do { } while (prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); s->match_length = MAX_MATCH - (uInt)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->strstart - 1, s->match_length); _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; s->strstart += s->match_length; s->match_length = 0; } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ local block_state deflate_huff(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s->lookahead == 0) { fill_window(s); if (s->lookahead == 0) { if (flush == Z_NO_FLUSH) return need_more; break; /* flush the current block */ } } /* Output a literal byte */ s->match_length = 0; Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } squashfs-tools-ng-1.1.3/lib/zlib/deflate.h000066400000000000000000000315361410627516300204110ustar00rootroot00000000000000/* deflate.h -- internal compression state * Copyright (C) 1995-2016 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef DEFLATE_H #define DEFLATE_H #include "zutil.h" /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define Buf_size 16 /* size of bit buffer in bi_buf */ #define INIT_STATE 42 /* zlib header -> BUSY_STATE */ #ifdef GZIP # define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ #endif #define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ #define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ #define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ #define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ #define BUSY_STATE 113 /* deflate -> FINISH_STATE */ #define FINISH_STATE 666 /* stream complete */ /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ const static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct internal_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ ulg pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ ulg gzindex; /* where in extra, name, or comment */ Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to suppress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ uchf *l_buf; /* buffer for literals or lengths */ uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt last_lit; /* running index in l_buf */ ushf *d_buf; /* Buffer for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ uInt insert; /* bytes at end of window left to insert */ #ifdef ZLIB_DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ ulg high_water; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ #define WIN_INIT MAX_MATCH /* Number of bytes after end of data in window to initialize in order to avoid memory checker errors from longest match routines */ /* in trees.c */ void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. _dist_code[256] and _dist_code[257] are never * used. */ #ifndef ZLIB_DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) extern uch ZLIB_INTERNAL _length_code[]; extern uch ZLIB_INTERNAL _dist_code[]; #else extern const uch ZLIB_INTERNAL _length_code[]; extern const uch ZLIB_INTERNAL _dist_code[]; #endif # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->d_buf[s->last_lit] = 0; \ s->l_buf[s->last_lit++] = cc; \ s->dyn_ltree[cc].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (uch)(length); \ ush dist = (ush)(distance); \ s->d_buf[s->last_lit] = dist; \ s->l_buf[s->last_lit++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ flush = _tr_tally(s, distance, length) #endif #endif /* DEFLATE_H */ squashfs-tools-ng-1.1.3/lib/zlib/inffast.c000066400000000000000000000312621410627516300204260ustar00rootroot00000000000000/* inffast.c -- fast decoding * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef ASMINF # pragma message("Assembler code may have bugs -- use at your own risk") #else /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state->mode == LEN strm->avail_in >= 6 strm->avail_out >= 258 start >= strm->avail_out state->bits < 8 On return, state->mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm->avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ void ZLIB_INTERNAL inflate_fast(strm, start) z_streamp strm; unsigned start; /* inflate()'s starting value for strm->avail_out */ { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ #ifdef INFLATE_STRICT unsigned dmax; /* maximum distance from zlib header */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ code const FAR *lcode; /* local strm->lencode */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ code here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ unsigned dist; /* match distance */ unsigned char FAR *from; /* where to copy match from */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; in = strm->next_in; last = in + (strm->avail_in - 5); out = strm->next_out; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT dmax = state->dmax; #endif wsize = state->wsize; whave = state->whave; wnext = state->wnext; window = state->window; hold = state->hold; bits = state->bits; lcode = state->lencode; dcode = state->distcode; lmask = (1U << state->lenbits) - 1; dmask = (1U << state->distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ do { if (bits < 15) { hold += (unsigned long)(*in++) << bits; bits += 8; hold += (unsigned long)(*in++) << bits; bits += 8; } here = lcode[hold & lmask]; dolen: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op == 0) { /* literal */ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); *out++ = (unsigned char)(here.val); } else if (op & 16) { /* length base */ len = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); hold >>= op; bits -= op; } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { hold += (unsigned long)(*in++) << bits; bits += 8; hold += (unsigned long)(*in++) << bits; bits += 8; } here = dcode[hold & dmask]; dodist: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op & 16) { /* distance base */ dist = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; } } dist += (unsigned)hold & ((1U << op) - 1); #ifdef INFLATE_STRICT if (dist > dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif hold >>= op; bits -= op; Tracevv((stderr, "inflate: distance %u\n", dist)); op = (unsigned)(out - beg); /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { *out++ = 0; } while (--len); continue; } len -= op - whave; do { *out++ = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { *out++ = *from++; } while (--len); continue; } #endif } from = window; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { *out++ = *from++; } while (--op); from = window; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { *out++ = *from++; *out++ = *from++; *out++ = *from++; len -= 3; } if (len) { *out++ = *from++; if (len > 1) *out++ = *from++; } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ *out++ = *from++; *out++ = *from++; *out++ = *from++; len -= 3; } while (len > 2); if (len) { *out++ = *from++; if (len > 1) *out++ = *from++; } } } else if ((op & 64) == 0) { /* 2nd level distance code */ here = dcode[here.val + (hold & ((1U << op) - 1))]; goto dodist; } else { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } } else if ((op & 64) == 0) { /* 2nd level length code */ here = lcode[here.val + (hold & ((1U << op) - 1))]; goto dolen; } else if (op & 32) { /* end-of-block */ Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } else { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } } while (in < last && out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; in -= len; bits -= len << 3; hold &= (1U << bits) - 1; /* update state and return */ strm->next_in = in; strm->next_out = out; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); state->hold = hold; state->bits = bits; return; } /* inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - Three separate decoding do-loops for direct, window, and wnext == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes - Swapping literal/length else - Swapping window/direct else - Larger unrolled copy loops (three is about right) - Moving len -= 3 statement into middle of loop */ #endif /* !ASMINF */ squashfs-tools-ng-1.1.3/lib/zlib/inffast.h000066400000000000000000000006531410627516300204330ustar00rootroot00000000000000/* inffast.h -- header to use inffast.c * Copyright (C) 1995-2003, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); squashfs-tools-ng-1.1.3/lib/zlib/inffixed.h000066400000000000000000000142741410627516300206010ustar00rootroot00000000000000 /* inffixed.h -- table for decoding fixed codes * Generated automatically by makefixed(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of this library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, {0,9,255} }; static const code distfix[32] = { {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, {22,5,193},{64,5,0} }; squashfs-tools-ng-1.1.3/lib/zlib/inflate.c000066400000000000000000001542361410627516300204250ustar00rootroot00000000000000/* inflate.c -- zlib decompression * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * Change history: * * 1.2.beta0 24 Nov 2002 * - First version -- complete rewrite of inflate to simplify code, avoid * creation of window when not needed, minimize use of window when it is * needed, make inffast.c even faster, implement gzip decoding, and to * improve code readability and style over the previous zlib inflate code * * 1.2.beta1 25 Nov 2002 * - Use pointers for available input and output checking in inffast.c * - Remove input and output counters in inffast.c * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 * - Remove unnecessary second byte pull from length extra in inffast.c * - Unroll direct copy to three copies per loop in inffast.c * * 1.2.beta2 4 Dec 2002 * - Change external routine names to reduce potential conflicts * - Correct filename to inffixed.h for fixed tables in inflate.c * - Make hbuf[] unsigned char to match parameter type in inflate.c * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) * to avoid negation problem on Alphas (64 bit) in inflate.c * * 1.2.beta3 22 Dec 2002 * - Add comments on state->bits assertion in inffast.c * - Add comments on op field in inftrees.h * - Fix bug in reuse of allocated window after inflateReset() * - Remove bit fields--back to byte structure for speed * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths * - Change post-increments to pre-increments in inflate_fast(), PPC biased? * - Add compile time option, POSTINC, to use post-increments instead (Intel?) * - Make MATCH copy in inflate() much faster for when inflate_fast() not used * - Use local copies of stream next and avail values, as well as local bit * buffer and bit count in inflate()--for speed when inflate_fast() not used * * 1.2.beta4 1 Jan 2003 * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings * - Move a comment on output buffer sizes from inffast.c to inflate.c * - Add comments in inffast.c to introduce the inflate_fast() routine * - Rearrange window copies in inflate_fast() for speed and simplification * - Unroll last copy for window match in inflate_fast() * - Use local copies of window variables in inflate_fast() for speed * - Pull out common wnext == 0 case for speed in inflate_fast() * - Make op and len in inflate_fast() unsigned for consistency * - Add FAR to lcode and dcode declarations in inflate_fast() * - Simplified bad distance check in inflate_fast() * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new * source file infback.c to provide a call-back interface to inflate for * programs like gzip and unzip -- uses window as output buffer to avoid * window copying * * 1.2.beta5 1 Jan 2003 * - Improved inflateBack() interface to allow the caller to provide initial * input in strm. * - Fixed stored blocks bug in inflateBack() * * 1.2.beta6 4 Jan 2003 * - Added comments in inffast.c on effectiveness of POSTINC * - Typecasting all around to reduce compiler warnings * - Changed loops from while (1) or do {} while (1) to for (;;), again to * make compilers happy * - Changed type of window in inflateBackInit() to unsigned char * * * 1.2.beta7 27 Jan 2003 * - Changed many types to unsigned or unsigned short to avoid warnings * - Added inflateCopy() function * * 1.2.0 9 Mar 2003 * - Changed inflateBack() interface to provide separate opaque descriptors * for the in() and out() functions * - Changed inflateBack() argument and in_func typedef to swap the length * and buffer address return values for the input function * - Check next_in and next_out for Z_NULL on entry to inflate() * * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef MAKEFIXED # ifndef BUILDFIXED # define BUILDFIXED # endif #endif /* function prototypes */ local int inflateStateCheck OF((z_streamp strm)); local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, unsigned copy)); #ifdef BUILDFIXED void makefixed OF((void)); #endif local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); local int inflateStateCheck(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) return 1; state = (struct inflate_state FAR *)strm->state; if (state == Z_NULL || state->strm != strm || state->mode < HEAD || state->mode > SYNC) return 1; return 0; } int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; if (state->wrap) /* to support ill-conceived Java test suite */ strm->adler = state->wrap & 1; state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->head = Z_NULL; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; state->sane = 1; state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } int ZEXPORT inflateReset(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->wsize = 0; state->whave = 0; state->wnext = 0; return inflateResetKeep(strm); } int ZEXPORT inflateReset2(strm, windowBits) z_streamp strm; int windowBits; { int wrap; struct inflate_state FAR *state; /* get the state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 5; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; #endif } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) return Z_STREAM_ERROR; if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { ZFREE(strm, state->window); state->window = Z_NULL; } /* update state and reset the rest of it */ state->wrap = wrap; state->wbits = (unsigned)windowBits; return inflateReset(strm); } int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) z_streamp strm; int windowBits; const char *version; int stream_size; { int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->strm = strm; state->window = Z_NULL; state->mode = HEAD; /* to pass state test in inflateReset2() */ ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; } return ret; } int ZEXPORT inflateInit_(strm, version, stream_size) z_streamp strm; const char *version; int stream_size; { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } int ZEXPORT inflatePrime(strm, bits, value) z_streamp strm; int bits; int value; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; state->hold += (unsigned)value << state->bits; state->bits += (uInt)bits; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(state) struct inflate_state FAR *state; { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } #ifdef MAKEFIXED #include /* Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also defines BUILDFIXED, so the tables are built on the fly. makefixed() writes those tables to stdout, which would be piped to inffixed.h. A small program can simply call makefixed to do this: void makefixed(void); int main(void) { makefixed(); return 0; } Then that can be linked with zlib built with MAKEFIXED defined and run: a.out > inffixed.h */ void makefixed() { unsigned low, size; struct inflate_state state; fixedtables(&state); puts(" /* inffixed.h -- table for decoding fixed codes"); puts(" * Generated automatically by makefixed()."); puts(" */"); puts(""); puts(" /* WARNING: this file should *not* be used by applications."); puts(" It is part of the implementation of this library and is"); puts(" subject to change. Applications should only use zlib.h."); puts(" */"); puts(""); size = 1U << 9; printf(" static const code lenfix[%u] = {", size); low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); size = 1U << 5; printf("\n static const code distfix[%u] = {", size); low = 0; for (;;) { if ((low % 6) == 0) printf("\n "); printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, state.distcode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); } #endif /* MAKEFIXED */ /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ local int updatewindow(strm, end, copy) z_streamp strm; const Bytef *end; unsigned copy; { struct inflate_state FAR *state; unsigned dist; state = (struct inflate_state FAR *)strm->state; /* if it hasn't been done already, allocate space for the window */ if (state->window == Z_NULL) { state->window = (unsigned char FAR *) ZALLOC(strm, 1U << state->wbits, sizeof(unsigned char)); if (state->window == Z_NULL) return 1; } /* if window not in use yet, initialize */ if (state->wsize == 0) { state->wsize = 1U << state->wbits; state->wnext = 0; state->whave = 0; } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state->wsize) { zmemcpy(state->window, end - state->wsize, state->wsize); state->wnext = 0; state->whave = state->wsize; } else { dist = state->wsize - state->wnext; if (dist > copy) dist = copy; zmemcpy(state->window + state->wnext, end - copy, dist); copy -= dist; if (copy) { zmemcpy(state->window, end - copy, copy); state->wnext = copy; state->whave = state->wsize; } else { state->wnext += dist; if (state->wnext == state->wsize) state->wnext = 0; if (state->whave < state->wsize) state->whave += dist; } } return 0; } /* Macros for inflate(): */ /* check function to use adler32() for zlib or crc32() for gzip */ #ifdef GUNZIP # define UPDATE(check, buf, len) \ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) #else # define UPDATE(check, buf, len) adler32(check, buf, len) #endif /* check macros for header crc */ #ifdef GUNZIP # define CRC2(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ check = crc32(check, hbuf, 2); \ } while (0) # define CRC4(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ hbuf[2] = (unsigned char)((word) >> 16); \ hbuf[3] = (unsigned char)((word) >> 24); \ check = crc32(check, hbuf, 4); \ } while (0) #endif /* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflate() if there is no input available. */ #define PULLBYTE() \ do { \ if (have == 0) goto inf_leave; \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflate(). */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is structured roughly as follows: for (;;) switch (state) { ... case STATEn: if (not enough input data or output space to make progress) return; ... make progress ... state = STATEm; break; ... } so when inflate() is called again, the same case is attempted again, and if the appropriate resources are provided, the machine proceeds to the next state. The NEEDBITS() macro is usually the way the state evaluates whether it can proceed or should return. NEEDBITS() does the return if the requested bits are not available. The typical use of the BITS macros is: NEEDBITS(n); ... do something with BITS(n) ... DROPBITS(n); where NEEDBITS(n) either returns from inflate() if there isn't enough input left to load n bits into the accumulator, or it continues. BITS(n) gives the low n bits in the accumulator. When done, DROPBITS(n) drops the low n bits off the accumulator. INITBITS() clears the accumulator and sets the number of available bits to zero. BYTEBITS() discards just enough bits to put the accumulator on a byte boundary. After BYTEBITS() and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return if there is no input available. The decoding of variable length codes uses PULLBYTE() directly in order to pull just enough bytes to decode the next code, and no more. Some states loop until they get enough input, making sure that enough state information is maintained to continue the loop where it left off if NEEDBITS() returns in the loop. For example, want, need, and keep would all have to actually be part of the saved state in case NEEDBITS() returns: case STATEw: while (want < need) { NEEDBITS(n); keep[want++] = BITS(n); DROPBITS(n); } state = STATEx; case STATEx: As shown above, if the next state is also the next case, then the break is omitted. A state may also return if there is not enough output space available to complete that state. Those states are copying stored data, writing a literal byte, and copying a matching string. When returning, a "goto inf_leave" is used to update the total counters, update the check value, and determine whether any progress has been made during that inflate() call in order to return the proper return code. Progress is defined as a change in either strm->avail_in or strm->avail_out. When there is a window, goto inf_leave will update the window with the last output written. If a goto inf_leave occurs in the middle of decompression and there is no window currently, goto inf_leave will create one and copy output to the window for the next call of inflate(). In this implementation, the flush parameter of inflate() only affects the return code (per zlib.h). inflate() always writes as much as possible to strm->next_out, given the space available and the provided input--the effect documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers the allocation of and copying into a sliding window until necessary, which provides the effect documented in zlib.h for Z_FINISH when the entire input stream available. So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it will return Z_BUF_ERROR if it has not reached the end of the stream. */ /* XXX: Not original zlib source code. Various "fall-through" comments were added to the big-ass switch block below by David Oberhollenzer for use in libsquashfs. */ int ZEXPORT inflate(strm, flush) z_streamp strm; int flush; { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ #ifdef GUNZIP unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ #endif static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; if (inflateStateCheck(strm) || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ LOAD(); in = have; out = left; ret = Z_OK; for (;;) switch (state->mode) { case HEAD: if (state->wrap == 0) { state->mode = TYPEDO; break; } NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ if (state->wbits == 0) state->wbits = 15; state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); state->mode = FLAGS; break; } state->flags = 0; /* expect zlib header */ if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ #else if ( #endif ((BITS(8) << 8) + (hold >> 8)) % 31) { strm->msg = (char *)"incorrect header check"; state->mode = BAD; break; } if (BITS(4) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } DROPBITS(4); len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; if (len > 15 || len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; } state->dmax = 1U << len; Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; INITBITS(); break; #ifdef GUNZIP case FLAGS: NEEDBITS(16); state->flags = (int)(hold); if ((state->flags & 0xff) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } if (state->flags & 0xe000) { strm->msg = (char *)"unknown header flags set"; state->mode = BAD; break; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); state->mode = TIME; case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; if ((state->flags & 0x0200) && (state->wrap & 4)) CRC4(state->check, hold); INITBITS(); state->mode = OS; case OS: NEEDBITS(16); if (state->head != Z_NULL) { state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; case EXLEN: if (state->flags & 0x0400) { NEEDBITS(16); state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) state->head->extra = Z_NULL; state->mode = EXTRA; case EXTRA: if (state->flags & 0x0400) { copy = state->length; if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && state->head->extra != Z_NULL) { len = state->head->extra_len - state->length; zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; state->length -= copy; } if (state->length) goto inf_leave; } state->length = 0; state->mode = NAME; case NAME: if (state->flags & 0x0800) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) state->head->name[state->length++] = (Bytef)len; } while (len && copy < have); if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->name = Z_NULL; state->length = 0; state->mode = COMMENT; case COMMENT: if (state->flags & 0x1000) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) state->head->comment[state->length++] = (Bytef)len; } while (len && copy < have); if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->comment = Z_NULL; state->mode = HCRC; case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); if ((state->wrap & 4) && hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; } INITBITS(); } if (state->head != Z_NULL) { state->head->hcrc = (int)((state->flags >> 9) & 1); state->head->done = 1; } strm->adler = state->check = crc32(0L, Z_NULL, 0); state->mode = TYPE; break; #endif case DICTID: NEEDBITS(32); strm->adler = state->check = ZSWAP32(hold); INITBITS(); state->mode = DICT; /* fall-through */ case DICT: if (state->havedict == 0) { RESTORE(); return Z_NEED_DICT; } strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; /* fall-through */ case TYPE: if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; /* fall-through */ case TYPEDO: if (state->last) { BYTEBITS(); state->mode = CHECK; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN_; /* decode codes */ if (flush == Z_TREES) { DROPBITS(2); goto inf_leave; } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); state->mode = COPY_; if (flush == Z_TREES) goto inf_leave; /* fall-through */ case COPY_: state->mode = COPY; /* fall-through */ case COPY: copy = state->length; if (copy) { if (copy > have) copy = have; if (copy > left) copy = left; if (copy == 0) goto inf_leave; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; break; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); state->have = 0; state->mode = LENLENS; /* fall-through */ case LENLENS: while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); state->have = 0; state->mode = CODELENS; /* fall-through */ case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = state->lens[state->have - 1]; copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (const code FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN_; if (flush == Z_TREES) goto inf_leave; /* fall-through */ case LEN_: state->mode = LEN; /* fall-through */ case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); if (state->mode == TYPE) state->back = -1; break; } state->back = 0; for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; state->length = (unsigned)here.val; if ((int)(here.op) == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); state->mode = LIT; break; } if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->back = -1; state->mode = TYPE; break; } if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } state->extra = (unsigned)(here.op) & 15; state->mode = LENEXT; /* fall-through */ case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); state->was = state->length; state->mode = DIST; /* fall-through */ case DIST: for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; state->extra = (unsigned)(here.op) & 15; state->mode = DISTEXT; /* fall-through */ case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif Tracevv((stderr, "inflate: distance %u\n", state->offset)); state->mode = MATCH; /* fall-through */ case MATCH: if (left == 0) goto inf_leave; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; if (copy > state->whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR Trace((stderr, "inflate.c too far\n")); copy -= state->whave; if (copy > state->length) copy = state->length; if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = 0; } while (--copy); if (state->length == 0) state->mode = LEN; break; #endif } if (copy > state->wnext) { copy -= state->wnext; from = state->window + (state->wsize - copy); } else from = state->window + (state->wnext - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ from = put - state->offset; copy = state->length; } if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = *from++; } while (--copy); if (state->length == 0) state->mode = LEN; break; case LIT: if (left == 0) goto inf_leave; *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; case CHECK: if (state->wrap) { NEEDBITS(32); out -= left; strm->total_out += out; state->total += out; if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; if ((state->wrap & 4) && ( #ifdef GUNZIP state->flags ? hold : #endif ZSWAP32(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: check matches trailer\n")); } #ifdef GUNZIP state->mode = LENGTH; /* fall-through */ case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); if (hold != (state->total & 0xffffffffUL)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: length matches trailer\n")); } #endif state->mode = DONE; /* fall-through */ case DONE: ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: default: return Z_STREAM_ERROR; } /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ inf_leave: RESTORE(); if (state->wsize || (out != strm->avail_out && state->mode < BAD && (state->mode < CHECK || flush != Z_FINISH))) if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } in -= strm->avail_in; out -= strm->avail_out; strm->total_in += in; strm->total_out += out; state->total += out; if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = (int)state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; } int ZEXPORT inflateEnd(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) z_streamp strm; Bytef *dictionary; uInt *dictLength; { struct inflate_state FAR *state; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* copy dictionary */ if (state->whave && dictionary != Z_NULL) { zmemcpy(dictionary, state->window + state->wnext, state->whave - state->wnext); zmemcpy(dictionary + state->whave - state->wnext, state->window, state->wnext); } if (dictLength != Z_NULL) *dictLength = state->whave; return Z_OK; } int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { struct inflate_state FAR *state; unsigned long dictid; int ret; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; /* check for correct dictionary identifier */ if (state->mode == DICT) { dictid = adler32(0L, Z_NULL, 0); dictid = adler32(dictid, dictionary, dictLength); if (dictid != state->check) return Z_DATA_ERROR; } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary + dictLength, dictLength); if (ret) { state->mode = MEM; return Z_MEM_ERROR; } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; } int ZEXPORT inflateGetHeader(strm, head) z_streamp strm; gz_headerp head; { struct inflate_state FAR *state; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; /* save header structure */ state->head = head; head->done = 0; return Z_OK; } /* Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found or when out of input. When called, *have is the number of pattern bytes found in order so far, in 0..3. On return *have is updated to the new state. If on return *have equals four, then the pattern was found and the return value is how many bytes were read including the last byte of the pattern. If *have is less than four, then the pattern has not been found yet and the return value is len. In the latter case, syncsearch() can be called again with more data and the *have state. *have is initialized to zero for the first call. */ local unsigned syncsearch(have, buf, len) unsigned FAR *have; const unsigned char FAR *buf; unsigned len; { unsigned got; unsigned next; got = *have; next = 0; while (next < len && got < 4) { if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) got++; else if (buf[next]) got = 0; else got = 4 - got; next++; } *have = got; return next; } int ZEXPORT inflateSync(strm) z_streamp strm; { unsigned len; /* number of bytes to look at or looked at */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; /* check parameters */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; state->hold <<= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { buf[len++] = (unsigned char)(state->hold); state->hold >>= 8; state->bits -= 8; } state->have = 0; syncsearch(&(state->have), buf, len); } /* search available input */ len = syncsearch(&(state->have), strm->next_in, strm->avail_in); strm->avail_in -= len; strm->next_in += len; strm->total_in += len; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; state->mode = TYPE; return Z_OK; } /* Returns true if inflate is currently at the end of a block generated by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ int ZEXPORT inflateSyncPoint(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } int ZEXPORT inflateCopy(dest, source) z_streamp dest; z_streamp source; { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; unsigned wsize; /* check input */ if (inflateStateCheck(source) || dest == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; /* allocate space */ copy = (struct inflate_state FAR *) ZALLOC(source, 1, sizeof(struct inflate_state)); if (copy == Z_NULL) return Z_MEM_ERROR; window = Z_NULL; if (state->window != Z_NULL) { window = (unsigned char FAR *) ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); if (window == Z_NULL) { ZFREE(source, copy); return Z_MEM_ERROR; } } /* copy state */ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); copy->strm = dest; if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); copy->distcode = copy->codes + (state->distcode - state->codes); } copy->next = copy->codes + (state->next - state->codes); if (window != Z_NULL) { wsize = 1U << state->wbits; zmemcpy(window, state->window, wsize); } copy->window = window; dest->state = (struct internal_state FAR *)copy; return Z_OK; } int ZEXPORT inflateUndermine(strm, subvert) z_streamp strm; int subvert; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR state->sane = !subvert; return Z_OK; #else (void)subvert; state->sane = 1; return Z_DATA_ERROR; #endif } int ZEXPORT inflateValidate(strm, check) z_streamp strm; int check; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (check) state->wrap |= 4; else state->wrap &= ~4; return Z_OK; } long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return -(1L << 16); state = (struct inflate_state FAR *)strm->state; return (long)(((unsigned long)((long)state->back)) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } unsigned long ZEXPORT inflateCodesUsed(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return (unsigned long)-1; state = (struct inflate_state FAR *)strm->state; return (unsigned long)(state->next - state->codes); } squashfs-tools-ng-1.1.3/lib/zlib/inflate.h000066400000000000000000000147321410627516300204260ustar00rootroot00000000000000/* inflate.h -- internal inflate state definition * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* define NO_GZIP when compiling if you want to disable gzip header and trailer decoding by inflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip decoding should be left enabled. */ #ifndef NO_GZIP # define GUNZIP #endif /* Possible inflate modes between inflate() calls */ typedef enum { HEAD = 16180, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ EXLEN, /* i: waiting for extra length (gzip) */ EXTRA, /* i: waiting for extra bytes (gzip) */ NAME, /* i: waiting for end of file name (gzip) */ COMMENT, /* i: waiting for end of comment (gzip) */ HCRC, /* i: waiting for header crc (gzip) */ DICTID, /* i: waiting for dictionary check value */ DICT, /* waiting for inflateSetDictionary() call */ TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ LEN_, /* i: same as LEN below, but only first time in */ LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ MATCH, /* o: waiting for output space to copy string */ LIT, /* o: waiting for output space to write literal */ CHECK, /* i: waiting for 32-bit check value */ LENGTH, /* i: waiting for 32-bit length (gzip) */ DONE, /* finished check, done -- remain here until reset */ BAD, /* got a data error -- remain here until reset */ MEM, /* got an inflate() memory error -- remain here until reset */ SYNC /* looking for synchronization bytes to restart inflate() */ } inflate_mode; /* State transitions between above modes - (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: HEAD -> (gzip) or (zlib) or (raw) (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE (raw) -> TYPEDO Read deflate blocks: TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK STORED -> COPY_ -> COPY -> TYPE TABLE -> LENLENS -> CODELENS -> LEN_ LEN_ -> LEN Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN Process trailer: CHECK -> LENGTH -> DONE */ /* State maintained between inflate() calls -- approximately 7K bytes, not including the allocated sliding window, which is up to 32K bytes. */ struct inflate_state { z_streamp strm; /* pointer back to this zlib stream */ inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip, bit 2 true to validate check value */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ gz_headerp head; /* where to save gzip header information */ /* sliding window */ unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in "in" */ /* for string and stored block copying */ unsigned length; /* literal or length of data to copy */ unsigned offset; /* distance back to copy string from */ /* for table and code decoding */ unsigned extra; /* extra bits needed */ /* fixed and dynamic code tables */ code const FAR *lencode; /* starting table for length/literal codes */ code const FAR *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code FAR *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ int sane; /* if false, allow invalid distance too far */ int back; /* bits back of last unprocessed length/lit */ unsigned was; /* initial length of match */ }; squashfs-tools-ng-1.1.3/lib/zlib/inftrees.c000066400000000000000000000315321410627516300206130ustar00rootroot00000000000000/* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #define MAXBITS 15 /* XXX: Not original zlib source code. The following 2 lines were commented out by David Oberhollenzer for use in in libsquashfs. const char inflate_copyright[] = " inflate 1.2.11 Copyright 1995-2017 Mark Adler "; */ /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) codetype type; unsigned short FAR *lens; unsigned codes; code FAR * FAR *table; unsigned FAR *bits; unsigned short FAR *work; { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code here; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ unsigned match; /* use base and extra for symbol >= match */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)1; here.val = (unsigned short)0; *(*table)++ = here; /* make a table to force an error */ *(*table)++ = here; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ match = 20; break; case LENS: base = lbase; extra = lext; match = 257; break; default: /* DISTS */ base = dbase; extra = dext; match = 0; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); if (work[sym] + 1U < match) { here.op = (unsigned char)0; here.val = work[sym]; } else if (work[sym] >= match) { here.op = (unsigned char)(extra[work[sym] - match]); here.val = base[work[sym] - match]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ here.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; min = fill; /* save offset to next table */ do { fill -= incr; next[(huff >> drop) + fill] = here; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff != 0) { here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)(len - drop); here.val = (unsigned short)0; next[huff] = here; } /* set return parameters */ *table += used; *bits = root; return 0; } squashfs-tools-ng-1.1.3/lib/zlib/inftrees.h000066400000000000000000000055601410627516300206220ustar00rootroot00000000000000/* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 0001eeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribtution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns returns 852, and "enough 30 6 15" for distance codes returns 592. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 592 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, DISTS } codetype; int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work)); squashfs-tools-ng-1.1.3/lib/zlib/trees.c000066400000000000000000001253611410627516300201220ustar00rootroot00000000000000/* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-2017 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* @(#) $Id$ */ /* #define GEN_TREES_H */ #include "deflate.h" #ifdef ZLIB_DEBUG # include #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; local const int extra_dbits[D_CODES] /* extra bits for each distance code */ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ #define DIST_CODE_LEN 512 /* see definition of array dist_code below */ #if defined(GEN_TREES_H) || !defined(STDC) /* non ANSI compilers may not accept trees.h */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ uch _dist_code[DIST_CODE_LEN]; /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ uch _length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ #else # include "trees.h" #endif /* GEN_TREES_H */ struct static_tree_desc_s { const ct_data *static_tree; /* static tree or NULL */ const intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; local const static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local const static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local const static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Local (static) routines in this file. */ local void tr_static_init OF((void)); local void init_block OF((deflate_state *s)); local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); local void build_tree OF((deflate_state *s, tree_desc *desc)); local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); local void compress_block OF((deflate_state *s, const ct_data *ltree, const ct_data *dtree)); local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); #ifdef GEN_TREES_H local void gen_trees_header OF((void)); #endif #ifndef ZLIB_DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* !ZLIB_DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef ZLIB_DEBUG local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) deflate_state *s; int value; /* value to send */ int length; /* number of bits */ { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } #else /* !ZLIB_DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = (int)value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* ZLIB_DEBUG */ /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. */ local void tr_static_init() { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ #ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { _dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse((unsigned)n, 5); } static_init_done = 1; # ifdef GEN_TREES_H gen_trees_header(); # endif #endif /* defined(GEN_TREES_H) || !defined(STDC) */ } /* =========================================================================== * Genererate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H # ifndef ZLIB_DEBUG # include # endif # define SEPARATOR(i, last, width) \ ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width)-1 ? ",\n" : ", ")) void gen_trees_header() { FILE *header = fopen("trees.h", "w"); int i; Assert (header != NULL, "Can't open trees.h"); fprintf(header, "/* header created automatically with -DGEN_TREES_H */\n\n"); fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); for (i = 0; i < L_CODES+2; i++) { fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); } fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); } fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); for (i = 0; i < DIST_CODE_LEN; i++) { fprintf(header, "%2u%s", _dist_code[i], SEPARATOR(i, DIST_CODE_LEN-1, 20)); } fprintf(header, "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { fprintf(header, "%2u%s", _length_code[i], SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); } fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); for (i = 0; i < LENGTH_CODES; i++) { fprintf(header, "%1u%s", base_length[i], SEPARATOR(i, LENGTH_CODES-1, 20)); } fprintf(header, "local const int base_dist[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "%5u%s", base_dist[i], SEPARATOR(i, D_CODES-1, 10)); } fclose(header); } #endif /* GEN_TREES_H */ /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ void ZLIB_INTERNAL _tr_init(s) deflate_state *s; { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Initialize a new block. */ local void init_block(s) deflate_state *s; { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->last_lit = s->matches = 0; } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(s, tree, k) deflate_state *s; ct_data *tree; /* the tree to restore */ int k; /* node to move down */ { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(s, desc) deflate_state *s; tree_desc *desc; /* the tree descriptor */ { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; const intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; s->opt_len += (ulg)f * (unsigned)(bits + xbits); if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); } if (overflow == 0) return; Tracev((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length-1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes (tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ unsigned code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { code = (code + bl_count[bits-1]) << 1; next_code[bits] = (ush)code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? s->depth[n] : s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code+1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(s) deflate_state *s; { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(s, lcodes, dcodes, blcodes) deflate_state *s; int lcodes, dcodes, blcodes; /* number of codes for each tree */ { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes-1, 5); send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); put_short(s, (ush)~stored_len); zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); s->pending += stored_len; #ifdef ZLIB_DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; s->bits_sent += 2*16; s->bits_sent += stored_len<<3; #endif } /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ void ZLIB_INTERNAL _tr_flush_bits(s) deflate_state *s; { bi_flush(s); } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ void ZLIB_INTERNAL _tr_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef ZLIB_DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s->level > 0) { /* Check if the file is binary or text */ if (s->strm->data_type == Z_UNKNOWN) s->strm->data_type = detect_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s->opt_len+3+7)>>3; static_lenb = (s->static_len+3+7)>>3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->last_lit)); if (static_lenb <= opt_lenb) opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else if (stored_len+4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, last); #ifdef FORCE_STATIC } else if (static_lenb >= 0) { /* force static trees */ #else } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { #endif send_bits(s, (STATIC_TREES<<1)+last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->static_len; #endif } else { send_bits(s, (DYN_TREES<<1)+last, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->opt_len; #endif } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); #ifdef ZLIB_DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, s->compressed_len-7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ int ZLIB_INTERNAL _tr_tally (s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { s->d_buf[s->last_lit] = (ush)dist; s->l_buf[s->last_lit++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } #ifdef TRUNCATE_BLOCK /* Try to guess if it is profitable to stop the current block here */ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { /* Compute an upper bound for the compressed length */ ulg out_length = (ulg)s->last_lit*8L; ulg in_length = (ulg)((long)s->strstart - s->block_start); int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { out_length += (ulg)s->dyn_dtree[dcode].Freq * (5L+extra_dbits[dcode]); } out_length >>= 3; Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", s->last_lit, in_length, out_length, 100L - out_length*100L/in_length)); if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; } #endif return (s->last_lit == s->lit_bufsize-1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(s, ltree, dtree) deflate_state *s; const ct_data *ltree; /* literal tree */ const ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned lx = 0; /* running index in l_buf */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->last_lit != 0) do { dist = s->d_buf[lx]; lc = s->l_buf[lx++]; if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code+LITERALS+1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= (unsigned)base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, "pendingBuf overflow"); } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); } /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "black list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ local int detect_data_type(s) deflate_state *s; { /* black_mask is the bit mask of black-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ unsigned long black_mask = 0xf3ffc07fUL; int n; /* Check for non-textual ("black-listed") bytes. */ for (n = 0; n <= 31; n++, black_mask >>= 1) if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) return Z_BINARY; /* Check for textual ("white-listed") bytes. */ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 || s->dyn_ltree[13].Freq != 0) return Z_TEXT; for (n = 32; n < LITERALS; n++) if (s->dyn_ltree[n].Freq != 0) return Z_TEXT; /* There are no "black-listed" or "white-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(code, len) unsigned code; /* the value to invert */ int len; /* its bit length */ { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(s) deflate_state *s; { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(s) deflate_state *s; { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG s->bits_sent = (s->bits_sent+7) & ~7; #endif } squashfs-tools-ng-1.1.3/lib/zlib/trees.h000066400000000000000000000204301410627516300201160ustar00rootroot00000000000000/* header created automatically with -DGEN_TREES_H */ local const ct_data static_ltree[L_CODES+2] = { {{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, {{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, {{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, {{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, {{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, {{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, {{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, {{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, {{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, {{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, {{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, {{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, {{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, {{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, {{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, {{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, {{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, {{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, {{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, {{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, {{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, {{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, {{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, {{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, {{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, {{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, {{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, {{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, {{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, {{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, {{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, {{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, {{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, {{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, {{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, {{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, {{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, {{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, {{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, {{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, {{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, {{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, {{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, {{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, {{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, {{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, {{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, {{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, {{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, {{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, {{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, {{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, {{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, {{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, {{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, {{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, {{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, {{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} }; local const ct_data static_dtree[D_CODES] = { {{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, {{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, {{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, {{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, {{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, {{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} }; const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; local const int base_length[LENGTH_CODES] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 }; local const int base_dist[D_CODES] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 }; squashfs-tools-ng-1.1.3/lib/zlib/zconf.h000066400000000000000000000343561410627516300201270ustar00rootroot00000000000000/* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary # define deflateInit z_deflateInit # define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzfread z_gzfread # define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ # define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader # define inflateInit z_inflateInit # define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif #ifdef Z_SOLO typedef unsigned long z_size_t; #else # define z_longlong long long # if defined(NO_SIZE_T) typedef unsigned NO_SIZE_T z_size_t; # elif defined(STDC) # include typedef size_t z_size_t; # else typedef unsigned long z_size_t; # endif # undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif #ifndef Z_ARG /* function prototypes for stdarg */ # if defined(STDC) || defined(Z_HAVE_STDARG_H) # define Z_ARG(args) args # else # define Z_ARG(args) () # endif #endif /* XXX: Not original zlib source code. The definitions of ZEXTERN, ZEXPORT and ZEXPORTVA were removed and replaced with the following below by David Oberhollenzer for use in in libsquashfs. */ #ifndef ZEXTERN # if (defined(__GNUC__) || defined(__clang__)) && !defined(_WIN32) # define ZEXTERN __attribute__ ((visibility ("hidden"))) # else # define ZEXTERN # endif #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) # define Z_HAVE_UNISTD_H #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ squashfs-tools-ng-1.1.3/lib/zlib/zlib.h000066400000000000000000002737571410627516300177620ustar00rootroot00000000000000/* zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.11, January 15th, 2017 Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H #define ZLIB_H #include "zconf.h" #ifdef __cplusplus extern "C" { #endif #define ZLIB_VERSION "1.2.11" #define ZLIB_VERNUM 0x12b0 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 11 #define ZLIB_VER_SUBREVISION 0 /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough, or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. This library can optionally read and write gzip and raw deflate streams in memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte will go here */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text for deflate, or the decoding state for inflate */ uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header; typedef gz_header FAR *gz_headerp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. In that case, zlib is thread-safe. When zalloc and zfree are Z_NULL on entry to the initialization function, they are set to internal routines that use the standard library functions malloc() and free(). On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use by the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_RLE 3 #define Z_FIXED 4 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 /* Possible values of the data_type field for deflate() */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary. Some output may be provided even if flush is zero. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. See deflatePending(), which can be used if desired to determine whether or not there is more ouput in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. This completes the current deflate block and follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the output buffer, but the output is not aligned to a byte boundary. All of the input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output in order for the decompressor to finish the block before the empty fixed codes block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to seven bits of the current block are held to be written as the next byte after the next deflate block is completed. In this case, the decompressor may not be provided enough bits at this point in order to complete decompression of the data provided so far to the compressor. It may need to wait for the next block to be emitted. This is for advanced applications that need to control the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used in the first deflate call after deflateInit if all the compression is to be done in a single step. In order to complete in one call, avail_out must be at least the value returned by deflateBound (see below). Then deflate is guaranteed to return Z_STREAM_END. If not enough output space is provided, deflate will not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the Adler-32 checksum of all input read so far (that is, total_in bytes). If a gzip stream is being generated, then strm->adler will be the CRC-32 checksum of the input read so far. (See deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was Z_NULL or the state was inadvertently written over by the application), or Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. In the current version of inflate, the provided input is not read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit does not perform any decompression. Actual decompression will be done by inflate(). So next_in, and avail_in, next_out, and avail_out are unused and unchanged. The current implementation of inflateInit() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), then next_in and avail_in are updated accordingly, and processing will resume at this point for the next call of inflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. If the caller of inflate() does not provide both available input and available output space, it is possible that there will be no progress made. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. To assist in this, on return inflate() always sets strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. data_type is set as noted here every time inflate() returns for all flush options, and so can be used to determine the amount of currently consumed input in bits. The Z_TREES option behaves as Z_BLOCK does, but it also returns when the end of each deflate block header is reached, before any actual data in that block is decoded. This allows the caller to determine the length of the deflate block header for later use in random access within a deflate block. 256 is added to the value of strm->data_type when inflate() returns immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the stream completes, which reduces inflate's memory footprint. If the stream does not complete, either because not all of the stream is provided or not enough output space is provided, then a sliding window will be allocated and inflate() can be called again to continue the operation as if Z_NO_FLUSH had been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the effects of the flush parameter in this implementation are on the return value of inflate() as noted below, when inflate() returns early when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output produced so far. The CRC-32 is checked against the gzip trailer, as is the uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value, in which case strm->msg points to a string with a more specific error), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL, or the state was inadvertently written over by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is to be attempted. */ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state was inconsistent. */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. For the current implementation of deflate(), a windowBits value of 8 (a window size of 256 bytes) is not supported. As a result, a request for 8 will result in 9 (a 512-byte window). In that case, providing 8 to inflateInit2() will result in an error when the zlib header with 9 is checked against the initialization of inflate(). The remedy is to not use 8 with deflateInit2() with this initialization, or at least in that case use 9 with inflateInit2(). windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute a check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no header crc, and the operating system will be set to the appropriate value, if the operating system was determined at compile time. If a gzip stream is being written, strm->adler is a CRC-32 instead of an Adler-32. For raw deflate or gzip encoding, a request for a 256-byte window is rejected as invalid, since only the zlib header provides a means of transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this function must be called immediately after deflateInit, deflateInit2 or deflateReset, and before any call of deflate. When doing raw deflate, this function must be called either before any call of deflate, or immediately after the completion of a deflate block, i.e. after all input has been consumed and all output has been delivered when using any of the flush options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size provided in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if not at a block boundary for raw deflate). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If deflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. deflateGetDictionary() may return a length less than the window size, even when more than the window size in input has been provided. It may return up to 258 bytes less in that case, due to how zlib's implementation of deflate manages the sliding window and lookahead for matches, where matches can be up to 258 bytes long. If the application needs the last window-size bytes of input, then that would need to be saved by the application outside of zlib. deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been set unchanged. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int level, int strategy)); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the strategy is changed, and if any input has been consumed in a previous deflate() call, then the input available so far is compressed with the old level and strategy using deflate(strm, Z_BLOCK). There are three approaches for the compression levels 0, 1..3, and 4..9 respectively. The new level and strategy will take effect at the next call of deflate(). If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not take effect. In this case, deflateParams() can be called again with the same parameters and more output space to try again. In order to assure a change in the parameters on the first try, the deflate stream should be flushed using deflate() with Z_BLOCK or other flush request until strm.avail_out is not zero, before calling deflateParams(). Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be applied to the the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if there was not enough output space to complete the compression of the available input data before a change in the strategy or approach. Note that in the case of a Z_BUF_ERROR, the parameters are not changed. A return value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be retried with more output space. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain)); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for searching for the best matching string, and even then only by the most fanatic optimizer trying to squeeze out the last compressed bit for their specific input data. Read the deflate.c source code for the meaning of the max_lazy, good_length, nice_length, and max_chain parameters. deflateTune() can be called after deflateInit() or deflateInit2(), and returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen)); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be called before deflate(). If that first deflate() call is provided the sourceLen input bytes, an output buffer allocated to the size returned by deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed to return Z_STREAM_END. Note that it is possible for the compressed size to be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. */ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, unsigned *pending, int *bits)); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not provided would be due to the available output space having being consumed. The number of bits of output not provided are between 0 and 7, where they await more bits to join them in order to fill out a full byte. If pending or bits are Z_NULL, then those values are not set. deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, int value)); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits leftover from a previous deflate stream when appending to it. As such, this function can only be used for raw deflate, and must be used before the first deflate() call after a deflateInit2() or deflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the output. deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head)); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information in the provided gz_header structure are written to the gzip header (xflag is ignored -- the extra flags are set according to the compression level). The caller must assure that, if not Z_NULL, name and comment are terminated with a zero byte, and that if extra is not Z_NULL, that extra_len bytes are available there. If hcrc is true, a gzip header crc is included. Note that the current versions of the command-line version of gzip (up through version 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be zero to request that inflate use the window size in the zlib header of the compressed stream. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see below), inflate() will not automatically decode concatenated gzip streams. inflate() will return Z_STREAM_END at the end of the gzip stream. The state would need to be reset to continue decoding a subsequent gzip stream. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit2() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the window and there is already data in the window, then the provided dictionary will amend what's there. The application must insure that the dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If inflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync searches for a 00 00 FF FF pattern in the compressed data. All full flush points have this pattern, but not all occurrences of this pattern are full flush points. inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when randomly accessing a large stream. The first pass through the stream can periodically record the inflate state, allowing restarting inflate at those points when randomly accessing the stream. inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, int windowBits)); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted the same as it is for inflateInit2. If the window size is changed, then the memory allocated for the window is freed, and the window will be reallocated by inflate() if needed. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, int bits, int value)); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the middle of a byte. The provided bits will be used before any bytes are used from next_in. This function should only be used with raw inflate, and should be used before the first inflate() call after inflateInit2() or inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used to clear out bits leftover after feeding inflate a block description prior to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the return value down 16 bits. If the upper value is -1 and the lower value is zero, then inflate() is currently decoding information outside of a block. If the upper value is -1 and the lower value is non-zero, then inflate is in the middle of a stored block, with the lower value equaling the number of bytes from the input remaining to copy. If the upper value is not -1, then it is the number of bits back from the current bit position in the input of the code (literal or length/distance pair) currently being processed. In that case the lower value is the number of bytes already emitted for that code. A code is being processed if inflate is waiting for more input to complete decoding of the code, or if it has completed decoding but is waiting for more output space to write the literal or match data. inflateMark() is used to mark locations in the input data for random access, which may be at bit positions, and to note those cases where the output of a code may span boundaries of random access blocks. The current location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. inflateMark returns the value noted above, or -65536 if the provided source stream state was inconsistent. */ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be used to force inflate() to return immediately after header processing is complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, terminated with a zero unless the length is greater than comm_max. When any of extra, name, or comment are not Z_NULL and the respective field is not present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, unsigned char FAR *window)); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two logarithm of the window size, in the range 8..15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general deflate streams. See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of the parameters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ typedef unsigned (*in_func) OF((void FAR *, z_const unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than inflate() for file i/o applications, in that it avoids copying between the output and the sliding window by simply making the window itself the output buffer. inflate() can be faster on modern CPUs when used with large buffers. inflateBack() trusts the application to not change the output buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw deflate stream with each call. inflateBackEnd() is then called to free the allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only the raw deflate stream to decompress. This is different from the default behavior of inflate(), which expects a zlib header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those routines until it reads a complete deflate stream and writes out all of the uncompressed data, or until it encounters an error. The function's parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If there is no input available, in() must return zero -- buf is ignored in that case -- and inflateBack() will return a buffer error. inflateBack() will call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() should return zero on success, or non-zero on failure. If out() returns non-zero, inflateBack() will return with an error. Neither in() nor out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). For convenience, inflateBack() can be provided input on the first call by setting strm->next_in and strm->avail_in. If that input is exhausted, then in() will be called. Therefore strm->next_in must be initialized before calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These descriptors can be optionally used to pass any information that the caller- supplied in() and out() functions need to do their job. On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR if in() or out() returned an error, Z_DATA_ERROR if there was a format error in the deflate stream (in which case strm->msg is set to indicate the nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); /* All memory allocated by inflateBackInit() is freed. inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream state was inconsistent. */ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: 1.0: size of uInt 3.2: size of uLong 5.4: size of voidpf (pointer) 7.6: size of z_off_t Compiler, assembler, and debug options: 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) One-time table building (smaller code, but not thread-safe if true): 12: BUILDFIXED -- build static block decoding tables when needed 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed 14,15: 0 (reserved) Library content (indicates missing functionality): 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking deflate code when not needed) 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect and decode gzip streams (to avoid linking crc code) 18-19: 0 (reserved) Operation variations (changes in library functionality): 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) The sprintf variant used by gzprintf (zero is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned Remainder: 27-31: 0 (reserved) */ #ifndef Z_SOLO /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if you need special options. */ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress() is equivalent to compress2() with a level parameter of Z_DEFAULT_COMPRESSION. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In the case where there is not enough room, uncompress() will fill the output buffer with the uncompressed data up to that point. */ ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen)); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of source bytes consumed. */ /* gzip file access functions */ /* This library supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio, using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. */ typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) 'T' will request transparent writing or appending with no compression and not using the gzip format. "a" can be used instead of "w" to request that the gzip stream that will be written be appended to the file. "+" will result in an error, since reading and writing to the same gzip file is not supported. The addition of "x" when writing will create the file exclusively, which fails if the file already exists. On systems that support it, the addition of "e" when reading or writing will set the flag to close the file on an execve() call. These functions, as well as gzip, will read and decode a sequence of gzip streams in a file. The append function of gzopen() can be used to create such a file. (Also see gzflush() for another way to do this.) When appending, gzopen does not test whether the file begins with a gzip stream, nor does it look for the end of the gzip streams to begin appending. gzopen will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. When reading, this will be detected automatically by looking for the magic two- byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the file could not be opened. */ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); /* gzdopen associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since gzdopen does not close fd if it fails. If you are using fileno() to get the file descriptor from a FILE *, then you will have to use dup() to avoid double-close()ing the file descriptor. Both gzclose() and fclose() will close the associated file descriptor, so they need to have different file descriptors. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided), or if fd is -1. The file descriptor is not used until the next gz* read, write, seek, or close operation, so gzdopen will not detect if fd is invalid (unless fd is -1). */ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); /* Set the internal buffer size used by this library's functions. The default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or write. Three times that size in buffer space is allocated. A larger buffer size of, for example, 64K or 128K bytes will noticeably increase the speed of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). gzbuffer() returns 0 on success, or -1 on failure, such as being called too late. */ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. Previously provided data is flushed before the parameter change. gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not opened for writing, Z_ERRNO if there is an error writing the flushed data, or Z_MEM_ERROR if there is a memory allocation error. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If the input file is not in gzip format, gzread copies the given number of bytes into the buffer directly from the file. After reaching the end of a gzip stream in the input, gzread will continue to read, looking for another gzip stream. Any number of gzip streams may be concatenated in the input file, and will all be decompressed by gzread(). If something other than a gzip stream is encountered after a gzip stream, that remaining trailing garbage is ignored (and no error is returned). gzread can be used to read a gzip file that is being concurrently written. Upon reaching the end of the input, gzread will return with the available data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then gzclearerr can be used to clear the end of file indicator in order to permit gzread to be tried again. Z_OK indicates that a gzip stream was completed on the last gzread. Z_BUF_ERROR indicates that the input file ended in the middle of a gzip stream. Note that gzread does not return -1 in the event of an incomplete gzip stream. This error is deferred until gzclose(), which will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip stream. Alternatively, gzerror can be used before gzclose to detect this case. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. If len is too large to fit in an int, then nothing is read, -1 is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, gzFile file)); /* Read up to nitems items of size size from file to buf, otherwise operating as gzread() does. This duplicates the interface of stdio's fread(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfread() returns the number of full items read of size size, or zero if the end of the file was reached and a full item could not be read, or if there was an error. gzerror() must be consulted if zero is returned in order to determine if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is read, zero is returned, and the error state is set to Z_STREAM_ERROR. In the event that the end of file is reached and only a partial item is available at the end, i.e. the remaining uncompressed data length is not a multiple of size, then the final partial item is nevetheless read into buf and the end-of-file flag is set. The length of the partial item read is not provided, but could be inferred from the result of gztell(). This behavior is the same as the behavior of fread() implementations in common libraries, but it prevents the direct use of gzfread() to read a concurrently written file, reseting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, z_size_t nitems, gzFile file)); /* gzfwrite() writes nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfwrite() returns the number of full items written of size size, or zero if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is written, zero is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of uncompressed bytes actually written, or a negative zlib error code in case of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf() because the secure snprintf() or vsnprintf() functions were not available. This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file condition is encountered. If any characters are read or if len == 1, the string is terminated with a null character. If no characters are read due to an end-of-file or len < 1, then the buffer is left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL for end-of-file or in case of error. If there was an error, the contents at buf are indeterminate. */ ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); /* Writes c, converted to an unsigned char, into the compressed file. gzputc returns the value that was written, or -1 in case of error. */ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. As such, it does not do all of the checking the other functions do. I.e. it does not check to see if file is NULL, nor whether the structure file points to has been clobbered or not. */ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); /* Push one character back onto the stream to be read as the first character on the next read. At least one character of push-back is allowed. gzungetc() returns the character pushed, or -1 on failure. gzungetc() will fail if c is -1, and may fail if a character has been pushed but not read yet. If gzungetc is used immediately after gzopen or gzdopen, at least the output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). */ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); /* Flushes all pending output into the compressed file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush is only permitted when writing. If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such concatenated gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. */ /* ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, z_off_t offset, int whence)); Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ /* ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); Returns the starting position for the next gzread or gzwrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream, and is zero when starting, even if appending or reading a gzip stream from the middle of a file using gzdopen(). gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ /* ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); Returns the current offset in the file being read or written. This offset includes the count of bytes that precede the gzip stream, for example when appending or when using gzdopen() for reading. When reading, the offset does not include as yet unused buffered input. This information can be used for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); /* Returns true (1) if the end-of-file indicator has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set only if the read tried to go past the end of the input, but came up short. Therefore, just like feof(), gzeof() may return false even if there is no more data to read, in the event that the last read request was for the exact number of bytes remaining in the input file. This will happen if the input file size is an exact multiple of the buffer size. If gzeof() returns true, then the read functions will return no more data, unless the end-of-file indicator is reset by gzclearerr() and the input file has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* Returns true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). When writing, gzdirect() returns true (1) if transparent writing was requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: gzdirect() is not needed when writing. Transparent writing must be explicitly requested, so the application already knows the answer. When linking statically, using gzdirect() will include all of the zlib code for gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); /* Flushes all pending output if necessary, closes the compressed file and deallocates the (de)compression state. Note that once file is closed, you cannot call gzerror with file, since its structures have been deallocated. gzclose must not be called more than once on the same file, just as free must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the last read ended in the middle of a gzip stream, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to using these instead of gzclose() is that they avoid linking in zlib compression or decompression code that is not used when only reading or only writing respectively. If gzclose() is used, then both compression and decompression code will be included the application when linking to a static zlib library. */ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is closed, then the string previously returned by gzerror will no longer be available. gzerror() should be used to distinguish errors from end-of-file for those functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); /* Clears the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ #endif /* !Z_SOLO */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, z_size_t len)); /* Same as adler32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note that the z_off_t type (like off_t) is a signed integer. If len2 is negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is Z_NULL, this function returns the required initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: uLong crc = crc32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { crc = crc32(crc, buffer, length); } if (crc != original_crc) error(); */ ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, z_size_t len)); /* Same as crc32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and len2. */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, const char *version, int stream_size)); ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define z_inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #else # define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #endif #ifndef Z_SOLO /* gzgetc() macro and its supporting function and exposed data structure. Note * that the real internal state is much larger than the exposed structure. * This abbreviated structure exposes just enough for the gzgetc() macro. The * user should not mess with these exposed elements, since their names or * behavior could change in the future, perhaps even capriciously. They can * only be used by the gzgetc() macro. You have been warned. */ struct gzFile_s { unsigned have; unsigned char *next; z_off64_t pos; }; ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #else # define gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) # ifdef Z_PREFIX_SET # define z_gzopen z_gzopen64 # define z_gzseek z_gzseek64 # define z_gztell z_gztell64 # define z_gzoffset z_gzoffset64 # define z_adler32_combine z_adler32_combine64 # define z_crc32_combine z_crc32_combine64 # else # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define gzoffset gzoffset64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 # endif # ifndef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); # endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif #else /* Z_SOLO */ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif /* !Z_SOLO */ /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, const char *format, va_list va)); # endif #endif #ifdef __cplusplus } #endif #endif /* ZLIB_H */ squashfs-tools-ng-1.1.3/lib/zlib/zutil.c000066400000000000000000000164341410627516300201470ustar00rootroot00000000000000/* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995-2017 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" /* XXX: Not original zlib source code. The following 3 lines were commented out by David Oberhollenzer for use in in libsquashfs. #ifndef Z_SOLO # include "gzguts.h" #endif */ z_const char * const z_errmsg[10] = { (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ (z_const char *)"stream end", /* Z_STREAM_END 1 */ (z_const char *)"", /* Z_OK 0 */ (z_const char *)"file error", /* Z_ERRNO (-1) */ (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ (z_const char *)"" }; const char * ZEXPORT zlibVersion() { return ZLIB_VERSION; } uLong ZEXPORT zlibCompileFlags() { uLong flags; flags = 0; switch ((int)(sizeof(uInt))) { case 2: break; case 4: flags += 1; break; case 8: flags += 2; break; default: flags += 3; } switch ((int)(sizeof(uLong))) { case 2: break; case 4: flags += 1 << 2; break; case 8: flags += 2 << 2; break; default: flags += 3 << 2; } switch ((int)(sizeof(voidpf))) { case 2: break; case 4: flags += 1 << 4; break; case 8: flags += 2 << 4; break; default: flags += 3 << 4; } switch ((int)(sizeof(z_off_t))) { case 2: break; case 4: flags += 1 << 6; break; case 8: flags += 2 << 6; break; default: flags += 3 << 6; } #ifdef ZLIB_DEBUG flags += 1 << 8; #endif #if defined(ASMV) || defined(ASMINF) flags += 1 << 9; #endif #ifdef ZLIB_WINAPI flags += 1 << 10; #endif #ifdef BUILDFIXED flags += 1 << 12; #endif #ifdef DYNAMIC_CRC_TABLE flags += 1 << 13; #endif #ifdef NO_GZCOMPRESS flags += 1L << 16; #endif #ifdef NO_GZIP flags += 1L << 17; #endif #ifdef PKZIP_BUG_WORKAROUND flags += 1L << 20; #endif #ifdef FASTEST flags += 1L << 21; #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifdef NO_vsnprintf flags += 1L << 25; # ifdef HAS_vsprintf_void flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void flags += 1L << 26; # endif # endif #else flags += 1L << 24; # ifdef NO_snprintf flags += 1L << 25; # ifdef HAS_sprintf_void flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void flags += 1L << 26; # endif # endif #endif return flags; } #ifdef ZLIB_DEBUG #include # ifndef verbose # define verbose 0 # endif int ZLIB_INTERNAL z_verbose = verbose; void ZLIB_INTERNAL z_error (m) char *m; { fprintf(stderr, "%s\n", m); exit(1); } #endif /* exported to allow conversion of error code to string for compress() and * uncompress() */ const char * ZEXPORT zError(err) int err; { return ERR_MSG(err); } #if defined(_WIN32_WCE) /* The Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ int errno = 0; #endif #ifndef HAVE_MEMCPY void ZLIB_INTERNAL zmemcpy(dest, source, len) Bytef* dest; const Bytef* source; uInt len; { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } int ZLIB_INTERNAL zmemcmp(s1, s2, len) const Bytef* s1; const Bytef* s2; uInt len; { uInt j; for (j = 0; j < len; j++) { if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; } return 0; } void ZLIB_INTERNAL zmemzero(dest, len) Bytef* dest; uInt len; { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ } while (--len != 0); } #endif #ifndef Z_SOLO #ifdef SYS16BIT #ifdef __TURBOC__ /* Turbo C in 16-bit mode */ # define MY_ZCALLOC /* Turbo C malloc() does not allow dynamic allocation of 64K bytes * and farmalloc(64K) returns a pointer with an offset of 8, so we * must fix the pointer. Warning: the pointer must be put back to its * original form in order to free it, use zcfree(). */ #define MAX_PTR 10 /* 10*64K = 640K */ local int next_ptr = 0; typedef struct ptr_table_s { voidpf org_ptr; voidpf new_ptr; } ptr_table; local ptr_table table[MAX_PTR]; /* This table is used to remember the original form of pointers * to large buffers (64K). Such pointers are normalized with a zero offset. * Since MSDOS is not a preemptive multitasking OS, this table is not * protected from concurrent access. This hack doesn't work anyway on * a protected system like OS/2. Use Microsoft C instead. */ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; (void)opaque; /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ if (bsize < 65520L) { buf = farmalloc(bsize); if (*(ush*)&buf != 0) return buf; } else { buf = farmalloc(bsize + 16L); } if (buf == NULL || next_ptr >= MAX_PTR) return NULL; table[next_ptr].org_ptr = buf; /* Normalize the pointer to seg:0 */ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; *(ush*)&buf = 0; table[next_ptr++].new_ptr = buf; return buf; } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; (void)opaque; if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; } /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; farfree(table[n].org_ptr); while (++n < next_ptr) { table[n-1] = table[n]; } next_ptr--; return; } Assert(0, "zcfree: ptr not found"); } #endif /* __TURBOC__ */ #ifdef M_I86 /* Microsoft C in 16-bit mode */ # define MY_ZCALLOC #if (!defined(_MSC_VER) || (_MSC_VER <= 600)) # define _halloc halloc # define _hfree hfree #endif voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); } #endif /* M_I86 */ #endif /* SYS16BIT */ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC extern voidp malloc OF((uInt size)); extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) voidpf opaque; unsigned items; unsigned size; { (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { (void)opaque; free(ptr); } #endif /* MY_ZCALLOC */ #endif /* !Z_SOLO */ squashfs-tools-ng-1.1.3/lib/zlib/zutil.h000066400000000000000000000164651410627516300201600ustar00rootroot00000000000000/* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef ZUTIL_H #define ZUTIL_H /* XXX: Not original zlib source code. The definition of ZLIB_INTRENAL was changed by David Oberhollenzer for use in in libsquashfs. */ #if (defined(__GNUC__) || defined(__clang__)) && !defined(_WIN32) # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include "zlib.h" #if defined(STDC) && !defined(Z_SOLO) # if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include # endif # include # include #endif #ifdef Z_SOLO typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ #endif #ifndef local # define local static #endif /* since "static" is used to mean two completely different things in C, we define "local" for the non-static meaning of "static", for readability (compile with -Dlocal if your debugger can't find static symbols) */ typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; /* XXX: Not original zlib source code. The visibility of z_errmsg was changed to internal by David Oberhollenzer for use in in libsquashfs. */ extern ZLIB_INTERNAL z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 # ifndef Z_SOLO # if defined(__TURBOC__) || defined(__BORLANDC__) # if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) /* Allow compilation with ANSI keywords only enabled */ void _Cdecl farfree( void *block ); void *_Cdecl farmalloc( unsigned long nbytes ); # else # include # endif # else /* MSC or DJGPP */ # include # endif # endif #endif #ifdef AMIGA # define OS_CODE 1 #endif #if defined(VAXC) || defined(VMS) # define OS_CODE 2 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif #ifdef __370__ # if __TARGET_LIB__ < 0x20000000 # define OS_CODE 4 # elif __TARGET_LIB__ < 0x40000000 # define OS_CODE 11 # else # define OS_CODE 8 # endif #endif #if defined(ATARI) || defined(atarist) # define OS_CODE 5 #endif #ifdef OS2 # define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) # define OS_CODE 7 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include /* for fdopen */ # else # ifndef fdopen # define fdopen(fd,mode) NULL /* No fdopen() */ # endif # endif # endif #endif #ifdef __acorn # define OS_CODE 13 #endif #if defined(WIN32) && !defined(__CYGWIN__) # define OS_CODE 10 #endif #ifdef _BEOS_ # define OS_CODE 16 #endif #ifdef __TOS_OS400__ # define OS_CODE 18 #endif #ifdef __APPLE__ # define OS_CODE 19 #endif #if defined(_BEOS_) || defined(RISCOS) # define fdopen(fd,mode) NULL /* No fdopen() */ #endif #if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ # ifndef _PTRDIFF_T_DEFINED typedef int ptrdiff_t; # define _PTRDIFF_T_DEFINED # endif # else # define fdopen(fd,type) _fdopen(fd,type) # endif #endif #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); #endif /* common defaults */ #ifndef OS_CODE # define OS_CODE 3 /* assume Unix */ #endif #ifndef F_OPEN # define F_OPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #if defined(pyr) || defined(Z_SOLO) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) /* Use our own functions for small and medium model with MSC <= 5.0. * You may have to use the same strategy for Borland C (untested). * The __SC__ check is for Symantec. */ # define NO_MEMCPY #endif #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # ifdef SMALL_MEDIUM /* MSDOS small or medium model */ # define zmemcpy _fmemcpy # define zmemcmp _fmemcmp # define zmemzero(dest, len) _fmemset(dest, 0, len) # else # define zmemcpy memcpy # define zmemcmp memcmp # define zmemzero(dest, len) memset(dest, 0, len) # endif #else void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); #endif /* Diagnostic functions */ #ifdef ZLIB_DEBUG # include extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error OF((char *m)); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} # define Tracevv(x) {if (z_verbose>1) fprintf x ;} # define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} # define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif #ifndef Z_SOLO voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, unsigned size)); void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); #endif #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} /* Reverse the bytes in a 32-bit value */ #define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) #endif /* ZUTIL_H */ squashfs-tools-ng-1.1.3/licenses/000077500000000000000000000000001410627516300167235ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/licenses/0BSD.txt000066400000000000000000000011371410627516300201560ustar00rootroot00000000000000Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. squashfs-tools-ng-1.1.3/licenses/GPLv2.txt000066400000000000000000000432541410627516300203660ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. squashfs-tools-ng-1.1.3/licenses/GPLv3.txt000066400000000000000000001045151410627516300203650ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands 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 . 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 . squashfs-tools-ng-1.1.3/licenses/LGPLv3.txt000066400000000000000000000167441410627516300205070ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. squashfs-tools-ng-1.1.3/licenses/LZ4.txt000066400000000000000000000024371410627516300201030ustar00rootroot00000000000000LZ4 Library Copyright (c) 2011-2016, Yann Collet All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. squashfs-tools-ng-1.1.3/licenses/hash_table.txt000066400000000000000000000022371410627516300215620ustar00rootroot00000000000000Copyright © 1988-2004 Keith Packard and Bart Massey Copyright © 2010 Valve Software Copyright © 2009,2012 Intel Corporation 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 (including the next paragraph) 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. squashfs-tools-ng-1.1.3/licenses/musl.txt000066400000000000000000000140741410627516300204520ustar00rootroot00000000000000musl as a whole is licensed under the following standard MIT license: ---------------------------------------------------------------------- Copyright © 2005-2020 Rich Felker, et al. 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. ---------------------------------------------------------------------- Authors/contributors include: A. Wilcox Ada Worcester Alex Dowad Alex Suykov Alexander Monakov Andre McCurdy Andrew Kelley Anthony G. Basile Aric Belsito Arvid Picciani Bartosz Brachaczek Benjamin Peterson Bobby Bingham Boris Brezillon Brent Cook Chris Spiegel Clément Vasseur Daniel Micay Daniel Sabogal Daurnimator David Carlier David Edelsohn Denys Vlasenko Dmitry Ivanov Dmitry V. Levin Drew DeVault Emil Renner Berthing Fangrui Song Felix Fietkau Felix Janda Gianluca Anzolin Hauke Mehrtens He X Hiltjo Posthuma Isaac Dunham Jaydeep Patil Jens Gustedt Jeremy Huntwork Jo-Philipp Wich Joakim Sindholt John Spencer Julien Ramseier Justin Cormack Kaarle Ritvanen Khem Raj Kylie McClain Leah Neukirchen Luca Barbato Luka Perkov M Farkas-Dyck (Strake) Mahesh Bodapati Markus Wichmann Masanori Ogino Michael Clark Michael Forney Mikhail Kremnyov Natanael Copa Nicholas J. Kain orc Pascal Cuoq Patrick Oppenlander Petr Hosek Petr Skocik Pierre Carrier Reini Urban Rich Felker Richard Pennington Ryan Fairfax Samuel Holland Segev Finer Shiz sin Solar Designer Stefan Kristiansson Stefan O'Rear Szabolcs Nagy Timo Teräs Trutz Behn Valentin Ochs Will Dietz William Haddon William Pitcock Portions of this software are derived from third-party works licensed under terms compatible with the above MIT license: The TRE regular expression implementation (src/regex/reg* and src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed under a 2-clause BSD license (license text in the source files). The included version has been heavily modified by Rich Felker in 2012, in the interests of size, simplicity, and namespace cleanliness. Much of the math library code (src/math/* and src/complex/*) is Copyright © 1993,2004 Sun Microsystems or Copyright © 2003-2011 David Schultz or Copyright © 2003-2009 Steven G. Kargl or Copyright © 2003-2009 Bruce D. Evans or Copyright © 2008 Stephen L. Moshier or Copyright © 2017-2018 Arm Limited and labelled as such in comments in the individual source files. All have been licensed under extremely permissive terms. The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 The Android Open Source Project and is licensed under a two-clause BSD license. It was taken from Bionic libc, used on Android. The AArch64 memcpy and memset code (src/string/aarch64/*) are Copyright © 1999-2019, Arm Limited. The implementation of DES for crypt (src/crypt/crypt_des.c) is Copyright © 1994 David Burren. It is licensed under a BSD license. The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was originally written by Solar Designer and placed into the public domain. The code also comes with a fallback permissive license for use in jurisdictions that may not recognize the public domain. The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 Valentin Ochs and is licensed under an MIT-style license. The x86_64 port was written by Nicholas J. Kain and is licensed under the standard MIT terms. The mips and microblaze ports were originally written by Richard Pennington for use in the ellcc project. The original code was adapted by Rich Felker for build system and code conventions during upstream integration. It is licensed under the standard MIT terms. The mips64 port was contributed by Imagination Technologies and is licensed under the standard MIT terms. The powerpc port was also originally written by Richard Pennington, and later supplemented and integrated by John Spencer. It is licensed under the standard MIT terms. All other files which have no copyright comments are original works produced specifically for use as part of this library, written either by Rich Felker, the main author of the library, or by one or more contibutors listed above. Details on authorship of individual files can be found in the git version control history of the project. The omission of copyright and license comments in each file is in the interest of source tree size. In addition, permission is hereby granted for all public header files (include/* and arch/*/bits/*) and crt files intended to be linked into applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit the copyright notice and permission notice otherwise required by the license, and to use these files without any requirement of attribution. These files include substantial contributions from: Bobby Bingham John Spencer Nicholas J. Kain Rich Felker Richard Pennington Stefan Kristiansson Szabolcs Nagy all of whom have explicitly granted such permission. This file previously contained text expressing a belief that most of the files covered by the above exception were sufficiently trivial not to be subject to copyright, resulting in confusion over whether it negated the permissions granted in the license. In the spirit of permissive licensing, and of not having licensing issues being an obstacle to adoption, that text has been removed. squashfs-tools-ng-1.1.3/licenses/xxhash.txt000066400000000000000000000025751410627516300210000ustar00rootroot00000000000000xxHash - Extremely Fast Hash algorithm Copyright (C) 2012-2016, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. squashfs-tools-ng-1.1.3/licenses/xz.txt000066400000000000000000000014001410627516300201200ustar00rootroot00000000000000liblzma is in the public domain. You can do whatever you want with the files that have been put into the public domain. If you find public domain legally problematic, take the previous sentence as a license grant. If you still find the lack of copyright legally problematic, you have too many lawyers. As usual, this software is provided "as is", without any warranty. If you copy significant amounts of public domain code from XZ Utils into your project, acknowledging this somewhere in your software is polite (especially if it is proprietary, non-free software), but naturally it is not legally required. Here is an example of a good notice to put into "about box" or into documentation: This software includes code from XZ Utils . squashfs-tools-ng-1.1.3/licenses/zlib.txt000066400000000000000000000017271410627516300204330ustar00rootroot00000000000000 (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu squashfs-tools-ng-1.1.3/licenses/zstd.txt000066400000000000000000000027251410627516300204560ustar00rootroot00000000000000Copyright (c) 2016-present, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Facebook nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. squashfs-tools-ng-1.1.3/m4/000077500000000000000000000000001410627516300154365ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/m4/ax_prog_doxygen.m4000066400000000000000000000500221410627516300210730ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html # =========================================================================== # # SYNOPSIS # # DX_INIT_DOXYGEN(PROJECT-NAME, [DOXYFILE-PATH], [OUTPUT-DIR], ...) # DX_DOXYGEN_FEATURE(ON|OFF) # DX_DOT_FEATURE(ON|OFF) # DX_HTML_FEATURE(ON|OFF) # DX_CHM_FEATURE(ON|OFF) # DX_CHI_FEATURE(ON|OFF) # DX_MAN_FEATURE(ON|OFF) # DX_RTF_FEATURE(ON|OFF) # DX_XML_FEATURE(ON|OFF) # DX_PDF_FEATURE(ON|OFF) # DX_PS_FEATURE(ON|OFF) # # DESCRIPTION # # The DX_*_FEATURE macros control the default setting for the given # Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for # generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML # help (for MS users), 'CHI' for generating a separate .chi file by the # .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate # output formats. The environment variable DOXYGEN_PAPER_SIZE may be # specified to override the default 'a4wide' paper size. # # By default, HTML, PDF and PS documentation is generated as this seems to # be the most popular and portable combination. MAN pages created by # Doxygen are usually problematic, though by picking an appropriate subset # and doing some massaging they might be better than nothing. CHM and RTF # are specific for MS (note that you can't generate both HTML and CHM at # the same time). The XML is rather useless unless you apply specialized # post-processing to it. # # The macros mainly control the default state of the feature. The use can # override the default by specifying --enable or --disable. The macros # ensure that contradictory flags are not given (e.g., # --enable-doxygen-html and --enable-doxygen-chm, # --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each # feature will be automatically disabled (with a warning) if the required # programs are missing. # # Once all the feature defaults have been specified, call DX_INIT_DOXYGEN # with the following parameters: a one-word name for the project for use # as a filename base etc., an optional configuration file name (the # default is '$(srcdir)/Doxyfile', the same as Doxygen's default), and an # optional output directory name (the default is 'doxygen-doc'). To run # doxygen multiple times for different configuration files and output # directories provide more parameters: the second, forth, sixth, etc # parameter are configuration file names and the third, fifth, seventh, # etc parameter are output directories. No checking is done to catch # duplicates. # # Automake Support # # The DX_RULES substitution can be used to add all needed rules to the # Makefile. Note that this is a substitution without being a variable: # only the @DX_RULES@ syntax will work. # # The provided targets are: # # doxygen-doc: Generate all doxygen documentation. # # doxygen-run: Run doxygen, which will generate some of the # documentation (HTML, CHM, CHI, MAN, RTF, XML) # but will not do the post processing required # for the rest of it (PS, PDF). # # doxygen-ps: Generate doxygen PostScript documentation. # # doxygen-pdf: Generate doxygen PDF documentation. # # Note that by default these are not integrated into the automake targets. # If doxygen is used to generate man pages, you can achieve this # integration by setting man3_MANS to the list of man pages generated and # then adding the dependency: # # $(man3_MANS): doxygen-doc # # This will cause make to run doxygen and generate all the documentation. # # The following variable is intended for use in Makefile.am: # # DX_CLEANFILES = everything to clean. # # Then add this variable to MOSTLYCLEANFILES. # # LICENSE # # Copyright (c) 2009 Oren Ben-Kiki # Copyright (c) 2015 Olaf Mandel # # 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 any # warranty. #serial 24 ## ----------## ## Defaults. ## ## ----------## DX_ENV="" AC_DEFUN([DX_FEATURE_doc], ON) AC_DEFUN([DX_FEATURE_dot], OFF) AC_DEFUN([DX_FEATURE_man], OFF) AC_DEFUN([DX_FEATURE_html], ON) AC_DEFUN([DX_FEATURE_chm], OFF) AC_DEFUN([DX_FEATURE_chi], OFF) AC_DEFUN([DX_FEATURE_rtf], OFF) AC_DEFUN([DX_FEATURE_xml], OFF) AC_DEFUN([DX_FEATURE_pdf], ON) AC_DEFUN([DX_FEATURE_ps], ON) ## --------------- ## ## Private macros. ## ## --------------- ## # DX_ENV_APPEND(VARIABLE, VALUE) # ------------------------------ # Append VARIABLE="VALUE" to DX_ENV for invoking doxygen and add it # as a substitution (but not a Makefile variable). The substitution # is skipped if the variable name is VERSION. AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])dnl m4_if([$1], [VERSION], [], [AC_SUBST([$1], [$2])dnl AM_SUBST_NOTMAKE([$1])])dnl ]) # DX_DIRNAME_EXPR # --------------- # Expand into a shell expression prints the directory part of a path. AC_DEFUN([DX_DIRNAME_EXPR], [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) # DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) # ------------------------------------- # Expands according to the M4 (static) status of the feature. AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) # DX_REQUIRE_PROG(VARIABLE, PROGRAM) # ---------------------------------- # Require the specified program to be found for the DX_CURRENT_FEATURE to work. AC_DEFUN([DX_REQUIRE_PROG], [ AC_PATH_TOOL([$1], [$2]) if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) fi ]) # DX_TEST_FEATURE(FEATURE) # ------------------------ # Expand to a shell expression testing whether the feature is active. AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) # DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) # ------------------------------------------------- # Verify that a required features has the right state before trying to turn on # the DX_CURRENT_FEATURE. AC_DEFUN([DX_CHECK_DEPEND], [ test "$DX_FLAG_$1" = "$2" \ || AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, requires, contradicts) doxygen-$1]) ]) # DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) # ---------------------------------------------------------- # Turn off the DX_CURRENT_FEATURE if the required feature is off. AC_DEFUN([DX_CLEAR_DEPEND], [ test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) ]) # DX_FEATURE_ARG(FEATURE, DESCRIPTION, # CHECK_DEPEND, CLEAR_DEPEND, # REQUIRE, DO-IF-ON, DO-IF-OFF) # -------------------------------------------- # Parse the command-line option controlling a feature. CHECK_DEPEND is called # if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), # otherwise CLEAR_DEPEND is called to turn off the default state if a required # feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional # requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and # DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. AC_DEFUN([DX_ARG_ABLE], [ AC_DEFUN([DX_CURRENT_FEATURE], [$1]) AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) AC_ARG_ENABLE(doxygen-$1, [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], [--enable-doxygen-$1]), DX_IF_FEATURE([$1], [don't $2], [$2]))], [ case "$enableval" in #( y|Y|yes|Yes|YES) AC_SUBST([DX_FLAG_$1], 1) $3 ;; #( n|N|no|No|NO) AC_SUBST([DX_FLAG_$1], 0) ;; #( *) AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) ;; esac ], [ AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) $4 ]) if DX_TEST_FEATURE([$1]); then $5 : fi if DX_TEST_FEATURE([$1]); then $6 : else $7 : fi ]) ## -------------- ## ## Public macros. ## ## -------------- ## # DX_XXX_FEATURE(DEFAULT_STATE) # ----------------------------- AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) AC_DEFUN([DX_DOT_FEATURE], [AC_DEFUN([DX_FEATURE_dot], [$1])]) AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) # DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR], ...) # -------------------------------------------------------------- # PROJECT also serves as the base name for the documentation files. # The default CONFIG-FILE is "$(srcdir)/Doxyfile" and OUTPUT-DOC-DIR is # "doxygen-doc". # More arguments are interpreted as interleaved CONFIG-FILE and # OUTPUT-DOC-DIR values. AC_DEFUN([DX_INIT_DOXYGEN], [ # Files: AC_SUBST([DX_PROJECT], [$1]) AC_SUBST([DX_CONFIG], ['ifelse([$2], [], [$(srcdir)/Doxyfile], [$2])']) AC_SUBST([DX_DOCDIR], ['ifelse([$3], [], [doxygen-doc], [$3])']) m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2, [AC_SUBST([DX_CONFIG]m4_eval(DX_i[/2]), 'm4_default_nblank_quoted(m4_argn(DX_i, $@), [$(srcdir)/Doxyfile])')])])dnl m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 5, m4_count($@,), 2, [AC_SUBST([DX_DOCDIR]m4_eval([(]DX_i[-1)/2]), 'm4_default_nblank_quoted(m4_argn(DX_i, $@), [doxygen-doc])')])])dnl m4_define([DX_loop], m4_dquote(m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2, [, m4_eval(DX_i[/2])])], [])))dnl # Environment variables used inside doxygen.cfg: DX_ENV_APPEND(SRCDIR, $srcdir) DX_ENV_APPEND(PROJECT, $DX_PROJECT) DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) # Doxygen itself: DX_ARG_ABLE(doc, [generate any doxygen documentation], [], [], [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) DX_REQUIRE_PROG([DX_PERL], perl)], [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) # Dot for graphics: DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_DOT], dot)], [DX_ENV_APPEND(HAVE_DOT, YES) DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], [DX_ENV_APPEND(HAVE_DOT, NO)]) # Man pages generation: DX_ARG_ABLE(man, [generate doxygen manual pages], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_MAN, YES)], [DX_ENV_APPEND(GENERATE_MAN, NO)]) # RTF file generation: DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_RTF, YES)], [DX_ENV_APPEND(GENERATE_RTF, NO)]) # XML file generation: DX_ARG_ABLE(xml, [generate doxygen XML documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_XML, YES)], [DX_ENV_APPEND(GENERATE_XML, NO)]) # (Compressed) HTML help generation: DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_HHC], hhc)], [DX_ENV_APPEND(HHC_PATH, $DX_HHC) DX_ENV_APPEND(GENERATE_HTML, YES) DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) # Separate CHI file generation. DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file], [DX_CHECK_DEPEND(chm, 1)], [DX_CLEAR_DEPEND(chm, 1)], [], [DX_ENV_APPEND(GENERATE_CHI, YES)], [DX_ENV_APPEND(GENERATE_CHI, NO)]) # Plain HTML pages generation: DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], [], [DX_ENV_APPEND(GENERATE_HTML, YES)], [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) # PostScript file generation: DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_LATEX], latex) DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) DX_REQUIRE_PROG([DX_DVIPS], dvips) DX_REQUIRE_PROG([DX_EGREP], egrep)]) # PDF file generation: DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) DX_REQUIRE_PROG([DX_EGREP], egrep)]) # LaTeX generation for PS and/or PDF: if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then DX_ENV_APPEND(GENERATE_LATEX, YES) else DX_ENV_APPEND(GENERATE_LATEX, NO) fi # Paper size for PS and/or PDF: AC_ARG_VAR(DOXYGEN_PAPER_SIZE, [a4wide (default), a4, letter, legal or executive]) case "$DOXYGEN_PAPER_SIZE" in #( "") AC_SUBST(DOXYGEN_PAPER_SIZE, "") ;; #( a4wide|a4|letter|legal|executive) DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) ;; #( *) AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) ;; esac # Rules: AS_IF([[test $DX_FLAG_html -eq 1]], [[DX_SNIPPET_html="## ------------------------------- ## ## Rules specific for HTML output. ## ## ------------------------------- ## DX_CLEAN_HTML = \$(DX_DOCDIR)/html]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/html]])[ "]], [[DX_SNIPPET_html=""]]) AS_IF([[test $DX_FLAG_chi -eq 1]], [[DX_SNIPPET_chi=" DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])["]], [[DX_SNIPPET_chi=""]]) AS_IF([[test $DX_FLAG_chm -eq 1]], [[DX_SNIPPET_chm="## ------------------------------ ## ## Rules specific for CHM output. ## ## ------------------------------ ## DX_CLEAN_CHM = \$(DX_DOCDIR)/chm]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/chm]])[\ ${DX_SNIPPET_chi} "]], [[DX_SNIPPET_chm=""]]) AS_IF([[test $DX_FLAG_man -eq 1]], [[DX_SNIPPET_man="## ------------------------------ ## ## Rules specific for MAN output. ## ## ------------------------------ ## DX_CLEAN_MAN = \$(DX_DOCDIR)/man]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/man]])[ "]], [[DX_SNIPPET_man=""]]) AS_IF([[test $DX_FLAG_rtf -eq 1]], [[DX_SNIPPET_rtf="## ------------------------------ ## ## Rules specific for RTF output. ## ## ------------------------------ ## DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/rtf]])[ "]], [[DX_SNIPPET_rtf=""]]) AS_IF([[test $DX_FLAG_xml -eq 1]], [[DX_SNIPPET_xml="## ------------------------------ ## ## Rules specific for XML output. ## ## ------------------------------ ## DX_CLEAN_XML = \$(DX_DOCDIR)/xml]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/xml]])[ "]], [[DX_SNIPPET_xml=""]]) AS_IF([[test $DX_FLAG_ps -eq 1]], [[DX_SNIPPET_ps="## ----------------------------- ## ## Rules specific for PS output. ## ## ----------------------------- ## DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])[ DX_PS_GOAL = doxygen-ps doxygen-ps: \$(DX_CLEAN_PS) ]m4_foreach([DX_i], [DX_loop], [[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ \$(DX_LATEX) refman.tex; \\ \$(DX_MAKEINDEX) refman.idx; \\ \$(DX_LATEX) refman.tex; \\ countdown=5; \\ while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ refman.log > /dev/null 2>&1 \\ && test \$\$countdown -gt 0; do \\ \$(DX_LATEX) refman.tex; \\ countdown=\`expr \$\$countdown - 1\`; \\ done; \\ \$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi ]])["]], [[DX_SNIPPET_ps=""]]) AS_IF([[test $DX_FLAG_pdf -eq 1]], [[DX_SNIPPET_pdf="## ------------------------------ ## ## Rules specific for PDF output. ## ## ------------------------------ ## DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])[ DX_PDF_GOAL = doxygen-pdf doxygen-pdf: \$(DX_CLEAN_PDF) ]m4_foreach([DX_i], [DX_loop], [[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ \$(DX_PDFLATEX) refman.tex; \\ \$(DX_MAKEINDEX) refman.idx; \\ \$(DX_PDFLATEX) refman.tex; \\ countdown=5; \\ while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ refman.log > /dev/null 2>&1 \\ && test \$\$countdown -gt 0; do \\ \$(DX_PDFLATEX) refman.tex; \\ countdown=\`expr \$\$countdown - 1\`; \\ done; \\ mv refman.pdf ../\$(PACKAGE).pdf ]])["]], [[DX_SNIPPET_pdf=""]]) AS_IF([[test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1]], [[DX_SNIPPET_latex="## ------------------------------------------------- ## ## Rules specific for LaTeX (shared for PS and PDF). ## ## ------------------------------------------------- ## DX_V_LATEX = \$(_DX_v_LATEX_\$(V)) _DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY)) _DX_v_LATEX_0 = @echo \" LATEX \" \$][@; DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex]dnl m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ \$(DX_DOCDIR]DX_i[)/latex]])[ "]], [[DX_SNIPPET_latex=""]]) AS_IF([[test $DX_FLAG_doc -eq 1]], [[DX_SNIPPET_doc="## --------------------------------- ## ## Format-independent Doxygen rules. ## ## --------------------------------- ## ${DX_SNIPPET_html}\ ${DX_SNIPPET_chm}\ ${DX_SNIPPET_man}\ ${DX_SNIPPET_rtf}\ ${DX_SNIPPET_xml}\ ${DX_SNIPPET_ps}\ ${DX_SNIPPET_pdf}\ ${DX_SNIPPET_latex}\ DX_V_DXGEN = \$(_DX_v_DXGEN_\$(V)) _DX_v_DXGEN_ = \$(_DX_v_DXGEN_\$(AM_DEFAULT_VERBOSITY)) _DX_v_DXGEN_0 = @echo \" DXGEN \" \$<; .PHONY: doxygen-run doxygen-doc \$(DX_PS_GOAL) \$(DX_PDF_GOAL) .INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) doxygen-run:]m4_foreach([DX_i], [DX_loop], [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])[ doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) ]m4_foreach([DX_i], [DX_loop], [[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag: \$(DX_CONFIG]DX_i[) \$(pkginclude_HEADERS) \$(A""M_V_at)rm -rf \$(DX_DOCDIR]DX_i[) \$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR]DX_i[) \$(DX_DOXYGEN) \$(DX_CONFIG]DX_i[) \$(A""M_V_at)echo Timestamp >\$][@ ]])dnl [DX_CLEANFILES = \\] m4_foreach([DX_i], [DX_loop], [[ \$(DX_DOCDIR]DX_i[)/doxygen_sqlite3.db \\ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\ ]])dnl [ -r \\ \$(DX_CLEAN_HTML) \\ \$(DX_CLEAN_CHM) \\ \$(DX_CLEAN_CHI) \\ \$(DX_CLEAN_MAN) \\ \$(DX_CLEAN_RTF) \\ \$(DX_CLEAN_XML) \\ \$(DX_CLEAN_PS) \\ \$(DX_CLEAN_PDF) \\ \$(DX_CLEAN_LATEX)"]], [[DX_SNIPPET_doc=""]]) AC_SUBST([DX_RULES], ["${DX_SNIPPET_doc}"])dnl AM_SUBST_NOTMAKE([DX_RULES]) #For debugging: #echo DX_FLAG_doc=$DX_FLAG_doc #echo DX_FLAG_dot=$DX_FLAG_dot #echo DX_FLAG_man=$DX_FLAG_man #echo DX_FLAG_html=$DX_FLAG_html #echo DX_FLAG_chm=$DX_FLAG_chm #echo DX_FLAG_chi=$DX_FLAG_chi #echo DX_FLAG_rtf=$DX_FLAG_rtf #echo DX_FLAG_xml=$DX_FLAG_xml #echo DX_FLAG_pdf=$DX_FLAG_pdf #echo DX_FLAG_ps=$DX_FLAG_ps #echo DX_ENV=$DX_ENV ]) squashfs-tools-ng-1.1.3/m4/ax_pthread.m4000066400000000000000000000506131410627516300200240ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # 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.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # 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" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # 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 # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # 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 # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program 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 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 . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 25 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 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_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"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" 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 PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi 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 # names; the ordering is very important because some systems # (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. 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: # pthreads: AIX (must check this before -lpthread) # 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) # -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 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 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. (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,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $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" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) 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)"]) # 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" ax_pthread_clang_warning=no # 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.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # 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 if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) 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]) 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$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # 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 # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: 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_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 ], [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 ]], [[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" && \ 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 ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant 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])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: 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 ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD squashfs-tools-ng-1.1.3/m4/compiler.m4000066400000000000000000000030331410627516300175110ustar00rootroot00000000000000dnl Copyright (C) 2008-2011 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Simon Josefsson dnl -- derivated from coreutils m4/warnings.m4 # UL_AS_VAR_APPEND(VAR, VALUE) # ---------------------------- # Provide the functionality of AS_VAR_APPEND if Autoconf does not have it. m4_ifdef([AS_VAR_APPEND], [m4_copy([AS_VAR_APPEND], [UL_AS_VAR_APPEND])], [m4_define([UL_AS_VAR_APPEND], [AS_VAR_SET([$1], [AS_VAR_GET([$1])$2])])]) # UL_ADD_WARN(COMPILER_OPTION [, VARNAME]) # ------------------------ # Adds parameter to WARN_CFLAGS (or to $VARNAME) if the compiler supports it. AC_DEFUN([UL_WARN_ADD], [ m4_define([warnvarname], m4_default([$2],WARN_CFLAGS)) AS_VAR_PUSHDEF([ul_Warn], [ul_cv_warn_$1])dnl AC_CACHE_CHECK([whether compiler handles $1], m4_defn([ul_Warn]), [ # store AC_LANG_WERROR status, then turn it on save_ac_[]_AC_LANG_ABBREV[]_werror_flag="${ac_[]_AC_LANG_ABBREV[]_werror_flag}" AC_LANG_WERROR ul_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="-Werror ${CPPFLAGS} $1" AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])], [AS_VAR_SET(ul_Warn, [yes])], [AS_VAR_SET(ul_Warn, [no])]) # restore AC_LANG_WERROR ac_[]_AC_LANG_ABBREV[]_werror_flag="${save_ac_[]_AC_LANG_ABBREV[]_werror_flag}" CPPFLAGS="$ul_save_CPPFLAGS" ]) AS_VAR_IF(ul_Warn, [yes], [UL_AS_VAR_APPEND(warnvarname, [" $1"])]) ]) squashfs-tools-ng-1.1.3/m4/zstd.m4000066400000000000000000000012531410627516300166650ustar00rootroot00000000000000AC_DEFUN([AC_TEST_ZSTD_STREAM], [ AC_MSG_CHECKING([whether zstd supports stream compression]) AC_LANG_PUSH([C]) ac_zstd_have_stream="no" ac_zstd_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $ZSTD_CFLAGS" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], [ZSTD_EndDirective op = ZSTD_e_end; ZSTD_compressStream2(NULL, NULL, NULL, op);])], ac_zstd_have_stream="yes" AC_MSG_RESULT([yes]), AC_MSG_RESULT([no])) AS_IF([test "x$ac_zstd_have_stream" = "xyes"], [AC_DEFINE(HAVE_ZSTD_STREAM, 1, [Does zstd support stream compression?])]) AM_CONDITIONAL([HAVE_ZSTD_STREAM], [test "x$ac_zstd_have_stream" = "xyes"]) CFLAGS=$ac_zstd_save_CFLAGS AC_LANG_POP([C]) ]) squashfs-tools-ng-1.1.3/mksrcrelease.sh000077500000000000000000000064771410627516300201530ustar00rootroot00000000000000#!/bin/bash set -e usage() { cat < - release version to create in X.Y.Z format EOF exit 0 } fatal() { printf "Error: %s\n" "$1" >&2 exit 1 } askyesno() { local question="$1" local choice="" read -p "$question [y/N]? " choice case "$choice" in y|Y) echo "yes" ;; *) ;; esac return 0 } ##### get the command line arguments [ $# -eq 0 ] && usage [ $# -eq 1 ] || fatal "Insufficient or too many argumetns" new_ver="$1"; shift VER_REGEX="\([0-9]\+.[0-9]\+.[0-9]\+\)" echo "$new_ver" | grep -q -x "$VER_REGEX" || fatal "please, provide new version in X.Y.Z format" ##### parse the old version information old_ver="$(grep AC_INIT configure.ac | grep -o \\[[0-9.]*\\] | tr -d [])" old_so_ver="$(grep LIBSQUASHFS_SO_VERSION configure.ac | \ grep -o \\[[0-9:]*\\] | tr -d [])" current=$(echo "${old_so_ver}" | cut -d: -f1) revision=$(echo "${old_so_ver}" | cut -d: -f2) age=$(echo "${old_so_ver}" | cut -d: -f3) echo "$old_ver" | grep -q -x "$VER_REGEX" || fatal "error reading old version number" VER_REGEX="\([0-9]\+:[0-9]\+:[0-9]\+\)" echo "$old_so_ver" | grep -q -x "$VER_REGEX" || fatal "error reading old so version" [ "x$current:$revision:$age" = "x$old_so_ver" ] || \ fatal "Error parsing old so version" ##### generate new library SO version libchanges=$(git diff --numstat v${old_ver}..HEAD lib/sqfs/ | wc -l) if [ $libchanges -gt 0 ]; then echo "Detected changes to library code, updating so version" added=$(askyesno "Have any public interfaces been changed") changed=$(askyesno "Have any public interfaces been added") removed=$(askyesno "Have any public interfaces been removed") revision=$((revision+1)) if [ -n "$added" -o -n "$removed" -o -n "$changed" ]; then current=$((current+1)) revision=0 fi if [ -n "$added" ]; then age=$((age+1)) fi if [ -n "$removed" -o -n "$changed" ]; then age=0 fi else echo "No changes to library code detected, keeping old so version" fi new_so_ver="${current}:${revision}:${age}" echo "" echo "current package version: $old_ver" echo "current library so-version: $old_so_ver" echo "" echo "creating package version: $new_ver" echo "With library so-version: $new_so_ver" echo "" if [ "x$(askyesno "Is this ok")" != "xyes" ]; then echo "Aborting" exit fi tag_name="v$new_ver" release_name="squashfs-tools-ng-${new_ver}" # Make sure the git index is up-to-date [ -z "$(git status --porcelain)" ] || fatal "Git index is not up-to-date" # Make sure the tag does not exist [ -z "$(git tag -l "$tag_name")" ] || fatal "Tag $tag_name already exists" # Change the version in the configure.ac sed -i -e "s/$old_ver/$new_ver/g" configure.ac sed -i -e "s/$old_so_ver/$new_so_ver/g" configure.ac # Commit the change, create new signed tag echo "Generating signed release commit tag $tag_name" git commit -s -m "Release version $new_ver" configure.ac git tag -m "Release $new_ver" -s "$tag_name" # Prepare signed tarball ./autogen.sh ./configure make -j distcheck echo "Signing the tarball" gpg -o "$release_name.tar.gz.asc" --detach-sign \ -a "$release_name.tar.gz" gpg -o "$release_name.tar.xz.asc" --detach-sign \ -a "$release_name.tar.xz" echo "Generating doxygen documentation" make doxygen-doc tar czf "doxydoc-$new_ver.tar.gz" -C doxygen-doc/html . squashfs-tools-ng-1.1.3/mkwinbins.sh000077500000000000000000000152611410627516300174630ustar00rootroot00000000000000#!/bin/bash set -e VERSION=$(grep AC_INIT configure.ac | grep -o \\[[0-9.]*\\] | tr -d []) W32_ZIP_NAME="squashfs-tools-ng-${VERSION}-mingw32" W64_ZIP_NAME="squashfs-tools-ng-${VERSION}-mingw64" W32_DIR="$(pwd)/$W32_ZIP_NAME" W32_PREFIX="i686-w64-mingw32" W64_DIR="$(pwd)/$W64_ZIP_NAME" W64_PREFIX="x86_64-w64-mingw32" PKG_URL="https://infraroot.at/pub/squashfs/windows" download() { echo "-- fetching $PKG_TAR -- " [ -f "$PKG_TAR" ] || { curl -s -L "$PKG_URL/$PKG_TAR" > "$PKG_TAR" echo "$PKG_HASH $PKG_TAR" | sha256sum -c --quiet "-" } [ -d "$PKG_DIR" ] || { case "$PKG_TAR" in *.zip) unzip "$PKG_TAR" -d "$PKG_DIR" ;; *) tar -xf "$PKG_TAR" ;; esac } } ################################### get xz ################################### PKG_DIR="xz-5.2.5" PKG_TAR="${PKG_DIR}.tar.xz" PKG_HASH="3e1e518ffc912f86608a8cb35e4bd41ad1aec210df2a47aaa1f95e7f5576ef56" download pushd "$PKG_DIR" ./configure CFLAGS="-O2" --prefix="$W32_DIR" --host="$W32_PREFIX" \ --disable-xz --disable-xzdec --disable-lzmadec \ --disable-lzmainfo --disable-links \ --disable-scripts --disable-doc make -j make install-strip make clean ./configure CFLAGS="-O2" --prefix="$W64_DIR" --host="$W64_PREFIX" \ --disable-xz --disable-xzdec --disable-lzmadec \ --disable-lzmainfo --disable-links \ --disable-scripts --disable-doc make -j make install-strip popd ################################# get bzip2 ################################## PKG_DIR="bzip2-1.0.8" PKG_TAR="${PKG_DIR}.tar.gz" PKG_HASH="ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269" download pushd "$PKG_DIR" ${W32_PREFIX}-gcc -O2 -c blocksort.c ${W32_PREFIX}-gcc -O2 -c huffman.c ${W32_PREFIX}-gcc -O2 -c crctable.c ${W32_PREFIX}-gcc -O2 -c randtable.c ${W32_PREFIX}-gcc -O2 -c compress.c ${W32_PREFIX}-gcc -O2 -c decompress.c ${W32_PREFIX}-gcc -O2 -c bzlib.c ${W32_PREFIX}-ar cq libbz2.a *.o ${W32_PREFIX}-ranlib libbz2.a cp libbz2.a "$W32_DIR/lib" cp bzlib.h "$W32_DIR/include" rm *.o *.a ${W64_PREFIX}-gcc -O2 -c blocksort.c ${W64_PREFIX}-gcc -O2 -c huffman.c ${W64_PREFIX}-gcc -O2 -c crctable.c ${W64_PREFIX}-gcc -O2 -c randtable.c ${W64_PREFIX}-gcc -O2 -c compress.c ${W64_PREFIX}-gcc -O2 -c decompress.c ${W64_PREFIX}-gcc -O2 -c bzlib.c ${W64_PREFIX}-ar cq libbz2.a *.o ${W64_PREFIX}-ranlib libbz2.a cp libbz2.a "$W64_DIR/lib" cp bzlib.h "$W64_DIR/include" popd ################################## get lzo ################################### PKG_DIR="lzo-2.10" PKG_TAR="${PKG_DIR}.tar.gz" PKG_HASH="c0f892943208266f9b6543b3ae308fab6284c5c90e627931446fb49b4221a072" download pushd "$PKG_DIR" ./configure CFLAGS="-O2" --prefix="$W32_DIR" --host="$W32_PREFIX" \ --enable-shared --disable-static make -j make install-strip make clean ./configure CFLAGS="-O2" --prefix="$W64_DIR" --host="$W64_PREFIX" \ --enable-shared --disable-static make -j make install-strip popd ################################## get zstd ################################## PKG_DIR="zstd-v1.4.9-win32" PKG_TAR="${PKG_DIR}.zip" PKG_HASH="9ba7e4126cf614719442c81b1de6498b6d20bf5cb0b866c6898cab7fdfa738c5" download mv "$PKG_DIR/dll/libzstd.dll" "$W32_DIR/bin" mv "$PKG_DIR/dll/libzstd.dll.a" "$W32_DIR/lib/libzstd.dll.a" mv "$PKG_DIR/include"/*.h "$W32_DIR/include" cat > "$W32_DIR/lib/pkgconfig/libzstd.pc" <<_EOF prefix=$W32_DIR libdir=$W32_DIR/lib includedir=$W32_DIR/include Name: zstd Description: fast lossless compression algorithm library URL: http://www.zstd.net/ Version: 1.4.9 Libs: -L$W32_DIR/lib -lzstd Cflags: -I$W32_DIR/include _EOF PKG_DIR="zstd-v1.4.9-win64" PKG_TAR="${PKG_DIR}.zip" PKG_HASH="0bc374dadaec1fa879d5b2329d11b17212fb5251fe119e237a75e72d5e0745e7" download mv "$PKG_DIR/dll/libzstd.dll" "$W64_DIR/bin" mv "$PKG_DIR/dll/libzstd.dll.a" "$W64_DIR/lib/libzstd.dll.a" mv "$PKG_DIR/include"/*.h "$W64_DIR/include" cat > "$W64_DIR/lib/pkgconfig/libzstd.pc" <<_EOF prefix=$W64_DIR libdir=$W64_DIR/lib includedir=$W64_DIR/include Name: zstd Description: fast lossless compression algorithm library URL: http://www.zstd.net/ Version: 1.4.9 Libs: -L$W64_DIR/lib -lzstd Cflags: -I$W64_DIR/include _EOF ################################ build 32 bit ################################ export PKG_CONFIG_PATH="$W32_DIR/lib/pkgconfig" ./autogen.sh ./configure CFLAGS="-O2" LZO_CFLAGS="-I$W32_DIR/include" \ LZO_LIBS="-L$W32_DIR/lib -llzo2" \ BZIP2_CFLAGS="-I$W32_DIR/include" \ BZIP2_LIBS="-L$W32_DIR/lib -lbz2" \ --prefix="$W32_DIR" --host="$W32_PREFIX" --with-builtin-lz4 \ --with-builtin-zlib cp "$W32_DIR/bin/"*.dll . make -j check rm *.dll ./configure CFLAGS="-O2 -DNDEBUG" LZO_CFLAGS="-I$W32_DIR/include" \ LZO_LIBS="-L$W32_DIR/lib -llzo2" \ BZIP2_CFLAGS="-I$W32_DIR/include" \ BZIP2_LIBS="-L$W32_DIR/lib -lbz2" \ --prefix="$W32_DIR" --host="$W32_PREFIX" --with-builtin-lz4 \ --with-builtin-zlib make clean make -j make install-strip ################################ build 64 bit ################################ export PKG_CONFIG_PATH="$W64_DIR/lib/pkgconfig" ./configure CFLAGS="-O2" LZO_CFLAGS="-I$W64_DIR/include" \ LZO_LIBS="-L$W64_DIR/lib -llzo2" \ BZIP2_CFLAGS="-I$W64_DIR/include" \ BZIP2_LIBS="-L$W64_DIR/lib -lbz2" \ --prefix="$W64_DIR" --host="$W64_PREFIX" --with-builtin-lz4 \ --with-builtin-zlib make clean cp "$W64_DIR/bin/"*.dll . make -j check rm *.dll ./configure CFLAGS="-O2 -DNDEBUG" LZO_CFLAGS="-I$W64_DIR/include" \ LZO_LIBS="-L$W64_DIR/lib -llzo2" \ BZIP2_CFLAGS="-I$W64_DIR/include" \ BZIP2_LIBS="-L$W64_DIR/lib -lbz2" \ --prefix="$W64_DIR" --host="$W64_PREFIX" --with-builtin-lz4 \ --with-builtin-zlib make clean make -j make install-strip ############################# package everything ############################# cp -r licenses "$W64_DIR" cp README.md COPYING.md CHANGELOG.md "$W64_DIR" cp -r licenses "$W32_DIR" cp README.md COPYING.md CHANGELOG.md "$W32_DIR" rm -r "$W32_DIR/lib/pkgconfig" "$W64_DIR/lib/pkgconfig" rm "$W32_DIR/lib"/*.la "$W64_DIR/lib"/*.la ${W32_PREFIX}-strip --discard-all "$W32_DIR/bin"/*.dll "$W32_DIR/bin"/*.exe ${W64_PREFIX}-strip --discard-all "$W64_DIR/bin"/*.dll "$W64_DIR/bin"/*.exe zip -r "${W32_ZIP_NAME}.zip" "$W32_ZIP_NAME/bin" "$W32_ZIP_NAME/lib" zip -g -r -l "${W32_ZIP_NAME}.zip" "$W32_ZIP_NAME/include" zip -g -r -l "${W32_ZIP_NAME}.zip" "$W32_ZIP_NAME/licenses" $W32_ZIP_NAME/*.md zip -r "${W64_ZIP_NAME}.zip" "$W64_ZIP_NAME/bin" "$W64_ZIP_NAME/lib" zip -g -r -l "${W64_ZIP_NAME}.zip" "$W64_ZIP_NAME/include" zip -g -r -l "${W64_ZIP_NAME}.zip" "$W64_ZIP_NAME/licenses" $W64_ZIP_NAME/*.md ############################# sign the packages ############################## gpg -o "${W64_ZIP_NAME}.zip.asc" --detach-sign -a "${W64_ZIP_NAME}.zip" gpg -o "${W32_ZIP_NAME}.zip.asc" --detach-sign -a "${W32_ZIP_NAME}.zip" squashfs-tools-ng-1.1.3/packages/000077500000000000000000000000001410627516300166745ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/packages/APKBUILD000066400000000000000000000017021410627516300200120ustar00rootroot00000000000000# Contributor: Ryan Barnett # Maintainer: Ryan Barnett pkgname=squashfs-tools-ng pkgver=1.1.2 pkgrel=0 pkgdesc="A new set of tools and libraries for working with SquashFS images" url="https://infraroot.at/projects/squashfs-tools-ng/index.html" arch="all" license="GPL-3.0-or-later" makedepends="automake libselinux-dev lz4-dev lzo-dev xz-dev zlib-dev zstd-dev libbz2-dev" subpackages="$pkgname-dev $pkgname-libs $pkgname-doc" source="https://infraroot.at/pub/squashfs/squashfs-tools-ng-$pkgver.tar.gz" build() { ./configure \ --build=$CBUILD \ --host=$CHOST \ --prefix=/usr \ --sysconfdir=/etc \ --mandir=/usr/share/man \ --localstatedir=/var make } check() { make check } package() { make DESTDIR="$pkgdir" install } sha512sums="3f66cd9034997104e2d3281e271e8ccfbdd6ecaa98313636dcfd5251717e173ceede27b4a94342b011707fc1e96884ec733423f978697c6420915d96c153cf3e squashfs-tools-ng-1.1.2.tar.gz" squashfs-tools-ng-1.1.3/packages/Dockerfile000066400000000000000000000026341410627516300206730ustar00rootroot00000000000000# Dockerfile fo build a package for following Linux distributions: # # # * alpine # * archlinux # * centos # * fedora # * debian # * ubuntu # * opensuse # ARG vendor ARG release ARG version=1.0.4 FROM $vendor:$release # Args are not globaly scoped ARG vendor ARG release ARG version=1.0.4 # Install tools required to build a package for several distributions. # # Create a user and add it to sudoers. RUN case $vendor in \ alpine) \ apk add alpine-sdk sudo ;\ ;; \ archlinux) \ pacman -Sy; \ pacman -S --noconfirm fakeroot binutils namcap sudo ;\ ;; \ centos|fedora) \ yum install -y rpm-build spectool sudo ;\ ;; \ debian|ubuntu) \ apt-get update ;\ DEBIAN_FRONTEND=noninteractive apt-get install -y \ -o Dpkg::Options::=--force-confdef \ -o APT::Install-Recommends=no \ build-essential \ ca-certificates \ devscripts \ equivs \ libdistro-info-perl \ sudo \ wget \ ;\ ;; \ opensuse|opensuse/leap) \ zypper install -y rpm-build sudo wget ;\ ;; \ *) \ echo "Unsupported vendor '$vendor' (version: '$version')"; \ exit 1; \ ;; \ esac; \ case $vendor in \ alpine) adduser -G abuild -s /bin/ash -D builder ;; \ *) useradd -m -s /bin/sh builder ;; \ esac; \ echo 'builder ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/builder; \ chmod 0400 /etc/sudoers.d/builder USER builder WORKDIR /home/builder ENV vendor=$vendor ENV release=$release ENV version=$version squashfs-tools-ng-1.1.3/packages/PKGBUILD000066400000000000000000000030351410627516300200210ustar00rootroot00000000000000# Maintainer: David Oberhollenzer pkgname=('squashfs-tools-ng' 'squashfs-tools-ng-doc') pkgver=1.1.2 pkgrel=1 epoch= pkgdesc='A new set of tools and libraries for working with SquashFS images' url='https://infraroot.at/projects/squashfs-tools-ng/index.html' arch=('x86_64') license=('GPL3' 'LGPL3') depends=('lzo' 'lz4' 'xz' 'zstd' 'zlib' 'attr' 'bzip2') source=(https://infraroot.at/pub/squashfs/${pkgname}-${pkgver}.tar.xz{,.asc}) sha512sums=('3f66cd9034997104e2d3281e271e8ccfbdd6ecaa98313636dcfd5251717e173ceede27b4a94342b011707fc1e96884ec733423f978697c6420915d96c153cf3e' 'SKIP' ) validpgpkeys=('13063F723C9E584AEACD5B9BBCE5DC3C741A02D1') groups=() makedepends=( 'fakeroot' 'binutils' 'autoconf' 'automake' 'autogen' 'libtool' 'pkgconf' 'm4' 'make' 'gcc' 'doxygen') # checkdepends=() optdepends=('squashfs-tools') provides=() conflicts=() replaces=() backup=() options=() install= changelog= noextract=() prepare() { cd "$pkgname-$pkgver" } build() { cd "$pkgname-$pkgver" ./configure --prefix=/usr make make doxygen-doc } check() { cd "$pkgname-$pkgver" make -k check } package_squashfs-tools-ng() { #depends=('zstd' 'attr' 'zlib' 'xz' 'lzo' 'bzip2' ) cd "$pkgname-$pkgver" make DESTDIR="$pkgdir/" install } package_squashfs-tools-ng-doc() { arch=('any') optdepend=() depends=() cd "$pkgbase-$pkgver" install -d "$pkgdir/usr/share/doc/$pkgbase" cp -a doxygen-doc/* "$pkgdir/usr/share/doc/$pkgbase" } squashfs-tools-ng-1.1.3/packages/README.md000066400000000000000000000057161410627516300201640ustar00rootroot00000000000000# Package builder This directory contains files to build packages for several Linux distributions. ## Build using Docker images Packages for a specific release can be built using Docker. Use the `build` script for that: ``` ./build VENDOR RELEASE ``` If you want to build all supported vendors and releases, you can use the `build_all` script. Packages will be output in `_out` directory. ## Manual build ### APK [APKBUILD]() containts all definition to build APK packages for Alpine linux. From a fresh install setup the build environment for a reqular user named `pkg-builder` (user name is up to you): ``` adduser pkg-builder addgroup pkg-builder abuild echo '%abuild ALL=(ALL) NOPASSWD:/sbin/apk, /bin/mkdir -p /etc/apk/keys, /bin/cp -i *.pub /etc/apk/keys/' > /etc/sudoers.d/abuild chmod 0400 /etc/sudoers.d/abuild apk add alpine-sdk ``` Build the package as `pkg-builder`: ``` abuild-keygen -nai abuild -r ``` ### DEB The [debian]() directory contains all definitions to build Debian and Ubuntu packages. Package building for Debian like distibutions is a bit tricky. In this case we want to add the codename to the version number in order to differentiate builds. To build package for version 1.0.4, run following commands: ``` apt-get install devscripts build-essential wget source /etc/os-release wget https://github.com/AgentD/squashfs-tools-ng/archive/v1.0.4/squashfs-tools-ng-1.0.4.tar.gz -O squashfs-tools-ng_1.0.4+$VERSION_CODENAME.orig.tar.gz tar xfz squashfs-tools-ng_1.0.4+$VERSION_CODENAME.orig.tar.gz cd squashfs-tools-ng-1.0.4 ln -s packages/debian DEBFULLNAME="$USER" DEBEMAIL="$USER@localhost" dch -v 1.0.4+$VERSION_CODENAME-1 -D $VERSION_CODENAME "Build 1.0.4 for $VERSION_CODENAME." mk-build-deps --install --tool='apt-get --no-install-recommends --yes' debian/control rm *.deb debuild debuild -- clean ``` ### PKG [PKGBUILD]() contains all definition to build Archlinux packages. Run following commands: ``` sudo pacman -S --noconfirm fakeroot binutils namcap makepkg --noconfirm -Cfsir PKGBUILD ``` You can check the packages using `namcap`: ``` namcap -i squashfs-tools-*.pkg.tar.zst PKGBUILD ``` ### RPM [squashfs-tools-ng.spec]() contains all definitions to build RPM packages. #### CentOS, Fedora Run following commands: ``` yum install -y rpm-build spectool rpmdev-setuptree spectool -g -R squashfs-tools-ng.spec rpmspec --parse squashfs-tools-ng.spec | grep BuildRequires | cut -d' ' -f2 | xargs sudo yum install -y rpmbuild --clean -ba squashfs-tools-ng.spec ``` ### OpenSUSE Run following commands: ``` zypper install -y rpm-build rpmspec --parse squashfs-tools-ng.spec | grep Source0 | awk '{print $2}' | xargs wget -N -P $(rpm --eval '%{_sourcedir}') rpmspec --parse squashfs-tools-ng.spec | grep BuildRequires | cut -d' ' -f2 | xargs sudo zypper install -y rpmbuild --clean -ba squashfs-tools-ng.spec ``` Note: * `spectool` does not natively exists on OpenSUSE, hence source has to be downloaded manually. * `zypper` is used intead of `yum`. squashfs-tools-ng-1.1.3/packages/build000077500000000000000000000026431410627516300177260ustar00rootroot00000000000000#!/bin/bash # Build a package for a specific distribution using a Docker image. # # ./build DISTRO RELEASE # # From a given DISTRO:RELEASE it create a new local Docker image named # DISTRO-builder:RELEASE and installs tools required to build a # package (it does not install package build depends). # # It also creates a builder user to prevent from building packages as # root. # # Once the image is setup, it starts a docker image and run # build-helper to build the packages. The packages files are stored in # _OUT/DISTRO/RELEASE. # vendor="$1" release="$2" if test -z "$vendor" -o -z "$release"; then cat<&2 $0 VENDOR RELEASE EOF exit 1 fi source_dir=$(git rev-parse --show-toplevel) output_dir=$source_dir/_out empty_dir=$output__dir/_empty if test "$vendor" = "opensuse"; then vendor=opensuse/leap pkg_dir=$output_dir/$vendor-$release else pkg_dir=$output_dir/$vendor/$release fi image=$vendor-builder:$release mkdir -p $pkg_dir mkdir -p $output_dir/_empty # Build docker image docker build -t $image -f packages/Dockerfile \ --build-arg vendor="$vendor" \ --build-arg release="$release" \ . docker_args="-v $source_dir:/source-ro:ro -v $empty_dir:/source-ro/_out:ro" docker_args="$docker_args -v $pkg_dir:/output:rw" docker_args="$docker_args -v $source_dir/packages/build-helper:/build-helper" docker_args="$docker_args --rm" docker run -it $docker_args $image /build-helper squashfs-tools-ng-1.1.3/packages/build-all000077500000000000000000000007431410627516300204730ustar00rootroot00000000000000#!/bin/bash vendors='alpine archlinux centos debian fedora opensuse ubuntu' alpine_versions='3.11 3.12' archlinux_versions='latest' centos_versions='7 8' debian_versions='bookworm bullseye buster strech' fedora_versions='32' opensuse_versions='15.0 15.1 15.2' ubuntu_versions='bionic focal groovy' source_dir=$(git rev-parse --show-toplevel) for v in $vendors; do versions="${v}_versions" for ver in ${!versions}; do $source_dir/packages/build $v $ver done done squashfs-tools-ng-1.1.3/packages/build-helper000077500000000000000000000065731410627516300212110ustar00rootroot00000000000000#!/bin/sh # This script builds squashfs-tools-ng packages inside a Docker # instance. # # It can build packages for: # # * alpine # * archlinux # * centos # * fedora # * debian # * ubuntu # * opensuse # # Currently it can only build package from a release (tag from github). # # TODO: Find a way to build packages from git checkout sources # Path to the source directory mounted in the Docker instance. ROOT_RO=/source-ro # Path to which packages will be copied after a successful build. OUTPUT=/output . /etc/os-release build_alpine() { cp -r $ROOT_RO/packages/APKBUILD . abuild-keygen -nai abuild -r cp ~/packages/*/x86_64/*.apk $OUTPUT } build_archlinux() { cp -r $ROOT_RO/packages/PKGBUILD . makepkg --noconfirm -Cfsir PKGBUILD cp ~/*.tar.zst $OUTPUT } # This part is a ugly hack. # TODO: Find a better way to build deb packages. build_deb() { # Fetch sources wget \ https://github.com/AgentD/squashfs-tools-ng/archive/v$version/squashfs-tools-ng-$version.tar.gz \ -O squashfs-tools-ng_$version+$VERSION_CODENAME.orig.tar.gz tar xfz squashfs-tools-ng_$version+$VERSION_CODENAME.orig.tar.gz cd squashfs-tools-ng-$version cp -r $ROOT_RO/packages/debian . #ln -s packages/debian DEBFULLNAME="$USER" DEBEMAIL="$USER@localhost" \ dch -v $version+$VERSION_CODENAME-1 \ -D $VERSION_CODENAME "Build $version for $VERSION_CODENAME." # See https://packages.debian.org/search?searchon=sourcenames&keywords=debhelper # https://packages.ubuntu.com/search?keywords=debhelper&searchon=names&suite=all§ion=all case "$VERSION_CODENAME" in jessie|xenial) dhv=9 ;; stretch) dhv=10 ;; bionic) dhv=11 ;; buster|focal) dhv=12 ;; bookworm|bullseye|groovy) dhv=13 ;; sid) dhv=13 ;; esac sed -i -e "s/@DEBHELPER_VERSION@/$dhv/" debian/control echo $dhv > debian/compat sudo mk-build-deps --install \ --tool='apt-get --no-install-recommends --yes' debian/control sudo rm -f *-build-deps_* debuild debuild -- clean cat /tmp/squashfs-tools-ng* cp ../*.deb $OUTPUT } build_rpm() { case "$ID" in centos) if test $VERSION_ID -ge 8; then # install doxygen sudo sed -i 's/^enabled=.*/enabled=1/' \ /etc/yum.repos.d/CentOS-PowerTools.repo cat /etc/yum.repos.d/CentOS-PowerTools.repo fi ;; esac cp -r $ROOT_RO/packages/squashfs-tools-ng.spec . rpmdev-setuptree spectool -g -R squashfs-tools-ng.spec rpmspec --parse squashfs-tools-ng.spec \ | grep BuildRequires | cut -d' ' -f2 \ | xargs sudo yum install -y rpmbuild --clean -ba squashfs-tools-ng.spec cp ~/rpmbuild/RPMS/x86_64/*.rpm $OUTPUT } build_opensuse() { cp -r $ROOT_RO/packages/squashfs-tools-ng.spec . # Fetch source rpmspec --parse squashfs-tools-ng.spec | grep Source0 \ | awk '{print $2}' \ | xargs wget -N -P $(rpm --eval '%{_sourcedir}') # Install build requirements rpmspec --parse squashfs-tools-ng.spec | grep BuildRequires \ | cut -d' ' -f2 \ | xargs sudo zypper install -y rpmbuild --clean -ba squashfs-tools-ng.spec cp ~/rpmbuild/RPMS/x86_64/*.rpm $OUTPUT } case "$ID" in alpine) build_alpine ;; archlinux|arch) build_archlinux ;; centos|fedora) build_rpm;; debian|ubuntu) build_deb;; opensuse|opensuse-leap) build_opensuse;; *) cat <&2 Unsupported distro "$ID" EOF exit 1 ;; esac squashfs-tools-ng-1.1.3/packages/debian/000077500000000000000000000000001410627516300201165ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/packages/debian/changelog000066400000000000000000000055211410627516300217730ustar00rootroot00000000000000squashfs-tools-ng (1.1.2-1) experimental; urgency=medium * New upstream release. -- Laszlo Boszormenyi (GCS) Sat, 26 Jun 2021 07:35:56 +0200 squashfs-tools-ng (1.1.1-1) experimental; urgency=medium * New upstream release. -- Laszlo Boszormenyi (GCS) Sun, 23 May 2021 16:19:32 +0200 squashfs-tools-ng (1.0.4-1) unstable; urgency=medium * New upstream release. * Add upstream metadata. * Update packaging bits. * Update debhelper level to 13 . * Update Standards-Version to 4.5.1 . -- Laszlo Boszormenyi (GCS) Fri, 29 Jan 2021 23:20:01 +0100 squashfs-tools-ng (1.0.3-2) unstable; urgency=medium * Backport upstream fix for normalization of slashes in filenames (closes: #979595). -- Laszlo Boszormenyi (GCS) Fri, 08 Jan 2021 20:57:22 +0100 squashfs-tools-ng (1.0.3-1) unstable; urgency=medium * New upstream release. -- Laszlo Boszormenyi (GCS) Thu, 12 Nov 2020 22:19:55 +0100 squashfs-tools-ng (1.0.2-1) unstable; urgency=medium * New upstream release. -- Laszlo Boszormenyi (GCS) Sat, 05 Sep 2020 18:55:05 +0200 squashfs-tools-ng (1.0.1-1) unstable; urgency=medium * New upstream release. -- Laszlo Boszormenyi (GCS) Wed, 05 Aug 2020 19:35:46 +0200 squashfs-tools-ng (1.0.0-2) unstable; urgency=medium * Correct package sections (closes: #963155). -- Laszlo Boszormenyi (GCS) Sat, 20 Jun 2020 11:51:59 +0200 squashfs-tools-ng (1.0.0-1) unstable; urgency=medium * New upstream release. * Library transition from libsquashfs0 to libsquashfs1 . -- Laszlo Boszormenyi (GCS) Sat, 13 Jun 2020 19:24:26 +0200 squashfs-tools-ng (0.9.1-1) unstable; urgency=medium * New upstream release. -- Laszlo Boszormenyi (GCS) Sat, 16 May 2020 08:35:00 +0000 squashfs-tools-ng (0.9-1) unstable; urgency=medium * New upstream release. * Build with LZO compression support. * Backport upstream fix for missing header without LZO. * Update Standards-Version to 4.5.0 . [ Pino Toscano ] * Enable the build on Hurd (closes: #947830). * Don't ship .la files (closes: #947831). -- Laszlo Boszormenyi (GCS) Sat, 04 Apr 2020 15:45:30 +0000 squashfs-tools-ng (0.8-1) unstable; urgency=medium * New upstream release. * Update Standards-Version to 4.4.1 . -- Laszlo Boszormenyi (GCS) Tue, 31 Dec 2019 08:31:34 +0000 squashfs-tools-ng (0.7-1) unstable; urgency=medium * New upstream release. * Backport upstream fix for fstree_init test. -- Laszlo Boszormenyi (GCS) Tue, 22 Oct 2019 18:40:57 +0000 squashfs-tools-ng (0.5-1) unstable; urgency=medium * Initial release (closes: #932971). -- Laszlo Boszormenyi (GCS) Tue, 30 Jul 2019 11:44:14 +0000 squashfs-tools-ng-1.1.3/packages/debian/clean000066400000000000000000000000131410627516300211150ustar00rootroot00000000000000config.log squashfs-tools-ng-1.1.3/packages/debian/control000066400000000000000000000102051410627516300215170ustar00rootroot00000000000000Source: squashfs-tools-ng Section: kernel Priority: optional Maintainer: Laszlo Boszormenyi (GCS) Build-Depends: debhelper-compat (= 13), pkg-config, libselinux1-dev [linux-any], liblzma-dev, liblz4-dev, zlib1g-dev, libzstd-dev, liblzo2-dev Standards-Version: 4.5.1 Rules-Requires-Root: no Homepage: https://github.com/AgentD/squashfs-tools-ng Package: squashfs-tools-ng Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends} Description: New set of tools for working with SquashFS images SquashFS is a highly compressed read-only filesystem for Linux, optimized for small size and high packing density. It is widely used in embedded systems and bootable live media. . SquashFS supports many different compression formats, such as zstd, xz, zlib or lzo for both data and metadata compression. It has many features expected from popular filesystems, such as extended attributes and support for NFS export. . As the name suggests, this is not the original user space tooling for SquashFS. Here are some of the features that primarily distinguish this package from the original: - reproducible SquashFS images, i.e. deterministic packing without any local time stamps, - Linux `gen_init_cpio` like file listing for micro managing the file system contents, permissions, and ownership without having to replicate the file system (and especially permissions) locally, - support for SELinux contexts file (see selabel_file(5)) to generate SELinux labels. Package: libsquashfs1 Architecture: any Section: libs Depends: ${misc:Depends}, ${shlibs:Depends} Description: New set of tools for working with SquashFS images - shared library SquashFS is a highly compressed read-only filesystem for Linux, optimized for small size and high packing density. It is widely used in embedded systems and bootable live media. . SquashFS supports many different compression formats, such as zstd, xz, zlib or lzo for both data and metadata compression. It has many features expected from popular filesystems, such as extended attributes and support for NFS export. . As the name suggests, this is not the original user space tooling for SquashFS. Here are some of the features that primarily distinguish this package from the original: - reproducible SquashFS images, i.e. deterministic packing without any local time stamps, - Linux `gen_init_cpio` like file listing for micro managing the file system contents, permissions, and ownership without having to replicate the file system (and especially permissions) locally, - support for SELinux contexts file (see selabel_file(5)) to generate SELinux labels. . This package contains the C libraries needed to run executables that use the squashfs-tools-ng library. Package: libsquashfs-dev Architecture: any Section: libdevel Depends: ${misc:Depends}, libsquashfs1 (= ${binary:Version}), libselinux1-dev [linux-any], liblzma-dev, liblz4-dev, zlib1g-dev, libzstd-dev, liblzo2-dev Description: New set of tools for working with SquashFS images - development SquashFS is a highly compressed read-only filesystem for Linux, optimized for small size and high packing density. It is widely used in embedded systems and bootable live media. . SquashFS supports many different compression formats, such as zstd, xz, zlib or lzo for both data and metadata compression. It has many features expected from popular filesystems, such as extended attributes and support for NFS export. . As the name suggests, this is not the original user space tooling for SquashFS. Here are some of the features that primarily distinguish this package from the original: - reproducible SquashFS images, i.e. deterministic packing without any local time stamps, - Linux `gen_init_cpio` like file listing for micro managing the file system contents, permissions, and ownership without having to replicate the file system (and especially permissions) locally, - support for SELinux contexts file (see selabel_file(5)) to generate SELinux labels. . This package contains the C development headers and library files needed to compile programs using the squashfs-tools-ng library. squashfs-tools-ng-1.1.3/packages/debian/copyright000066400000000000000000000045711410627516300220600ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: squashfs-tools-ng Upstream-Contact: David Oberhollenzer Source: https://github.com/AgentD/squashfs-tools-ng Files: * Copyright: 2019- David Oberhollenzer License: GPL-3+ Files: include/sqfs/* include/util/* lib/sqfs/* lib/tar/write_retry.c lib/util/* Copyright: 2019 David Oberhollenzer License: LGPL-3.0+ This package 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 package 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the complete text of the GNU Lesser General Public License can be found in `/usr/share/common-licenses/LGPL-3'. Files: lib/fstree/fstree_sort.c Copyright: 2019 David Oberhollenzer , 2019 Zachary Dremann License: GPL-3+ Files: debian/* Copyright: 2019- Laszlo Boszormenyi (GCS) License: GPL-3+ License: GPL-3+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. . This package 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 General Public License for more details. . You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-3'. squashfs-tools-ng-1.1.3/packages/debian/libsquashfs-dev.install000066400000000000000000000001231410627516300246020ustar00rootroot00000000000000usr/include/ usr/lib/*/libsquashfs.a usr/lib/*/libsquashfs.so usr/lib/*/pkgconfig/ squashfs-tools-ng-1.1.3/packages/debian/libsquashfs1.install000066400000000000000000000000331410627516300241070ustar00rootroot00000000000000usr/lib/*/libsquashfs.so.* squashfs-tools-ng-1.1.3/packages/debian/rules000077500000000000000000000004161410627516300211770ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 override_dh_auto_install: dh_auto_install # remove libtool .la files find $(CURDIR)/debian/tmp/ -name '*.la' -delete %: dh ${@} .PHONY: override_dh_auto_install squashfs-tools-ng-1.1.3/packages/debian/source/000077500000000000000000000000001410627516300214165ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/packages/debian/source/format000066400000000000000000000000141410627516300226240ustar00rootroot000000000000003.0 (quilt) squashfs-tools-ng-1.1.3/packages/debian/squashfs-tools-ng.install000066400000000000000000000000301410627516300250740ustar00rootroot00000000000000usr/bin/ usr/share/man/ squashfs-tools-ng-1.1.3/packages/debian/upstream/000077500000000000000000000000001410627516300217565ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/packages/debian/upstream/metadata000066400000000000000000000004231410627516300234600ustar00rootroot00000000000000--- Archive: GitHub Bug-Database: https://github.com/AgentD/squashfs-tools-ng/issues Bug-Submit: https://github.com/AgentD/squashfs-tools-ng/issues/new Repository: https://github.com/AgentD/squashfs-tools-ng.git Repository-Browse: https://github.com/AgentD/squashfs-tools-ng squashfs-tools-ng-1.1.3/packages/debian/watch000066400000000000000000000001631410627516300211470ustar00rootroot00000000000000version=4 https://github.com/AgentD/squashfs-tools-ng/tags .*/v?(\d\S+)\.(?:tar\.xz|txz|tar\.bz2|tbz2|tar\.gz|tgz) squashfs-tools-ng-1.1.3/packages/squashfs-tools-ng.spec000066400000000000000000000105331410627516300231470ustar00rootroot00000000000000## Spec file to build squashf-tools-ng RPM package. # OpenSUSE has no dist macro %if 0%{?suse_version} > 0 %global dist .sles%{suse_version} %endif Name: squashfs-tools-ng Version: 1.1.2 Release: 1%{?dist} License: GPLv3+ URL: https://github.com/AgentD/squashfs-tools-ng Source0: https://github.com/AgentD/squashfs-tools-ng/archive/v%{version}/%{name}-%{version}.tar.gz Summary: New set of tools for working with SquashFS images %description SquashFS is a highly compressed read-only filesystem for Linux, optimized for small size and high packing density. It is widely used in embedded systems and bootable live media. SquashFS supports many different compression formats, such as zstd, xz, zlib or lzo for both data and metadata compression. It has many features expected from popular filesystems, such as extended attributes and support for NFS export. As the name suggests, this is not the original user space tooling for SquashFS. Here are some of the features that primarily distinguish this package from the original: - reproducible SquashFS images, i.e. deterministic packing without any local time stamps, - Linux `gen_init_cpio` like file listing for micro managing the file system contents, permissions, and ownership without having to replicate the file system (and especially permissions) locally, - support for SELinux contexts file (see selabel_file(5)) to generate SELinux labels. %if 0%{?centos} > 7 || 0%{?fedora} >= 32 || 0%{?suse_version} >= 1500 %global use_zstd 1 %endif # rpm-build / rpmdevtools BuildRequires: gcc BuildRequires: automake BuildRequires: autoconf BuildRequires: libtool BuildRequires: doxygen BuildRequires: libselinux-devel BuildRequires: zlib-devel BuildRequires: xz-devel BuildRequires: lzo-devel BuildRequires: libattr-devel # Need to be explicitly declared on Fedora BuildRequires: make # OpenSUSE has a different lz4 and bzip2 devel package names %if 0%{?suse_version} > 0 BuildRequires: liblz4-devel BuildRequires: libbz2-devel %else BuildRequires: lz4-devel BuildRequires: bzip2-devel %endif %if 0%{?use_zstd} BuildRequires: libzstd-devel Requires: libzstd %endif Requires: %{name}-lib%{?_isa} = %{version}-%{release} Requires: zlib Requires: xz Requires: lzo Requires: libattr Requires: libselinux # OpenSUSE has a different lz4 and bzip2 package names %if 0%{?suse_version} > 0 Requires: liblz4 Requires: libbz2 %else BuildRequires: lz4 BuildRequires: bzip2 %endif #Recommends: squashfs-tools %package lib Summary: New set of tools for working with SquashFS images - shared library Group: System Environment/Libraries %description lib This package contains the C libraries needed to run executables that use the squashfs-tools-ng library. %package devel Summary: New set of tools for working with SquashFS images - development Group: Development/Libraries Requires: %{name}-lib = %{version}-%{release} %description devel This package contains the C development headers and library files needed to compile programs using the squashfs-tools-ng library. %package devel-doc Summary: New set of tools for working with SquashFS images - documentation Group: Development/Libraries %description devel-doc This package contains the C development documentation files for the the squashfs-tools-ng library. %prep %setup -q %autosetup %build ./autogen.sh %configure make %{?_smp_mflags} make doxygen-doc %install %make_install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{_bindir}/* %{_mandir}/man1/* %doc COPYING.md CHANGELOG.md README.md %files lib %defattr(-,root,root,-) %{_libdir}/*.so* %files devel %defattr(-,root,root,-) %{_includedir}/* %{_libdir}/*.a %{_libdir}/*.la %{_libdir}/pkgconfig/*.pc %files devel-doc %defattr(-,root,root,-) %doc doxygen-doc/html %doc doxygen-doc/*.tag %changelog * Sat Jun 26 2021 David Oberhollenzer - 1.1.2-1 - Bump to version 1.1.2. * Sat Jan 23 2021 David Oberhollenzer - 1.0.4-1 - Bump to version 1.0.4. * Thu Nov 05 2020 David Oberhollenzer - 1.0.3-1 - Bump to version 1.0.3. * Thu Oct 01 2020 Sébastien Gross - 1.0.2-1 - Add Fedora support. - Add OpenSUSE support. - Bump to version 1.0.2. * Thu Aug 20 2020 Sébastien Gross - 1.0.1-1 - First package release. squashfs-tools-ng-1.1.3/tests/000077500000000000000000000000001410627516300162605ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/Makemodule.am000066400000000000000000000014121410627516300206600ustar00rootroot00000000000000include tests/libutil/Makemodule.am include tests/libfstream/Makemodule.am include tests/libfstree/Makemodule.am include tests/libtar/Makemodule.am include tests/libsqfs/Makemodule.am if BUILD_TOOLS if CORPORA_TESTS check_SCRIPTS += tests/cantrbry.sh tests/test_tar_sqfs.sh tests/pack_dir_root.sh TESTS += tests/cantrbry.sh tests/test_tar_sqfs.sh tests/pack_dir_root.sh endif endif if WINDOWS else check_SCRIPTS += tests/rdsquashfs/pathtraversal.sh TESTS += tests/rdsquashfs/pathtraversal.sh endif EXTRA_DIST += $(top_srcdir)/tests/tar2sqfs EXTRA_DIST += $(top_srcdir)/tests/corpus/cantrbry.tar.xz EXTRA_DIST += $(top_srcdir)/tests/corpus/cantrbry.sha512 EXTRA_DIST += $(top_srcdir)/tests/pack_dir_root.txt.ref EXTRA_DIST += $(top_srcdir)/tests/rdsquashfs/pathtraversal.sqfs squashfs-tools-ng-1.1.3/tests/cantrbry.sh.in000077500000000000000000000025321410627516300210520ustar00rootroot00000000000000#!/bin/sh set -e CORPUS="@abs_top_srcdir@/tests/corpus/cantrbry.tar.xz" SHA512FILE="@abs_top_srcdir@/tests/corpus/cantrbry.sha512" TAR2SQFS="@abs_top_builddir@/tar2sqfs" if [ ! -f "$TAR2SQFS" -a -f "${TAR2SQFS}.exe" ]; then TAR2SQFS="${TAR2SQFS}.exe" fi COMPRESSORS=$("$TAR2SQFS" --help | grep $'\t' | sed 's/ (default)//' | \ tr -d '\011' | sort | uniq | sed '/uncompressed/d' | \ sed '/bzip2/d' | sed '/zstd/d' | sed '/lz4/d') for size in 4k 8k 16k 32k 64k 128k 256k 512k 1M; do for cmp in $COMPRESSORS; do for threads in 1 2 3 4; do name="cantrbry_${cmp}_${size}_${threads}.sqfs" xzcat "$CORPUS" | \ "$TAR2SQFS" -q -c "$cmp" -b "$size" \ -j "$threads" --defaults mtime=0 \ "$name" name="cantrbry_${cmp}_${size}_${threads}_T.sqfs" xzcat "$CORPUS" | \ "$TAR2SQFS" -qT -c "$cmp" -b "$size" \ -j "$threads" --defaults mtime=0 \ "$name" done # check that the ones with > 1 thread are identical, # then remove them. for threads in 2 3 4; do name="cantrbry_${cmp}_${size}_${threads}.sqfs" ref="cantrbry_${cmp}_${size}_1.sqfs" diff "$name" "$ref" rm "$name" name="cantrbry_${cmp}_${size}_${threads}_T.sqfs" ref="cantrbry_${cmp}_${size}_1_T.sqfs" diff "$name" "$ref" rm "$name" done done done sha512sum --ignore-missing -c "$SHA512FILE" rm cantrbry_*.sqfs squashfs-tools-ng-1.1.3/tests/corpus/000077500000000000000000000000001410627516300175735ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/corpus/cantrbry.sha512000066400000000000000000000256521410627516300223560ustar00rootroot0000000000000050a018b7920d499e1a83663532fb83a5059b26223acacc2b36d7e1ba27bd153cf72717267b6133e091d60ac7513cab9b2639a05927b94f9bf904971d6cf64cc7 cantrbry_gzip_128k_1.sqfs 658989cb453dea4de8cf0789054142fa2c6c3efcc2ed22799f53144c700149c1fbef2731c28a2ca36e668bd91cdbadd8483dbcc11d2a731565c161773aa250fa cantrbry_gzip_128k_1_T.sqfs 58f8d23addf8cc7316ac0ce656d562b4ec89b574a25fcaa610997162f0a00029b828f1c951f4fb08a3c06d6a44ffa9b6aba0a713a4f6e693f550ec8ca2a2cd9a cantrbry_gzip_16k_1.sqfs a11f41113d581b1aa34c67fb2c8409a8c2bd1c9b754031466a87029f10eee57f60b941b3d857b27d503ae8ff7e0037dfc5ef1d4e1f121dccfd7c31debe06a23e cantrbry_gzip_16k_1_T.sqfs b13038007946560dde60a48be2f701e46f311e568c69b02d46de013ebc39fed83954989d7de409669f7aa3a5d26cad9114b97264887524b86597a621aab3aec5 cantrbry_gzip_1M_1.sqfs b13038007946560dde60a48be2f701e46f311e568c69b02d46de013ebc39fed83954989d7de409669f7aa3a5d26cad9114b97264887524b86597a621aab3aec5 cantrbry_gzip_1M_1_T.sqfs 591aa4da9fedbe793a681ac788606ba7f985d06a2162f15c710c65edbb3cf80664e7980f263ce377688b7980fcf560bbefa65ade80d2beab395ce477576d3524 cantrbry_gzip_256k_1.sqfs 31fa65bce121dd64364e05b011a1b62a2ee631513895f132a43952a17a3b48c24a8ee0f735fdc040e76a07962b55ac559b3821a399ad7dd183e0c4ef46fb7f45 cantrbry_gzip_256k_1_T.sqfs 522a26d6399366a7174414865e1f051211ab239dabe2221e85bf68250d20e2e6504a47c1b146f34c9fbc60e35aacd5d08feafb5386bfba7e326a8321d7cef660 cantrbry_gzip_32k_1.sqfs 77bcae784b8ec7c4aa80249667f774ffa738966eebd58050a4869236fff3f370f00e0aec9d82c56c64f6952a3937760e3533eef70cbab24aac78bdc884aa1a5c cantrbry_gzip_32k_1_T.sqfs 502e8335e346247fd501761eae36508973c9f8959528e5dd85f9e86308f0e3dac1a841d8c144950476547c2ee113e05fe5e6ed81a08c09a08b8e1aa4ab6a8e61 cantrbry_gzip_4k_1.sqfs 06988721106c2420a834a4a06e4fe85e396855ce6ad1c315795de3519a49a176cbdf52c21c5b1a4de1430fc0d1514d6a2535056578b3e137d90b9516a0fd7ff5 cantrbry_gzip_4k_1_T.sqfs 1fd67afbc46b3032a471cbc0e779b1c4596076ade53993d1278cf1034144ff1ce927efd1f640594027aee9bf76d936d79aff4d92871428b95085049380459360 cantrbry_gzip_512k_1.sqfs a7851b1f400ac6b13d5a9e8efc6d250dc7509761823e0cf8697521e49d24e7e076eb4471326a4599d9e83bb469f250d68a45ebd87a2fde8c79813f8363c4c6a8 cantrbry_gzip_512k_1_T.sqfs 90aeed342683735a68f7ad5af61d4cd2b5ac9ce55304a884147639a4a5297aaa51890e7cf0167e4dd67d516f776bf7ffc7bdff4c12c2d53c74b39eddb2feef9a cantrbry_gzip_64k_1.sqfs 07ee8f6f5b68feebab503098a91d22d0676ccb84b7a52b9d28c277295504a32f198122fa2da577abc11cd5fe13b02fcba7a12d46f1dffa7f2c6c106f1b2b491b cantrbry_gzip_64k_1_T.sqfs 9263ce6c9ea458b7c379e14281138ee27fb0362ee85e9cc90b84e49a96b28cb567d64a702ad0e4263248882a9d0378b458d04da65fd9c5cd58fd591e1e07c794 cantrbry_gzip_8k_1.sqfs d76e3410f9c8d52639e173c25e3eb28e53d7c1705e800294a609377b5429a379b881073966a01fdcb3094cbfb47c564bd9e88f52942faa56921fb7ede72a08e2 cantrbry_gzip_8k_1_T.sqfs 83f2892d9edc100d800641cf87ff5e6ff26cde19b66ea4d9e3eaf0717662013e3039f1ba161fb27b2ff8084d1be89f115da5f907c7a10d6caa40caa1e54c2250 cantrbry_lzma_128k_1.sqfs a2e2c8283d76da6b15bab48f08aa927ddf266aa90f5ed47845f83638e7d65f5bb4703dcaafbc07d69bf60f061c8b688454ef97ce20554cf65352570c674e9761 cantrbry_lzma_128k_1_T.sqfs 8c9f8ed0ef69a4342dab5e336550228c655eef4d85486af4c5bfd5609400b28bdabde5d0def00351a3a2e71b42a1d10e37ea07a0cc8e5ab0bdbf3c00b7360320 cantrbry_lzma_16k_1.sqfs 2dfa86a8e9cf353e9c1f903d8cc1bfb3b44a93d4ed93978eceffdd4e038c935dcb63a9a556013b1b88792c3796241e9ade2ac040a8e0b1db70cda5d6c27a88c8 cantrbry_lzma_16k_1_T.sqfs 760254668be762d9a04a38fd4ca55d1479861c8cbcfa6543e0b0aceb19869930e6c60804409d591438eb698bbbaf359c8c0d8ffd30771d613dcdb5af075c5c80 cantrbry_lzma_1M_1.sqfs 760254668be762d9a04a38fd4ca55d1479861c8cbcfa6543e0b0aceb19869930e6c60804409d591438eb698bbbaf359c8c0d8ffd30771d613dcdb5af075c5c80 cantrbry_lzma_1M_1_T.sqfs a0aa30bfe7df9fe0f22161e4f2fe4ed17913e28431c1a790dc640598dab21538d6d70f6d690b06b785987bf7664b6183036ea3b8f418f21e4db73c639df53aa8 cantrbry_lzma_256k_1.sqfs bc8d02fa87a5bc5990f3615975066abfdcff4882ab343d062440c160c6b20f64f3cdaceffeace839960a7d5486b0552c83029a178188285555aed1c5244f5962 cantrbry_lzma_256k_1_T.sqfs a86d70974b7c989412868ca7bcc466f1e6b31de7ff802998dd2811bbaca2611c13a4136183a6b0e70006a13a0e932429ade7d7a1c6a8a5fc85f99070b0906795 cantrbry_lzma_32k_1.sqfs 8bb85b028bdddbbf90f93023d48f59fa0957887d2aea15d191c06783a12ae8489dd4a9d56e9ba76a9a04d1286aaa9b50e39cd9b773f97d5656b4f17c87fc3b20 cantrbry_lzma_32k_1_T.sqfs 6651ac83d9c3430a84d345fe13337114e344fabee986adc5948601734aff53728799a348341b5a52764c439bd70ce6487ed6f4e39d9f8a5d0c50b1130078d890 cantrbry_lzma_4k_1.sqfs 0462b85e5e34600d51e03cd71eba8c16d4a7fea420169ad8547cd050508cf647117b9d6e2cc7604cf2cc797236b5387ce6185607c0c0532da5d4867f0eb894d9 cantrbry_lzma_4k_1_T.sqfs 5dc0f1aa8b3e7cd8179d545aaff7d13b8bbe6c5586e0127f4a53db3bdd8a5e41bbed3e7eb9fbf45d7ef4844be769e5c807cd8a7e8b3faca7b0fb47a7048dfb01 cantrbry_lzma_512k_1.sqfs fff2240a977d77c8b30fe351fdcc4bcd88ad794fc07120326ceef814670e516ef436a0fa61d9168abadaf2d941f51ed5dca66b21408270198322d950282a7998 cantrbry_lzma_512k_1_T.sqfs 415a769478dd048d8fc49999be2618704cd8fb6c57d02459b1f2541fd1dfd74068ae2a6c5cbbb3830da2e3d96537666efed9282afa5aac30464e8ab3d6cec8c8 cantrbry_lzma_64k_1.sqfs 2ad876bfd67a749228b4a844846625a981e47e2fe6a0675f74a25eae7f9e972a99e69456b801fdc3f1969a69277c7c1234ea701d70725fb381e86248f60e8480 cantrbry_lzma_64k_1_T.sqfs 86b21f121ce4b121efb3de79ae4af86d03a55dcf858ae2dab9277c2549f2da0488517c7d90ea27d78c0d3bade10ace40955200b4d486d7947d746c09e8994b84 cantrbry_lzma_8k_1.sqfs 496c13f2ed9d697667cee7b1b45c7b79f6b6c95de5bb5519931203d3af346c2cbe28a4f2bff9c444c980fff4f01904273af60d2640be5309c57894c5b282e8cf cantrbry_lzma_8k_1_T.sqfs 1720c41df079bbe7f5f818f4d9fac4194f806184291de071d793cf79a0ff22eb5cfbab737d6cce728a580354d526e08ff029d4ac6edae2461e0efbc105c22869 cantrbry_lzo_128k_1.sqfs cc0e1da5fd0e163204db4dd8f6c8525597510e9045b6bf76ff6ce7df64e457e7647bbe43f8fae0e573f41296535ca57b7d4918d77734fe47fed661e5546f345b cantrbry_lzo_128k_1_T.sqfs 964efbc1b1dfaa2f141beaf9bac78599373d6a60e9ff8346fe93f0a79fb3df471de139a3b015242e98ad585d89e4b87f013bed3f86bb25874461dddbe25516c4 cantrbry_lzo_16k_1.sqfs 8b6822be1f3bd0e2b902e1335546fe00059b8ab6722925ba040c8bd00028088679e251a9dd89b29a053d4964f65f0cc7811742db44505899a37f7fc12122bcbe cantrbry_lzo_16k_1_T.sqfs 4039057786b94852a05f7a0a1acf69d762a64d24b26dde54d48fdac48b150c869f8db9c83fa32690af4685de80e65307cb9ea0418df20193abeaa0862c31c368 cantrbry_lzo_1M_1.sqfs 4039057786b94852a05f7a0a1acf69d762a64d24b26dde54d48fdac48b150c869f8db9c83fa32690af4685de80e65307cb9ea0418df20193abeaa0862c31c368 cantrbry_lzo_1M_1_T.sqfs a9f1ababf079816f2cd327a8f13c67139d5e7d5f4109c7dd9a70e6492ec0e2422244c8a6dd3078aed465197c234e148ba9946445212c259a6707ff0fe05c0a3a cantrbry_lzo_256k_1.sqfs a72d49068578f2bfffca1be7a7232f1287cf62a5f06e93044c2bfb1354d0df6581c1fb87e51c76d1144f2eea5fdb05a1574a6602089c26a09c9a5e61b8a852a9 cantrbry_lzo_256k_1_T.sqfs 05251b1eb204351a21841dd1b09945600f7ed590c8f859952c10c153f42d24f62ae8fe899fe5f914d1fb061b3e2dce1eebcddc0a6469e784313aedf8f2e45132 cantrbry_lzo_32k_1.sqfs cac10385f612427668c9d4810032717eab80e5fd6ad13afc1cb2536d992e1800f5c6b1121a0d5f1cea92cbea394dee5de6a1dc80bd8fe6d2e66966675674cf55 cantrbry_lzo_32k_1_T.sqfs 2fad88c25bbcd5a229a946ea81f028241c209cd836c2ea6cf3fef33d96a6ee836c4efce28f7a487d2ff1a458fc37e0acb68cfca28a20168c738ccda3f6426d6f cantrbry_lzo_4k_1.sqfs 1be653b9b5eedf41459412071c77e31edb432d4bbc31bcf8ea60aadabe9f8ad615885c5af69bb6c85a4bd7f1c3a42ee6317f1eccd8bfcfd53d6407e414ff9f7a cantrbry_lzo_4k_1_T.sqfs 534dddb50bb45a318b5ca34d9385b13ee3053d6585a0844f6ef48f0fa53789cda16d56f4f9c70067ffe5a2c53923d12458b0aced85cd2477bb27a261823f5f9b cantrbry_lzo_512k_1.sqfs dfc273d802cd2127ce54c09a4342b2ef4d883b0616b95d882fbfdbefc22d2411ebcaec2a76809d0ee308b8a7624fa0ad867d98efbc8611f7576725674b20f764 cantrbry_lzo_512k_1_T.sqfs bab610ffbe99486457799bf3ead48b4496ade3af2f981f57f7d6ccde7a6fa5ec6ce435acfe924c7232405cf3fb670926411dfd63d1476867ec8e66ac38492c02 cantrbry_lzo_64k_1.sqfs e110dce5add52d391502f2b378c4cfd022ab2cee0b6e0c5416a8e8900c484ff43c47651a7dff689914984ee86d38043b027d797c82c7f63ad14ba25484e5a777 cantrbry_lzo_64k_1_T.sqfs 5237b499fab40c78d614c9bebf3d06bd80b9220d26948a5174619d42d5e64d0fb00fd6bf626a8a3ebf545097b3f6e2b7b71f909122bc065ab8f59666bba6b52f cantrbry_lzo_8k_1.sqfs b02963c4926b10a2966b0bad76d17058d996ad950cfa09dbf98b66c304952da4c6e99ff38d28f3869a4d8fe3d5a04c51f3dad2cd879f8b4fb5bfbbb62582bdcf cantrbry_lzo_8k_1_T.sqfs 9ae1f6923b7992c80876822e5b52a705c9f6b0ff7ffbc93fe8625bc5a46b92900e1b3cb822144f746c29e173e6c7d074de3fc6153ef53bb62745099af9a7973f cantrbry_xz_128k_1.sqfs 29da607ddadf0564cf65e27079d0a2291d7f32cc9ad44db9e5e52d86802dda83fba6fed60a0dc1b618aa8025629ef075eb7b7b5da0606914437ba1d3e6bb687e cantrbry_xz_128k_1_T.sqfs 6d1aacb5740cd26f3febc52ebdb8698dcbc39bf897a3a9ef0e8c49d125b1cfd05188e2daed66813d0dc309e4cdea3bc7a6a82ddff7e4d294d9f2121b67a5220b cantrbry_xz_16k_1.sqfs 98c40886419e0d27d1e93a848f83a9cdd5f8dfa37610259613839e93fa775f7ee232ff2ef2d43df8cde4b5c30a0366b6e0d838551f6e2442a900719802c7d3ce cantrbry_xz_16k_1_T.sqfs 233d86abe20c45e12c6266271eabb2762c4f3343d0724ac1f462da13c19d791dde6d210a4fdfa381acb1ecf1574f3eaefbc792e653a0aeaf7595d94db4ea9523 cantrbry_xz_1M_1.sqfs 233d86abe20c45e12c6266271eabb2762c4f3343d0724ac1f462da13c19d791dde6d210a4fdfa381acb1ecf1574f3eaefbc792e653a0aeaf7595d94db4ea9523 cantrbry_xz_1M_1_T.sqfs f57fcb76f28fc24ea8d718aea299a9477764acac00a7c904b1439d3b3a7197c536faf7915768e37824051f5cda4720ac62e1766ff78e173646cc001e0cfa5b1d cantrbry_xz_256k_1.sqfs c8c4a6e1e33ae2fd251ce1fdaad8e6d6bba9549852140b0c960db166086ddc489173eb3bb56e95ebb65196b30c7d03f61cb47b63bc1edce1af106208f2e35e55 cantrbry_xz_256k_1_T.sqfs 313f79650996199fda290de76383e5c149e782e374d712a7046056e506dd487c95c5001cd8c3a37a386f8932e6657159e43c0480ee6d9dc669e198b498c73d91 cantrbry_xz_32k_1.sqfs 7eaa174d3e47899b55377072001bd29a55a14a099eb789c5bdb8b8bbb58be72d5d16e6701a0019b4faf0aca157cff4d578839385e89c73b11718e0c9b27de9cf cantrbry_xz_32k_1_T.sqfs aa6357d486e87814dda4a34a2dcf322500ddc9cc2c718e50c9bf0249c513bf189a241609dee3b5389b0a0942c17dc2da70d6c00b09555ad5c2edab3a81e554cd cantrbry_xz_4k_1.sqfs 2008226606b42860ec2c840fb978a85843fd38ddf015da29973aa6822c4296fa841e5abb739daf25ebbacc3c58281fc05b4903367b277557a9ae89e4c6d4daf2 cantrbry_xz_4k_1_T.sqfs ed5091314cb7f56994034bf5d1d3c030286f1823127447a960533b0e78e742e218e3512d5bc2ef33c43d17412af65df9cb734206fe4041648f91d56305bc148d cantrbry_xz_512k_1.sqfs 54576ab50c01b7919f805dd3199a8d30b65381bc2719b82312daf1345137438f9c7d2b9dc609313517cd4a5f510ef487cfea9a8a57f1d1b72501277aedad5bd2 cantrbry_xz_512k_1_T.sqfs a3dc5c3d0927a9100b5d47eac775997824d1b327f3eeee0719c5913c54c4d3fb06e919712352f12118a57fc70fb83e94e76c55e0380a97bafbd9dddd73d51e7f cantrbry_xz_64k_1.sqfs f3ccb7cdafb6a7ff6cb737158f4f133424c4d9c4bfda940053b9682d967216fcc168042b5be288223a5ee53f0024c4b23293aee38b17b74429ae7aa097c0f263 cantrbry_xz_64k_1_T.sqfs 5f6a71d723fcbd8ae69c7af2c3597d2fb5d75d1ba7a676dd0913cdcfcd612d0215c34c0fa6ef43102430faba945bc3c3138c59ebe064d5a4ad9d83cec41fc1a8 cantrbry_xz_8k_1.sqfs 51797ecf598b15eb9561bb029a9bf0e395edcd8376d4113229f9a13cee87b7ce851a2e156d05076ff1ff8ae72f775d710c70ddc1b4e98abb6ee78fa48414bc18 cantrbry_xz_8k_1_T.sqfs squashfs-tools-ng-1.1.3/tests/corpus/cantrbry.tar.xz000066400000000000000000016652341410627516300226100ustar00rootroot000000000000007zXZִF!t/]0 `ģcC-trQ{F+z~1wnwjHV\t^ kjP)%$eNJic9*l,&9!WZGSHb "iYkI1 B0H CV,ݠ : -\,5RθmʀƊef.h$#Y=5-^`Y.]C,^qy5F}wYV]ߢ % i+SkRT}8{9٠ٰ>׉.K̎ 70a1^w,žWYa ]{J)R JqLlcyݸdFZ F& Hb(#j\HkTAC|i# A;;fl2}BSk_ҙL!-41/M;^/-y/ԦO&ZĪ(HU)c\ǼNiuv} ~oR)O*&X 1T@x&,&vŀ:%`%\i7xUMr^SцR,}7J~o Ѫl}YϽl~.8-7[cq r[ fx`} )[Uifl!TI@}7,EjXW>ZݔKj`4I Pa pL DE 8>p*óΩC#rX$=3WcY碶+`Ę{IJCa"P&V{;rYzΎpD^R;(p_l~HRȱwe$GF">(zB;]2Tt֓/"` o$ccXέv=2;4Vر 76Tc@җhD5NZf4b!;o2*066lhC8jWhh୯ C7FcC.cH]oWq~nJ؋3[%L[ :{$Rw"p TWutb,tCkS*PZ,xyyoaX C@ɋjh jKE RuD %oo] !eZz_ FCKPvף(4J:'uZdw/Ý\{MWcbꥡ;JHCUT+\U."Dp*ƁWDJaz] 䁡t ?J֕5O`s36ywBEKP='@S`Cc5LY>Y03+H@**EuN;((to7n> dLwBNB{Hg5u Lqbox&Wf߄'{fO8 .,Y}&ᒮ{rI@AXF clU\u|dR)d>wό ݦLeH/?~/58#oqP "OiV ~]]m^?R<F}rmʢKB%)c`T}^r,I`E?uZ{2zrcXΈ;y ץ*GG.*\7Ts v*l@/}}0#Z*@&k gG]&FJxpuWmd#l ^9VVS$)isgBNxFun? S|T*4Qx_mmxo͏C@d3-}R7!M)-E^+J F8ut֫I'CC[ا[ݜNuE5sRlyh( 3Jfl8:9"=,jK( ,S$Z|5̽e@SFb +f]r8Di 4TO[%ܷi^Cܥ7K22QL"*Q !`tВq.fT?DSskTL;yb_A/k{1)VL^[Kl(QN@e0ZNZR/F-u8Xd{@T?>8s&{sI`$1)=ic kWXATxUZ vgbB/Tۢ╽[?qvхLT 'Is[JEtVџ]pO#vM YF0HdP="R@Hp[*pU1uy𕼋wF#XkB9+| <;:XN!rEt)QuJO@A)jZE'.cm<51 ,2j/!;R]A P?k$Cѫ/קV@z%-ǎ~|5ozH nS =K|V͂Wژ#l_{3eٖA;Yɏ+bZɢ_9bL=@"3=/hbNcAqwKjF12€6?g6ւLW9; u&qi;?Év2n{tXu2;Y[z3 `VzHG^#d9iq[ܽ(|{9eHEe (mVV0.K#aOv'N\ˬ$N5-=#T~Ҥ7|d2iwʦ|Qa>W94\oYua)D!>߸o "Is3 IN;V7 M0!i<40٪;ʦFkJf „_n pثS[ ob Ph]lS:_ U|lwd  )2yuT[|^Ojt*As~7kSrUUa\-lS|bQiWg8 Z'@=ǯFo+S:[(7[:z^"8GfxƱ2SZM^ѿV!+'%nLʓ&O`P VBb+ dDӰ"f."W\o9˼ٿX\R y訟G*> O}XZAa{W:`<;TJ )F"\AJg }IqCoZ{ֹF9`>-$V?$j/.La#קMt0kx`'Au6/7d 3'3=IGm8~c"\lMJ nɐ&!cjD%Q]C;+TDJ4w"BSnfud k&_[n^xpBK7*8kbM0f%nBcBk{Tǩs.$;CKm a00SR+-Kx,B@UGЍ6Ȥ6YOsQq|E5Ai2`kQW븬6CC' ڵ'1,~x*A^) %5KH8u[ͶP J\3mW)ž hDEXؐ4gԪK܃OdԀt*_7aw/GJU=X^1LTndflDJ{gȣOqqSu.(҉fQ ;9 q1ML=ukqWnr=č}1D Yh"V@ }Na'I\# ~ӑ&* FS;Ȏ6&S\2n_r}P8HQ mv jʒn٥38?'$]̙FerI.ϵ/&'Gc-&yoG |Oe}>8+li,(`/.<)$'Xn*I]8ZzB"cۺ|d2A d7Ci52[#K~ mFSӖX_j;=D!ZR̭BZE+(ĎzyͅdTUu+_u 5 Db_\Ш=U޴d<he|I-=^Íy_{I 7:\gʦ0i*NPNݽWBν1aO"M1qg7EnId7;j+-M Pee?ۛD*4|}#9 aPs]QfȩhfGy$9ꃲ[qoՔz!@+ M`לAV: ]-Xaӫ:nXz !MӦ&W"^^Qt׌ 2ohQڟtllBڻn[Օk6HB~O|3rq$-T BKd~<~u5rɒy(THw!Igq䀆m74P"{_ef+k=9ղp_YQi@ϞrZ3G\ ʈ\¼+;o"h~BŞ`RR⧃SxRw, RQUIWa5ǞJ`gԁ{iϢK]j|#Km 1=ä'P _[Ѝ6Ps JN޿ƾA"-I(6֣ qflE_Iu<0wEbWc#Ty`Jq{ճcD]/"-,D԰#CÜ7$=.eoƺ&75# #mL8شszWF?\& 'F0,f 3aNZDp$w`CI #6ջJ#6e PQz[Ql -q XU;ЋMr !g[?z,ZKv锵˒P|7~T&Yz +:Ta.}/Ų﷮h׃j9zB+0fm&c0-O:I= {dMQ_kyⲻعg/Z x.$=KVXHG8Wz_.`,m.#m=7OHHԨAE_ K ]4& m]TeQh4 [' 8xE^8qb/5Xi]0 |ǯ-Ns^D/6uXnK,9BG;ě.dB_Rg2p|=S:]X׬ݘI0{,[@ej:ܒ?-e <{\k.* "?ӟ?P@x3;aM6X ˬt簰Bc_aϵM!V6 ]CzbiB#Q`}]wHp-:\߁q6?Xir/ ֎\xgH+BJ@4@ɨ\V`? e_ۂ5wFrA:HDbP{~_K7)^_GΦ gh.YgvWfuely3/؉h͕@ѕf ϡ&LU7a`c׭e?P TY2,$G%6 à-e?:>ߋA^|dbjRd_# zߓnO8.vd!pstAWr 9}4XpwKG܀.J =5rzD;JQ<FSyRz;=7DžQLr|, E:ȝ٨I43|QYL!(YjzEC_ jߘƿ?vEj$D݀fá<40Zx 3==+C$Xz罅ƑtsQPK]3SJ:e{AUvX7jCCfBF+W}FD:*a^na(Dy?CEB]?f<,랜A ofv(fpO͙sdN&oؔ Uz~8ОRn6xȱΐI[+(OL X L*G])(5T8aI}sTkq qbcy!ѣ(q$SVEqqs4x*b.[%RJHp -Qm'1\o29X8pz]G [ASBW4= měds(&Ԃmۤu3ix.Xfk!7DD뒙'd[Ȫ{uuMwT-:F_@f:&Zi!ӂgpCL_u 1(a>vl6=q}dBo@z_}8qqdDz7IZ>_+sF9l00Y﷘! ENDW9e? hQd$x2`I^d銐Hm |n(/(FrD#iT"B4闷9~Sd)%lŽ*w!/ _=

P\:YW? Fc<«i![ p|0jXz7~Dkf-i49A]UjfԘ.6lr`6>H͵:|)PMkF2%\>2rK_)Zd8їH N(Kb։ 2 - <+\]ppLF``(P(`yCܙz'X]''+bc]xL+|vz:!ȧ). Is4h~FdOڰcU0^sěIxp|% z%@ysr&Baq%U h se[CyV: 9) ה6H^ﯤ(h+S=\p8j`f2h [!*=Q(b[6ZǃZs[Mgq<-qSM6va>)HBm uVP: =b:r ^' ^b]u_K@?„'EϪmlju+ i$C1^hKN9!.e9Y͸^1o_y98\ +wavݬQOc&fa M߯5REO˿-fF]Ӛ4CAmg'qESo8}'1-J2JFZ&`i3ѷtx/y+rn[:Ղ)i/$0;5E{1gPI1)qV:+H1.ZG?b,1LrOP@;XĸX)t8ObGbK?pi\ I>k'Tv^yO4z Qbn,ռ@D-v.ӏ\ƴhgzUp1{"]/NkK"w8 5H@6:㚹)q腼?AW͑}艆IGl_k\R~c"brFXuBUt3^e\Rس1BҐܧ')cǻ{8skEم!J pyX7LO1 -GYaELɭW08 s>Mnk7E&]!|<\=Ntx! UPv8w! `4}A*V [Ujyg*'9H\PIq*ؘ fTY;&jy/47E#(</1OBu`oB 9+gYdrD%5fĊ8r{=ula Uª|[ϫpkr[_!JG3>dM#!;徳 7E`LEG[d%g`Al}k,݅=5k`cU͛٘ :L5K.AޥfcKTnSU61Tl^{*gEHڿ FrS.yow \#Wn/ .uum'AM1brK5^AcD^Y*~.*fFlSn_2FfU!Ώa}0G`#x~ + 3LcFk1îƊeE嶟 Rf5LKݍY%%b$\9ZbGH_ܨ\̏qʅL7i"}'b~]޲My?@.`/δI iZËB11Q SʧRzceUH@&Oq 2\2Se7k+~`5DtM$8kNAtfN!E= HB>Zwl^;9qgg{|@7leRϮVZbJe1ʦ%M$or5J 0DvTQr%S3%ic5z\R4ߢķ>^F2|@ExnZѡIm`_[{oEkx忶wdEd-SQr Yg,ҜcR׷tKR] g?c #b M0o< X6_C7x2&PYZQ:=ϴ.S ,гNoٚ 9i DJ~Y,&%;}/| la\: ˥KkOi&S*[i90ަd/In DW2m9 c.`9!#ZaJsZ]e~;SAEL@3c` /h5fo^/V EwQX(Q]xJ>ktj gBmJul\c(Y N[}P2 ƭRʢr&JV6FAaT2*dwf'򶸅TT@*@*7{WMA912J 6D L_ *n]MQa=3.UyGXS N {"%"Q̢x|Ïy /NșΨsKNKqeRS!fCˉSݫe5~տ1;xXϖi Et;iO1yʙҝtvMo?N#2 B7B+n_͙jOM7l{@jjjAp*Qۭ',@C^1u"8P^W2W}@7%0da"OByԑdPeTוϬdHރ@1bANNmf8ɬdxC aDueXjMcʊ8ꕌ[P( 0L@5ɏ :3gr'׽ }PJcT_2MQU'$e)y>R.g ܒm|8i4^8{Ơ `\m.>b ItXrC#sDlS(-_Q5X*s`ܲQ\؄mяdK255q>~a_?\$!G>y8@!L,(oIx:ӟT_Xi/Lաwht!$ Aq$/% s4%E`VPG\\'g :nB Pj6az &u;uo'**Sls}e'vF/njMGTdֿN^)ʩ#h\sm+<2Gȥ]k0ZGWKh\:3'2r ^|tأf% Y];?̆…oWUGdK6~.aT?K3` <̐VwW 5ø fI5:^{e\)ڲ>?M@Yx@j6]zn'/ 3A=Tt,[5)E*cgx(qnĵUNJ>*r!(H!g @JMQ=@YV-J`.$zZ'sҵ~}= 5Y-U##qB@m !ABKMk6Q6p#o:tt!v]eËy:^F]y>[Y">Z #2s\|c|_Lܚ( ^h ~@ ].ƥ~I:"EГ2 ~ĕ.8&׋쁊f&4,KrZ_mv5pdOhgcYW<*<:3KX`Aw0VW襀DM7ӟ{h]oZ=HBk=v1zݾ#z!= Ĭ'؈p^uY3dC^M[Kq#wy]@æe:gIG,Qqxf\ٜFgի:^|+` ^9q2K[szs&,O ClWT.9MXa+Gm~[w??!6̣' WF<(ոi 9|}*?H[ܭ5X@8^ؿ𮴆N7fԙZ7d7L1W}ő(5;͢b³ }bjlڕc|F#Rn\L[z/13k=M!Ѳ-R݅I܏%]^˽>EhbЬ;^Kӌ=9e{[AcSćjлTVKƭy.NzWQi,ѭXR0PI!YD z?t۟%t.SĆ%0 H+QD{e+O)po-faf1xBJX#=d9 "V,dJl1Vx< H+|5ߙ2iB&ˠ=qd xݐlm` "pXoQ%H U%G) ך g#`0bj((f˦[ 3x/o$.*쓞:2l @H&e"r_acObiKO.> 7x `_M`N&B?3Pyf'mcm۴}5|Lcj<|&N~"}O?i@ {Dr-]LpXl\Ģ2iR RiMҕEho8sF]P33աxp]]Jm. Ro=t뗍M4E*jxy'i$ɿ{7Qnth4e*|lޘKڤtӱOE SUp2x8[䩁cCs\!.;ac(N-<7>yI^$ίS$!Te(hxɬ#iEs8z{9o6vRtKbH1z<*$ӏ d8g9籨z}ImEʁn]gD;#7bl#,CE踨=R Z=AI\_.Q77D * '#AK}Ywo]PRdvEdǃRk|md/%`i^]X 孨Z'IC:՚\uBG{-NQ=h b .,(-re%0'V)q&+u> u7nC8GW:dCVI' g|%Kjf3dqr: ǩ5N<4\)tVC|eȧUS\9^)3)Iچ R%Y:ܒP|% ~\2ˋ9^Tp3ϯY⫲%'%M6[iNQ G) Ӈ }n7[ąE]%逫4U/$8d L 8&cVJqwa V;g#"郭l7H\y`&qK+ Ԙ.ƁQO@+###D> 9E*$idrgJt@-m]IÁ{&2\!t?g 撚4 @U nLf# Y!`Bl͙5'J_pg1KU Ҍ/i̙жj$8sqGo]Gr~HBr9xn9Rg](ѡ/q˲HJ|_敧fү&fЏ?}AV=}W&l.Ŋ&f7pX(;f#,?GjX jnae;1>o'CUN8W!^f GBP0)@ #EәNcq3ܖץSL%)'PwqTI%1; #1zSHr`q\;;E2Irёm(49,# kpߏ/}`(W#0#syP)K'1. J NCpwv`18ە 5+|f]jŸS4(9/Cs|OY̧Q bUbENԽ} t`UG@*eyy8f%mV$ϓBhr#uPDXx#E bv#m2)^6,hмxta'p㷵ΟqJNp]lNKMO;GU̅wmM~ kqeA"ՆJ-K%6c)ڃt8ƞ\y}Ay$.U~PC 4֬؀uM(nx9x3f'dNm3̑97lVȢNP߁#h3MP:^Cy"֝Ҿ4B5m&stނh>l>`AM\;TE-bo2@s+]ݧ6=2^|2=̦冷ހ>(Rm8В; нX.:6UTR$_5Qo2ea*Js.zK҄W_QhgYkt B֬ '(%#DJ{4@-F+ATXyEMj* 0,Js[ޗƜW 7~|>aeqV9qw~ȣnpܯ]K/a=0Y :(1 ݢh_to 5) Fm^f--jx3Z9g#b2T̸PD vNG] EXky_Qr;vhcm&z&HS]'k!z+MTbn`5`C l{ϜeŜ=he&KMXRPg2S|cHɹq C\:v5>\w~hx:*po ziSiF-䲿Ϲ/^\r-$vW$C3JD'| gd}u ߢ Ta9 os΢ȍ\w7AghP]b6aa(ijNc =HjK&:υPD5\y5ȵBEH!0]a%ߜB 4RD"h=}հtEh~3.F@*Voź)9 7_Du1# ْՂzhLe4=%*߷knQ-6tWKᴕPi`Sj*X\8AQ&7QE8i4KDn-Ӊt?szY~sr/ nE}5Elj{]Ok(4uΰY.Ž5UԶc`Jbm?XJ:y':;~޾2X^,G 6Du*F}6TU|XVn:P6ΡjcX :4-̼!2Cw$Nk6HNPf(lբd%2V=GUŢ`E[ :yj+onq֥\T(.4*e CO:&oqQ J3γ[D/bZt4qNP}]r_II۬ks+6A̲;\WOr ? X$U"Nx:KԦ6u|k 'A& UnMz2\ 𣆟hIb4 =p=bloRSW4PGo* Z~ C<ӽ0<Uy ~B켈j%J.XhxWkŢOUaX|5V9t^EE)j"O(Lܽuêۍc~+ϗ& m7ޞ*>(v1d8 <ң\VUzk˪zYG*n&SPA[UL߽1ЎDGBũ6' dDmܼOG"8iA-.G.T]%ҀJ)s"ּ#~KTU)&=PI[tE!E#63]}e I]y%Mp^Gx|\ <%[G݌~!xc:6;H^-)>TdSWl$`=(\ PoR\Pao G:úE6Ҝ? 5;SWP-,⩓ X%m+γq}T xD {8~P"63gC 7Q5Ј4 /F[  8nLg!Z_1w1\PR~H퓋?coa7^Et`RPLb2> ]|h K|yʥʞٍurKfU@wO=q!#)Gl "jĶ5MN|+CS@U#PzM KL*bĕzx$e,?=$?aI)~:{JT{ș8hZaJDQIQ$H\Ib RH"x >\Br 5?p`17h&Sz[|wE*θ\Xn+DjYr C'# P]u lE1Muq2:3#:A=Ks)}HН׊ v4'cjAt%A1 \h'ǧ dLSFJѢ-ggt4yiUyV xTT44d@M9%Shi,Aڌ =zn_eLe@=loU Z5Oǭ_&x ?S֭0: ]dR93qhw7tV}F-[C?# ah!;]D=EǦ/^8z(ɸi{GDuء 'BoHJgLhXig?"DPT )")>$[G0z{[c}/R>Ip)c c3%_ W\Pϸ( --gM=o-}/Qԅ{nDܡЃ$- @HKS c~L)AB/V!2Sz >6o;KLtĈV}*67 XZ[Y REjǂx r3=9" "!,|ʅc^ \%`^s&";y;<)1BCe1!-o?]f{H !';ޑAա U¦@( s<_̹^WsI?% &оPqj8~G CUe8LG ,nw%l.̀.ܨ3g|g-;@qZ U٪B, fˢi37tnޑ'0]ת5ʾ}H PֽuUx#ID;'[-]6 'Bq{cQO\"#n Ǹ8IDzo +xS!"9r\IC^߉M$UùeՆ( O6/yLKѵa>:ݧ\+SmaCq.BPZL ou7~CNJ+[p `0NG]%Bu- m3BmyaXBg>4n{Sj>R8dg S2qP_X=C#_WdXrQF?^ [Dw}ˉl Щ!9-DW7c!c#Q7YjBV$|k/{a&UOWi2PdzX**톢$vwwʊff*:RU wI߻tfk@k|bE Scܻa>˫YyaFud@1DQǀBS_,bKs>ܕrͧBm\p^'tt w i-bZjyvv"77dZJj|߳Fdw7VߖM Q,{7.JwpǺ5_)Z" gj@y:?OsLL WmDo4;(!DkdU:_cI ]8pidBm;?4 AF1y,&d䢣Ju+=OW)d5u79}m"N,(TyI%?sa1ȯ+tk83}nPP+$=}m+H0b7$`Qztan5.v ۘHqJvp[_Ɗ̗F U|[ !;nhRT@LStAyYf;#+V} O4eќO K'= cy Pa{'ݽt1_O: ||>Bt%}99Pё1KeBL=e'˵߼֭" {&Q+PU7#ƌl@.X!/#k}zvztb55pbd僧؀!B&W,jiM drjNWlLVrG;(hx/2 }7u'K E~[N|eF]dn !jKNˣhLC/.U|q %RF6pP-CC n8,E[O祃 -wx[!cxMSxQ2cGm@aD 2t]U.h:rt@PVĺ0_4DI[:ݓ1_UY#jwr)5]zDD*,ET*܋@8RӋ"71s&zD(k˘' :hGLD3,˧{S;l`v<$?.&|)#ʟm=XEgմ'цBTapYsA^TaFfOӬ<؛+R0H=yU)a+ߚ,ݹοLM WJc$K$f%#PF62MBNgjXJє,SÐȎkr8s:U^s6 jٰjؾ5Jcz60%=0 QIDG撸&9eHNK5aZ9FZmk[bciyF3bt_+a.>"Zt:6i5о\5)pXKKPP"WnNԔzsd3EeH 5KH U+]ܡ->iZ-ohh|o;EͲ|G2赠Q sL}. +=OBu3MJFfG2Ql6Pu7PN8#lmGlj{]혾݉tYOxf |Xytv4 +BU8D&+{.7t⦰K1 VY7}5d GBԒ88PRf /9vtU #ybc&\<ʞrǽXƞg9pP D[_=?# LpR5ҹYS+u?"R olRZ(Q1} S sf#k6-"9-МpGU^Wټ!zޓO&&J,$~N-sR*MZ^o$ dJ8V^J&s,LQiR𰁡 Q,F՝ߘm?I_Ջ0EL!S TEdWbP3bAՌXϰ)r\_!nU&Q!W-$p8b:f-4*&YRn]dl&ɠ[V&)/^"3} ⦡XaIn0.MBm-;E *2l2_d$W+SlJ%[L9 #dxW/e:wnS6Sy!!A&܊16Yflw}Lp{8cӰ}7)Mꓕq,{*SOIAЛp;hٷsR3C~7#ssڝW+qDO(K,ض֤Q 8Oޟ{*@tw4a/@28] %?_9y .7I1U tw-n9&=ScԿ Ͽ5hC0fމz9P䭡>{cq'Sq Lli%SN-!TEHwaxGj~,ӯr\\Ĩ zPK^COFTCbz~eaLj{S!5;j_g <ć^)G5*cU85;gKU EJFjV;{'oӷ꾅J9~O—dn2R4s0 1D9KIȌɭgtqܡepJ:MiǑ އw@,7t?fj.y7 !42:=X3FɹgUnܚFn/-rv?FL|HBkd/B藿V:.#j˦yS=l_Ҍ\avU GB|,>1:DU'9y6Cr%%{kqT3'RcjK F~y4EN]ybY56Z,nL)Oֹn115p4-XP`Sb8_%CBu6+ٻ},n|sFDg +k%Haq_cڮ?愂$B}t>QdFrr鐍[%+H]X7^J|ȵQgFla9:/<Ҥ@&fűg8;bʈn1.e~vPťI0.( ߫(@Ciz6B>DnC_4oe_V_CeVK^(m}pwfB/A:r?j+ {!W\/z()5@pU0IIX5^PZ]24/W(n+5`P1)l)iSڶ;4p~YW|7"#M>z^X"5zzc}v&f g Ⱥa`6Ⱦxk'ƝqmIzB!KZl8#( %acO `~y[s7\ %cX๔6[wu`>AG^'k678L55E{ EŽո4/Þ>kK_] E: #Q2 Jg6ycaKN 6($cS=g-]WvZ?1|=G7|&bT؏H=ۓ_%!̑ng2J>cwZ &B̿w8}~􁩨0{$];u9X_'b,v@00wZ4>T̃lps;(Dr,N be)Y^~J@sBF=1B"w_U EOI#ƙ0ҧt5R~,j2ZXNƞLYM.i[!ͦJg5^:Vb&ݙk6%d2YEmG+ k v,|YKٙTERWeKGmyw I(ɫ.i*y2uvj)&ș!9_=u;1& ;L֌a2 UK.9)8pJT*4g?)ut -eDzE Ujf8<6 9ŋ+/HxK`B)9B^3ϧB@`3M޵l)XG=5|S+ k'576'Q=DpsDhhuˀso&b<{ Kfw/*//3-_ *n+.Ѓ3vO-#I R`>\6lZl_܁wE # ܉(/SQLdi.HFUGǽrc*j$͒/"IZI-bI矟 '&8+r1x2[Ī2gvWIFD^?K֎e.prf=N"b$bِWB{iь%z o+IgLWvxaÄBV p #98J5WpI("l)0^0U@ױSve!R u7UI|zgĠ9[ϐIfȓDkHW+* -ٗ]z>k]p]aev ES [x˄Lb_0&;鰴Oֆ. 1~haED/gm$̕ U'y$`y6Me7sǽ ˒}X]RA#w6RsQNk~lC_,|_Cg"ǻ%O^á 8cSekB4u<PojJكpÀÑOBN:ڿ.sKQ|HJqQRm"~QM;=xD[BXh5eFHKCďl'ܽ{^GWF=|?K x0 اD~tUeш=$m·ݬMbC) ~B&foWhD.I5%M5^}/~XFVUǬWd@R, O8zzm/uإ~\*wYl@ 'W*R~e@&1w٪ĴPRElg8$Mt!ǡm?ÊÊUT겺Nܪ z9jg%[Glº؝Y,Pv_Fp[)<ޮif`Iv{4_2 PFmQ?E)XVS U5ao˘ڭ`|'cop|(2$8H}H۶(n?֗^WsEY)]Ae"$t g7"n#N|{+q`e޳f9w^|8[^V !0^.x/pp9X W<< Ƨ/BDH'M$6 UEqr^"ofegRŘлD4G84.#JDUU}V-hA)˂w Fi ɻSmDϨZd/g,lH] N=6Q/+L/I.L rո"TeʭD㈮ŋ6[W؊7lzOb+$XrPz:L 6BgF ;M0pSlg{Qp53Ls^?'N,:ZB8(ϛ!XVnOYfHJ-+YFf~5z ډU7,"7Ejpr}ȸ=NOAAmNaBRC`7omҷΝHW)œ7Dpr/7M5 BVSyK d ׍Ѻ|I9^n[ *k* #l9GBIk8RwyY.%+>4,ƫ'2lm@}?_p x݋1-;d+ j#YsGټ䁑X;ֈGH(rd\F=Uh?Ed: qq*Pު/UYk *{s8ۮ-`8gZ~K/s0WBx7;HA-'ka}O3& G/(":^e*Oh(cJnQmUnD qw%4?"R3/˲L*4T$J{ލX봌 찅y o!1O1(BuJ-ˤ$lLMeֵdirH,f((z 6Ǻe0̑arW7, 4eYiV1$ă5NE'.e_nQM$Ix>[TѰ4yr~wKEyȒg2 ,\h"ARi?ٗ&J;#$l;)͊HIC.7#WOAj+C2rF ^c%q6@Iab4rK lqۆ*倅B+Mh>-Qİs+'R<&ދ!5[c0u?A'}LHsᶥOƎV]j0 3;7LEI6WL"8͐Sđ庈]R{JGAUǢ#(qRöVHpH5(H4=F=<:`'5 LLʂV|ʚSQX%3𔱋nӅj{@9EtpQ Oc`^8e'mӦP;QӤ%`IOX%"Er/E{"!Rt֧ӐlRzn/^ΒՒzc(w-Υmc.8L2݌o-FͅXX }mt#<ӇX1^ t#|`:24< G'T6QU(*IpJ40ՇQctmeFJ{*F]8,u=V_IQ'Tcq$eS{f fAǥ<"FS۝A7ɝj!4GZcq>GWApmJl gM$L5O n[*?M e|CFXZ" bƈdI"T7M_D%r!. HKM<s:5(g:z=W׉ZȐˌ,HBI[.ċΘm>?h8x >g>k$K|:_uJ0LۧUc2'ܤ0LUATG!-3'֪lH3Wm%?I;+:2Pãp M^p3@W cC;DZ5mˋ,I  IݳAsE^ 2b`9d00LLOP>X\)5L9xsko㉝m1^Ӌr5zeZ u3D]/\WTuNގ='~)`aQ]seA94)DI*PNA`N1 _-ӇY.Ѻ+~s)EJ_(y+O7<%}qG,0>;VV) 8|?FQmt\M,wP۷tc~:Dp$lHtj!iOGa@d(=yHBsǃ G\U.SRjd M{5Kq C'/2ivم.[V.NS^fi5]ѝc4E&XV̙9w@ieRoҝomj8Ff_,@:7$>K qFc}OIIbodntGѧAkM'!x Z5@0)#_蕷Kf#랑XZo `&UWd,;NK7LD-kqiǬB-L~I>xws΅wc14Isȹ %WӦVdR;[.jtR0َWh`}%q͹2d7fDqO$ P9t<.;p a3jBǫMU+(~'`Cdg/ lei"%v;IOKdZ(knɁ_)U/Z-ʜ֭CC3 &X4Ig q|׽<bww`oldۋuq4*R<)S7- m`P_،2QV%Ҁ-}vb`2.:ՉL)-:ewWw1`7/4 Rg1eP]@^6c}4V!'krFT9|Rt+u2M ? CH"Ղy N;yhu'#'T"FIG:eD'jIf 3ӯo MSY?+FB,W˺ɁM^.]NeK\qD`Kb+DDكfaKV,>/j+BcFVÏ+zE2`u G;=}r%$!dj3-,=ϻ9X\w7B5ɡʀe(rTZt FwrW(`&2X!x %=תɓu``ݥtqLyABO8=ȂHS5eC=#4BbjֲThCUW$iDV2 kkў g\Ujxݖ* %ь9Aʵ,r fOĮi1s^<9Y/y~+N!1 O6"]p4ϒQ~@yQGq"81JDq{IGR͞ESԗ AJe}(bL >ch\3sWk6H%GUt>}1gqTh @__]ci#-h=d1ӊ56z%:+Ė=`p ixb(g^%1eC}/뇄=+5 1MX\ A+O Nwj2؞-y\}!ЭȏO?nϴa.rR Dg,sαų;L#Bۙ2xY(Щ#'ҠXcnBUd7͔u}VZX)G(T}֗EY;H#LCx,WD6՜z0uW59)Qa2{0Hl.k$Z^" ;Wz{xk O@Ŝ_׏xuezߢpND#ۀs7D~?xAL;yx"(Ý5 "EyF-SINE7\b)c$G@],ggrtr|> ~Qr0uoB gXEfͺaOJKp_p cZOiz 臈RK" Cjmql1 ʎzJA}PATc1IEG/CI{U*Tٌ#2T] Y3\ G ,&EziExQ:K;|, τw9xh9ݒlzb^ljݧpU=Nx>XƺBh"2ij?H<-`Fگ/_7ȾvmyyTHnXP)  ; 4[bC@ښ#1ռ_޼ )铊ڕł 2 veRh3k5k)w9y մ2A!6dz̷CcҶy]GȪ #Xz5,.׾gWBvbdC DjBFAFǔa"H#AwCi9Kd9kgj[:PpdghKS2T|Pc:3+qf[@S€?Cv+Tߺ(ɐI @jEpxN.L, :[XI٦pj( ^$_6}8 ({)iq1b fҘ90_P18̉]߫:ꍏ's^mDV^s 2LPhѵhFJ-\du_̉1,\3t-gO7ˆgLL~q\괝Ku`.uEiPYzlV|J*3`B(I^_~W_W`ǝh>* t`$[i¦MMA^ꬮKbD\@u|=4޵ܗ8j;Iښa ^jYn l:z ^i ZJ<9gDAc-_^8#6<>5`U4Q(m7 Ea ! kָ˖N@CvoI]{ەr2\|g8oAt*8D'q3:.{H!ׁn $YSBEA_(4b5Ϗզk=ʮa04 71rr6ay|szCU!ni B+|ۍ>A+` DIȪ](8`dcf71YC fIJy7~G(eQbY®zXK6{,_] 1ex@O~[ݫݍ'PV*^NǸ#7 YAk6 1 #vm#KZzX:bؼayR_1WC{L-Nrg[^D Og#sᒃvͽ!CJQ4o4 )X"Q:Y,+tdsh. h~f(2:mpb~nQYxY8O t}(+j֊&ŐH¾,\c{Jbʓ4etJ-A2~f.I-W3b裵@ }Hh ݶ/}Byͨ/xG(ՙ:fqo_s%F7ؓݳB%S~0{jG+eZp[ezQ)|N& $tdw s3Uzr7$:J6BYK$OBRA-E~\%5 R74G [9Dw$~=ՅU{X0BoֻxK|ae#fo ҖLڤRV0L{ɤ8]J8aI:[e0(<q·:ăME&=oqM/gW{t1 ? 55Jt]ODH XEZ;FB8 F IZ `N~^-xr|8ZzLg+W?C`WSw$? ymѓ*soN0`g_= ?we>*{Bs_5 Saƃ]C\ވlr _$́3 xChR, '>!ʬ9XyWS dCE,c)fwnU!i!kX3LG__ |#,, rEn91(zk<ߡp'B:\kVga6!_OCX(ܱ,C.pšk3}DQ3ȿkI7<&j(MO/^3JN<9>Sy"tv?]$՗OPaE~pbd} k`  eDxfֺBNb',}s»Uީf+%,n0{\[i%8*3j/´*)Ѯj!_"ϯeE v- {f;y`.R#ٹeǴ_ٰHJ_LoZ厣3kgۄ[˘^(b XzC`֤Jy:<9a}&4Fz:$HzϩXg=Q F 1N9|qLɹxg,ƹt,o"MWg| w^r_#hEsk.VO_?WQO&b(E@^6[owX 4;-L9uཝ\Bêʡ H \W}%'m@YeIY"GdPqpRT "?Ɩ.hgZ؎Cִx,XܡLH΃"|M" Da0t:1GT3ܘ T2Cj (YcтR ?Oԉ;[v|$ b 4(ȅq"G_ )T}ŝ 1ifቨhs3N{n=%%a!GgEپaS!<طE[I$-ҋ| pGw*ͨ8zeΨRJ;d*Ϋi=HnбPr._%-^<BQ0p|nWjPlHOi5CF,="R } լ26jaH=z3=aܘV?>0m !?^e"~𘭯$/Mn55W8uV3X$䦤LQuًn Jeq oI?H-j 8ֽY'*B(o7>Ɍ*"QC a2G\Nn`g|iVM3~%Kc1eϙe:]F`CSu]!ղ~[P俨iʪU2B5lKE:O Orp#> ѩJX8ۂO8~<ްb^3mrf,ޠ$i4`63D).24 _Je|ؐ[K;3$3.U؁ՊpH qf_Iݐ2+Bм*x٤L5M}UKA "1Yy=qJ8?O^t4o} T| ,' R!+^=h;/5ePNM {-jNM- =%Df.oS2% (Vb.O~ u}yv_1CVqXx4~OX 1K7lF05{3YMm: }!* L6 ,2Qp-"ک Q mIDk  #LԫU|w~i ` p qX"Rբ tq\Tp^VI\eKlPxg`N02p}^b59 q\T*pyQumn>x#Rn1H3;qN(̧W4-ƦY8~uRRi4t*3\**hzWS&5@qkw[#me |:$ۈ{fA; #?0H1\ /<Y;%BC/@E6Eʛ{ؑ,$HX:.꜁#D!ϯg`Jrk OV5oA-Ls*G"b9BgBƟ5)J֢Z5yKB$X)HCQiz{:5z<J-tBsrBt+ڤtůxq:vvF427Dn}pId.&8y;∊(atwtK2DU<##)h̉S7 V:ߡrذCJJDrp3?HZ|3.Ŋ8L_Krl"Jl$`.jE Ӕde!M@&-%v=-۰ 5<"Obֲ2])HM:)jȷfrCiRzZJ,1z銟 S g-;99I!Și} |{48l6/<=H,&yy-CAJH:Ĕt\pCʐ/0ի~\#y>VT\簜ގFQ#B֢6U䲑mtVΚgbd'SZѬͧ#88:U9˅j2c$oo?¡+pZK/t8ZPD'˵ZZ8ƟHԏBѸ4&9UmJJf7Pvh@OEV6)0fgPAOHn$Rpr`EႲqQ+ (ۈ{ÇPB X'TCTjdPDa[\4'R%Jxq PEb^4ߢ Q5d [JdйpUf;+7ijWgR)zL '&a+58rIM4Дd;܆ݗ^[^>ƸwAP)u?V鋤6uJԭcd+3ua|7ӛ梧>“4//wv-?jp 21` HTWDXewFw̵:iG@:er㮩r_qXɸȠ dz2V%+vxV}uq?~L0 TGMt q րgӜzld5sJL(p+# / QR |wNwqߘAEus] $1=T[כּQ uo]3) <$=/AF-w$ l 1?u 5eOZ+FѤd׾t|[*?Pg#TV8!&G7p%Q(5ISmZ!3؆/fw8{r;J@<>ǥu|VJCx_jo@B%8>O?9QpWj aZqy磕s6T ̱ etp,MwTw~ޜ$>L NϯgGׇRФ$VaeC)_#Ϯ-B3DjgTHoE8rtJ*Ǒ[ފ]Q~aYIx-6O63Ju&b5^ F0z)4Mg`ti1hi bNlJU6q42Hw~quRw/̿ct >Jcڛu1[AoK)j2#&p߂t"fa/"`_[z#DFD6Š$Q;dy=HjB}"ݙ[}a; 8e~ȏ?q;)%-/ARHG^oËO9|Nsa#86in?t#U>4؊/( /K抗S[yyu(DP ڂb_s5QX~P}FsMҤDW;f8ǙQtV(c tc`)RGKlDl 3A=q7ucs"Ư5oKoR92-8XiFP: ‰,N іy ul3PJe4,[({OlBl;n"~w\kahS?!bھא1Lh:oTP% iʙ`R~޿4y?Ȥ[ӽW:^ 4!ʝC);$zg~v%S?@5vHܡ al*>ɗ9ś8b-y tkc;gߑKIQ\puy <ON|E EΛ y㇧l)ō]AGJ{o`^VB_;!?Rq"'CH:gS v0T%a9u-4qΤ .xu_5V4\qg)A'\Y/iNTJ`Y<}Qߔ!U;sfgػL1@>iپVv֢wOR)MOF +ZG/Co}i'1 K-巃I9%$q'^m^0fMp^ohO%sXT% *.aÄղkUB߼l>k}"{>JGg{z!Ex[+J0lȼ2]'n#(R &;u|h%c!#8޺aX/\LdDnz3J6j(,"h:5Ma!5(-Mz2 ܬ^f,7įfaA'f@}]q4cK_uN#y'>Lw(]mrFu9)_)-4yEpa%ΈyxFa <ǣkipՕ h,\/ZS_UJ[9D1 7@kx5k]35 CL1e2^󞀴^yFlj Ϋ}"bRGks#M?lK#FA=^RqRF$[UEK`BI&(+PJk23e -ؚ4Syr oOΤ-`ªg{˜۷(U1JOT1U>7z:y0$~|{=EA/YdC"jcL=AߝIcEkAlFʑ踢7aAB`ίG/œ|I,TnXy1`]bx .)G^e"M`#}}KI.|hX ?Sj.y}ZWolF.=h+ [^nvR{ pQt"jZw[*0[d#d+-kMCKТv8^m7RJtt w}~ĊV &x?oI¹g['Yr!E5%&}*J Q o >=R6vq`%BŨ"*s'Bv͉r:;cZ{67ܽUH_C/bR9M z$ tb-E%ZbPmI;$srpdx?[c^y0@32hjKbq&ZhR+A-҃iGXK$ܑB?ӓt$V|̷iGoNωz!n# ىsw52yri^a 䀴p&XˤLj":%}z鲰}1ޫZٻPè ;6X7⨌fyɑ3lXX˼郻1;wAW̤Bg~& WV8@MQCf#{ \5u}\67e@/{G|t+-2x:ЗÉn7Ed(d["o?-rӻ%[Ok?ZNS&f;!e%Tѷ'W,r5KOOQy(vFig43Sr[=I" VňUյ׼Y^5L _Q1+*N%-iGB&6~A0C 8~ȿ&C"%pWB];nH H` !3no2:2V0 J3 7$_HɈA#b}/FފMr[ [<)ZX:8R1AWV[SWcfߑDIz~UFUlJ`6z4=E2,J/7j5DĀ_6xvl>y[@-mtwqJvrWkkw󸯾 qL0@,_GdȿT rtMg(XsX1 w+D~:?myyE5H ׅmM慨7,is'! \=8'RoMp{PL >+D/`Lȴ7-c.# p`7@* 5ٮ#]zdX'׸WWz ׈b#=Eފ|(h?p76p #Qܦj=+sf@ %WŽwID9HRpK(5[BpxY(!4L(yZs~ MaN[۷o~<2]o|GT0N χ,H߳m]3!2+|IAhr 2s>'YCs]3<B pqуe ᬬWvaL"&5-I&ylۣ+bI5&/K}6ۘ<ueY[JS >Cc|=1J ]ΎE s6F fJv NV4'jN&U4OzUɚ&=p)VvN@ݸvghLK ϤZ=>> \j\[j{ϫn>a~M;m5vVY.q;CqRg~5@"MtM;$[8nͱHZ֩{ڣ]67į7Ɛ2A2ҿ㸛$v:SdXv;#|n["~tΏ  E-dCFixL? 嵨E;"NBACU8|;R_I7ڡq@"sfw;: N=ְXӳshe/{3)t ~0U[-҄#\ m I\0Gs"id%>>^Րa@L^V{o= M9iOg-0PoLd& [cս}w ^\,6ާb3/ʓ/bI@#ӿ[j8[S8\t0زXK:nZ?fi7_r r Zbu^r.IηǫnQw7JCZQ5٣mp>sWC EDJ<!r9J+c<?aōw M0I/;M\zz_o4o͓[[7QyElpV7N "̡VA;.9Jp S^_!AB,݊f S_\1)386ҳ R%?w((Ys 4øܺ(ox8E(L3:16"-v ~WU28KLPO e #vvrQ:"7^zA(2et (Bصp25̰{pIm l)y;сoA.}iC̢w1Ż9f"7+Oa_TeY] - i[>b0Ț3'j?Re$j&x=($ɖZ@.TIGXm̓]%԰h0 Qs,цfxjk[0 -#A|[?bd`ep5 Å;xh\^dL$zM(;SbخА qŀ}[0"r%^aX̨=;ǝUrQ NvR6.v\yu[w;݄]` T0QQ%YW.1(NGZqB<ʑ?c魯qdpـcR}Gi 3JJXE-^2=E?C>{56XŕjY7_mQblm>4iJ=JRrԎ&Y&7EML162&o&LOUZNBtF%ίU׿D֟#dxe50Ge6f]Ɂ-Blc&EL*l?%5e64t k|$ϷVe' Ϗ@ + 04lAW|&0eN5[yVsZ3xωf춪*:g`oOMEԁqN0э6Yx E+֖sȿGEԝܹbtd;/J9>D{9p U7e]/ 4IEHo-VT2]p ﰗV7}tXJ::LAJ sYV,MT".隬/BF#I*pSv_ן}x!q=06 !:z9bnrVdMQQuP6Oxƨ5-cPar׵p&8|ŔLDʛe:h9Ĭjm{.Փ:5vq:WO|c~ggM j<.9i}ٖ2 ?sPzP,%v$Qk?eQ>F7tO2?SfLҟ$%aöB gs;+w`]/^U:ϯ=> 恸WC?@<#dAz%cxPzT.ywObBg!P R$0Q%@Z⪻Y2˕b1򚂿Nw{dhRzq]&7QH³\葄eYD 4кy4m|2! X K'#=Fp.(W;1nb 6S^6Eŷ=~m?QC4C|tHRB k<[ ;Ũ!p㿠cq( le5J}3l]6)([eY`j9e=t4Ɏ{[`y`Mw2^$V"a1EЍQ0,l ~]:Y}MJ3zW8.2ci}seÌ!lӉ7쮠m&en;2Xdʾ"?kR,57֠7p/I0j6ۻ~Yir+6{0?F_D[֠!Ey/~L#95yfmː5xq}'}hzDיt731Vu|9ᆥYG&:Ls\Am^ÃX' bյ&\:Jʁ0O,7Õy^ƙwOKԤc*5H$a}nh;y6qr8Ctkڋ3XԈK=aPv̬ͮޙ +Edodqͥ%{'+\VEpyEkpBl]tJYIUtPGvB~zE ޜ"Z嚶+PBŲ# ,miߗdXߓRJd)G\ cvuG(W6:̦ Td؂У)&p͎4PUf=bx{\N n{ h]`*wްUaS鿀KdE;Cf HBe ;VI{jiyلLbkd>'lj[-3хJ\M(Fj.~IPZHʰ kPnH5Gg%VFRoe|IK1$->p\ R=JVsH#s2?LcMMjŜQ-m#GFpCERr*wMj(ABJ]z3 l6sj-w#lDn$?9كEDYDVLuGe.bmCmȲ;b9u=a;v`Kbb6$.S88'jɂLebC&(:ESyifyҁ_njRH!Ūw>Z՛OdL`)>-'31^} i>[?O7UlF'pCKG]d!RэEX\Kh0[eXt.cuNBè3Y]i"U2Yv[DED=?#pQrj7^~[7[냰^K=746';b:m\xza 3O\XpN_Sek [/3P۰1@Qadeql~ba竬/qcuRk'0.|Q2`? +%б;@X I8 GG ΡFk枇<86(y2dd8Z>5]Вres}N #DWzK-G %8E pp8L+%(&<1H?ֽ9uwZ[I* V{k<VC3_c U/URMI ꡈi+;0w#uŰ{=o86|IZ>Y7]%έ\q6άȅΖ>!R2dT.̪yv?wRy 03-՛B؅Lӷ"ReWQZ"Jsu;+ ^:ʒ P zW2s?/pV K+O@%5.-3ݹ3uX*v:{n!ţણb)ZI!e~+K{kRܼ,'BHTπ4 R"LjȻ?:_ dM4eSSqLs6hHsٰUk7:~OI)_J'& fYp#T}dT?:L%\At;H~mnSU.f(/!(/ f@?-pp!xF9; u$sE^,/G[e-R{(lܲt|*fU_z{K_DYb D[IqN5hO\?sW|۾QL: Oyn7Wonu~ <[82j]y'(dXjǧi˰ )~Ft=υr? Z/#.KBoY`tF=s5~)L _IZ d(*;m WCjZeBOTH@rBĻqsr ..QIG-ouS>LK'3>(FCʅޕMI.d/Ǹh\!u(i9#Uwc\qD'k = Iȏ-,7* _?ntFQYUY`^2.h  UcڑlDkI8#ư3qS:Mo|,+5X 0Yjw#yfϟ+.`)gec-⁌E*s[hKAmL o_8nn׌ɦ Xp6ՑT+8 -RVۦ=e`&'qNuJs YsU*^xS9R2}jPXsDD'kT;;;pddujNCAaa?*rzhL9'_r19(,>Xq{ŕ$ub ,~W.(" 1oLpg[y Ȋ+ bsu̕0f"; icO iSQK:5e 㦖!㩍sēJwၹ!,&@i"9k|5(IĻu4,يx>Ln+ـ;izW.4Kcu+`C_+ rq&C~+A"/ꕉ|G dl7`^e^ow̬pݑr ^C"63cMlYt[uZ@}n3a#|&iFǞ <>*HUØ$?H(eBU ϳ 8kCk'}\ 2AK;oAďK0/*3usrI;~CPDEGY2t7 p`ݬ&XHR7z/C9ΊA.-eR&OaMW{^0<7Lϴ)3l4~>ȰRDlB5),?_Eߥ0w"Yœ-Mj8jT5gt qf X!w5Nƕn 2t"wcXvLZo eW6| Naz(YQi\EGu: MM춰dI4Q^GBU1r9!zo^Ғ7Q]{Gh y1&[NJꪮӔWfjK$L&Wo u2\h!/.yۧr3ZtԭEU(qmbPn0A^*)lO:Yd%rɚO$Q*7*r+vrـ"`jF̠3 ^V~Rj03MDRlaGy@%`mVy08%lZcV1;o8{+cﲄ5(,{3OC7OC KT.`3]#{.J`"e`09Lޖ\"Em2^6 3w?4,Q0p3AeX 6n.?xaxݚMPٖЕbpzgXSv'0`EV3D]G~94(ir,cHyB2Vryh(\V8'޹6x,oW~!ew`@k#`|bo@3 f~ NUV5EcQL̵w0r]=dXi(2=j!S ,WbPϒhaa6Xʑ+^ɑ ,Æ|PF#@-3Zܞj87Y) OHrw,NIg,ִr1#Sg"bHj H"dxOUA˨I348ϑt k-an-hھ~ں ]{x5Q~3{a!S@`+k^l]K*zRMSwFGIayLPTs؆ e؈HJ)R5uk:C\WFd4x356 z [Z"Uȗ䰼=g϶ $a0@te9ܬٔN^[]- ݫX +Ҵ-x:+F )njh q1ٶKL&OIAJUxG@Z8=R|cP.:Ge ؗ(C;EqrMT#Nĉ,IlP23d=fWF}ء2I,prTL; ոINi-"pgZt@c/̈u:ZԖlH @Q \ #N*5R,>쭟VuC{}MV[8(DrF,ŸϝmyT궪E}f~q5CxDDTwXv9IMcvү([\z;ݱ hrIu2.0]osqCSˠn\~zluqʜwBR][l5hq썽8n=^qpc[G glmLkDjyMC--nBC/[bV`A8)/'9ӣǍ{d cT|h~%0g=)bv~ Fv^ PY9Kj~E5߼bi"/LBpwِN>G-'$A]ZO2@ , "*Z_,'NP쇯ɘmm M{׃G.-k*u<2)fnF *_o;ۺN&kMƊ*y*+ e=4=! HyEvC-R'(=uEUOh+wicZHi:Z}XұG"~0:G2byy j ?>~8O;4rjY o'#>^J(7Wdk@$L'$&K[#{ܭnCI{wV` 4G7n&?z[Jsᵕ_P R-} s쪶z" kVɗMz:V:Y 坒ntUޘO&}W\C3}wV!rxl6_HD}oTpcmZ% Th+$ëod}grxui^89;W лe)i˿H8u Rwn#%cjI4vRҒ;Zk~ ǣ2:wdaBd.'B'pX%ǦՈ /G< Ii@x=z|4kA&|E,rvExM'M9#Z9Wm}m"$k$jG0چQ/93:R@Kɶ'od eQu!{IQIC T]s m$OkMѓX}"&<} O} փ7Գaz~[}XxSV]wT[ϬAq;T+򯇯9RU*[;ڢzT7{]N,[Djb^z'Tgw(0_%a np(lUڨѯd1~Nt;7blֺr !q\0WC@~ æ"e䓖Sxnr_dS AtmY^r9+_1% z^ڶx*fSe/o翏"Vz6~\qBjh46nb]pq rl1>a Tt\XLXB>nHXF8fԹ+)2ɑi@DہF\G(,r(lASi0n,%8L I6M@~,P i5a,q9+ ˆ+(O{Vm nWVc°hJy> Xg|,&u``g(7~ZE-]kIyZS(PPpG—"zvI4N`b(ΰ#-M[b-6'︛0]YH2s* @DmȋC.eP+q18CCm6(X|`I ``r wL^i|xKB2L>͔ab&Xxrll'[ܓ <7ܭV` IUL.; Awd`KkJ Wy62H.* ftX*):Wx8zm*t ̗sG~ |PoaRIWU^Ae `5+=, ԴSToؕIW;6֓#:u&X;U.4.晒m٥}<mrZfc$ں{Q]4^qu8}T7; #Jy+Up&nSy?K! F™`A^Հ$;PFn!I 4H|i.V@\6]_UnΒM8^ tVJ51AЫϑ [Gq &LF̒NzD9 _xd~@ 4u<(mTI"x *QJػW̩|@ N'7B;eSi1ziXDM̝玖M͠ ?{OKhdnvO8jŽx^h)hZ"3ԕnј+cwBHK,kɱxDE$߱NAG$ [OlRU\&EAZCnohẃ)!`|<ֶ,c2Ti="LZX<} u'o|νGT="P)}kL3]nq2t&7);3X9\`Í̈()! ìCԴe߼ߍ]ɁErGO{` *_'vH2Dzۮ;n >PԈXHO0?H;4)h"b(nىHB=l;.&Ӌ:'G P1:z2K&d/pr(nMp-׏]Os_S7pq` }!! {4]U.]/BӸx}OzlD[%z|?,o b1I Ȅ{Kٜw:ņ-J/M<"O?~egcͫffj)0NOaW4"^o˜_Ӥua:̞Xu[yKPBd#J1A'`@`..SSWʑxGU*Q<} >C  /Ko8A2%{9@RMÁ8çYI*uzQ^ק(X(i#cD[CPOo۸lԾ傕1kmtt/c[ $c5rCFiRB(=?mY;:E zdNzZWߎwJC|.yDDaH4/O ͙3B&򠩚 W{mӠuΣgЅx&!C54E;Mi$7ca"$dnΔ#^ YٓcbI(r`0k /@ ) asX[}BmPdi ׼>RfLwH"n\$SJi…B#Q5MYu(E%yjzWUrs;ťU|ߞ?[7f47kH yHfݮKoR VEo[MN h)g O#_80 CL5ʃuV{sq;,lN=<}kaPjC!|_Dx*Zk:GH_߁+T߷`K^; %5bFєSDZNs~kR>p. i^~Ε׈۰uOyQ 0k(3Gpb5ŽXg6cK,ybI|[3cTE@gRq{8 {c _G6.(Kf37G$+`q6ew <VI{t ;G(?PcˮeGZLJBbs PC k y;Coro [b]o WƩ(պ *#szYF @76]_{Mf$hۜ\u>pHCCT`ECȼI2t1\Z~5VȴPm#A;^L`X )rF;"Ãm4=y>=ޭBn=ͺ?XHݺ&:F|Ҡb@d2s#4e%#j45ԺZZb?ȉ{[mPYH૓!kGH` ["Bbwّ߰*yq?1\ܑdX[Y+3Kbfʮ}f;[9@X ŝ?el) Fr&"?3zniCQUR4`?/ё3tEV0x(7=-.PJ:QJ3dX6 Fǽҵs^E}P.oRq2I!UUuQ]T%L-H#:/B_/X9}ñZx9;}Ny^ސ8!ˤen`/:|ZXyȶA6;MVE ڙQ#5~ꋗv#V^x˘:),DyEI+n*I&9r}_\5Ȭ@Ġ:9=T݁?.| x΋?,%MVǕpj/Iͺm ,N4Rq444ӧIA#E1lOfB&&4g!b/gril5Z!n4[E }"y7 ^* e+( &oQR5NfA/" 8!\0&ea1 5Wm `T]nZ>(' TAϦ/LutcF;PgP_;lǛY0z BMc|U0c,uH|K`t$uJ|RO^Z, UM=Qko׭s Zidn家1_iE&XmQ~ْq&:FVgqDZc¼:éD/5zӟjp֔>T&>\E6d ζs߆>,XH>wi'$qg3׼ [ 9mpң+zz%lD:@S;3N6@[L/u?CMk˜C4\quHEH/ibtm p{cpH}PHc1b|Pn$emp淂m3;Nih'WDZjpT*40#HYyx!bAiQ.>:Оn^䟊-E69 }< e]#_\>hwEV{iS t{jH,zeW;^ir{m#?@<4fk3D=f ?jV؉ R+=±iř%LSN F-z\ZƱKOP1*r P9 8j;[X` sdp_[O."J{Fh+8&Z`I1,֋nUϱ]xu`aV;PDg7o#Wv* "~`nd#ew@*ݐi;y_(٭H^g{UbkqvXp e`*x鶖]Z7 "qyky9G(c)k@y&`;]T5xS_exND7L]QOJLmۤBKr#dsʤ FcWa3}1MM5Tt.'KգꍩwJwbz˔?"h/ ?4>)wXj;ÇxJ>,K:4mbPװ{vX% V' $ s;>Xϱy%ֱLk ֬UWSmTnͦ<T mO H(Ŵ4!>U˄ƥͽt)J'} iI3w!})d[a6 ONkFa[u< +1r' CޗI I̡ Q^ь]SroVa73S@yhXrR8Nli'zt+a_p=n@-ÐgZTKɒ~,}BwUvIy:!PkA^SqLJa$>ata扯b2I":@6PGU)+Vp%.a{,?r%"˴Ts~VWZw-^4QNH\;A'f(0?\vLtgE8V5OI%Pg_^OaG/N\mhIC`} )/qᏚ|ˤ Z3q4wˆql[fPFxSD`{gϙ8=XQ @ojZ)iȵ~Ο[ThX(XD:eKz`V!z/2:%0]2TcC)&pӛC*&o]~vj S &LL, y 9a0>)Ga.\]b@C^ mߤsf"iz5 Mt).xڹB`D oGpe߯8g=jl͐PqD6Ķ-imNeDyo9Hk.J‚7YҕW Ggr: Ǝ yYA h ~4Fc)-<ÛQ^q\%3$pɭ}i~ǥ%'13ct-YK\B)6d4 | ~g=91_u H\Q[ف0TcQ VcǸ7 zκEڕmǝ}Ю})8@eVQO1x-o9#1F}t$a3S|:nJnnk|>hlί*0DnlLFrC6C|~94N:\o_hL$ݽ&/Tt??oh1mLq^;,0f\u;v~ʙ)2|9㼶9 2 _)߭H.T?(SjL{Vm 'g"UilKUklb47I)PZJ8xi ek?&HlZD&/#^}:x5j^D#\RƗ% 5ET1cmnMDi_pRت`t_KLFTZ]zTCʱMB.J hg`E83[SE+ iq95ٴEDOU=åUHݐg?v΄zn~zt-܊!#qGZCh#odL"݇ pk"rI2  18OҮÀฆH/ օ,'Pz ݙ 1P)z#A㱓 BNai%4nM>5%; =:gGU6oiv GF0 nv{e)Yea87B'z !h5R?c&XsNA>9o)'jVRj8SrX:P E s^=o&+$ eI4g>M헳KEON9#_ f:MhVuoHr\pXZ?cEձ6"D;@E{|YYCP Va0$|r( ~#(>ğ_oCBXD6NɵcqF1;Dhu%%wδŸ*%32\nza Rиғ%Ul8_GS>>u.ܮ*[w=$LO\Hi9' c5"ҬuPh)P>Kޜ_M2'%'Tcq{WbtF4 ,`X3:W-+C0/$無Ǒ~zyOj#UQ=V(>ܷqP-TX_5`\!B>mY UI3ui#< Ycv`~LR{Іf~ENyH.jy75"q)` <Н. & Ix K>i}gl^ӨW!YIs45G+qIUrށu;>+R,}sH[v*6 nf=}]GZZ!5=xB %)IcٮJ,iNmzrL6HC-lm5"K$  r1D4tKpn!(J^71zF/M ydǬQ!F{,nOC|?lKt!um@9ZyM+C=};̤e]-uӍht֨1UON&,+oa8O#kN) ț3If"$C]bRIDnP Ěj@١bswvi=jJgDXѼћ8@K +L`1I難}eidwb2$|jSQfr4q]w3HK"@3> , l$A8lppuJ͞I6i/3 ?!H.iAxs<1eba]`Cj*=lߔ]&;EdQjˇ5_adμW  -L8f.)UPYS=ʅ2APzP T!*T08A~6IJ FB\] :Qk=7@y\Jo1nM=e,}S#U sMiySy@] 螨G nفt6P=Ƣ y|;vdsc)Cʷ /ցQ])@Y:si x/fxGqkJw Y*R6rxQO~ 3U35J<"!=6uש&'ەAQ)L_`DuxL'J@;.-8n&Gn8(.I 1 )I7չtFAO467Pv3GkHUE;HD@DL}dTeѢdžuL9= 8#V< nwW s빍sZB'n o:\ `B<yA 黔x61I5i^N{qn& 8QWO Bc|qE=/zY=2|p&iVB'ځKu{ %@Zu N,`xQݒx!0\G5ꆯo5ȑx pLx91xj4䃪ՖB`\֭tuL'=e+_|lղ?k太0ɧd9TW*YGW.#4Gt,UHW+s #KWjHjCܲX2+]q1&y 05 ?H.FѦpT2F_nߢFQ_pN8LH)eA̳]27g%L[wDeFwz}mA \5-FmU,䖔0ykaة&/RP]jU$odbok}Qb`^ ﲫ].LfUqB{*OhR_ٰ7LRBv{XgRt2pTN5C 6n T=*k' ⊮ _<8#xa(\{>m^ ˕{\p=两vs#Jɹp9z%ʏ2B),a ~J*@E7}y?62KSqQCfz*znkr?JR D\W{qDm6XNli#BDug|'Ud.} `]9砏G)[`^{P>BEs{vɛ± ӽMcug0;a1g[$Ծdq9T\A=Yf9 7+%IG9\ Pojm&䒣J+|qԽx3 );ii{|-N0FjY1Ńx}n89t]{oӡfkd^6XΓbY-9J^L,|M=֖'n\4 #[X@KeP7w5;xkWjh+y,?fh!5zW5RPACsb?u9PR*V}m?M =pyԂ}v ݿ 5 iLqjE;D,mb-xYo~p.*/hh%z(x3bGv& T }wF+'kUYju{{ީ+&{l5FYWඕdj&.x"AN- U6u^բsn |gwN䵙'h;-G'@Җ~ gMurGF-AQ((-?Kz]G:><=v ir, c0UGvPk:KwMg25hWձOH"v @ޖ3Cw΢p-ZH'2^*#H?ѷ 8`ʺ)lǑ6϶`-YCk zeqkl_Ȇ̘(<&47͂X(1Bu1 3V 4 pdے.T|sOv, < Cgp_?9W߰˙cd ,.C!p]cdx*[+Kɍ9 jڑf W_M(O)ɭ [7K:oLjRz0@ԴVp},H zM_]9hi(¯G}8Ż||ؑV1:gj>q&8rZ~lR)N) X~; JhBJiX6 6j|(ϲ 5DOO\nަ'O+>+  ,/bO%㟿oj2G[W9['g kz (o~"'/?H>d'+*iTf~6" C{mjzIgOmHt=`0+5r`E,\o -'[5E`gƟ/0Z4+ImTlޗ N}20m;;y7~N-;s0--Tlm0 )_:ӎį4/B(5[ ˋMq=³BR#{ tΊ`G\ @}^QGG"6 @om[fѽ#o2XDFq$n!ߑai w'd/m6+M4Q.P*B5 4=4Aہ RWX{G<~v;%RCYo]lkQ16y,(, !hR:<}bz32-HE߇SZͳA赭lF ,HnosԿ g2&%WvUJ6 e&̐Y\/G[-I݌a},\ +Xֺ u ey¤5- J+CK4=rϝ$>ȵ`tTT6ew+ę"eէ4UFaêS+Y)@;#E@bŴ%66*PG2b9[صb㮫=ITˊhX7GiTʱ;(4wU3)z`LS/b5_I$5$40e%'R U% "T8 Ƴ=dK %ۊ;?M.σ)#p"])C:\T4։LAGgЫ6-k_9^GӋ1pɮWq-3rk3/cAC/5s8.쫺:cBZEYaVq""+fb4'ޜ ,_IakGNn{BB r(3*Who NP/Rco!Q+Fq!QBcQIİZޙ9ͣ 4Kzx/?[{8#2Nl=mXyי<I P*e+G}alLueB41dF7ew6+ߤa6Y3u(ENx:K\b;hbq f xn;`z=Y2)pϬ; K/?h>RK(\ysLUO Yk%[GmhPicz H-*UȁGJh^0)lk,|Σ_8 39WJI4kwq ܧ:A1J{wQȓ'59J7 %c>IzP)X"b%VLdž:993̿4[bogK$`GVm=kY샾GteLe[ 0$VjI:jxY %,TEp4c"A!?HƌC^OU-;kBu#6glEVaROnë3yZY+LYĠ A 9H2l㌺hv e萬Ľi"O%RT|Hg&{Zy}GbDChplJ햔9 yr Mw1\W `j~Ytw\ 8lԺ5aK7ymضx-؀nس+s"4yŎWH!ӆ7R=ʈ6ІIopžn,IS.9>٧+'vF.lorxQ匡XI0PQ/dfcJRF}-XL}d_o,LrX($@Ӫ7_$y.XkY Uشm88 ޚ]5klL0JDJt [&[->F(SSmK.osܱ.rlR}T`!}(_",'۾v:f:"KQHV`Ҁu,|lu+u2\؋ba%1O;w@wy[6D̍0_BY:lHX #Q0'z`^(wښRլ?Kq2d! eRh|g Ac^l>sYY$6ۿ%v{-簹IUdqeZ9.\12;v,H_1ͤUc@r`U#Ԙ3}he·X}ܪxeۻI5}Pp-ULALQGE&+"lP^q@&7Ï~ڲ;nmIy{AP~ Շ}"N2 /^3y ( 6~Fn\IO!omе3ocV{6R0@W{@;- IӄaE$+_\ajev]U% g\AR%.)EF`u=43yjZB3*nɅ 珈'gV2gٻjW>$*;ßUZּPZ҂y"P|,j{qX.02xA>jI b`/ l<4W)ZrΤBӅGuJUq20qG>0DsRNVM=&`M;$C,^QÛwҡ@1hT]z7Fb*Pxǰ<R8x^ pԩi>\6yfPGki}1$6L zlpG 5mP=p]n"-=YVUV[;0]'CMrJ[b)2r #zхjVHLkJ`MxH+JߖR_wDO,9[wSZBǂ˜-;CyԤfrusJ/}VƎygx'uM [KBmcQ͡=/ RDͶBuqq|"c IV`M28vD Bof=I6 Qb|;v"Q^@K$Than Jwy'+DtQ:IH;ñCt*pO> P`eM D_9zʕ*0uOL(#AT\IDnSaR/c{ BW _Q&Fn՝;LSKYT\N"u\ CXYp^>(&Y&sbJ*Vdd1%Hp11l7.&fHF~W#J~u@ov+UuYl,QU{7]+հ]CBXN6amIKj%!{"0?i/}>WZ Dm:0Js)ܭN.ȕEpUe;OB6hHc$=w~ӲLw 'P*hn&d-ܠGkfԴRbgx,mu48JNM7C HWqlOh\P=l(׫ygH'jav[?ko, v 7x5bLt街bP苙Uˉ~{IʼZڃW28R*k **cu_P3ı}9 Adɶ]X qPȺ Jf5D̞bfw^}QBǫMI6@]Dl($.bKN)#.r:$CdQk_UN4s[Tdy4Y gk: !HaP6.|\ùm>`Ϲ-f Q䀥]&mSq7>R_^onZ'O&F*JF_ms&c]fLU(U[h[2xso|Yq>x|y:q <`$fP\|ZŻ}^ӟlQIIaVSkByNӻ#.m&M4@0`?hL]bz *'M|oIT(O .FL?LO&,!rzkbHGGLTM~ءGDPE:Md)/UDƀ~VbYK xd29cLys 8[Ռ%y6rf/)V23GvɎvJ ZN2[x[r$InR'y= .[|ۗU] \귥-KwgIf mY(r@͢ ;MDfx9g'͋YfWWy5/$ۭrpbQ, ZRP}M<(ҕd3Au6E=4C0G+xFEPY=azLڼQ%Bd 2ir0ɠ #K,А"h 6iZe%͎(ђAk,Fm9JML"vq/"tB$z]\:m=`"+uX*?x˜{[Fx֣=jo΃aF;[KLQvLQ,kb"Y }43 QAqmB5 qi}<['ɘיYF{as n2 \"emwVEsq76N}H kLu\.G)/Qt0(沺CyrbaD^L< |]Z#-X3P.HO;M޶U-Wo\=wd4\1NәzZȖ.}!l0IF²nr%@sL#٣hpzH7&jWb`I~2,3j3;dXUj/7Wk]v<)M CesJ9O|㲨*MC?FW3 GM37ac_jz ~A>-"Zr`o;f43:6^+ħ49"$v䑻nFr)?3T /(P^bZuFj` >+a޿Ig梍XGee%-"82#|>7)T!A},PTR:SlǚU&seyL҆ ~gOH uߓ _$!X(4zP4w4_ʽQ'gl0ku:./Ic\1Yb-b ,b/njzw~DtRڨVſYek :/+PޭCY$/RX8~48YtŘeL쬉0# aMSu8'SPCu@"7 U|5Yu"_uR0&w$֔ Կ_=v0,ch|0=|.a=V|n!`aw| cN [O noxM&0Qx—lCV8p` fMKÍμ.//7~xD|Pu@澲g.FtNsna6?罒GnZXz*d:nI9e" snIf fxa//D6( r2]~s||Y?-oAgHU^}<r00%M ;;!Զ(agCTMlt?4E^X_m.P{)(=x16KьtKo԰ MJ U9J҄O;D0͋BԘw1/` %H`6B>P/dܰZKZl[k2s: [>aAFmXXALJvu ;k0 j+;"jt =js,SϭU,E}ݛڀ'>6^UfJ^"D"t+;8*,r2sX&|PV\3xnЀ˝U֥t8B-FAdf]w7Ah[Hp@S^x)GI⺃`r[?!&ٲAN5um0F{qYًgN|Ԑ`*n٥c/Q Ws@ [d7ң BCL1 3.E AÈzjvT͸4dݽrWPӃۉYgk e㖞* &Rɫwk0e~r Kll33JڸW<ªC.젭Ą2\]?%2 H=4MӃxXɃr7ʋm𴘙TK[NW# *ȘvI4jtHmqˆj}S廟k=n'7 rZј7g GԲr8EUePF]>1Sі@1u) YY,hvSbGf>e4HR9lqNwR8LMHHlc ڀR20T7YJD^) ao:` ae+$BzBJ5%´=u,^R9p|^v% dDUk" n"j~.$iވXj+ XQ(=KGKJۥ Şt*LF7r!f\\C#'55d)+"0[ҥ٩d_jp}xxZ~MhgȉʼJHu@fTh^D|HJ:;@kXs@X< I뾶aHÔLP\XX,`,):6{^r%u5lT*AY8B.>4c3IّH}~r G?[Cg~Lm$_xcINxyQbyX, s2Ȝ6 Q<.*r^IM|zvn} By ֚sWYH& M/ зp#n O7'mɾpK ?F3v۫(V.v fDoݜ_P4~[=(qz16&@y囐$Q 9' _3Zu"RuwĻߤu~ĖM5GҨ}ssi 2Hg#ogT'| 0|N.,l t=fQ~r_G"}x|Tati΋}P+M3$tX$ۏfżePذPqG>fKh0VM=o\%&$lIPFOg\$/U̦|$Zw5!CVbw@^[viʓY6waR +uqAG1Is;b&9.eeHi.fEa[Ex6]D0 gn|N̫s) |qFAν:5$0,gj\!,i#zy=5`BT^ǂSXD)%\%-r8d{̃n$(P^׻cgz;"ZZih8㩽]̈3׮mpe{AṁƔKէQKاr̲v%mT-_OIE DSfl Ty,j҉tT3aAz}o0whՂs1$L g4JP^\; u= 0O)πT9QC#Wd I?=3^d6%-kLk_ZЅpUkRc_zl3eGXPRwM㦪9?\-*ASl6?9nrzx6VH%)%Lm)ḗ@ΐ}^!h+}N[~v$9x]MMmg4zҕl 3u+JpOf6azaae68JbWH)4 a$CPyR Bfn!7 X;h[d_n`[qQ _BG(,Ulvf~H50ސG35 NQ@C!]ΩVhZ4G*hR U|RHއ*()r@€Vh[I?A? Qެb Wgb.si&h 8~ 7hL#GfP֥iɵsL4mr>Ou+.${[7y^s [JzrlqnO9p9Oۤt%^̤3҄re㠐o-o)Le;!ԇL%t2=4'ݏ`0$ִ(u!DdȾ76'xǙ*r0hB඲-rm )&o wYU8%k;~qGOq'"$$`T%ɕH]I[_aNqO4EhDx -2֘unnJJb3aьbO}51$ AZ-Cۉȷ:kl~a>xݺ#0w (ϣ`!žcx hY$"b0 XdHe-Nc?$oeQ)~Tnҝ#+gLü(lHޓGp~_9w (D=h^Mzq@c\c= ah$w~4&B㌯ߐhXl#yClQt$vg3Ѣ;ϳ|e0 ?}O2>Z [nn7O1+-{(VT߄ݍXbNr8ңҠ `7Õ\U&2IY+/(}d4@QKwʾ 'АԶ|O 7pf&M6*ߔ4ׁ/-{kڻ 4G%wS$jzjMUxl?1!jx)/=?6z`j,IIBÞ*A۸|d ~8CzH'e#I 4GgB82 wujձ_8~dԅoB?T:qySmDE&_U>ʵO?dj.-#Q<.b,D3 iۮYkPhZ )n]Sg|3JtǣU?CjťtA3䛶 3!goyTAھap`y?86( 1vB5ܒ]`U[}L.nUJ2Mf4*KF =Jfg]-Ћ?@݂1m`Ce!p=s_/.CgB}-xS,K5WXx7ի3_%>J }\Ԇ*Cz[elhAv@z> VR-3 WRo*uΡMu/.>AAZU8è z-]ݘu>N1q#^utd,,#Af]+P:q0/>Ү1\YNˎ~n+ӟc,66 ̵ FF.9PusƎGdUd')@qU=?s)t!Qѝyb^޼ t8CGMѩ`_th#ݹ6z~AVmF?~RpY5nx68m" btwm.).J\Ckȸ;5(:bhfD*% N5kL$[T]'T#)|SlDKCA7ʿEHnd@6K̈́Yomqmd%@2nS"ITEQ5׻GI͈vRqEe<oAC"&"Vod^`D ӵ"d}-j[O΀Օcwb]l&* FTdٰ(, tuS:V@^y2#+8E>!D|6:^nMei#F;ZbiO6Yk9A! ]t߲OPH Pl1`{:6f(ԧHTcXOSVrIDZ:U$ e/EOSHka,t$Kd[:( /1W`-,c3 8ܩP%^WnːkDko!񣣙kŗzboJ\? #|O0@kپ$Zvw>jY̰q(>!zH+2ɣoKd2w|Z8Kyv&.u'EThcL)>Qy.YlUJؗeIiڞE;Ccη d >4Ykǩ0b@2}wh̾!ic1b gr| тdT%m: _]_BRܴ:aUAtWbk3pCn@X azg5nHm QlOZ|SEa^tH̨ kh~Rb +iO2b+UtavHD̮>$3x2WT E!$,-wԆΓ²Gǝ:ξB5~ t99iݪˡs-$X.3D~z%)_TmzA*y~(u>Zs}Wp2fu7wL15sS%,|╗\ m:8iw+7C0h&Xp5]2ێ|5ax9:EP|SZ,E1IEyyݴ2̈59q;K+ѩbmzCqcjG}W҄>e$ߣUĻSho7m-*mx }oZ%PP_|g<© , T,ҪEo/.BeE?vKY+_S%uh~악_=w+i 0O9}(o xWV\D1 4#ҊJڗ'rN}gR-  g{s1w>#z U$̔٭k!Ƣ(GZp?K` !}(&afLD |9XWb|Q(p)’(zv=o(=0zs\ *$Zk[ۉ ǵ@X+.MLo,]ug2IR\!>dZ$H*1tK $!Rرn[C6Uhќz ͮ~9DtLRMЪ$=Frzn}iNq#61ZWai74SYwl=ܒ{!ܫxMdP#8&xHp<|"];/ǚC? H0ye_ [RMr߽yk6۝K*jmgnm SgͰ=;c@1<4g) Az;Mڛ4R2ܛsRL\wR--: s@Zs.2n /a⚗)JHxE2Н`ؐ嵆4Dkؘ|,:/{̙^^u'F :j'zg|r]E؟,+}`h]vS}ÀB12 R ]2fft;,D c{e2PS爍{M}I,'67@3)~Kva٣px 񄐐Gjzdfk ɐH$OB"JLhYsS(#rhE{a\[]q&/x;M IE@9O ӷa[rGrY["=`QgqP:>pW{huLTPyG,~%Fq4m- w6[LႮYh&J|m@`%Q1R$N29/;u\_Z>C 7SY&ss}GQA fBLh,iHGq܆ɘU?e.9uO.>WBWUGR7Rɣ^Nrlǂ0c_5sS Iֽ=cAf ~ua^zm\,%̦ PyZhbN#-HLNǥ:KF\cjֲV5ХG]Akc'I/;>8PO|0,M!y83H #@az*6D=r͡}IYmfӼ!)O1Q{c%@Idc\ʾtZPLN;How)OV[dg{@eQNi~x=6s A4NkDFI?4W/+2y ~<ea1'9jU{ykx^ר ٢ьaV1Nc0ά!$w`n q?*d`#~g#)j $qDW:uTM >&q̙\/T` Snp*~ҋ|VVX æM Ȍ[E~ɏkebbd}ElAjQJln J-}Ov멮LƼ{lrd \rAco̚8Ap^m! 87*@o&Y%F^Tַ%&+nˡa3H,c-3;685S}0\d&)9rpHB3gNp9JF?~HT ?eF2MG +:e#l ϫ"$dl7@TW0q&$:сVǕ_"Neid+:Ugd RU=h&`o҆GV#Rٍ;vVk $N{MCP3Oc󶅡آni=2駷˜as1%s-?H`iM!11\a%Vh36}-AQGYĩ.]87&k8F쮆|Ƶ PHC&ɓ*\F6(aětӺ†)2E4Y8 աDm(k",9+CIT{BjT\.BUF$WmJqIawsʦI%JɰVrM'vN v7 =IR[EY*t֒čx)L^/0o^,l m{M34ch<Ш]~T:\9KBJy_w^Spȷ293-T ϟe1(9FuY˽,cC6FM9)t=Yj* %㔻IVZe+3_[Zuﳤ%z5V553jw]/׬Y3LZd|֐!DEr" -) S:er6 u|/UƃNգÁU̍b2c y9 3м`2`W2Īlm0먹X ։ 1YɍT[ɮ+04D-.Ö̯p!,³La˝h?n/-vCzg &P#ѱjя$zɱZft-%}e.G o v-|@մԬ)R;mE?oF%OX!W $4Cg'? $<| & Jjbrz|}z^]o fޅ8hLB+p')K{Yחwyёਙx{H}R# l=ZP`OZ?5lVmNqhDφJ*\ P(ǂli 2}x}Ba.qO>要%::Jz' v={qD gc~Z.d `ƨʹH0KDhԌ)KpJk>1@5@뫎餪i8xn!G \ȰU样Cӳ&E{œ˲;>2dq *jދ}*0pZ1_"LlíSx7?lY^( :5J.l^rJ xU8ۊ1GYl,3q_Zo.jWQ/NbH׽S{3'QB^E!ˮh}W˜qnq!#_`I X p>njy\pAfC>ӓF QG\JSi%6iiJH'KLv*SsGC ǚ2 ͟'$l >[M;Jγ(KtImYN|̦LU^{Y`bqF@Y-"Z`Ó4Y7$r}91g\ӡ0,͘`W| PGBʔk_clZH(й9P| (h1 łT>q6,M%^Ȃ=5W궐gd*_W`%\䟟|#`[p%ɓ؃gk}a 5AKDwY #KXA6Eo5땗oiϐ"QpW_u2|%o ġnsDÍ`iBB@?"@5fjqݰL@55I b8"t~ͅ,-KC\%\3?S_pf%;?l B2~}j0(7\ tbA׉jt1"Ξ;5ihE?dռgy o14ߴ*Oy&ڙr$|u|=.YgVGp]JK*#OHA*riU#k6l Jemf Tc̰MaCly(/:Wd(Lx_ojխNT{X0ʬ8l2inwqjD̞abS.+,3}bۃ`GJM&Uqіü n~π SR \l~B!?2}IVK)*ЊB"x*іV'PߒO!$ JsT&UdN*x1Ly9TD>8Esx0{{ gB`nlq!IyoHED5#*0 BeSR3ń댫.%˓>cچHVS~\ON1XH*6_җ=c1ZL;W^̺i;`sJ,|ݰ`3c#^S'"RP>nՅwL3 kC^^8 ^CwZiRnʼn2;|-<$p0%>*- @oNs;zOeOxF6׏vAG]|HLjex_@Ve׽l>^!OigjV eF]c7 ~_.JK_#A']|A lꓮ/{< ׭|R0 yY@AۗSO6NAȣȌY$h'؏+@K8y5^lyrI|ɍBӑ0W)IqnJ:3'S֩V?,wҳ , 帧K/e_ =Yyh.ݣ2gmzkӡI!,Sp4 2OGWJK5|/c6OUTOa~Th BJLqqy/(3G# 35"z,K22= $b/]$ekmR ۩}K,s0(UT̘֙yP YM(ir2ni3Ydb/BRx܆ST^Fe0^ 1w&aw5rqx}6A H8<  P)ty*|̃4-\QdJtHd7a|V֒6`]7:];ߎǀV]iZ+2<=``4(}B8WQ! _v{A! -"};.)$$`DFaW/L+S,HF8@6^ƚd5_E7NM \"~Ԧm6 %.8J',*y/CA Q] %.,.i)Ǵo#VV=A_d'qpeYp> DgkUGmP;!tW.1W:G\ÆyS7Z=s#̉ Ppox(] YN_I)$h.I'9:A&ܼ>Ά»Ƃ<. M?34 lK+m6pf]loB|-m\1}msJ6C8`T`"N\"(S/̓+`.6zc~^zO}$yxmJp1# [r%"ؖ2z9,u?L9tߣ2Bu }jѺMuix#DK]0]B f+/h@RXbH6{19GI 1Zy `TONŊ,BQ..T4=+c]9F/)o4FQ& AR*hVKKnl.ٔm!Qk>w28WղUyyalXrvBnP%nI Zg;Î 1tUDƣ %]}6쑌aPr+O+Pp*,t袰ds48]0h:<sؾl44j8βf2<:T0ēX)G+^`9MÜdfH!hwk#&dM:' )Pإ?d"Šc{=4ñ$w-q@/6?WU0چQnC92{l,N)c9.ף)yOW쩷t&jW(}.O^e#,,_"Y!<tiu,%XV-il>=x'8.OV]WzSֈcOeԞNV:#/IIMrHQ93M笤[qAa]>g4dn- R|QVF^r%%aP r' g5k.G^@3pR&gn9u+Re,X[Pf}+BI*+;s JQ7rldHD(Zv-5\/*@*͛&˲<*>nz>W e|=%-TgƷYMÌ!3&2Hx R] PC"CR {}A(Vw'UKiIiXqPegxXvWFxTCr* rV ζtqպS=gM+4B=] h|00D*b 22v}䵅Ƕ]?ϞVsRi%o>ұ U,4<.&DS5&^O Ctm'0kgTg*ѿc-Lޯ)1Ѽr^doH"hT)1Y\8V#[~ 9K`pK|^5f+n*R^} ~#Ծrݽ>TI͞a#LF`D~i1,2LܣEC|4^Vs'^b>LdLbl94̺ۂdE:YO2; bj%UIYf4|@Z9^sMe<^]՘<~t^-zH^JFFMW6jV4=KoK$#4>scB_@pv[as{}[L@qqy$"UDwq; WtṃޖPiq*G~dfbCR׾T+^Q8zPlO[Q"jZS~L&xW=P%Ϻ]oىUg_TXyŨ{LwRaQkcZ I>Nntr"B(}5gy~QWW6wFi1Egx@p|泾9;Iа[ 2rb:֥O5"e+]D1U~DnddOGMՈoA2.tQ;SPlЪ80O4C`?kE8fBc"|bW( kʅOX6SU8_F mN&WBq$"^v5޺R+"&nERŦu=+|>6P}i'b{(<`T >h*bGC%|TrmsClQU)n~8!oPN;)26 hM50lYu,l7@ EhX iS0--e;6Œoǡw(wkB@0=b]jW^r{5pnb$! f$/|)0~ќɗ^o ɣ#'Q~]5wJ`3fЊpP =Fٴ[3b.Ą&x?9Ɵiu7#v^+LpoQʼQc{kWip;YGB5Dh?jWLL`7#28H;+.Oj}40zVyڂ+@ #d:mΩ۞L)ݞ|2G#|]]}<^2ҞrA~GЯTd\a atd -;cYE.xc&oa;-q> !BFKcߺ&e4^_Uj 1J xԣ{5] \$LlN%=LyCe" QC/lSU_SS87 ep0Ȓ>ӱRӔjMu'g!NA[yg*\Ľ$S|Xc749bfY>FTҹђъBãf7fgǒ5]!`}s-{.Vܭ™QB6m"WԺQV x@gd0A'pNן(RG!=B0]MEoy^1/ɹeo&&ǘ IV r dv F='k| h58/v@AP1M}<8po `>7ZБxbJ|SOC!nE}En&WR&ֱ7 ᰩ sjxI@+/F-ǻ7\i/V3x  ۓˀ 0R"4'X4\|%$}p,lP{'܍'9Shg֠s7[ 5fc$s%,FpG)B,wR[<`XOg )TOspJ-o#;vZf%Yqgm:R:g˼ߠۊU":śu:vWn.07ԱKh w7N)k=jexuMMN[؂ Y #@Z)^3cݥ lBksp?{l7pC~`W$zHȜE.?21En" ld;sF&8ΊA,Rp+6 k*  ĮMVc!@Ĝ3NAY)yt氋i&ɕl&ehlJSMixe=\[@3X; X-MX]C42 P$CxԖcVZQrTfl"‡Tn ٪zJA 18c%'rqF*͛>:j`b6QɈdiG'-TM8}ohEħO" 򬆴B_Hjnj: I\Mܘsz'#㐝 ;qőwX3Sy.P499DNKObL3zz|eLol @bc4DItGzw8;l, ld)v΁/OϣLl(K6B4Qp9cLox{CrAӇ19ѭRO3mzh-Ӟ\ B؊L#888@Ujx ))&[Fյ\l\y|~7p}Nwl/z9OˀiK3 |kRϩIҁD/[7oȰ'A,1޿d٨pbϡ,)s־?unCxg3oiD R$d6[ꐿ>m1Ek~NȈH%)f ŭp".}뛥g./eJz/&q<ؿO'NOYpݽN&fB 'IK Z&S}Zo;dl&A58:&vtEKO-Ͼ3r"ۜ:]8( "\Z黕֮z:@% RnB+[/Ө˝䭭,[Rx\,PɷXRyf5MT2)wnسœ)q^B%8#qn.">=rBئ'Dȇl6f̡oQW(4ְ "=HX+B2]H7y$sP;ϳ|s- @`7YT0aJmIRg3IO,d &Pe/&^S4=}YXs5J෦d{PӍ@.?(ְjey-ݑҳW1{#or:_ǶMkXm"^@δ &y>)KѺ%,!EiQΨLY.N4sƸq۔$hB;"RRďG;RQ]Jx2b-@k s۵dbMmͥl2D[`m 9olkO;7Bq/*~ c!AöUlmN::Zo0psybpӄk-[2G] V0qS$jRuC(?7xgz&GnC: D r/{ɿ|;9L ޫ% {&BH,G=t=+rdƸ%~Muwc&ҋ)rF$" !P18h!RSq*Y c({'O* AA\rɸB׍W UAYnwj-ؔTd휭 ٍ K-E>xcԽnl6ԚH7b5B;e-Dlo Nm?G.QqݦbQbܫ=-qUxӣE%\0a䣠 {  .5I EZ񍸞eȝ-1hɂ'k S ύ^LejSP2㿬T>HcSGޛoAwA-]FdFՔ5)|8;+T֮ 7Y9Ak%r\Z3O;gW_A^F3h !qn, E+ixr:[ϖ3Y)dpG7?~;[<eGcCZ,$v0"c@ND^6LM5thAMi:~ k;[_Ֆh"H[t?&U vl+1鹚 |N̢mK.DAKmR1l\Ԓ>TWW =>ѻa9oD̞ =SU(,|Pq,Q#h|v 8={n> ![scN뾋F|Џ4S'Zk. lj=KaG ԋ&bxs)!ZLW[h/dUV([QG!ُznQ2y՝raD{u]։ɗ-*@~ R'm`OH.kОA٭2'9?o)´~e.*z&m3ITwDTaR{V ;3 qIx[1Zv .U3b9Y|,aO=43\.Z6Q\%UͿ ѿցGK.mD#qV;('@r6#`aš2 3EF6?ƛƪ@tEXg'7 Xu/Hn8+S}DG,xLA? MLQo[\~*dl T%;[uTֻVybw^,qBS]]P5zy_P}ivw CHWP4MbJl%o(]yK~J!^J(}0-zrBbOd 96v5O2b_ew.7P*\O]n/.wЌZ+/bsV; LNn"0"Hp6J%mXɂߠnS ?!O"PUSI _/3-xOd$X)fMfBv u/ZZQ:v t|` ^$|;~6[Sc .5T(/`n *![%;P; HĄАj}{' 'nōTVtn9 yt׀#;f43 ڄ[F0 ;Ԙݐ M KE3a pMqϑ4-}bqQnk^[âyk~_s tm%3"SUa$iO.Ǒ_Mv]M-b_c{L닅뉈DQ{Ǡ}1إgvjN+ƍ/bA MA IUv`^ʽӠ6eLLOײ3?!7 j1}mz/;K@B*SfqΌ5uc{[`Z(QQrưY zZ ſge3y7 euxHP`o"8&7.k'+Wka ΁J ~jiÂGBS:G*C;C,)G7 vc<KVhXF[RO&Z,TBBǕV@ lB%noaOץ {#r`Vok3}t| h@hJ^chUS`ST$M0X KÕ0~i 0S[E"TːS{djj\/{հJ?Cpu[jLBg{]aOY~~Q FW#cEj&r_ioO&yvq=wi}]Wo\2ب5awѽ6L8/pi7ч&Ǝl_RjZl4kl4^p;b126*ɀ^QҞG/Oc gC g *@ú! i/Eo%M1JJn-ӈ!n}Yr7GCw+b1i3%;aw;˳'( VGDk}/q#@P(-J<.ܠ3aaQ8#nR+ p* (F&B9*9Ãi?t+{ [i ƄgHh9=j]x?`shQ*uέ̞t%ɬϡWZdRVzI(&| ׎mOG/9K[|iӪ8)UƾTQ",Zv>Aj ƮQ;67P$n&;F[+LͩFxBJtderR ~x=CF'(Iwu΢n<*ϲ:R9 ƜД 81 4 ;}hts+,.(JeRkc,L6A6\c9뭘ͫ5W8i_zšf5mH.Askʞ : \i:XH>2-gtOA#{%6@g"Enqa u9K֜$1ZTQ?Ά H z  r "Śjfk˂oYiTD2~J֙9K+cQe0T+J=GI:e W9m0NYcdڐ^nkrYՠkNLQi;rcUr/o`J-ȓ /9O]Y`rqxwx(%n2i~;jtgZaS0*ܒua¬~sKH0kzRsEfAWn=888v SoyC.vt>bk1I#m);7bB+zCg&U oPZodlPB ʁkDh}|0N2u i >Q%«{dHVx(nSF#>C BLgw!{&-o)U{2Y ?tGɾ$FCϖ=⶛U!!;OeyC/:<;WN;}U-+, #e-T^X)C"ϩ?w??8!P;50 SqRW ҌVBi7xTRS԰hq KF<nhw=6C|e iZ`;}sP9ΰ>b,G_Iм'Y 7Ҝ#XG֤lOgI7}䁏˔}c_` N5*rdkcFY@v,.J,jpdn>̞ >TBPFM`kUW$Vxe9%얎'G<TGyJz\4ji y SXf ZHm4ޯ}G|9ٹ͓Lms&mOIR+CZkt^(lP4jz.G5 NɛY6)SOO+^ji*Mq8E9&ݐӞ`yAeWąڑF!%3 f 7u#)(ܪ(fWttJOՋRP}JEgZ6y֣] m PuPG]:"e79zΪ`c_S7]I U} D_S`"xi9boW\^I&BRP=\o^屸c}j Z]V5oDԬcU"ͳxeb?K fV %`v-|ٌ3h҉HA3-Z`$87f #y#|mKjLisN,]&5e1 [П@{tN܏1!C|zI'EvDVg,F_PeLcNͿiY:fU=f^W[xٛ155o."CŦWw%,$ =||Y E@Bҟ/O}'FE"ČP;ԗ1!4q?X/E653 Oj=Y;:熾)(PD⤮Y7h|ObEsQo#ӤFT6aÏT$-`2U6cL¡9y%ApU{͖{-P56`R/ `ȥvQWbW[kkf܌TiSyzܒZiiR<٧vu}0g7K,mV&2AcJMycLOAt8KY濴o.+]rn]C+s'Grm({ ޖŃoG8߀%@~4ӇRbMI% d'/#k 7q<`7z*hQ#]w]oĢtՁBf{l8v :ft{~ Ht "p# ;;ft<:Ly76n0z?usGJ #{6ЖǮʟֱ5C~ jUd0U=xYgoR"b?XGdů$PzrULM:*.Rh oub⑇*18s>*nEawX)8tkiy:ԥ35 @Ģ)juӒ}۹c~V dsV.ҕm*xԺ> ]v]̩zyH0m`MDi0wL6tͬ WBy^4d˷` Sj5[|R{rL3} h༥C'gtj[9hj3x)6!(2>( atVdID~ oу(l?A`ٍ6KkYv-Ts£sl%I+Pc\it֣ >j8]RPZVSe(ORϗ׫H?r`^ 4k,:"L1)!;IC :ퟁRbJNU娻3Ig+]h5,i[E`gD3TmeϴvU~~&LnSB=ԌԴՅq1VBƬ^l+[kyѵ MHS GŇP[p J` c^FBoXP52z3.kFUV~1I! lAe1Qq%"PI)'sW.KR% Y-t^D)39C]M&:R)@ۚ;Com u7*d0}z?_mSى^SbMtWRYɟ2lp:X&&]E5aH3A}|UAhJ%U0 qO2!$r- t/1 h Ia@m7 +_5YٶdoK^CuK6ͱ 㐻~aa`^upSo%/λͲ ]Rc hneOV0냭 oexp&B<@o? jUsk {Š񉞍qn%QC{FK*hrAȒ` _D#۴AI=@2_jz?K0e Wa fd句JI(ka yY'Y8cn*]ޟ]Arv=c(7[Rќ\[bt̋"j~S}6LiFǸ[vˍr,S h_fW2q]d7v)itQ1QT2ycQUP(fKJAUrM[0'ˊ%)te8,)P* /lY溭w"YUx~@.$ g"h 5 ZOFP[{γpOFޥkhT~`6*ԗqse1bv_$mF1.zZI> M:n W1zGI^*> %A%bT2ɰvGѱf$Y`"*C '쑏şK4 Mp5i@PSY7\VV}0F͜naQccn:FH 'oq TNb.n┯kwCo^q;A.u\GgP@|\[(:LR#aJ>%OT-I$uEs8IXhJdnvfn -DvJ?*o .Ř!15 t]"'>%] ʥab@pt{ĈF^ _.BKn)Maw koYE%UA[I>Q})5T@:,o<8\Dq9~:ҵqW"u᪈8c̀tgWs*2& x>dX:Wtg~X,t =ͱȻA +_ {#3j#ͺ"7G3Ar\sk+emWr;aTC0@E 4T W C8:-f" 2JY* ^=!jpgiY awU˩+km}oj{,bxi!ӷta jS>t@ [6Ւq!E!jWY̔gIA}K$K'kF$`u^hR#Q/7ўaIG'lm0UԱ>9-yrǀcH229O$&_J|o: ۣ;NutGt%$[fBB ?5`%0_pt+1f2|٪ǘ>?ǎe3Ua$tr!<򛑈XIFSO6^<'%YDC!YXDBhƌ1,KQby*#',Y˰w<91Y$Ґ@/e@t~Eo N|Sa8̒1}'a:on똶 3@_Y6O), L9$_ִ`E(ք; YN k[My8.Jr:<<8ҭǖKq ݪ{-O} ųy9~FHi!{U!3Xhkyɦ+aj:ǘ6 ~ϰ\W30gMyjuf;Nn~âBƾ YrZAHpM:֑a,QeZWF"4 eLWI %~c"A/+UowYH=CIj`i˃ײ|"DdcPK2d_s}{q 0bphX$7oZͺMghsBAޑ}4)gSK^A&# 6g/ [)_=Dz߆yny\G;ۤϮnZ ǒ$jl3vlc!QH"&Kw9gȲc(oHqO<-񜽈=.ٳ5f,ZO'؉Y]>&.!.-d(^Ԟ3isE8F-9GZ38Ry]t~rj\(|~֢‡c4M!"*_V!chgy5s"eSF[b#mȸU3O' 2Bwr3]B|u$ VLmbζu@p@1#DⰷpeL+|iǬu D]G Y/ufZCX>ztxcfF6=! I*;!-m^=eT_J?u;= ;#:Z!8DP/.z8-1HٌR&-Q5۳/J+\&jˏO4u:gGg7q؏6aŻ0@QSUi2ob!"h14 jZПF*Pq[z"u= ~Sr! DN5̉4H~U:el 5|ւcZT'!5qo\ ظO' )'Ox6c߇2 djNw-3G&k@KC|b t?~7* TISa EZ c(As@A-qBo?BzcJA_o$c&2,݅O-!ֽ'>,,zD+v$@ XZ[p4F ]^X[h'>r: 7ax2Lۇ;^Q(`Yʠ&:!N~5*_[^d0 Pұh›I7Cq)(5a&@0#f$:VsS fŴ֔hBnJл' Ĵ?4Uai2g%Hǻv3j- D @*5OVWʱ18./K'/7s `Uެ콇Uɸ]|v03Up +[vP}^}nZJ똸D=G\;"9q+@]k8sF8뱷ځvd+1-̎C=i.;GjxX/l>6Fwagwf` ͸zƬ B)rS ?qQdZ:[ɮ8߄ق ug)G٘& ;dG(5Ue?ﵬA (vN+O Ir=O-< o8 0c7J,duFrxrGڕldb6LI|w0ܐFΝ%+\Bև0b_޷[M5k9TW -?}=:S&}]Z>Nҁ*| I58{* /dhPV haw = 1_ ƌtC~7CvOezB+"/h*(LZ@e(X~Pzr˚NCvqxoRʨ{]ٽ I)ʉiҾ5&dT|(F )3C*[އTpd5nizKZ\ 7 ;[e{}8HUP|rݨjL Y]8RejA2bIEbJMus<;!faq >XPy+w#ѲȗƁcWNُбPIDULݴ^W+ kIU"ft-1EJH}"R$TYP f΋v:4Yx= s'",@QʦT]AYt~l0!2 K"j;6kt~n*;"rȷo;^G֯8FUˠ0z[386wG+Ӷ/TBC;]fX!̛'gMf9 $uF°޳wR0V eS>`]A? gܥ^.֗hCpaOVw=41sS!BOĉh®EK{6кN s o,j;Aj b%<pt+f-+]q>VHH~g]Un,=ުD[$UC# 6eEh֙ kܰNs'K4qȈ >O̠DC dk#Ϣ~tB_M,DƄdzٰi/s1|Gv&kG$ZjqM:fGgG&mR4=wlX/@RL`/` k!sN. U,ٯhZv$ԡ>Ţ),F۶Ң q%VÉ$m-|&TH)ɉAam&X%"GudJ.uO'%8 1f gd7NQyAuE 7q|`S h.x:| po*A$ i0CL|Ho|~ԔJUfSHh&zF/ ܸ>e`Vs,g#]rU X+.+8=}[|Ir). ff50i#6XEF`Y@X mu1 oj+ilPx2W D33Cj/{]‡f h*EGw2wև{c5Fl6* rk1dRO5k0Cܑ11TL]`7P 9 Lݐ8m_v3WǧlF@j !u !*[OeCT%Q?b_ ٢IK2B{$JdsV+jD2Ŋ"<߬ăťrU(kau| oZNMA2!6I]YQlr=S_q:r5#\8IcboW/!SQʮ.}v1YKB獣ƕ^F6Hb;!E)ܘ ^TXe3 ʈ|M3ԭB#=s,e9ll.f#To*4`V #bmu } GA+? Td 0|t{RBGfrz~/ \y. r'X,lh-@s0a\4cȚ/`%AʡoloˀXTt U{.7]}xW8O_ϙ8 b)['2cL<ܠKUd'i9i2 BU(g%Bn\0 hشH4 xgeBX<%7^6ɼ%g?QIMi6SH:ʰKW»6Q>,~iSue۴b?+.F:^s쟂0D%SW~z;r`ŇɧP$Pu^1 9Y,& >)?42ND-,FB1lW@2TBj4 dB=%so, A @SO:% Xְs@PK\XH(7妑1} &}(2j sVԳ::- ?,BmIs6!t(Cge Ml9!(@3v'7$~(v]PϙYZ[¥h Iy-r͏1bɟt]4qwx8^\E*kNч&782X(fF1 勥 du"y<4d4f"wo :I>񙖫+nzV'46z_bB DY{XjWS%AHx^Ygz/pvL'AP!g2hfTcv_#&ԕ,gp]Dhp5&5 LI繁8`j{+sty#6s9K|l :c2}紋?y6/@Fg|9>mE..s`HDշ o4. M4V/ubfRw_O7o$3dxH=\f%[ rúӖ8gSiiUݿ|)+wl#eS:E4cbϭJVxNQTY p sr.}n[=n\ T -+yԡ/K6J6_]@5SJۡ{^"k G:88{S69Mj l;y$ҼS$*M kʞ@WKܘmrg'$U۾]4zC$;βGg Je,O׍<'?g+̙zȏHM*䝭܊94ۑIIYF*%цVh/ŢLשn͜[.DRyMe}+!م UeRf#Sc;ILp,u٠z"o! ^T!U*Y ԓ3B CP2}-z ŵɷgoleFNR@4:Z i̧GAwԲFg66+ )on6,u2RWi ٘:QZ@ӕZZrè#T?)J-7i;M4ܨF9Ȯ1E`?1/ci7݈$dKK-X?nYKfmyޓhҝ[~D<"$E*U1AJ%EM-9CmJ5M41{dF똒5l(=#Kϛzr5P;C$⷇U4smLRDI4iwlrs t ƓN3!ꑮsЕ&!P|n5aC"VQOrǮ4F9 d[p<džb*lCu&{WiquZ*I6uk 3: 3shH,zsdz pCԩ_ÌyPS+^D:Siν. R64:I 9272'"[lH5fC-IzZEv $bi}\{<T )Udڏ>ldYvъK\ :ZIFYx 8C5{`^Yiz/X(C,vj!6 T'z/#fN&iӝHKzs|0u}8 mf\}~>vY V~kmIW* GdoOIqb%aTVT\DxE%@=MM0Վ/)2NXMDA2y6\<)$_C^_SIZq_0 p+ijIEڽa,*hH:؍bRJ^s \VˆՌxU@.A8+d=3bЙ0g@l坻#_+Ү3Tc(LzC`:v Vvc37d vYSvri.DzS~5aX`@}֜~Bp]"N@?h;ɨ̹1l!QhNh}6 'Mv- yT:R7I][ 3S*>a(^<9p'|Vdj0xK?wt5~ cbNO%h2^"HiT[I;Ӵ15zV_%"ԇ1蓧uVcRD٩8d fR]EUa!شIsry=i;ϕ Hbޔr\ 9?6p/1]PS%<jԟU gq͓8vWN 2%³kd40;`5eA&xpAbJ\0.A؂NY>nR$d]%>b.J(siӄ[ NA&+%*<ڗwv`j}?> `3.gYrȲ^tGj+*dGy`R.by`nGV1&imyC>*w fcel?- 4VcJ%>Dt0ݓQo|Ұl\ >՞|3E"RXTx`*i!. y;֙k+]]QUokA- 1#ϩCh>!oR w6NO#q׈w~ <7~.eORyvOpsd _6K hXΰ#ncѕ[lYR%7X;@Ğc`6 h(ug!Md=‘FPTGJK Db'Q9UVt. \RMAE6PS8(ԧ+ ?Wk]SK^O՚Yj|l{cOy_m$37Փ\2/H^ЄzpACE !FCE6x;z|f=$g61{;ru)Tu'&9MhGC  plQ0u{ܹ 1Qg<%0]M9,ILQ:NsPB&g;]iF /qK ڮk8@ G.n_$V/kq:O߱=DD#>V/V5hwDw͠\s=8yݸp2#26 1cFwy[hbKIuMP#bæ\R&1P!q '_t3-XuYNHW2XZ6T Ӗq5sXƲ 1߷L=_}m9j^4SGG-kYgU3zV,T}4z}2p%x-SM^H9m ߜTo c[޵B$MTxwz}yxӛ_#Fs&~`kfvWiɭ,ޢDK%)V +n$qxJ˔vbZ[u(GSe'a<6NZCc K/C ~LV>:n|BOj):bIsOzL & s!E ]`9!ы9bb$Sz-;=8EU)cvsOg^G9j'EbT ]_,[ >w"F jrӦyŹ5 >\:Ceִʈt<Tlվ;jR)tQ;$'J i[w*Ch/4D>'dE2APćݷywXX-Pg3?@n.7*pM {\qIK+Ϙ\ 2ಉ2R$ U싓pST.'9"fsE@ nBg\OBWP6p'~7BK-"'z_UJ|ͅ1>rxU}Y5c)Z= b7 {߼kHlmL`PLtdLXKvfr*b铩ty"Ln;PRPg1U~$?O.Rl).rdiy`y[ヲ "#EP.%h[2 qZ} .g+? TَDbY0׷AW+&@HF%y?5 ¨+{-`!Lφ#_q ȌםmRЮtϑP3݌5d;fޥL lY MDd/efw#ϭ>][C هg+%Pte3|agy2$9nd3ߥzSJ;ZثN|]qV`J~­(OtCWr\n.H'ޫ}6`#,TlL o#xgF<dJwe-.l9 {n_Xz'L :/#1@|טh-YrU;R킝T;ėHw`wD$wRp6 '5|5*3<焢d`]t=m:i,|c^mf7K2m@mR s ?gޡ\PaZ=!v=i7!f- 2qݳ$udbf 7̈́NP3R; P?k|ձ!z]azjm(4#2rwLXvB3d9`d3Yi=L@_8_Q"p߃,޷:Г7n'@vp4XZBDX2rpVgnHYRgj?O Q>: #pغČkں]]Ҏ%ņPjw e3gZM k@1'L?ҴHg'ݩڢIH0-3Y64*38NF}aKӵVn$&D0^"JB:L\ZC<0 BpE hAsԒA(')-5CM EJ%1avoJ!!RJK6ELB g}z *%5AKIl\z {d\N˙w-u X1m 0qC@.FwGb7Yn4p2YhtZ=1za _~aНҐ7FU,]P0*icQ)^ʬ[_bR:ɛjWCž馾𧾉lCjw26N1:+WڂiP>Ny#T./}AGj+EMy#u&+V2 y:>ZͧJ"d#Sr:2Y ]34845$%[i ӴL-C|Ij l{ec+TLLn; 6/j^\ޠΛfk|jDr\g;~7ѣN }4bu9pd8`&b]Vzp w|y@?נM2+gVHȘ`R0l-YX:u0' Y;*rUȯàCfVogޫ9]T@6sbcM&7>VRlSz1 abGFuLҡ/PHhgHG/Te!^w-)YL_ok4BdE1ޙiLgoP$DzowS1ܙZsG}' 'QtKQ6LlmWmƈWi3ɝH48W{=26iu@2}  ?Ři +N;T\z3V1d"nP\9%S jהJI;CSdMku)a'KeQ5ө_/ L@ ݾit(gև@2eNڒ8,:_6oG3ݚ| }_<KE(BȢ8!CZoU= #97fc8d??A+&f-+lͻPSc A3GJś&NR{yO@G 0Kz(P2"2.{;D콇4rBO\> N6ZX1t̩+3u"^6`~lG}12ZEb[& rXD&Ĭít9˗U[cn*%uoN3DQI!S4#nhI6a==pnuTRʆ44rMŹڄZ+gȱ'g1)AoZQi<e#bgB3G`qLsSĔ骍4k*l.A<<[lLxD3A|gS,pX&ׁvcM$z=>, nx ǣ؂ԊKϘz͠*Hn]hdepBۀ =f7m%q ЏkZmӸ_w@Hꖾ_ȥ.ɜ0~'ՇǾJڕb䖿H(['bGWs~^q*hX.;g%EK _J1=!*{8?5,Ƭ,ögv,%M%h; ՆޚǠA-SR4G)9ZZvrO'WZχ M٢}K4{kn?c|\QU1-9 ?90)?)Y݁nG4ncy*$A~5֖(݆ŇWUMOЬM81xKDeu Zk:6G.! [v/89L{ƚ*E[̾cbiQ!3pjw| :%`8dbwXYav X5npm sƒ;-K&ժ$/=Lnpҥ|IMGf4Ujزz@ОH4yRƕ0>;.I#JWa9rvm;ζD>(˗"FlZǡM_j(nhǡj_,sP tlU+rÏμ㘰:b-Z5z 5cqkp"y=O !ƝuC'_r17 9Df0lp .fAr;;VuG\̳w:Q_ ƼEX) S>OQ=moI00zǑ:s2+H9r7VTZB%hC?PZ{4!"A-~ CM TbIgljh?Pta3C~'M B(f@Oǒ]Pl==`o⏅,c6ٯjN,Gd;@94p% ۔Ia0욝dmFʺD/8M!pVt'3FU01f{+i 崩&٬C$pFքY=Γ 6K2ڤj W%"pnb%-l`#1 YlInc *zPVboi˂/R‱\A}U1COEXxUmNw6IlOw;" pku'oE91%z13Ù]tVQH?R0fL7%{+w' o = d9 !Kt;^˲·el*n/ e#'Վdb^< gҝ\>U߲8' d \=Қ8" :C' X:~>04w qr Y~#.{OZևQk PѰ%iHb*W&U[Onٌh[8ȩcn@X5AC?(z&$ۄ®0;[3v9P?x i߆V2igJaT/H6X1mF0迆96Sx7ܮ;%ȄFw it7Yߙm'Jj?}LU]5W/4ZzC*-iz1~E=]րn!;$4h!Tq6TpRQ0-V0:iQCޅ!r+!6n2ą_-9_% 1߽Єm8IV l#}-p~5TfxyPWhv7Z8 XTE%MEJ)# >/!e$0/Xl}gK=_N h:)RPQҎP0 $`'Dc둫K0.. ϊOlADag "EmAonGSCQyM*N߁*D Jf*S݅rhmOҥ_77znJ?B &2O;^oE|`t|Ť[TbjaRG33[ȣzlz%:LPr#w쏫PtLhG4F/lP:2sʔ?&%tb3Ԝ /McoGjHb #!LN4#l*!㹀?>eoPxB__PmW.0ʄ]Lw# b}!8KӢ]5-yCm7JPt`ܒ̽k[`+u* G;8Om C:3 9Ds>]ѹB2+պ z|sА YU_dCg<23iuaq}=F qt%Ұ F'iзtU<#yf/㝐kn YTH.e_̴0JUўv@Ik.? R룴Ev|م8 q'M(UǚL C }{ښ9|=6.Wuи<|56B(NSPE4K j;!;RWY46mǃ*-Wjy'0n#rC1(%RYD`7>M$:ڗ so#ֈ)3qiTs@¢ܘY1vGBǁjV)8"H86WOAw)G9L 3ԙYSLlsR9oĊSIc 4t%e]݀/-|_@MncH^ʘc1J+ɴN31ufff n,1'ұ)F;ٽNܟuyĮcF eb'22(^ܪ.9ŨDHUKI}-񗂧y8˩L/5gQfCqsYNHonpȼCpM-)5h+4⟿u⭸ :sFrn 3\#TFLX:ݍɻ?-ρϩ4ʧ Wx>bubx1t0+4H4-&r^VksxPpgME~58֚ ,HaC9 L:<7 G*`H0?~J Dá˄kFG=CO`oPK,\/NVhUq)v]o O$ 3_oN܂8L@UoV&n!xA!͇v'# OYZ78tRpy@1`6`fJDR̷# U !PR$_F{ŌevT?I36TbmHf6GhV6i8jn*Rm8(_1AGW_("hC֩(&#0;r2‰Lh fP{ցZ_!z0-[E}<GI; ;DvoJ+b/;-a^S&!zȝo܍'`E? G، "@UNr|Hhqr6q1;f.]k!"~Vm*XF̸M[!VCL^ Jw7Y7#\UnzyѦu6k7ҰD,F8 qy[<:G#Ld]rE2d/ن~DP= Ɓލ@M<;M[-V_V*jMe=|❀)FC`|i& O<å T@٦Z)B@ esK+>^e3FL=txT/ )($_h2tA&uOTnp.z #jCej_bYs_s5P ~ɑְ4Đv}[""1vZ .rِ*aznkג/iQO: (6 ځ[r>qc)i#s{P>@au s҂^']W[Y><ݛ ~@0`2cFu&*_qv~𩷬/9$8JH(6c%&D>6[^ PF]X4+h #e!aY]RK1x AM IG#Xiyʸ;>KyS}lr3i40@^^ AUI/C$0˶4dH| 6+2̠0ؙʝ,M[;<\#ѧvpSbQ-SkxO*ޫy8o u[/xݘkerh@+`ye%31C'l&/%Qbv'̽E*;ӏśQ4.$u6fz%̣uN 2^|?w`@;EL45ʚR]mw!Eg`8."Cmfe^nGq9B>O"y*ԞsK&NßŊUI S8f`2 sx6v]h))Kd(=(hY%y 鈚TD ~gvUZTSU٦q@2J⯀Nv?Jb}g}X4/\P"qeփ|-n?0OfrhxPC GOcl@Տ_xU2 tB.\3X ܚ1DP1\1yFo&]6d>59U>鸘[DJRٺ=3?`)R?A.<(?(ږh'z%QjS"BG}MB A.!Ȣ|5/Iܴ|#I[mj:!hmW3d**A0^G ;ߧ d΁K,3@˭Q!h/ϾիXK*)K&S^ÐL-48aΰאEPިlBO$CT<'L.l28 l.1zP0.7nDVPX^,%* H~LUNVL IǑ(H\"`{,BJC˒EBF Gx[l&Ȍ]m}r\9}szxO>G`[!KM<2˥af}ā^` ^k?C› @yN'}OV gb+_ŬZ靾G*]KkYl8vʇx+nD{-ҷ"@ny?+'3|;sfmՋF)_MmäG+MPƩ UlM˧W3Ŭz'o'nj W%9> { ,,<mYv\j%mҹRu\`ud2NGt΍,?U%_L--IWl_eb>~;tS\+tp3fC- F܊(43eyT Waun!UG_STnhBb(ʾoJGL;)), u_2i` RBtn9N~נWu-Y ĈoBSZmv2r{kK'ojޞٖ`#[wdQQ2f6M|PhB[G."!lTIH6ZJB~E&S"}K"f o!z wY8`aoL/7y;8Th,lr\/(ħ׽ Q'qA:I XJ6 d}" sA odWB2IuE5L:wۛ:4!~\h!Q v.L93 a:/K6#``Zͨ91HBͺv,X =)o`Ic` +;(εa@2TGR^GQw}«5_%&|Q:kk1Q^p"喎j 2k*E@?\7=C $ڐ0.1W{HIc`HȣȉG壶7rxgzvb]sv"Hίe*| !k< dt| m!^nuC໸{vbq}հj`DGneJIu~pyT>LB3Z/Xhmp$9h|Xɳ1֠~P$DHyB\]kpbD( 9r=>zVTUgk1Լ̍L坂63:TӔw\/PhFu&Ecub ^vRp󔝱X@Ȳݳ+Q0.|>O {`1N`2_䲦u۹HRNg%<el!]Uՠy|-sG' 3n`dK(B7m9!ܴWm9d_:X`w}_[r8`ǽѶm cAB%Ч @Fݪ'9I M\/#sD % [](ux`\&`|oV)b"m)8xҾ;w!quaM<(^4Bk+tV?Ue&TYV ?;hn>'ɽ>I^AO'fUPZxD8k^D&r^$pewnha2}V1beMYqcن iiIQp-pvK0w ֡bkceȬ;OޫZOFBP%nH>/+yΝ|p=/&p4=f-MHEo@ >vv$mu芴< ǯ쳿MT3'^vq*{)q2yd(ID˓} B9WL&] #!F{P l2<yyѢiY˙N[HVK"J"21ԏdO1!;L`R[QКc:3~U`EQ}Řl@b{ YϘ9j S7"JGH˳}kd6B-eHb'۲^|>&27a1pxrI\?B80#'c;kim"?/ s 2j`jo`_r%K5%%;QDH6S!ǯ0m|ANc:Yt/7{1="- X3}ǀ 䬪&v3B*Je\|/(2lKՋ0(>YV4 7Y / u35<ߦK4H`O~ Lc3T`1Feo/鮟ZihSkՠ/s-h}*d[oٌ>ڙ~W9C'6.)ChI(QHW+׬mz=5@{bަO89ױB UYwc cŧW#_W%CD؍|[NDJdS(OTS8<$ީE'['Ѧp|2Nctr,~aGxy/m**9mކ>򛯴#"sA32Uj[r,߅`.2;~ 5,a|:L1躇YmjW/љWw-fѳa/ DhvN?M9|.9gܵ8hlb61^r+9##OȞ a'8~ }E 4!M}:N?R BWu\$OHXVY>峮ٵ*evbN(.b'ka,bTņYְ6z1?K!s8Y(r{-ţ"sb0ë>p~"&U9 jn^p~cL Z/USP Q<|}2o<-w)!Pa,)@U/Az@oh'Pbo]:@d[ znfkWug n:H#tp Z.-k̼8s?-Fݗmgvx;ሥy)>m$*fk^O\>QxDVђ U7nt'Zxm &cpE|Ug5ytBڌ%z 'uo .|؃yHh:-gx:Qi~:mH4R4^VUF6EMVd};GsU lu-2:|l Q3[Ux+jOIpO4LN'54jۑ8:4tH`;+k}^A,tOImAU ,ZNnnu.^Yi3.֘s Âg@՘jAf)uzuאcb"4\_Ģ;߀n߂L zRq}`GW̃\$J.<ۍE`밲o#~Q., !U hn ߗ 歬"А_,c<)V8ۄ=]9KU]y(ZA 6`. w@ :DCVsQ] Z9 `Jhil xR6)zU81MY珇A `!Jn\TE5KumY&Mx~yٵVvCZ`=P} \r?IQ.Oڦ`߈M ܘJ8=éxh-|@Wz=c6?h\ ԮD0N8SHym%;;x,7ًC辍 K9̴3zdE j^+PaS>z$u ̪MГT{+z==U)ƸWpi9;}^(8[rY*ē|Cx],6Ov͉G3dEƻۜDgksЛ;,>5D =O4et `׫w! YKժN<0(0bJsW>= |" +f>zEiA3怲tT@,mVoO9y3uD ͑`T.^\8ru X?o^ut[ sSX=fjŇlvRc_ύ[nՐx7r8 ("^ ֛[խ5"]m7E.S.JOy^.22eI<n2Z4[ՋpLAS1A>* %$<}4^! x^6WN)n ֝+&KHolۜIhHYI6HCҷY'?| K2lʙ*P5+}Ȣ[~W@~BUWt[Wʟn60R\sGI#1;2b<u¶v2)~mV+F (_Q kD_4- ̫bBT$f *6-40,Ch Egdl+h[:5b-\85s)^+Cu~zBLy`[zYu v!,ƊjER ?灙uYy0jL&CW-hV " dN-iGt|VmUPqyh@jǵDGz+L@ҥ_‘ rLD.eI5Է]BDr9_#gjD#!VK~+yLV%Wbw$VZCvHgA߄ڞgta%٘([o䍔 PɿZ8엓"1>佑'5O$0o QP,dG]os[B˿(f{~'Jp|eMҢCDW0wkEk`WYVTe:@;Ϧu3YV S1cTO~-N["UI}ޕ%Qn>@aDx8]WNdDqg//G2]C`\檢J, CxQ AHEMH cXDV-!PLjx_g `bdȀAc)oq 8Hu<ND`L=]\h?Zsހ@>t8 ź6W,W[lIKBGE Ʃ%x>MWYW!=Hy6@#W@ ؉m)ljzSzWQ:=\Oܾ?N=XYbZ@qXR+9e)3;z!lXe'xK|Y`##9Yʍm?]3A= WGkl0SIE-yk?xG:IaT,+ =õ<0.S mJlq-cFSY9).EzV5\R$#s\o1[8ٽ~36T"h_ɰtŶƕLuiqSghj ȋ7{oB汊 8P،A"@G_Ҭa巬s*%`w~/@\<frGx 0ԟk].-ZlWvWJD @񀠚5D$`L90<=7xrT>KMjP xWg]ϸC81 tMv4 zi eӡ/,X0bW mCd@>lY<# .'sM˞0d30{ Y(}* @[2O چ i]5nS< d'#P h4$u ٦ho$vP|gψQgLþM- )Jp#g˹7 LrC=ŅӳP&IF;h«g*ppС6ɸYr{"9TXT(|S3%VyvOXsZ?\Q| dk*uyzWw &/9Q;qW,`S9vӆy@қu{!ɋn;vqPDjMfJ%O{NJX |CK#Hj:{Mr3c"&؎ \  \mi I'Wa+Isl.$ÕEVA*sf7uxzv^36wx;T1w.`ʼnP&̳fEdgI&-^h^0_Uu\ds԰FT $#uDR3\PvIU Q+2kg׶kHP"H,2CNYٙFF2p[N( q XڕSS;v"]0?ßS SY!җ|Ŧ Vu!h[SF,r|1``8>lsB8KГoK>dMˍa:{J\y"SW< Ѭ%Xk5qg%,&y]jLsYGrp_i32"?6hϣB~yD:`,x*RbJ̛oH썌6\4ܞrX9PcU S7C܍K 4nz5V`ӵk&P;B߬))<[;[/ȏx2/%zCcAnY=0 WŸagÅve7bg6r?5T/'F'y IDnK2dN=5wpS%mdө?y~R5k݆_4P!={q8P1a✛zh5GI]*e(`޻ }BT@щܫq}vc%+V> ] N]M \M?y*^G7|C6nbC=WK P*=ș>ItLE~a]qXblq:Z29q/~'<oEEжV7#I%&.u8 W>%:nWs 7މKymL0G=[r5Oo`+ѶuS<+?Dik:<g$+~6|@[[hb)I(KQZ4841١dآ}j|gSRI"Y9*׽(0;rD1 l{C V lkWnwVZE}zn3`}oG%z˚;ep{)}reT|4y&"`mwɲRxJ>KC|TUmfitɷQ˦goqG^⦃7I(X_[525V_;fsRSM!ݖnWmra {zb_K("-{r67,jќCpVje9mP;/nU@TF11ൄ0D}N1P[w(zYfGo+pL=$#W!3#RZ}|`oTt "\,<\.x4v]0$f3oJ `RE{Fy~r;t"U)N< 6}fqZb!%fDM@%zMC*hBj7'\@DV=2SVq2Qjm.lES/gC Z/YJ qݾμuR1%rՠ`8όΣt7X<ː?;)ۺEK>*0%DI $6q=k"1 yQ /Qfx<ژh/Ј )u?Zm6  hhyq,&pф-Uq7Ĥfo{(vͥQy/e!`tAI׆qvSjȻ8Hh,?T1؛ww/< ,:~Jm/s*Cz1hk6;.# UP7/Mĉ@~du *j?s yg%?\K<㼅_d.? UʻieK5j7ָ+A$墒cFgH3EۦҪyz Ogqr:\%"޵{nX/@,_^E=@!dǥ7'dž8T 1Boݓu{I%XѫJ:e~^A>AmөuAb<͜l% (XܠL;CF=΃f4 P V,u}o;xC9PboK@drby~Tq!9E7 e_}>7*xr4?J:P6E ;RYf7˩/)U ٬wPN6՘BY[ c΂wvWhĶ~iv'm¸rv$;Љ߀aQFv9 s'F?T  gnW-Lޕ+Įө<LkUX'`w +n\#2Ȩ_(`ikWOng'7@̗֯MSR$e[U/?QwxOZC@C(je_[GXI(xɤZlQzJVh ۣNoO1xAePM]φ_D'x-ez|3[,~he"CQ%o[4|FAsWUѺЅD|/(1QÄ3 cd#^i.3*+ڛ,U"j#6wcY@VjṀV⽸M7 Vx3+X; N߆^c(Xpjb:j//Fк| |W/ƽ!Ll]^āՋC}J,d[% y+r fo?$nlc33_x_A:lgyS_1]b23ӥaWm{f`T9H# A墱ռCQf+ ./o̚|+sϷ7d\A7QioAM׀t~ 1>,66/@W-XXJoī⽽H4>|Ml4Ωaì ./e ;,"uOHm6]In;| !J'W4TB,,EMG[ANϘs}Sj^DKJ"7fh/EQbd/1)e^Suȵ8i)+YI?!qbXS Gѭ7gӬDzIj^>g8׿Nc&n%֒ K5ob1@1̓ڱBfx ^X$Wt$Nk!'F l nkpxH}#L F27ilۮ{ jn>뜾%uVs黗O}>ki fiWA)B>< T:]卜4Am8?/4dV0+Zފu#AGtj\Ýd[lcx׈gq tvT kפ2$~EjѧKS.'=XOJx]^ӵц CqlFW%$-=Luޯyj/ÐzP(b\:& |E5rmlCa= GH ^}#JFQ#:BJWWwjH0Qqwo3$ ꍆRqYԀfuoω?pݲ45oиS#+0d ~g#cZMc-No m`ML8I͗Zq;sBMk[I؃1E0c4c)D]0M|JXOHTerOn,tFW(qVE.1E@S ?:tAѲ%0+aF d◉&ovZ=ab$wvьF; LÊsfm%pnԱ%>`ٛNF$H7)WjGmCx.oe?bQllT,1&N[ar!E}?ďQID͚|vp\>`? !燬-oƎ'jE%|#1V۝YAE+^S4͢k FdhF $;īq)'=^Pʂ:|6*J| >APfRĀܟ 9E<T ,C=+]'*ɬ[ZҙpX+bAdov*7Un$C 끼v ae}uDjrAwDьvY=Z,ͼbbT| *i’pqϊQ4ҹo*?̢Է`I:Y3]GsiX%ߤSHqh qHw'R==MLl:oz/u<)P_HGP[{29k"XSncmS>{ zk4 h^aUXwԶHu\IkRrڲ,H‘ԏrXמ2R/3n ‘+vF>zHg` wcĖ{?6܈E"/ .cIHlVV žGY.̠q2(tƲЇM0Ql|-ӝ/mB| jRdûKBm+Q d((hnxQ5%93RW2 S* ,ILW7;82=?;n_!XuQ2T|42KڝM3,B}!ŴCCbywB[o:gGG_%twvS\<鞠ȅ "9}6Wȵ?-Wa#M[NLbR# h,jaɃ&@fZPX7٧Ektȥ.taRh9m=J%d]BE5 YׁC!jY~l^mtje)O r_IiVV}IS|]MZ&{1O [O͕&|o)?jߎui'ŇqjĴY wlIh> gfl1d+KmJ]EB(s&xA^!o u@4|PuvEwMŨj`;,bozt.7ҐI n­Vq8rϘzvGB/^_G)0&Ԯ=2@4U8RDޞ]%S,Dxv8*1pS-zy!pNe_ DFW6fGj-u}#ŧm}'cd:|ΥHT!2l avyKT0QLZ6a8ބ&p46(ɵ.=W摝T#˟f| PiNb& ξ U#n :}A?|3EZm}5o%>&$dW|Bqzٯx)*QѮ"zV RIin=w~(vRz{:Hℏs:'v[S-ҝ攥di[|OdYEjJMuv8Չuj+os\*]Ub`xo< םv>| ͯ=]jGN>Lj:m(Fy#۪zs9SNUoriA ZߢJk\Cl~_Ѥ/9ÑWJDD˰B?WM3.B# 1Uq^6”Bv>,q<'n]yj' w 436tά!{Qm=-X@EcOY.l|F?rbRD'Ӕi2IE):(DG8MAЛ*Wn֘f'==yEn8Sw?W3sqe׮2|T8ر6bàqrf␍#60Pd34,7\eT`3O?Wxn} SkJ./)3b0g@rzw$]b!LjgBaaM韋Wdo`!:!+2ӝv-82|+ Q}nӋ0bn#w.ahGj`@S7eFf~rh^%,)y-؁^AɷQxmP;]QຐIstoGScCz\o#~]~ >-ịkzV 2ZL!_dqf3ԉJ#xIQ^ƞt75]nZ)/Q=hJwTZ`R)B ص! 2CqX\TqWp4ʆsn2w]%zy y":z?[ٳ{fkPMv1`{>0#U[~bp᱅ Aa𷤱veJ]gFƷH'r?X1<jLDt{!sh\yv073烜SGz]shO)¯TG,zW U^ f9nj:ѯbh3[:HjCpM+MW Z_We#7wZoSihsfN/7 6Gq0;xC.Oviޚѐ6K(0%s}w*u#I<ȅb`@B}'2Gt74&j=5@5-@V]<UU`T%Lb,Y+O`AD=ly9 ut/%pJ-W݃Hv$쪢"c=bmi3=(cCy|пz)"Hछ FekPq H*\e._?duWּEγQN}9𿾧2.=6 kfW0~k >[idn=DRl"(eLhvEfOnutCE...ۀ^:۲X8wt=@ Fsv)X*4. :N#yC}ׯv!y5H(¼Se`늘X2T`8~m3}k >!t?Tށivnn_1pUuL(FDzÙTö‰W& 9Vޓ,'.`p3ʎ8%8<<Ӫ}YwȌr9Mt@f g#=D"36#K0T\n#ʫq c)zaD& n˽!{wY =붚9jo=bA7D nHɝ8@qv&-%MMK׏7cT?McVuL X3vw i廼9\zPLTKPopG/ks@|'wGa5DNEyMJ Ѱt6'yׄpdH|q\Gu+>n[)K UBA)(m".[صmf:B$,? 1F_z@.&@X$S^lP&ZMZ EV.Or6,/T\|0c 81{aP?T;0fm%ָ\co#t+3E\++gUD=.y2~r: CeЈB1- W|nOxI0Q'qoz5Om $TU(;Ĥqi䦋8/',.," ;6,4g. A+X8v_BB#۴8KCvA-՛a"PAD~{X,aN _=z9.憘݄zlԱC>Jp}peWqi[cw[U9^i?0LY`>q%?Z9W.T+ev_>.\[  gɶ[@ު|u]4#/xX%4$zSO\C9`˔u,;7%nYӵ4)_Ve0ElewDzq%XOpinݪ9%$T{ʵS'oj0{Ȩb=m;H߫?p\\?Gnٲ&l-W v Kѐ~z/ܔWr4,e'@\85B/FQ-]顆9J6C*k Ō&׋c M& n̎_2VqՀ+n L<槬4B.I˺8ë?T1kV}UOz\Qz=.XSɊ 5̹G^f >1lo]JYs賘F+KuxWE>y̫"JOh4EuB~bT{P|kD 畿kjܰs\{rGop}TaJfdTf#&1'iGiЩgg@ k'ZKBWj4),-FwQ/_ŋTZn@§X*OMF[M̆c5RT2%R0LjP&Z \.gD{֯}Ri{t_["GR",Plul$ěPKVc/@{G]5Nmk2[|3/2\./ro{`%&w{Yo(:A+9"X_OF)Lq[x(|Ӡ~h8P6)HCKJBk xE0Q6rvuC.]suYc`(Se>dB՘ЃSpn6RMхIUl(j&sT~[x0 3iG%}'YހjBUW=u@H` N{K@+dT9ɼ}(# 2&W`?0[+}7Fk>'"t(uE /hD_Q"myS$٩r}kRJVM_3͛ h"~T=[|IIFs5%Z93k^Pt# Li_Rx Y`^zJUt]Ҽ\F[7y# |ƕJ#Тh gv>?--*770$ n0Ho΍sFJiɈImȐΛydzKwul3_'K!{ ʮ ]ALM;=,Xb u;dtJcFrv|*+zl.0w]$gk#20Lc S3U_HM%v@y\3{{$.fętX(i،0YQUfqI/SCoznT6+JeQTBsЦ{hF}"pkvE>c66jWUxضJ9feU:K% UDpm{/VS=ujߧ(sKnu A(۰rvWGIX3F (b'^g*B10x2T֓Fc'h#Q#9<|=QKŅӄ'i6lWÿ `[) @n(x5+[63&z9컟;tDibWpHv[RG(c`@ߋ~^ĖӲR[o]Z$֠.[d2&:pp_Wu:o.X^sU# ɾ1lwͭ(ݨ:Ig_z0\㟾 g4?LVܞ:Y/5JUY\P+(b&m%oW3ܔ 9Cy;{^Ξ8dn؇~O3a"E+ţq|qf!tuO/"n#' ZUSnz!r'px$bBԞfxz0 )xTr謗p[fG5/Jݣsb ` 9N$\"xPQ+񦫖*[j`it<7nұ[ e 2:Kaj6M f.8hr$߇;2!=]:ia'3a4D/js଎ׁ  g .>3 > tL͊`]@Jh , k ͎el'fbP-KF B'R%[a]"fZzUgGjr!/ЛlC9mD|BעGhŤ f/T''2͈X0 <6j"3 P$7B>|S*|8-iHU"U&/Oyf~1g$&7* v9"T<3}.U`B{a){N$?B0}٘AXZװMY,8 fbBmwydiGKBN//O#Fmkd1{ej(Лy,(]^ƕK*lk}/m%2MYD]뿈RsЛ[ čHR|0qDg>I< .h QL<೻F{]T"gj{*wi9XmŽϜ!ZA#lM,pc(?mf_#g15$Ԏѣt,bH4*6`5DZk$+tn*2y(9|I|b_D pS<T! qO :"hD@ \Cx?@ss|s^ǂpu5rG GSޔ:gmB;1^%`2{aCQx;9S ť%ͽ~--_k0E&ttZ9R b&lϮh&z5%!+ĮSWphE]=Ո0<%YwL\}Z]3w=MoIKr4"}{V88 .Gq^Bd3q f(j獜[Zp26^bc'+ut"%n.Y?@ a/ej_)#,Ԇ 'G|]Δa!<[;1aݎhib 92E+ c&MȃV*̳=J1^ #~"dbSmvBJ- 2WS&k1DI]*.n^IU IgW%M'&&bs> )?D;v3rB0-Ɗ9@uBU݃njA4ǒ.WGuk*QE0k8= W!8P' JX@[MGJˈG 1J"D@vk+ "K76*~uH:ܺF-Fmo9>:gͺ~FUȥ @>,~Jk$(n|N$D3̳<=}Hip[xx)@&S8j3 %@:ywqz⍇ ERtVֿNrђmGRJc("qv ?{HֵJxDMlFUi%(L5҇[lN@"&"2慨,ɝ¸F1@cϪKe"3^j2C=]I[1mile &yIrN`5:鳾t2W ’ kwDc gE%}=)1/Z l^+rDv`h<88wosB'm* 6h|w 45L 4tANmSeq\1V ~V&겶󋁣8CHRZ96&=lSVaoMQ I30ITG\E' .r m>>><|!#cvYC{ݵ(ZTZU\܁Eކnbz(Ef%{{F6_!G+crAFbL[1x/*{u!vNE[5q>AP%![kk@Gaz>3{Ze3%/f7 +~$hfd}B28|M}wz%č}"D.Qgd-(k,T[n X@e nosKazWҠ=aVEw9n<6f'Gi2^{0v6S55 aɼu Yo[QiT:\EBt-H fZTӾ ҇Vӵ兏l|Tx4-dl$T\@3`҄l™ #@ UR=aKol6m}'oEEHE o||DV@#n&r!@ps|ϡS@9=I(H!q;!Õ}ĭȍ,|(LOi*? 9)^ u|(Ku_A[Uޭb^Ø6mwAXx NU#7-5tibyc&{6Cz+isd[?s-rpiw.O`rw; ;I{N"0]'}`9SF9͑ppuքЙ D1z_P6my*F6'h#z*hT9ag?_<֋ {Ph iP-'i;T&U8 e ?''ڂ,}><ޢ];XDWVR'XF.c%)Iud xT')\heDM\y4lœP[IѰ9nR]ԫ~0%}>X}6.TwLMfv6>t2bI"%;b @Xԅ6@-(.(DG%4`T,ϬRx[ ;W 3BN0сAj g4F6R0_]BKM5y&,=V& ɶթڐ1`nhܒ+S>շ C*'(<㊨bY&_ߤB= Z«H#ov4k*>y5OQ,DO.ӈ6/Ȫ1_SRw{jW#+}J,^6%cws졋^魍[m'9\hYʦR֔y=ManO=[OGnǻ!-#fYk:sVl-$ErMsgFu+МV@2hQ+-aNS{ p}Ӕd^iO"ryB<Km0{sh4uu)jݫ"`Yqy9 mwd'3C gߟ{7n.H >0(DR * -xcDg"qQG]Ɇ}(3_E?59fJ.^-/)n` GP2ܹC\^^WCF]XE;|Zޓi9%pZh<~(P5WߛTf^#"1v{,_}/6,F$fv`љjF˘R'~eF\G )VɫvB*AOC.C8ѻM%(3JIqӹ8)i"ø'7&/wo)csx ]&UK"X$.ÕıIAEy3zAH0ĺ?]#\դil/"82H&͹ Z' "ioR_`׎at?1A@?6+4)8{ix͆+OZ$MFN h'!TWXl&OQxm-D>̖PQ:dͭC kIiL":'7~L6"%ӔRƼ6'd [@{Ϫfkܥ^Kzi9%fU9L'"ռd3X*տaW <tv1ы{g 92 ~Pdr1"GPQi3nq\#B;\Cf þ1OЇ%Z['sBp鲒Pn/hdZ[ ȢL突z։ac6Sb bae'%MHl j?0=@D@ȬLCMy~F( xP`\vc|wҮ<-#Vb'A6h;`ne Yk2nͦff#’q_C>w~Y܉<X, wI|a隷qߦJRt)ˑɎ׈߅ /i\ O9{p }.;xaɴzF'Gq,iST|{ rL' \cxH+exM]va@0%AwI3we-Xо4q2Zрy(+l.½HJaXS{C?`"{XE)JIJV T6:S誈ˆ蹖/#6/ZՂ1'k< Dq0QG݋Fup^gqv}Њ:iF 7%E@wΡf?{}F#"u\,n+9P!QUĥ;3^tOX"L,'eluCe(~gZѾ$ |ݷn2-<Ō$"$zxKYIae7;V,mǠk.AWo؊(cܔ13 yoX^jI3ӳ< CX vm'F[^YSJh %7oԝcyX)gc8n Yƍ"l V#tkX;ۓ`̺.>3̃8P2jW .{JNO 5 LXrj=!a8w4HK(HUҨ-GO_con*'CNl{ :;xY̭2{q!\֗6[,UcSLڹvin*F x8Fې3%!p3"}\1w{,_֫zIlYgR," ~Zg$J8x t֪3} Wٻh} "60VE6{:'_MKP1Ir_>1i3/BT(gKH7)'lR|sL)-':MGɘ,PLAVqn~F/`0 tB*Mz"߰ƽ*(d+1|1uMǙlA!Uߛ,r*rS2dȎ2tE;F ;Nbvc{q,èF=^O XzҼ3[rI-!8>jXQ%قA|[8O^ `hҘ\ loM8.} SA[|T$Q'z(v?P$""CPhuV}Jq5%Q4Q"-2o~083_T_@ySB~*s͘|]DU[Yt&Zn}Aϒ6z~'{1^gHnХ?m”=faK ƆKn ROW)U+]zg˚js;KyZ@9ImcMI~6KDq %jmىOq=ē3q-"PK3ffa{Vǫ_\?ݲ"`<וc.p뗐h2FViò,fiZ6돆¬[-t+:`N- "p @c ^BW]K QgN,UjruVس1@>e/W M;Vݏ@mvmIs0fքᕘ&+N w/HaZ!C\p偩>>\&QᙌYH(p}=d|Ftz7=4s=L0+>Bs+ } خt |x,VNFӈ@|HH[U:9A9(9G? J꺇[~,frl zQJHsDLs2 {|ޟ> CU=+8~jLAMfE<:mqhjHWIsԻ`/VN K2oQ*KJ-4tClTݷU oZcDo~~s7 M}561o>ʩ3G Efumw\isb#pvy􋜏u[ }/)Ɖ! γ};ܡd#Jy7|im]5?s |SqLEr H[}:&?EEhk᠈-8 E=W}ܽi`H5sDS μ?$N%!v@}br"Эi:[.q-aKIVPlLi8Z/l&S-2O{k{rXOr8l]zb"nKj6WOlnY̅YnYȍ5W,E;bQҿT#2M>zMX5#3VZc=ˬk)0Ib=Ts37\O z_"`t=KKҁ1P%@}jRhDDk1 C.b'R(^Ox&GyQ8-nu*eve81,L(ke*h\>'U1;;Gb/)w{Y<4!֘&XWw3b"z19몴fʎW\8 t;{79$` /w7r|*l_c}m I#LVVgdI.D%֐g'_c?R~@98}q9D=h[Y=s& \J8s=Oz FB6^w&GN=;pj101qYf5B݀\>.i;JA/>րc;-yNWQs3ɦރ6lWѩN{C>}O87鑒G'$k/}UD$T g`0ԌcG4݂`|?'[dniDh>\Rm.w %_vvTK9W3(|#O1+Lwe.(o/m'n-i#Q_|/P} ʆ ?0dkQobij  |::R=(-Q|>Nѵ9xH1y}qD"|w#@>kRI '/+Cf#GZѾ_T*l @-PXnw +@;h!4p3g );1v-ySwY|Ϋ!V+(#Y"8\ #y2駹]%4Յn\jyլ> ow|$Hv[o#LV{vCO` f6,r+}".5fYڠMIKRt";D/W;uBj0 &_Z~YP!o,jsѠ]ϷT{;۰U%@>6A$JmPɠTBtD#kܚHq"}ONp2"[>Z ęsI7Zю3T _T3wK$9S6arOX-PdM,j?*Vc:ƔgMIVTc[~2XiM'Mv*G U_^x|:N«W[e;9 WIxKEj} 69u3ԯI;Ū(F p2UE7z>).8Hnc@= pnβU8{5d]'_0f-'̪Qa[oWT$FuIfr:k(.4 eRSKo mhaNWv7*-gݵ94\ECmkvxVͅ),X4"⡍݊R4l4K! `-oD]饣02Nٚl^„`xy yd;YvZ~7dԝcY Cdw?E_ސe ċȭٍ-\cBp?45#LƲX jkM9u58nx:ڕem[5q uo{2~$(G+"a98Z[57%J+ڹP[DzRTEyѫnnN8,OTSR~D]z|.x-cpoD` IM$0{Qjec& /z*n[1Ec0)vc=n( 4{%g,핐pA@ۧB[m'A {a_H.0AqtwObE(${Wj`$o..,-IFd\d[lY^*~ d`J-WCd(Y’j9^Fy~Hkү"ÜіVҀh{3q- ̠t<]fA`)K2?qAOLo=h +Ӆ{vCn?h9DJTz~" >S~;ٍ ~V*)e*Sc_,瑩AJ4y.&&GP'Ծ-(֨}:Ulm3mR_nQ<שH"(yDe9vNib71'Rd .YPIZT5  FLwJ-ZqʀDluɷײQn[Mh&?]ow4r:#h(/FnZzY cfGc\/y@SYgj\4=^ k5NVsqYc/ ^PfÃɫBX7 [ AF;SG .Ѝ>LWu{6|{!V6IS{/:~?rA,߱v?LPdOko%[}瘲WO*sJ)E8 7O x7nMS΀.#RL JZ4pW%AF::߁ĭvgCQ qnHnPc\ci*K 0'_b&U;=.,N]E( i,okE4ɯvo$a+Csϧeެ 1EZ`JbpVݕ8K"ߊHY+gQ\)1LO_!w}FRL ȸJweƙP 5Fd 0%T0r_"eKaVe>1KV9RLcܶ2" A2yזo!. +d^'c-N+_'V#䅳] 5f >wpJXR<+2x,_Cp{$>yę[^j煮Pb6WbI&yYō+;*lP=(##cVW=Bwnۿ&(I8p]\j![Ceգ;=a$ؾwpj `'Kc u˒aQR6%Ycy8GߪUm{ӈ~J /䢤;fc0HթՠQټhHjVa2H QX3 2W خ K4@>eD#xTQ-t̀s2H*/!y{ӥH8%GvyUWOٻ^;&jVd'У4X` `4p A nXnF*B(6| pA>}F HyRs+}1J BVjLKI*:8GqT3|aI!%0O71G]@Xy/&)gOxPfoT9LFMatcB4a?6IMɲ== [9D^ocnT'U'0W5"/ &"o,R]6RXwRRĤk,66ctX~C}g%|B`ȞNUsIӺqsw\wto&\uVk䂘z85'c(g]z$f'ZIS)BhVnrZc@ԴA̕P+>eZP߶?ߐ8iX4.|!=\!%Vz;qЍTN[ hq332#'+߀j2ot+E%]պRXsd>Ľzⴟ9إ-,7e/K@t*SZ]obD6~|=$ӜXL##!.qF/%잃A*X(HIϖhT06WM R>~eX&(, n$]uUיq"&dip̼ݔR+q$\yutRm&1ȅ582$7߶B5l^HctFYPa)>3v'=*CqCiB}awگ}KBx 9qpAm4k10 (f3"ӉIߓ[#/Fѭg̊'w43!GhVR;. I{]KRC]yj?xyXܞhݛiXaWE/f`ұKJshӬ)~[2|/4Қfq_{>pDѸeyeNPodE$|e.E4ͤހ0tӠ r=^KNwz^F )WM#1sm0Gm[V?s2l!sV&y/J3;V<ՑwsGn kٲ?$EEr/.Ͻ2j55Ӯ_:gY]@C0j \c CzĎR/Aaw`Q01_U+nj>7L }OⷵB |J9ֳ* ?Z:)1PdN:+vl Thșfۼ8qOXi70VۢTL"hrQF*&(|^|uúM]!X' -H@s (w.߲`S={R~QZSIUj]]8#N>DgYItw`WHF\ks ƃ0ꫤ *7AhJrI$$i^ɛd!YS]bu׫G=π_UuGRY[۴R,쾈ΜoUM_x®$4Ϋ[:9Wn4(m* ԭ-ɚ-P:<*6=z$mDYqF, 1 q1.X^kv"Lab~\fiJitzčSj3kr:pTaH.+@lگIB]3rfi(/"wSVAGn~щ]7,ʷ6„0)0hRHy ś㿻Y:r:pq,zoLZD%g.Ëʩ/ B.e'!Z `ߝ>U8*5pp>u \d1HeFʈ=Lzb]<Z{g7d:@ScpElV*,7H:>/H2fαma^f:r=.%v)c²\:aWW$✩'pl{:ӶH?Hʊg.QL+4Y^q_x,L׎ti}?0qKlq~4J$Sv AvEup_ϵGz# dADjdL_RT}]Ӑٯ?dpR عTQtfAڈʹ]NgEAN>7.?nu7nXr*,5|&xkt)/l;Ԏ B0ǚ(S'%D8LNvLU.*NuJ rM_b5nS}@ۈ%IqB'VhYHxA!jNь%5Nwr1v[׊!&M%F5V3I @};hL"Fq/Ffm3M6OwZȎoaA@|e@% V!zR\ר{n5^.D?5,7gMsڌعm$gzїǖk)']7:z5E. JE4ԴKælV8oQDFM}9O? "}\&9lS FK}*W W^BQղ%R\0NZ L@IxWG:Jv7zUj)P{Fo7e&ΕHB'G?tt2gL'tExSm2j0O^_V Ŀ&|x8]T]44Fv*ZgV|BV͵Ğթfp j/Pd{\Nz#xHHqSOg{P)vM{ՊRm8gm nvRu PGDa|S̒[!=iet\.7cd>l:otddi$ t+Qd5=R/"T sW`{O!COs_*5sZWF4(My|KLQ=Ќ\62_SЎV:#t^Do0fuȲb:<  #[{'AxL{̚Spj`'5iʹ迪TY˃G7qKzGCcr`WMw Ͷqr5b S#k + )%q/'Vam>@\ZzjCx.zJngŽ+ʖ HaMp҂PwL62\XQOCyv@.;7-  YlDH#s!vQMbFvMpǀ? *vd =Ƕ3i,N˯" \W7өbk:6o uMWp)L4ǨeDZ"*#HofbdqZf6.ŧҰ Pyb,\zUOQF L8'xq>0XAs[q0z b|2еI xS`0lA۫te -,YAA[xػ3갫'# M); aQMZiT>RWVbDg.BdMpLp-eChZBtwd\#G-STmy䢚<1fHX׼@gn%6⨊=L)f0cƱN![]5$bEÄ73L< Y}]R$-Zʸ9$ s7`si43{P&@Iq#"' !ULW(20̄[fYe'A1NUi o8P6F !E.I7Jc}smfm@> | yfn4UYXH  CU<hDĂލMMr>ćgsw˙ >`Ż7:b܍FgEͯs8dZ`^ݸ׈qFl>b7F>w 6Ղ${Fѓ(?&ZpQpUAeitBBoM`]21a@߃po30-e+4jew $(!?|x $E h$w (E]X5z}MU|Q"} vb x8 fϸ,( U$!6f]phظw0JޤEhZ[.ǗĨ$5/g2I~Yq|^дZbӲ+uZY‰&T]B/+NkKr̴YpʩEKb;OO(TF!+` -T g J^6;V߯d2[u裁'̽8!YJX;{a m)}$g{.Rʱ\\*Px5.;͎ d˺iȮkQu!{WR˰5t{~ǹ z=6pjzp:P RCC饮%m_?o5ϭAAf^K<R?:&] JZ.dNXǮrM+;>x]HQuӘ'e4& UdlXV۹'1ׂ+ G"8Q-3[h8TgƗs4,}(VC&f#7$9:a 35|)E끋6)1úɨet87mO[`56Y<a ϲu0^ d)}0?:f TYy%SꓡkucĒU+[T;P3IOΦG4"8"ZwhD{NJ"=07B N4&B::g嚪Mnjk!!Nid$_B hBoӖe `!h~g헱=09j$.`B M cЄrkkf;ǽlYqpV !?1䯥DvӬN3(Ab,G(0H:[Hۭjh 165U+w 1}|>u[4oc#P|[N$c 1e\> @^כ^M4(TnvFwc}R[6RV%<)vS$vX 3l2sbxg7zCrC?˘i.M_x3ꇴk);WcR=$jq&-h5; c {Kޔ9g2O+Dl!+G$Rnc=.H Qb(GQR#xfo+ŷ@\8Yrb^z btLnϠ3h4nj(u $(4KG w8#7d#cx*[ Lx.tL?HFx[ݔG1A5Fyݭ*MVB㫏~+W{G7.2zNdnZ5av:tj~M +OQr0|GWiNIVT e1V{wQL]EьMv{!JPGLQr" 4 %6HffQ׮ z_هUzkŷtG> 4:YU|}k!vƥ,̚+cy ׶L >Uko<ۈN~:X$2f7QdB T)ΆCQ/΀aR ~Mr$#G2:(yDRΎ`x6Ciyr ?}q4RL1.=plIs~9._4kSO_nDc}̀oЍ 3]i7$1<l!_ ޝ*arXDO\Rl#l Fm4&T{^5VD7@w|DB &۾0GUC@ffYU\N S9:W=ޒ"ˀMdvh!ܡX.߷ pi@j qwCTT}#FƝ^P}k\UGݐ+$RÜY̾=E a+y DOk;E,FYlM'1v&G <- Yc1~ t5(:C#ghPiXy+`n[|Oa Lr8и@X&eқLSK75 T6|)FŃzT{Q#KQ$Ƕ4|QW5bB! ^5F?;N\sr00UdTr]ҫVǵ@h~Wxk,w3Kd⅋Gee0ĞC1Riv 3?#\3,TZ~ށ-NėaePDZ;iij[ebh9߼ws(>`b Vt“ZKK~H(:~-G#6&c| /E3]s֩Bh3}^n[qۇ.;X;QhK\o=ݭґ$GKBb?hnqV{HS`S^ʉ `&$F_RDj\ =ixh@'P%,DSUڡǮ_zCH[c!# DuuV%ەt x '>/IN>16c>|{0Ik^J}4{+wi(ríDv:@;< ? ٠ĺ];{,F*R0T z]#J{4mC-!{ꕚ~+& U0'@Z_+2FwRƾBb$ [ކdP#^ΎXu'rtfϓOpG˿w0 ޴#F<{p@r; bqP>NL͐Z:ga~B? 6 &W8%+ wN4qT">cNEW JVe@+'_SXGn`dxnHP,\4H kM詥QV̞Y@фS]k2 N~:::)I5פGPzeKP%d8:<qvA{`N'MPFNW?٢[F,纙#sE롟*ftySR9=\7C_y8.e XĮyߝu!lxIp[v/by6l9B$ ;gW e'kLwhD d5ϼDT-.λE'zAעZ-dTv Iz=ssW ;+9=> i +V;Mu->bz$S?! Z&x/kG1G_H$vqgB*{HB KS@znL S~^~NdRcڏ:<+nj$މOХT`cV67#rdz)ŹNv@Z|~;%d0RG$=6zEqQɁʒ/['V5.a #/9UX>Cl= 6a W}CJWőaN㪒__K8MF?OaSp pid&PM5N4:ؿv_0E` lzD=iLykR a.P X]y'w0M댑:AM(r`Ryܷ`rMMgVJ`/ &ص',KNU^cWP Dp-=;%g#=;L`d{ɇ}+3^ 2 'u-<8eR/>ʃ-Ϩucn^/@KO )_@&Gw?Y-Q2op &õ0W ۉa#R2av+)E]E 4S3Hj| cF.BlT@V.2jT؜7])ɷp@:z18i"4z0OpWjW@L:[U$0ۚn~ɝ*G؅6\j Ү9=rݪVe19laj ~,|L7%DG\"Sqve`8M 3]y0#(-:{ Ŧc+Jv D8buNc2vGsu(i k "dnxNa-@ *E/J$\oc_>A^!'24T<|_jRwޗ uI?s&^8A ۂ|"LU`GE3 b?sZu3VUT$ٱ(]E>3 c&GuP-uy%X5cm$ |V$ns!4!pJ}䜚#H~"XS%JٳRkY1#&8-_WMNė6 "s(`iBǂ9]w[a|խpk4^XR,}y93bQivۤ^ݯP \`zw & C V[:|x6Nq-<11#/*[#=58f׶x9Wc_4 QC~c=[fZcyN\rwm.)#*xRGjc5@Ӧ&綱ݝp*0QHrPgͬU.#CX`݌0? c۞sʓ*6/@ 7.eƾJrwa")E?J@$cPFILWm7UJC-Y[{' `>c)C$㊅At㋎=8<:-7] Gsrj]>#!{_*l}} 湷j;;0]Ȗ |ؗ-~3#k=潎`{SuYM(wg ~*U>'9Stw` džch% d~*v߷TB(EoGorz({7[#'0HIpٶD 46GmǑ/ qr7lMe A4)COZRᗆwD!>L;h9}u^@*y Ɉ .O%2>/4K.- lljz%I_o>T5 8Yv'å9s?213iIDt~TC Rϐl"1VeRJg ȗf^uVr^q$Z0g: *H: |_-Dt-%ws] t8HUk!~ 9Z <ߟr/Swx+`$hͿ˶6;K I9>ȧmMޓF~cRSb-,_hp@8֎;{BcT]d:@9 ߇?fOQj))+;'<~..Lo*"?+Jc<_fZ"hǸ+.lSg̞-qn~҆H,KF} OxtظH#E)薈~A}C?"i ] E|B?uy /S iO܋(av/:3<%v~lC􏳠@;-Ve~fT[4dʡe*L5:B ZBk/7UqM6;f=e!_oG=|GGO9ٰɼ_^F\?ƮJyA_ב SHQe[}uQh:]OsE,!>s]=dr ]tzE0GgS[R$Й=3 pv&u¼Sv ӑN718$91lӠg <# '!ks9Rnl(j% u{)"~?Ey:mQHZ2q&~e(w*rYZ{2؟ f]biam*2*`G"<Hxb1\I†?Lү߭=O87yޛJdzd `JBGH'Bl«V ]RkvAׂęoE٦(Ǚ`<mɼҌbJn'"1T%]5v.yY«}Ua9;7BdU"X<\c0y$SA2M @ho>k W~xұIF CHTq.?Kj(R=(X G sa)e#c\tP{3q)O(Ҕ8鳽^ǡ o%1:ѕ5`/f|EC:׾b=9)TlϟGKbXkr:DcgZsKDOzU!&ݯZv{>}Sʽ9-FՌqsW'aegݗaȠ\SJv窜8 JbΨVq"L^#>Prej).]3]W*~p9*Nd|UPi'V^hM@c~`C*8۩IP=gmV^䞋CK3Eȸ_[J6j-t'Na$[7+5?W:z Tj*i,/)#ݾ3=~~3b/%~g[a@3>?49-р١.zBa3Q[&C9_ -j"ݼRE̊t~xZw@fk ~"@? 麴yB\q @nx6I8_WzD gqa';?ԛp~3El!Wh3f.A*+<2fShI{G#1䀚4?Vu.hƿ.4u8qF].V ӄHbs3Hbw}k5P`F7/ُzİкBB瓨- 0ֱH^ţ&My75!iǃ6Ku(2jkO=p3ICH^|\x3k#jC_š`qihC%ɨn /ɎmNT[J#ZBºb)%* ;T[op]̂)7j^z>CW~TKu<ǺA233Q(0%x eWTǿLAPݛz&-qG[/?fyӨiHrq Z27JgIC΄0~T,c4M'e4bѡz%l%L^&1fc;RѲ-G\hq)nƄ禆( '#N͆sG1)a5Gom6+% }XKtMN`Cl^.smiS8RN1Z&vEi䥫vF"OW&xB﫸"mh؃1Ꟊ*P,~u]bFt309`UMeɃvyj:{Ra0]'9(ҝ'eXLPw9B,P6)0a({8T"ո0Njvs䧒!|p(¶Js!!;5Psx/QǸkc@ .mac[h0 d) i+Ƈ0Jeï HKl;@Rŕ˄xow2tᡂg6XtA3D ӛZThBː i8 nTt ,e2UaG.l J螭!\iZn!M+Vm3ukMDPܫrvDC 2#F6 "-Wfl~@`Ӥ\Zz@ϖЀ'"ޓ z.K= D0A^Oz"al~Ooq CɃHccC "|Ճ-xZ|_N@2mqr'(Хo`^)`Ƌ_![dXQښ9# R1v~aO(~2䣤RՔHuQvdF3J_tUtMȮ<F;YR4t gխ։"9u[}|e {v÷hֱ=kpFd-d+;@P\)!F"E(v^-DБ'ԉe:לkmUq4;mYwFc7}뾰D4WL_kzNig-b37 >jjJњ?wySnq?v;p)v Ƽ4.i=oxt{O4,pr̀ɥ;E;6r!E|ZGU¬a<A.4#BsP@CfeJ^<+ !}>Qm 03lۚ߾Cq'_Ÿyg M[&?_r9k\ޜ|\a 79#<)kc*R݆Y#Sz3 UpB|(0'VMZU~kMY Q [2f -_E|-I˼:/ӶuG[;PEv\'{f3 u0Kil2f.;f?+Q#v1%orPoze3,X(Ť;ֈd`yHv|6̹kQ~3ʾS9tGC2 #k_4S%ƨǛ6#+`YUjݻԾaDCwKtO k4@֋ΰg:PMPuOىs+5v7xRM-Nڼ=xNRžS`.vX=nˋ%;'װ-xWZuY<SlWNrDnf kIixxO647'x3kA3tP݈VK?+X>քNA475Q"J4VK9ڀ7a -XV;.Ն8M7] wti *!P]|yF9%|HO_~4͇tRĊԥ1l,_#q£J@2׉9P?Vm8[axݍҹ#!">۴LU(lL)>8E8ig +[J@Ug}+23fƤ7X̤SI ut(k?nnn^>0*)E2B"=͉ZbJ)[ |Бöqxm 9T&~ Sez<~nؓy@˅7TK֜þc هE*eqD#{?~fOs%7jfYBzL&t|{l+rVe)/EԳ!jV|dQUw5beM+yPm'wĀ>3Wy.E ?jtߙ~\?M8/ǎc~'qZ5y!&0Tw.q#0o<`KE?BǙ&}. 4B$ !G蒈A*^13;}^dQ%AB8oN*]14wYvHVZaP<(*4N~[/Mȋx.[!dR ɹMVEż8ƣTTSt CDo3rcte119VMT]0͉Qf.L*>>*M MBĶ)H6=s3(|#ҧوYB+ײtWkh- U5&-}<̍"Dg ze ma, eN+o2m?>KMIiŕCذSI2망LTED`U(ͧ*>ʓ"/ D\k2I lV5{)=@_-,\=O?nk3TKJ;%wëWbbt ϴѶ`Ʊ Z @{ilF~y^+LVlkJJw-ldQFOt#5*A?,{AE9WKɥ-:zD؉WynǷ 8u,qq DV:\8N2'~b!wjI+F('_& DvI?4 |ioh_Qc|G]quLa`E,$3^dz`Pmfe"\clT̃"Bw)}dru~=$z<#:lt;ȼLBg"nggPӏBgv[k8}IytHWu֋$&8R&bKljc!#wpJw\K4*?Oh,aO*بrߙyltK!S4 ᛄ.2*YVS #_>$37CroTcirlβ4 ;`Sw o҆}"09l\ WRPwxTصg@*kaQW:K+2v&̡P96$fPV8@_0q,;lWٌE-t0⣞KX,jɟg<,kmݖd}:Vž, _Q$!Y8ҷ'ߴu9 [AySyeiF=Ԩ&o{̲$YǟPPZ(a ( - E3AE{P'y u*)oh}? W[hnYTMr)ؼ?/$W=Vi7OfZXDznDS^14Y&Ƿ8aմ-b N`m` q0ttXP*j^u!X1/iëA,ٻžsJet;o| c UFw-Zɋ.4cU+٣A[x7L,=yơ_ML7e!z̤_CoFdmVY:cMkdzeBt$i(YR `sm +qP0rrL00Ro"Jb|4M_KZ~!o _+ݐ|}L[D e[#cW!Gd0V_r޽\OTBTjɕ%sƊJǂTM i=oL7_umrQ tټBjmȯ!wu$?[]#ֿ5 ",=͋.D#ũ"h@/ύØΠ%ZJXZcNM]B^垇F!`C"t@~3a Pɸ4iآpA !#XNؙjΔJ09s5Gc_@K9yV9!NhHI . u'Hrah)`,:#ەUjM*7uTH| j61BrFBteXkѼ!v`*Z SތMM4C-;>y-A *# cK@Q\{): i9Q8z9įnvFkg /P/l% Lv4C?A1G*i܇@pv4N֟x0Γ=g$Tl@;h,(Y ǵ5rk1hGx4:aQBʚŁD1ĝZ7U{ʹjNO{m TR5ri|6ЪO#grmpP%sÊoRf0 qDH:u |q{ xL8;NfQ~-_O=֮TlbZLi3eUgC ,\V/~{.qD\d7EyVBYN*ţ1V~䡲.n֤9|u='zYS`' HkrYS7p!!J0~\!;}T iǤXZHKT4`4a{|Din]bhI19k,hRzJ QV Cbo'e I" 8rEw*9GLd|Vkrm%L97vR2DѰqu'xE#M&ᴅ,iI-cلxE z;m VXnP^"}ɲoFV'*<{,Jt;xp [1',Bbv!nH6|eftG?۴ acI)#YP 郖jT$};D{bゲAKX*enVl?Q<"u/Y$wEuzD‚/dDds/=~]{Gy(#W|$^#hU3@yXȄvUkƧ~9]fQZ\QMS:p`uWbY7^}-f/,FqpFJp3{mV գj _QŴh>fm@xBaNr7A .0mV~cSKAR:s:EC?rLFU4C3]-Gb; f=O^Ώ}9U~'[5 AeADOel `עɹ**ښ~?;,R[[>xfYA,Ay4BGPр4 \I!.`V!"ީ&OOn"etjTFA5ۗj+x\wnA<7-T@:+o&<Ӂ-|G F[ 3v0H7'^NԲF(; n$mHikl(lX9lڶ k6 tV٦y[ʄG8򁸯5żcB_ A]Qǘݭ3C\rH;4f]S[ f\3(,#r<;FQ0mr$0Uq%FMG}Ewl=߶(e %gڒ¤TQ We {Eͮ72eOF[1%;\sV ÎO%J {UP+4BNd[%c^:,ů޽(RU?4GlB2 4j=ufWޕ@k|euʱm7;I59 ;Q֖ )LL_<}z]h[uetTYlA¥uF!/<&О*a*w< r+kO&:d-Bb%o^3F0ډܧ  4>?Pw$çf]crpj $մ?eK7Zf,ѽB㝶dLč[DڭG.?TK;,u v\!q|ޥh= yG-t]0)dg &ٶ: :B3VuzK H;,oX|a,b|?!eNEt/(z,4D Z̋A Uj!$J'/ M ,RhK"QWV (Zo&X Ǽs"v^XƉۡŎ"LCn?_ɔ>Hf CTEwbɔj@ZNtR 4bSBej7̔k<::M, شVQ~N8O\$hw;ª1 cs J)>8J{9xs>ֽnFDc<4?H'$k<I4?bӯya:؀ +j[@\S(8>wF/gp w~f达=zk$yGkЍî:OU7Vݗ"bZt ƒEO岆;*P.ZxJSlIB#ػ[4k|*7VNM\LVKH/1Rp~(RyY}wpz͊ ` ٜ-]-!I P;AR  *:] 4:;6x"Nx=a骚 X\ԦcG;K XhAP3qeg48'_!ܶ'KW5E^[P~ }Bjkmg5*ȉ(^?qGd}љfj&GjX1 a)||п JSașz;*3:tmnhf^\u`6&a/ Eqi%U)B1y2#Oa%ΧIʔEi\vϣrvqdvɼ:MrAlDBW3넬Zu9+w wG(9F?% 7<8j!׮E\F6n{jF xc"Ҁ+Ug<ؗbK(LLT@t*{c MWpw>0queuCgZxH輼Gɢulgg7e[^9Rȍ6c_ Ho?2 ?> :WoXh;+X(kS3~ap)DIkxVkBObuoLAH6̷ѽi8!.floShH6|miW|cD%im]􌘼@A;oodJ5upk?g}6;؜־bzpZ7 a եͽ!,WU'Bnb\7F \E~8Do_XUĥ˗3DwB#d 7 ~F]pZU|]/vPhKM8 iW^x_- s^GļRz/u&Qi_(c3"~܄72(Q&/g y+5 tWI\FCױ?u Q}[ M]3@GSI9'Q2COjXd?'կCKYܴT~i7bn[ߵG CǛ }-Ќ=E7o":-2q=DX ]֏ .%+RDA"Sh0u,6Q{˵.3I'pqT`Wܕr-ӇR&4oX6d }hc9hYOըINC60eJUHϿRB)HEk?Ȋ,X$uSu&cupgdtPh?C8]Y%CbB,1-Msy [8ŋB`HJ:eP*N-AÁNǟ&qWѮ`n 'dDp0d_k%Yna@:hЇAhc<Λ 0FdR-qfQ_aך~]ӄׅG^"(c=!'/2*ebFu?swek(@H^uΉHWsSbРIO爲ekTU! Eڶ/7lnS0| $&+O߹lg¡6+s|"ێT[ +>VnVE4KqDQyЖȤfB.ߙftBdՋXB |}]2n4HڦŽ"0$͋$e]K/y[<õiw߰N|AI7Jm&,>8QkĆ>\٭pqKkY}%220gxыl;ḣ%EBh:p%9 Ł(eQ- u-n_fhd| q`[@ 6zV{avxr6cI|njB3v{rt6q4=DQ})kMM u-y=u7F<$>KmExmtW ۂ}ōS>/hHM_HE2(Ӆ{g.!*;†s.zOr*I};C|sUzk׼F,Ne;Ȗ&؂ѫ% ĉ5&㽖55Xw.cq~ŗ:Mo0l^̗L#k[;giL#]ËW 4pN%Ös>CN(PijUU<8Gč-Z3X_\ MUCnWUl땁"J_[Hd&xr+6Oxo˟5j^aEJ2}:05uO=ȭhE k'56jcN~vSȄ t组ٕ::5ꐶ.>xQ;](XA§wmջ @ JLǼHڵ};_| F+o[Z'AKL;¾PI eƏK'EpR2L]3'ws = ({]w~?yD;`QF9& .;c>-y C O=:DoXJ-$eg9Y*>O6+O^ėrp")WZ%MWF%G [jso㫧|Z6? xW֔*2šF,p)[(z Tł|Gxd^qM SEOjHAVDQc_[1S;nh9j:/1'ƤIB#`$%DK(ǧr y?@ ZZmýw0BL'bJ{zԏ.j)VhQde!΃sYH4A}| dwX|R,mʊJ;ǠcD9 .Us1}R;tFWS-HQ?|Š+~ț PdB QWGyj"5?lDT& IKUovl6,oAo ]fSTܷJIICEԧc3m!extPs 4a솼O:^[ZZ{!C64ϣU}AXŷy~/6bYӆa,ǽ}pо15geP#HB}'ukNce|HN=Ac2Q?ǝ%K! s!7̷/3ƻ^ʳ m`]r^(q1;_ t3*u$l0{fmn$E&O_EZh޹jNE0C%rܦxG=-NkNjJh\z]9LUwYy :ձue/v-NEn@bçcoF)3߳w.T(g Y3=b@lW_SO*)V /Ia;N=;7_Z2bH7RDqf)@rVG5<)+&ߠgv=GGY<ZDT6 o$}c8Zp STuB?~GD'hdA9ܫh';8ܘbνsW ;倸f{ږqqt6rNݕENsq .1X)Y^R,dhX*3\̵? Ex ɿ3:q~5e [Hdw~xE1䛔0^ſ4&llbߢȆ IWa9:t"H.Knsz \dChRO|zt)&:ɣ%}V_zѦE8M^ØH3ƅPftchfj;篑l h='֡gC+ *3_zk3I1LI+^](j]M#Y@gUvGH. ߓò"vjX{"-#R̴tO y̶Q{V"Ь7eh_xǭ3͔7bRǼHlBlG d?Ma&_aIλ՚i,xPܡ0Uyd 'z* }'J˾\ooM1 1Ce)\-Կv , =z7{m AgqmzS>?L> @4aSjhv(.pԾ Yil 5+#aRRy9 7z0^DhXsSnR"Ǥ;إD_t>rFd74,,akpeLD}IsfU; qpΚ MccfsNb4r+#kd;wZs K0641zei1)[oS'xIfBo/4RǗNB׎EwQ4^^&iL8т+A^^}v6.t_m(i߅cBb% 8YQ2KsVGf*Ɯس1v'c. NՓ7g)#ma2R`?giQMQ/Ks]bp|0c ˝I.K_i$w1K<7} ""0tANkɒ'2k.G&}|Dj0 9ڕMh15*8ӸU~*m5t7ZdjHG(c)Vv σ_Ī(mP )j٣JHcߥH btU`SYOV<'%dzE 7Iы@p~ݭgI`g[`Gb$Zyʟn'E"$9mCW|+jQa'u4K*;lNJg_ !T]N6`v눖6 _y/)Z}qPCu94wh!8Uh*@/9?$i ֋Ny6ܪna\E}mDGWET GMu{²=hH7PrsD 0CJ .pfz9Z_V2ێuAI\*uA6(m&m} o w!J{p ::g/7֗eQWBp [v? z "|BgG{bpUSZFp*A/C cmu2,P#(Ie']1} EGf?@0v+:PŽ3X)R> tS 澎)8Jz)=< g˛QSWq6iɶ*jiBaK[Xm,!$GHl4%L )C촜vJKbCk:wr/kg݄NmԛGֺ)FW4^=qlU؅AƝXHWOQچ0; X1~B)g}-Vq_z)m( qp6?0K qkB78G;>K]H~ƳM3^Aɑ@h$ 5YSuO5jF9hlgvN_+ Z,}-Y\z3l l0Wឝ/v Q \$KX їw2P]\c<c]MZ PYg_h _,dB:Yjb^O>cMYvRL9t8@g}X- 3xB%!)?HNujأR %7"ΕSD%R-aS@ڎv,"5#5]ץy!y7@Gۿτw>F4T+q>[gPPx+l[LYQ9ϺK `k?vvl_u!DELY)Uɬ+ݎ~[)%z1.$nlN5{LlrtWSZ0 *T ?-.HU^ bVbFHx9AN~C..%0t{y-Hiàhf>_g]y6'ÈF(V},7zٌ7Ux cw\v}w\'Һ0wV$YRykzT9F1_歳@k"*" dZnT[. ^TF[GT%=Ƃ?욎48D!p=n$DJTb;=pQzмFզb[)6u.'}6z-+CM3@~@S̱st^l؈ć7]l ^,Lh2HעH,} $sg1VX),Ikj@g~3JQ;8`V&ZzGPïf l !P0,=児YsQEnﯯ:m Ґz+Ε}xG sh1N,W ,QNcf*88~6f R3/GJ_FCAJOg]LA0M(L{#_B>[w'S@RSɖϤZi?'yu; YK@ QWq+uv yHvz9`5͏>`qoT  "SsK[ܻnG#b,^tDN~X{UzeHT+nO6յQ`:?_X K [B6ϕ;2ƚ|d@tFȔp +ړ]" n~hڧ7nVa#qw2/TMTnwzӾ!(wT || TTgx.J "uxdu  4Cq-+t w?豤X<ǨNnZ1.Bn|A'7'|(z,i"_R(Dwc7|.#$G.6@nO$+|aWҎvk"DicgŃ1 :َM.OxٙC $ulk(5d!ż{0qT:=vRo 3ס<T¡Ed .=/ LU*#F& I w`;<::8R~|b.N2Zq]}PjqIgz/[sT|S"AUi8ɆI!ѓ"po@B ᲂ5a\{}fA<~ՕT)zPe^sÕc hA+Y޲d tY >-!?&-<ݒv٘zChHb?eOidZ- o~$ist2/.ķecTEal V(eۉaW1cr~r QB@-ϧb骨vW>GE HG;TuhmZ #5C(0 \ i-FWWkUNQd 9qԚh-4ep8>esj1O-cB.4R\*2#s8}Z #ø8m%!M 4`2GZ/ GzPMh/z_s̓ DiYh;}uZ|,|*Ĥ2kFOuIZYQ } "ZfuRʪH:,;ZWWmIp֪u3ƔaQ[a'|8x`t.'UnYW)jN}m_52TVn/iψhY {X8p kO98-3(c9 * Q,Ƶ4Pkw.;6[if3"Rl)DǵNв1!Rs `Ĩ p8B)$I[*4_4EiY;AFҝ {AAz/m qOҽrE?԰.%i٠p6׀Z6 >]?MiA^8WrvH,` F&Eo!{",t73s˔6HD(XchO4,$a]uok4e pԴF]&-d%۟000Kӫ< iahuN ld~]P3os8EKe%ڗ$ ݡQֆY4jT0)"S"JIDV 66y衿E5?qn/.DYQ o0XPaA>@: &5U>LGEMx H O1U?K.J J35*"_г+Ʈy`J#o% 8A.T#nE)>Kx`3s Zy:g^gWYo oJ旡oG4EEۥ4 N[ e?++>Q2[ -[!i¨dl((:_N0?ޢ03Ew 6K֋Rg¡\/&16 O/ּs1&bL06lQۡ!CI/r Q̌MZ&!T=OFI{uA'UE?/A7W bJ`N&޺+$~>-!ӟ,/sv}GgX ~_g{FQGH:>ɿmʵw%b}Pm/*t't:9$~m!}jWýǦY 5bJ{VLI_Doǥ\+P5BXm:ڙm _V5`9nҞ,ܖ8j_ao-E* q!>ZhT9zaR4UO+,&馳|hjޛŒI/Qh+1uxC)o+ͰvmV')D;yNēb؃g Й֋-, o~UEN[>,pzOd6'8\EYyzgeN6T "5.JAE(a &k|mzШ.j2{ItA V !V':} mw~xǣE82Mp?^{($͗L5X7CiN i%ڎy:N/uػ\W.0@V;w2З7I? >wlq#J<(.t)R{v%ov }qܥRht&%bhQ){_dxI.RZ|+̊Ql2j^߹-)?^7 6Z\x@:켳-*+Dp4}H-Sb:ؐv}'/8`Q܂y'ez k1%EC$ ˻İ=rC  ܺ Y4]" Qp0B"C>Ę<Ɩ%hmuGzI6+xd9ܳpnKh T9Y+:H _6: q{D$Ev]a ff]5zAC&E-*ggY]$4mFbhNz%\.,;ri^ + 4iUҕtX| zIĶH/:C,/\Ոn47h2jd ⿿`zud_I%ܣsى9G/TP[ ci,p6^8 ?d}3^GVlYt3`g]Pއ$Z+{y Z5H8 $]c*J@,}-O;RuDk N -<C`Ds0se|Ab`w֐Rg&M/q A߅@kh6Q3%v9ϰox_2Y9b fݒZWE_dŴx˧s;aj,"=)\ͺI#g 'Do2zik~P^OpɋuS !vO};f^!d(ѤsT˟sSva^2p@p,1Xg"#r<)țQ]" A~+Ǯ!t}VgrBSmu+#p NtGAa+ңgDY;fh÷1s;$!OKI( Jq|Ki`Myycjnׯm [(MuYy܈Ѱfr\& v n\0,oԬoS 3W >Vso;*Rb82`mm;P-wmjᛶG;+ܼCӺlH/DA .҆!2_tNE*{H2yUeKY9 $AtD;d΂˂D"8 ܤ +O3'l*T&(d8# g IlOe~謅 i3*~W5-t[oGxX;Oϗa*l_"]Oay}Z[+Td^?JnV9 dzΒN@JPhwO:<-Y[;-G?9_ ntO4b>e /f5To}ۭ}m魤*?[\zPTPC*(.#vs;X8`6'M!ƫ኷EJ6&Ar[rjV>`3'+-xj$౨ޗ'K|~+0vc2))v؉4G$I *a&sێܰ7j͓f'JHdv6VZ*IYi95Nđl8amB9;qA0a!ΜxL-VGCAi:Y=TA?s$0cۇs&O>Nܽ:S d\I({d)JwW'Kf%yK_:b2Z箻]U旔aMd-$^80ZcNna]@I}8hwJ_* ȠIJ)LnA7YbvA)KQ.2j}$͎4[H{cYA3ʯ9Ip`.v6 o-a!e 'uzb9ME{T/m<ўŹC ǫTj0$DJ0UQ:} yo&2%𜲶loilqbkI'J?#׃_n@}W&U098 e…NJsx"-ڙHmw8RNPAhDUlq5ƌMf쯏bDXck4̩х ^: cIx{OeT'7x;'&K;O*Yv lT-ԭ\nwèRZOjYY+s;|;MGjQ' P3ܜaYڑ%c4 ¿W[a%J4Jx>ꕐ9!΋U_E^n+;yUYq]|EQ;8]v?v&x|܃?o!ep۱r!`~C(z9)Ax%o]Lߜ+TkwàY/+_Pj['K^zVЅEex9dOg9U3Tg3 kvf[y,3p8m} &%m[eUK bҁ2n%YTTf쌛(|#.L&meREEW|H6D"2m;Zx؋ѬϒY%|Ѷ4-N>b>>2E`;h: 5־&Ğru02tǝ~b]'}ߟ:\E/}`:)[w?+gK.ExAel!@;IajNr B1F[xFѷjFTlu+|2KT!5^k?tݑ%'IPp7K>]~] hXxz s$4!YUk᫷z/IS Z##h_Ư ҹoZ}QcoCK u=,k(گv\EkV@lgJ!Ki/Q΀]XXY}6̈́9i7Ew.Pue G˛ؑ±9 4S]Cgbڭ3:sjG6t#Վef= X7~SP-s<3]Ԣ_҂5"##®t);Vৱeiٲ{i-~3'͖ݻM G?8T(Kp ؤr{Uh"حuЂ&Bf/06etw·RKz?? g1T6lf"ma`Z ||~nhFen j#urf3ei<ȗ#9\:p^kќ(G_]hB Ƕ ~Sc#:ow=h8ZR䘘DVX[7 ڌ=NB7&;9 -G(KTҰ`M5s+  ;+t_Ѡm` ZLIc+P[+ MW}Y\oJ / Z'uCD nT6ۼ!C[* F=o3jQBt 4(X֞5eWy?-k:.gFS2#$(]99攲j>hj~ d*y|8X=Iy?VHc4H b)z-XUТiH&̒[5cT{YwP vOjBM28pg8\_iO'hɈ/oQ9-&?Ǻz*EME ݎep9fBZwZ-)(k? vSzhه,qaAps=F K qlE:|_htv?8''p?dKzl}i/o$0us> ?n{!Z>S?r|/g3Ieo173ؓ@az }KH&M-{ׯ;1GLi ,=^!bqL ,ynx^|n maȶ@ Rz뙿̛4|\0doq=sXb}{.+uB@Bӿ}f~JT-%Eu:۳5]~q3z7>RBuyTg7bA21cvTf6vn K:itZQ_F&b&_>ݭP .@0OHDB<ȌC=4zJ۳iэx ~]ſ%MP-?b]Nf8)k=1x47k#V=3e|lq G//pm`0d$R~=9vamH;x(ㅹ5ό/تlF,rLW4#Ţ0IGRO\N5Kc+q^6 w?;x /ΐ97?SyrO qHb<,$ţ}7ڒm!xԏdR$画kU*1\h5BmG;trʩf#Z^$ M|Z dQ 6?sxo@iD6'RsIlW=PN\#n|_ӹrd;q \hr gDso>D]:^{'%ˋ2}yqM yfmo4B jDK5x|/)shΧ]iP98baFPmf1yy-04EdMF7~e)h?9ϔ&5A0mùS&)}Ic%]gָ2H݌WTy%ko*\\˯4$# "WzĒӋI(];oi$S8W'WY:")V3]"ǵm9P?TʝKllW$S(^!Թ+;,g؀L]G*mܔC0Ryqvt^H怋,]?UKো}ӳj=?+(v&2ߏ zC,iYpYȿqBl٦G,]V/3LԬ4>nѡ? ncw_\3?]'=h B>a\X*xV%;}YN:.ÞU6 (6oXNr1+Sꛘ~v \Oc'G։:qwB^/u<6F$O3V"Dx m.v%=,dJ[n0]`?EPU^i/Et6p#$S,NɆп;Dz ?\ْUwesQVņtad3ힱ i.UthX!!SVhg{#Q'mU`D#1]̢gtMti3/5a5~!bh~9^I[0E]Mz0An61Z/)WZ=l.H P7b ŌZF1&F@ٷQ>XnW3VH$9ɈU]4,/"7)`x=Y +k6@)Ї1wc܋ވ:_kVu#Lgzgw-r}1z8\@\e4TNLLedAC%Nl jՋaW*=-^z6ԭ2՝: s@Z,\ KP@Pl-)zk1GJ[M僬_tA_S#n> S( G //l%=Ni [[ښCWX~r:=YJI{Cqg{/L*UͤsSнU!ڭ^'{Xdk÷Ysm׹BW -浔 n6iHevА&c޳봰؁ʫw]j ;+40n]g4A9ރp(~o\TG7P]u6jZj^DR+HpƯ.tk`M2،Q&IO׹JɊ!Fă`s,f~+J B#EisyJ+Yc|wٙUW*ˀ0ti]h Od.tJٓb>%<#+DKg>ZWu0YSYʖ l-bH;[6k3玘,faI?rCWgT;TkGx䝇5.z/ '^e1ctw{V] v ˲jчY/N-\;m18> c*3wX?(e5C- "]G:9=/1](ØsHaA蹚"حDoawܕ#h8teOF?njuFa&x/lGO$FU,[,$:((3K(5^vR~s!-Ha^nXe!0e"&4q͌%fOuSa-Ul~^ܪa56V2K9%7Wjӛ)ƊGX< /̒êbV)xpGB٢E `|Wې6+&AH6Z2]<ǎzFe^< @R~9dYg!7V4]z8.~pB*\P}~*E^WB+r2v{%շ!8 Xd{D 9ʹK 8BxRN4{fm: 3#I*;SsVPx ZQLZ*y]aҗ6v#M5(&^K.HtWadȦDOK[QHd@t*MiJCh'ŧ\<):'l,4(<"[w ~#7 fo dE#AwFEP1-IڇMee)#܈3ǹH8"04w< _h!cnSyvA#L~7cZZtLgi"+ 8'HMGaGfʧK-.0z {n͹rqڮEI&[;(96hJdf>##n!#eFF'=OycL@JPM'Iۍ+J Yҭ|6ЙG/T ;:\e'Uσ]kPI{/,cJ4/5~pYJLT7Ν*lgĕu}q N7ua]ڱxl"ƴ.ڹh!eR|Bc❮Xe i :~DŠ; f9ARfQVuHjCWwȝӪz F"5De++,MKhiMjpWPM! ..|?*%u4#3ogwc E7Z`.B$, s fU0XB݌%*t\cSpXҽRn$u:qKb6ǗM>&0|V ՉNxY}ZÌVw Fycv{~FT3O[aX%6rTiѬ)unڜL+DtPft_ Zw~q0A2JvbO4۾_Q=Ny[t]VbpAÑZoe9ChH)h6m"xH"(?s璚V[Xats7l6A1-@r;G6qo7/U_#\ >`酦WFM4U|%`20Gam) T`ܶ #>\yCY.LM!5/h%l;ziP=SOԯ-qңJNkc M Ȇ\C&?Tc`;VG,,CR'@r@<'zN !yOYK T +u F#%BO-j-aKM\^xSK˚qL[(\#'3>p,\h0Bl:W_|bR(ԟFuZI$osv"f922cvA3(IjZKtnyrgt236ߗZ6~)B@1ajjMOw=V7P6 ˣ7Uqy8 (Kh%Ӭqq@Cp=οE!չ~tpwjWf^E򅒴v);e_3i r0 ֦>TL18vT-KR1띕)ZCݓM!#X5z}EZ>Ȱ0\wqvуnR$j~X@GRpw*+tN^؈?̴5/9WA퓉GǙu3"P\6p_21A*l00&]Nl{X|J#tNO|>L3ᅦ*xt,tD|!QzҨ3֎H(JN,u#KQkܖl/&d02 yX! 0OhI "İ$g{LdIn0$ ,v?d J5kcBeۛ. t[ǰ L [6J.%DBOAriyƦ" =^8so"E%F؝Kj/;5VqTz|%v^"w`oSk)v8H Eb¢Pr,ܲĄ7aK4=1k Jqx~mѮy=8?vNv͵OwRfٖ-9;_\so5ǭ J6M46?I ۵ǐJ{rFkmu O&ztK;tF7 Wy{i%ŹAL& c/`|JS/ m_$hV:ccF~8W{Ggi%kWOS\+e!H=$Io*™^ ]{F4dGn;39A\(]̔%D1*X$42̩qa72T>i0z0JaVΙEP([:Q8xS+01>"T`g@Yp"TeXq{Y3F쬎h^Zə7PHE)`Gؼ7CoՍgꈣ$VRVq܄7'(!AC;5+.ß*/(H,|%_ɗp2=ͪ9۵X-0|(!+Wm[`p{jk0#7qrStM.#@qLf/(m}jP""%hAŠxkxGŭ],K.>C3 -W0Cc2Ehڋ 3KǗU%>I6+]M[x. ZY%é|<#"=S^fU"$C@3?+g~W`3]3ueFpᱪ2ʃգ!Z=#l_L.$V=iIm{CFse": f vwj2;P Y>ok¦WuMWKxEzBdm NFMBl/V80yxas-)~ǰDŽD9D),Ԏd-@"AY,Wc *:eZMGH| 48ۢ6Mjуcqq$w-vIӾa:lpHK /z-ha?KjV5y罛AW hyj4O0'`cLcOGg_ |ʇ-BkC̒ kr֔~ E8Ι$L^c)QjYJ  [}CP^2p}ktOϋ&xcD3׵~Nן?wQo{T4{Ub8 զ`LUF+;ZoIPj08$%h`=a"+٩(J bLSh)LS?87\(,)GGjSJɳi#/ٻ4V%0y /&#Fu)>2u``h!EֹK(k~15}8 /d΋ZA$ԡ%"sJFF[cuh0[o.w^03Ō)[Z"s~MzwsoxORMFjFa|i9f*:f]QX]u2sBNd!}O~-[zWk >te(2 d <xɣ?fo %Ohc p@,$$Y<%Ip{pse!fP:mFY)-v(Bщ-6^._pzV5X<}+4E(=sJ(nD#>Jڑh6^W}DBRdH6o:u_v6CBJ j% aY-{/Gi~-їKDJ`|R[8Fu<|HhVc΁_ ɷ&<:ىq+H ߔ QbRM 5 K:Qi 53xUzG)<(8Ds,kERQ(+c_^+6r.Rgs\lZ $N(lnR0= 7a(hJumO]/]D[AfhdxW O'~0E)RGyr;c%:2.@ %_yhD+C7 UU 2[25]l5Tb2i*.n)Z]cʎ_B7D6zB"y nBs7VLQzX&_28ǚtPJLlռc 蜰^+.c ߹UA,fFnTO,JT)+f`^c5)(שqMmMFHƙsf3S!AezVbC&?!bdZ0^Lb+b<ՑfoQU"lp1"$ϗ7L&] , TR[rC*^]CnG|q (Q9.nա*s wWR:]E;# C2|sQ ^O*qj'~1ˉ*nio}ջnrQu U@Ɍ Q3?\\GVDl6J"?`:{|N,\R'4=OK(ڪ8~3 1_wSJ9@bo}brSc/HdEvr[ƙ- K$㐻 l^_Tv^(6Q~#ڑ3BΗ/rc9JWYڵ#)^g?Ra$PStMvi6Px~?]Ͳ"Eu#O6:nM@dV$3FHdEAe⫱_3I ox% Em7vfGS(=J; ",ƽ}2P6-⡑*!"^zVX_WgLH9cJ*6ׄM "A:'xRVҢ>2+bo~W̿ 2M^B>s :]$"%vOxfL)gUaNHrE(UbD 1nb09_OS}61̰?~v3ƦA {5q j1nK .^Eq쎿kwE&;?B{&m:9C/\2<ԟu3NS݂- P)H?I^#D@+r22e \"T9k=,4T2V6ӊ <0 rh:͈#&%inUs3zOZ>0Wע+~a:ɗ4] qּwe0ݖ6#eIʖӣX&`XhCā6"KDx^=jPGtRd4)*@_|oaHzi4cOZ%eߒ%\eٱb[/'5?m~X :jmI;`f 0ߜ늒D\e[ ͦ=)$%'xp֖]R8~yg>LΩ[q.C\^_Z/EEHN/xn2jȩcaR@3>r*8s+ͬ氰V P(WQi[P; 9Ԁe/׾wFUteP.qC 8۸VtP*>g@_:"㕨~| XF12=X<@\I"q߰J|hu<vڹpMVGZ8~v8ӤVd*-ras <of"{'mZiVzug,ysD|(vZvW*sk+ `K /ELGa168F+[ 8\r],Ff*aפAk0pSN Nh6!47:|"EP3ʡF2PBW5#>An|?-?Ҷ%s66C=ٙ0A8CKKV|0w5}Co.sy,x_ aXNާZKrCJ HM_Eeg e2Nu(0P2@ڃڸylL~;t#돱Ufl`ʔҵ?Ճ=Y(9xk=26Z/]vuOSAKzԋQ^q=kV6OD*dC7> 5ŁF{M,=2yz}xSlQ'0a;זN^;M;3^+ vYu[X$ 'y : .M*鋷G$FD%JǁɮJxeѓ-!Gl=zs`Ø9Wn7 p-' k-pMQԟ$zO—=A* P[ֈ E/B=J6K,~ѹR(ϒM]n9_":p|O_eh$Q}Za3Ӧ;83ؕOk(}c!hXCi(C~(N5iQ #:Qu\P$.u6WZ,X:vFw4t!CʾO] yG! D?30tJC;=( = o3@8;vφn\9>X}STꨑ 4tBQK/t9dD$ 1jqۛ#; xsjQ?0nj,1hoFt"F#8[›5rވTdH7]&/'V\75Իia!B@d.X VVk[;3uM.CZձW#da8y ,a1eؔkd>Y$[ te&J5~.jjfw1a"@|gk-@.YsA!gq^ku-w#cJQ)W1`3DK7RP6Y2fX9*8:ئ b=Hpe/+qƷu(D|\ Å AOr1xKd-OA|}ؼ;`6RP#;Ďգdbj,*KMJBXIW:v+%!:g4>2j[p SC~ άbgR0D RDb[Qa5Ik c NILm$Dh8rHbROYr)(v&0A843bC> ^ս /Uit^ain\"kGri&DB$^vΗo5Tʄ))4#XbRf(oUAz4@ful,&{|sz$)-3jjd )INHً,7Bmޜ2)[>б@ p3A:=HM. +!CQ\<z~LP=tzAMa^8``% rDaJ$KʡaU `Mw1BhfҤ!g[Ԕf7*pi*q >l -LكcӔwΑEyǷa~f9 ,x ,$ >v @\IU}X.sIRpeaJtH#d90Nr3tSpUm'VçhLn)M ]*lQ# qBv־&ԛE!j?|*hOV75t)c Cd#EJT|>|@@1(&A2[IU=ϥN6^BV69LP'>)-uX~m`1(WMx R?w+:Xx9pz') h_$!kyQv`܀9.(ƼcM}{ )xBb޵)$2~{7qlfx H@1`0Y #iQLW@iH$ `*=!pK62 wL$ךguCdܲ]AJAIq4ǽC8;!Q$hMWkQ`!ٶ˦Xŧ?uc%E_q ^H_vf ;u8T_bqy _o]Hs6"p#Fg/>#8NW ģg<70-j6Hu ͷtvY:B Q\Iy T F-xN3 ^FYs/\/ږbag1X% My?yb!dQHS'uHA,;W(]+RseL{_ cHz`^hi$̫?UQ> Z`8AYEsP>>|ٛD>e]+K/7~1L>M ;Y&޶[n1HZ"r ߎZ]ʣ@4+_A!Q]g(?+k1f瘘7W)ʓb幯o752Gҋ ^*ܖ`&>47mxp'rĽuEŞ8SO4:C55>J#4X1o*WAiz-(Y̖2elMd*OTy1< ;pGChw3hSÂH⃁ 1_tHbA}̊sV?GL_]Vl\M?fiw)v͌=_U/D1$ -n_p7q@7ߩXZν//D5L<))MRqlM$IoոN "6 -6~ڋ&-j""cjh#ȣ޵F0`Ioa?BC 0.i+d"剦X::)9䭽PQp1V081Ϸ0ћSA2u  $w~ KIFctn݈*9ouOe>ǚ5Qcts:I^ E䭖˳0ϰ<ۭߑ@_`L0>\ps nn]hFgydKozk^f͕X?12_A eBH-YʽƋa+䔠^$ @=N" 0ř1;lJ$rCQꆉ:K|6"Dяq"J,p8^.je3L۰es aUܦ\˨VQI ' oNsӵ9l$BwJ:hNE~%C5w(H"d6ehIɓK8 ~7w5k(b+֛mGG7>19(6$ K`=b=,ێfBl*x]cz1G-*B=NUlyWT3zqL-<5\'صXfeQ([?cl=|̷a«ۜ1sm2~sS !PobщDzqP_?&P#>1K:^q N]+5_(R>l%492h;|/^vfPU.|* ~!T,U162P M!t6&MSTp (*kVur>~x#kNWEHY{"Fv* 86)PaRpUQıw'It:o]'ܠm,=_^&qi?âyfZ?75Ub֚J&1@Fn"`zlSS?%|͈@^&lf~vF}YK.>AAdx Ɉ9ʵZWٚB@S_=ߣjCWH$c&[I b\JV2g"n=xFHqzd`V]*QWg#ƆͬjnChzJ˒y\hļ[7`9.H>v8*%eAi"aXûce,:mO6qd:lc1gwURjWqo{བྷ$ f7@p48#$_1YnT?ʠ )$(V̐G>&@GX=7|t35pxqLqӍ.qȌ@ddZy[%_i>킁g#˳w6xQ=lmkS6].i~Lǒ > ̭\桙s͑p,~z$NCZHōFT6GO2 q8 9C8!ǮaZV\/di:_py \*`ɨj¿s Vxhs#^} I.nXl8H^|/ɜjIlZwaYmZ/FVr\3mI?*TDzGv>"hg%yhQh CǞE oլ\l7%A~/<]4ޮ6w*zS;Ibnn &uND)|(O{{4LmWl)͊qKRWkQmIDjW ?s yrS]1tW.nn$]QzS6t#z\V@8cEW'?~N4!-W=߻suٜR=~u$)&yc WC]xĈ#u,((EY<)N5fR;")*(ybhڱxIgLʅeEe׀Y4cnwP 7Ur2tFI'+,|n9F6'0/vai2`˯n}B lx4rOog݃y3r0_B5Js&źٴa+*Y|2]O*L3d[)㉉IKwsWs\ӶG?S6*:#UT_jgʶD6~O@t#YN2_j鴿@/yH4㴤%.a(HWkr=CBɉ e!0bLklRQ>Cw:eYpzDS{>UΡoO@CȂ |?( G^e`A?`Cv5C1|sT=EL|``ad؛zp\3HkF^j9[P6 *HOB*N"U[jrbrB;|O8J೐TcD> j᧨|<+kqM !7D:NK(G^f,ѕSK` *n)IAe,_{ mrjpm՚0u'B43Y6SKup@%IP&k1rЌAYE&|-ἻyfO؃AbjlE7M'PW^@{\Q& $yώy5@>?H"X*R9chN:$bH |wʻh~"@b|23ͭqI86u'>[R;`{OU ߏC`\ jPC%l<(RHRt9UkBn+ ;/WIltҪ|B£LhB#RWBD2Ml$4 땝 F@ғ>@<`UDچCsvNT*$eHb|?W:`Qi0 _ì\Q5=`Y@~թskYPb[ÏT|ɪSj$9wV'Se Ur`+&rq~oϹ7.lIpѢ>Wc*/oaà;{⠤lPgoL2a hC|w]+gGUަ;yo.9Mr9`_'VQd(^MGB R*S*?\|dkPHjݛصrS-}?E> .ny;x|SeyZ##݃ iMqhE0Bϵ5osR~]Dm->Uzr"84P8@*t3sӅDU +{57jO)^<'՛: ם i`6i$?1.܎{5i/iY-Z+Ld8 :V0S]|޺G%:^B^%4lzKR? jNp$rN#Vӣ1Чl{f| iѧu4"}PDzоX28(J/EwEj1էPƩ7)ﴝ/ך> GmY 8 rӆ?u`G$U{ /#|8Bd]n66m |z"?;;;ހťB}{WBZRƏ9ѓl[߀{mL a ?`=><q\[qS jQ(ޫeভDuI"B:SqȕjyNX"MjRxL vU4Կ-NqŌ'_R7 fX8Oqa8|OUF;MNS+`yKUh%Zxދ9帏ff1XtVQl^H:1[9G$Љqk/QauǢ_Pdopp] 4#g #9Gg"~dX%0_fb&SDz Ϫ*|5rΞѦrO{CE#\JV1:̗3-xĈ}qQ /}pў˧fBw_⡧wɋf)h6!F$9neٺ`jo㫕73jGGcc8vWЙ_O~U5nchKwW! |͈Y8 W)l55::SVKd+1e%?v Zg\rmII: DJiB^Cx@KX'% 0W-.Q_Slx;€=jd}13`d_r˽t^*`*QH=ݣKg7ݱ+< 2+5" ;hoAiׄJFm'7uru[O'qY#l=7MPQÁ#F $Vubm_|7͘]oEtUD{^c T 6Hej'Ÿp Ffyל;rG@}jGtPEȡDa֗\le9Eq\{h% ;.FEg$'bZ]!? Б>aPUȽ ٭2Jl,~%SE ],BXz$mB\t:.]yx-l I`ۃONZ0pǞ>hR0 a ^ͱF6oftW71H~Wea^=;DzsSʩ?x<, 8 53(U#TX5Ns=3wC9 Cӝ͙9 +"J"%ΛtWT.PA9lXMQ---EeMK`\ԫ%b!|HD zngr2-AX!;#]G>pNV21uvLVz,evHYwoM9a*3F"x>ImSge\]z:Ϯ9/gM訋$)1»vܶ54>碛J>/FY7+9 %ZOrP]^2h_3HZ36b?O- >4l6I?q {q)|l!bc=/@Wɠt{}> Vf \'^c=sUf:Q"2=W{YڃT~^Q~l\VpMJ巄֣'oH17[ouiOcW|hM4h`e;QZ(zg,IJ4h5+ogb`a߿D\nmd\-;$T }<5|["j{2'h£ 3JkvpChAY4kg<:pA,;&|Q5R`kX _zʮeZe~+ίQm%P&()VcM9q@:DJjdYtE4 I8:MV4 b<oc%X+{YbA AN@mZ|mH.naJwBc)^^%t͝n"|*T'׃yÆnOOċ"wXx|Xwդo\D=.%'"L'5WbЌئp!8YW[1tM2aHqo}7 ]h,.[:+}ptԚ0̀ŐL uBCF:Psu`cϘ9֗_{ڝ0O)bz7D.>燫iяkYj@HXHf66\ƪx<bb X3J^8=~l!j]PFK/58[(\ڸ]~#훦tY{VV: a_6rC>9Jz5]^PHgJ_f|6Wunvdk>duiUBWZe,aHho?,_b/Q0h"(Rxøm*=,}Ar$*r|&âḁ4z.uF ly39A߯wT> mq@̫dv`?ײFXhTwp?NϧP[.cdO |Vb/#0Nc>zNM.5ss,7sU;`wf mUӅ8 3HT&ZcNI27:ɃΧܕ3TIʑuG6yFL{y|T=/Z"U$=`ґxrg± WˎEc [>7Cy{]1yUtw:2Z VI+iX]ej٨(tQdCOf 3ˣY'73Z=1vujxXIEcq$/&6_T+Km-̵ҟ0OuL*#=VAT__0Ʒ2n"Wo[ <͚ Ӕ c8qYkM@Z:caL>5PهkզsIrUlVM{+{5;%31){e .3Jءp/Uv0ߵk hipZdTje-ӈpC9KBp#Ǥo4'@%A0צHE%K  z:~hJKG5;iSAcďZg PL#W- Ip[s&H#ҡC㑮GC`nWMxbF:aQk" T,aFQ",DHʗ Eca/%{>}v# mJLRbbȮub N^s6$b"hr"ј^cm)rbV5Q +H \yaZɣWcا6d,)MDzx!_k8Hß]f 7"m K'HˡFȇ3 bYi,v+>oQڦW  &O\_Y]{r>="*o7:C> |LXDe-@jlf-֛n {u$P tKWyazmDKxYuɤV{&-V=PSx=^sr^G"DacfO[B_RӴߤr)B?tZ]%26(5(pC⹈QKmnᓾrp,D *k' 轭9U-HCz\\{ j=z]]tò_^6ҿeHL{xD|a0 h^ No-r&u,(vq*EcD5? +5o4-_JƕNV&@qZ%{rv)EF9"\Sxۘm1i{jv"U-VJ8V4Dh ގgc[kC|J!OYryFp;glqd8Jt)*='lp0N~~'(';^PӅŧ^PTgW:s_m1ik7&z7-,^j]c+ќ1vC-2LR隵F74$l=E}fʚyN˜Y.m`WK£ApJ9pIM*Z 5E=)MVa"\;dk+Ά -u6ZjʀPT g+’xǕ(F^&vBa1 5鿑QDʲ"=ǒ z ?qƄ)Jnҿ6j`8鉧̍br,?P M؅dEeNs~i)ԛ EHQw}-8w8m-Zޞ#8wl[ǓbB?#M^{8dw%p o0xG"6r9r*j'sLz"t/, o.];h8k/vD͂h_?:L-?bX0B5 ,l] wRuᱵ4]#t!d38gzfu$<갲F]<=v߄0~N,iA'AXY&S[ܱ~p"^@n&t{rh*ֿn1 FQjSE.@$4X/E_q+U u2iNdC 1+sNypjѪLec]Sov ƿl3AP6k鮆L3g̒rZ1]Rrj`&įgxEJ3wO!kn>gxjSL>BI4Z ,7&PvU7Lx{ц/\wÂkt7̔%[׹PKa b$a<O*f?})(2mͮM&i7"PdUƺ,Wɻ:/Ͷ\P0luB?cn-i9{ yך·I4e)m_MZ{=[K;QdLPl5N>U"g >WNgN]^iFE.J4\L0ah("9`]}Z9S1;]#*W3틻qH !xA)0ӌc#>&^ ޳M'5h"+Zf%6Sɺrͯ|;"{2R9S_l2IԶa,=*U< )Gqy>z#bB'}CXZ H۝ .7cgCOPHXRmz: P߹voe""x` <{8E `a M77dXG7d4ʇ|4~ZG 3>ӞGQ @#j|^lMVYpg?n,t$!i'kBj6^Ep˙e@Dp:|-~0H[HޞψB(#D)5O{Åk)`=X$`lj5!UJޕ-x(tCPWjXN"3)" 7ZM53\3ۭ:d*bġߞIFb`Fў42ۀw+ >6[Im|46s~*x/;ݖs6#kp@}W}C5x:{_Ef/z1A{f ^ey}`|X]sф̇ſ^n^c''|NFufo-.-I{i"-R{/U@/ǻ  }t= v$ƌyjHPnl")0GCFKaCW_aXClhHk qM%{7vmȘhĭV6Yg>]X ҭ훬=c %yod8]fABQUuҭC1m< GҭD_10Y1Y='uܮJ 1y X2\ nJ ޿]{ @яxtM#xۘ4!{ZXToNi##p;Ĉ#Dwzy r]Иɾa&"Mi}|?C(?)K#~Sȱ LctvE\OI{nb^@;! dԂ2qHt,r|\|§gBb@7tLAbeXǽ0'+yLh+/DϬ#rVpCA@C7Ÿ8 $t֤?iw&mÛO3ZW+ݔRǔsg D!vY@ m IQ*w-izWT+`P`"[[lP >٢]4 ˎ3OjeQ*bP0Wڕog;λ)%CbŐ5yt Ja6, $S"D<8453EI!c4ccExTXMJG_#S#^w2A idM.MIn 4#/5`} (9{~GS0&iv}žۃO;\-@RnO's OZ1s5[nc.JGqVV^!_6n]8o GV @\k EwEK}E}&E}$sؼ! &^3S h%$cTmzǫg  of%G4{x*T3 I.!xDm'[Fޠ!:Ù|E" -m)=>ˁ~u%7LEpVǥԎ$GΚgh=qdc3IUd 5섘xv_"**^)UnIy]mzWq|~2Ӡ$K[@e8}6DZ-ߺOW0?^K#?Yߘ. U_˳!v(Mxh"ºN1b:1)9H*:ۨ`NMh-7o->>{%"msΑx\^8І:j`\s;Qtp2BPA_:|H y~wBw*Qv7s iVE"B7, G;]7'wzJA= 2I>WJ"{wv_1Յ͠H`&n'>m,q.ل{6{s!RfE(\lB7JcGVdX&ձ܌kB:#m}zc&oނB3GJāA^A=o%4nzpr )S>|$HQk/DIv/R:F⹄lm~$_a[. ~yDR=T*btPuS҃_&u=dpvMKXܹFo$n_Kc" 0СV1b6]wbEG,"6#TKm 0Z"T9»5 DUA5ݿo]b STrZale | N g9oF(+B^?ڇS21d{a(Zlunӆkਗgj֊WT }HIEZ\(dw }6k\.M|XYFq[;Τ&Deb<)k?9 I7ЌEH.{,KvOs$Z 8@Kױh_SNӱN-ؾ`o8H%8_IHXT 8IbDEZ GT3.Q˛ ?[12^F9 ]o9ގ$0ڹ4?G(k4/ ;G iv+5(0C[F -~y-.wYӂA9W@FV~]", \ʮӝ 0ΥXn@Z;)3фn6")/̶r KawM(:( l*821n/&(dw-p-¶L/[h6SS{DcN<8Ts)5}R\An`l?`&=3`l"-!~o~\sx3̭MRV~KuM̃N4(ֺNզSli^oLBWUzo#Vfg *ݨWts#%BACrsl4#W^|nHSuhP*WEOŗn}>62CV7µy!~M$2m|>&lr+,+Z14p/"wщ*T1sFTH md?UZxmI'm~3W+hgA+WĄ}ܓ .Gl |`@r/>zhBH5'țQLmYT.O-,.~YΌx ;BݖAgcm`/Q54q:?PW )@#Nq=Wپ _SeaPF=;,v: BPia=7K'%j" 7fAĨ(Bfu9\{uݜq5=)s|γU}{}>pE}N?+ 8Ux'^w !ht/d(\mt\ԲFZp6 D^NS-_HElSB~q/KqMSV oОNuf#x>pH2G9Ђ(+G 04Fx*+16L1H 72Vq>,Mĺ&uii%Z>g&йG~V=cnjBr~VߝVAӯ*<"B&/@:cnȮ5I߃y5PҮ v+Q;P}:?ǢfPg|vaW*)meMYBA2U'Y<nY/]2#T 7|E&xmUv/xgTA<-r Ĺ+6w5_yiT)E.U'3y]4E }LkdP⋋Ph]@_ЄU+ka^eYP`,F~Js<.`jM7] <KFk!t1m77aj6f Xl#W7_d곲EnUikfam rB[$!nHz'(`4E5 cKuB>ď  lBf6 l8G^EsMr nyߊ&ti,JeYנ$9'T8>jsl8P6ߖHg*!%wCn7d8/bI՗r̽6h lͫ(),>7ά$F~0H z`|XUcF/0K'c|-]Bw~*TђP1v&,Z &!o,,!sQJOZ%-Te#=]r3i7ǧ $s$813å@6LU_JMKA4iD9ඬ^9q?PvG Yi:!ƿٵegOLJb(g8Ee'PZqѻ}gbwY_xT,:mQOrH3f}^ޞ1W]8Ho@j8Ũ/Ah ɸ{ :{6^7z'Ϟ;3rX$9Np0!ʡ 4Ѝm9[?`5ڥ#ru5|/X TӯiY/=~~- TSOL8 xh`YG w(HKat2dsˇ-ōB-Fb[fG F{ԘxŹ썬N8N\ie? LA SelLStqCAf! hBDE/zJ:orM2"䐯UK'gBf[@sb#5l"(^Lq* $EB!ٞ)Vfyo#߮_ {tGTP f{_sL)Dy@< %f16QeB05q@M֌.ICB4)k7G,~, W'LE!WNy~˹=Ґ8t8u,!t.^s6J /<wX`8 T*274*O͛߶:Hƣhl%^ 蚒}͑3e**JDt.9>7*Z tQNc ӦTT0E 2R{\1.oclp7iU_>P('JC\LTAƔ<$d upBH8y?D 剺3d4c*FHW̽N!=iiu0̗CY;\2\-!]LJ Y j0l<)_E5[,$,ҔV)ĶE`Qة1~sn 8*bC(ro%%y8BsNs,ynڦe,L `ǡte/gT]Ė| ^/d0L֎`a3T_jk`_ngφ l] :XnNqxxnȖYݦnV0(M]z3,ѥtcN"q2_t=7DH|?!ׅymOR.OK \G6.m1}"+ƹ=Z2&Vذ4\n_sA?7^Q+`0wi Pi)8 D39L5$NY-" >sV| C, I0k|.#H8=}ƃ=H@ 9 Ið*ޠ{_0dTQ͌d+̪Il|]nY%'mȬqьmB9 \tr( CyKjH)%o}zW(y2x%) 7.&&+S;wb5s=EihgL*Q=٣S@~f;?4KQ:'Kɾ#'&}l90pwYT9%JEHv} ~nFq_p^͡俑 E5:N)G K+ $oG`;Zj&Phh6^(&/Xh)jI8CCC,AQYIRX)XÄ%qtUnª>[8 E} dVm4.Ȉ+g4g0X*,,"=g#ghJ ZDtlVw/jŵ>p`$GL"W)@s[da,ccnMAϺı>3QN[9F­MbX]DnMDz4Iy;`֭ =pvaC'hZxt[P_O6)-q(zؼ'+:;j-u2pIBwF`Eޫ#Ѝ 'FDJAp+ͼzJ5|G*jPr[L!Dp8hS=@Vލ#ÚEx⊒R/! |g/~o{2u*t^͸`:]0BeeSKj"Y܄䯓k+ wU~m dm7ҧF QP^ߣ|ޝX̸O*wv#ݧ8fڀc.?m훚 cs)2ePiJYS0;=m%MEWA`}*ͫ %`c\c8.9l tJ̅^&`O=U`bz/WX6گGGsAu&u-IA32!⳯`2Y-Z;}Z(DC?#п-+iU,2ʇ46@]C&=Tk22/AוYyFQw0v'S5zO1 =o/;\0Ѳ?Xnf0Ѵ!?aZٱµ pehcYVf18z߭!)NtN#C/djE{yo[9f@ Kz |ɉpg ~~^!urYE[rfPųSͩ%sso&hxRY>$Et aE4 S8zAL1[9?*@n.Ge܎#ר.T:޵&wǯƔ8q"a6ggTRRMS1.Jr+WpvEW'Zv|D&!#62[ENWʸlM=w `{g#o>iOp=)Is @%8D-9_q)цU$x,|ck,i̭ 0j1Wһ2-$]>ѳ<\h^4^-ҡ7bF3Jpb+تSf 5G -]#͈`Oq4{羰3:p㘜gNcAe=L˳hLA/?ȹՑfAW{~݂wxj;x4Mb`>)0/6R>H>'7'. f/ԍ[J L .;& fVս"7 (t589KK #MNn` s[0ùkNb3Z5o1tCl_,nrS~f\D$fϾfK- K($&uڤ@Vb_~BW]ƌ=UnGk旆U:[ puX),B@tY qt]*C5&֡hmBb) O %G[p촕Es(r& KādUbQxQܵ3M4l>y=az%c,(RI,By~m܅LEؔ Ji0K`r~ZI~l)j ۱ؚ>7f㻴ʘ*Ҵ& $c:u ^C=8 >X} G!w۸0یuЯ |G;[քP~o1λdK:i/dbbezuѹ(پ7~qM i 8l4>Orx\r4ϚN\'ig3vNK@lv M!1"pa;_;QUS(ˇ*/d+\rϸ캇ŸWΫRΠV,3s ,Q zE2Yؔ)ZTȩ}#n(@ChH SPqȞbsrRf ~%[@j |(Y,rwP?6[ Y0ܺBYІ=|=ʻPt1N60گ97޶ˏ'66#' VWFLf} BH5ER;Yӫ&늌 FKs]4 ['g`AOܖseeCN fN_`rIF(\T+#kR8Ξc(Nfkќ-'"%r)W$u<kcuɋtVAH/ +J6t7cv! s\4D^謯Kjj'0Ĉz} _?dP̙ V:.$QKfHoٰ+psl:>p 1bm=c‡oul6Л\gj2\?=[t\@$s'fp>ǁATƉ.1bP8bLG?veZC6-c* >nu}~aA }-7ڟ*j E.35ޔev6,{Q<3؁+ " 6HD'WqƆH|!MQ5Տ2ITB5;p"cϜE} e"é۱HN$5 ?B|oco :9dp y1ά) nң:a }8 $,u=X^NtCY(0+zNKᤁ[6 `[C84ؤ6@4o݋8߉]s.dBG)F jl l2ixmp>_,j4fE\F@(43^$. WhX8r({iGZi . c 9W^E9V/fPcM̹8hXÂ$vXebsH"䈕ޭ) '6f|#k߼6i&[ʼn'LUN:@ Ϛ!MJ] [F ZHu'U. tǒBc46OG֧IօSrĪ vX]eVv~ [7ϐ#ڭaIj:(D4'?6¬1?g\~\j^`"DtwыPB6 G\=.ryFBHU.J&$&Lq}DJ!Q-!5ӈKh4Nՠ?xi 7~kXTD.Zp\LQȕ]ݏdU>]?AKYUTLie(9T`;%|1d@1}iY.m)[J53kXzCoBL}z2zQ([~IzV~Z2(|uXvlF͜:MEU=FTYq -Q"eTO;o|B̓=5:bK"Ʌaciq? )oԧv^]i[huy5d+sa=QQ_@H#a3Q^i-uj5(;\Od rq!5 =|V-֚@;l(I9slrYdF,\ND& w%75ٖeFRռ&wYУ#zٟ&u9GT;dK /I C< [->eB.V\rH}kWދ'.Hv%JZp(Ɵ1Ur" *xstMfމ !ACf!ǿ`\k85Eɻ9]y` (2ECT!j|ar>Ez9,ӘXW6P܏B ʫ 0*OBEzBmsE?-wv 3(>Z7ۨD ٖM+a@?b|gUMFKXC pi,|;> 2lM@9k)R&z"ˮOvh7 Z(yahE[0bvBTc唬n"ͯBATD=da\d93Yp,oTr4M1SvԟBK\$OnrK(bۭ܌z0Byv1LrJ8uo##\qAq>u/CWg_1۶ (_¼I7{a6wꌃ@Gq^Ҁis_$76d+,%\<~[z21I Bth+/s\]t4}6ښHdZ[>H f*N$ # 99(t"~h?Nd~̻F͕px&38tOl g:@߯JOP6ߡrSms{0J0mQJռ fڵO#5)fde,>l6*=WCT{Zgv=w.E2N^f8̜U$MkWs9C+u5Ÿ0'Ċ$bf0/vlW/}JZ$;ŭmn7-r*MkY\pȨW`柿溣zפcҔDF&1PK"KYZĄT"{p&k3SMw`f%B|pуMę5Q;b@UvcxrP|Z-cs˸9y% :7mYG^LZ6B],+>AɄWd ,;G MX81v'+Z ǜ~ MrHǒR83-\|GVv-#D<@h ? cm6O1a%ysyB|duA: UZf_!Wlaa/FW:oVudS<,ԘN0R|sHEH9H*nf_Be99]ɮjظ+" ƅ( vl-0fɇ9bJϖD=:I>ccƿ2Br_ZӽHo(bꥣ#5}%0)|nýl,ZF1\]Cy^~I/BN<\ʹfعjh\]s$1TD:= n~Jᖎd9!C#ZCe08µRP] YFԼrmv!D"1Kr:L`_]ZkLpo겻 d1,n3O@Z; T`z%fi3JϜƈ\5o1ٮ&lmT[jk`״j*o3i gQEGKe6K,wZMa&|f& aK~,U"#23rA\&k>[˭xP~iwzY)WzJe/obvȀ\)r*BL! ZhbU7}'Ld F-Q{k,M䃦56D|#Yᑇk@JJÝH~ -RlYYə|wRP("Y"Y։vWv&+` +}F,ClDctEn&Yx ?/䄟4-cJUJ }̡vݻxl'́& D֦5ݎC9y.0<ϜT`Tv[pH)4K]:@QQIm9~4okcȁ`gqTU _^?=+y=VU Tu<w#ݢy37궋oHoD72-Br4 kZ [iT&7I߳N .qv`L: 2hL7aF1+f=![OW ^[CY se{'4aj]LׇYi" ZVh6[UV2- aV s f疽0\(߹(ш-}1+бj=5uZ#u@}kSU)I^ $LoHs" ƾx`|򋆴۾EX򘾨~_n_0?ɓ`Ҝx.Ԋ`uw* $[C/{#Qv.B~̍MG1aRl]iZgyY$h )n9u}i7Uh ʐJ)]ɲem'Vӊ0w-%˖?pkzgg!ߢsk/ ur˜<>\oEaJv3Xi& OJ uJ!Icvoadg a;/:HlCøv=.|q`QIoM#?\I.VP) ~]IZU<#@xAD_8RC N-ZV!KE>|$kVqPD |Gh!8`CϷm6NWyk rAb>GZNZ:zn4ܱnJ(\lcTLMw[bgEBsu=cـ;0dI׀{=J5Zrp qvC]c ZF >iVRzFUV71~=YsTz%?|f×>kÌ И1NR] Yׇ ?5.<>k\Rn_lx.47͹=SF'ji5>j'Nʳ+OkVՁ1{vˌ7HCfhHlw~Y׫9CH݉SG(o3BVlS TI#a ]::=zW>y{[@M"<@t`!(DcA_HSMREɄR ~;r:dnS8f/i2"R8'+14 "lrwNdω5-`T$cNu/SFIp[U3gb=zd18Ywu`HWdú\ƛV(d"rַmi[p,nc_LMJmr 쒪V Q"Gy0=`x97ןrp6SZ?iCVE%2g6ڢ䋆?aI0Z|:!q Pφ1Fw=CBT!ѴsoPٱ?yDxvd !y"F.rzIW" j?*A@(HLd;mhe욉j _ub R] ]e-%Yӷ.㭼I X+u╿uvuZ|unr~eb{˴@=&O{3quU2( @ytz`(4usx hK639>6GdԒD@Q۽Rp{2˦? fl &:tm̌ui')_BȆ|y|pVQjQtN A331J"Fhjo*UwnD?M.ױGxКA+Wm s77XRp\ ە,UZ0X+Xk/^H/'' ̋^E_Nt)e4PP2}hcq%$lH]p-%lxi[ˣ3c][|(>G2\TIgp#+t0n[b-ƥLlGOܿD#m_.d=a\B>qǹ9t} ۹Dtjna 19^_C.JNJzރO^"z[HYwgƵRAjUԔh}`-?tF",Aŀ@rGيt6UzᶿnI.Jd-~΋ :@7M9 * x11Uʛ]Xvfͻ5+Ә?v'FEu`SX=f/,f?j;E=l &TaȹW. U iL3H4K*0QWKŎHi=f=h{M+Uq%Kv?(3\doDPd Q Zf йNP{ޛ8G G%%)OLs6G-G$S f虆 IV-h !4PĞ uXv>/ 3mZ)َe;(Tp-~3ȵ fם'ſ/ ~@w%ڧei@kzĚ ͋Frs#:itd'ihȱ5xÐ"ף0逈J`v[<"E|$wdןba?H<$GGnDr.a"zn *~Í{u ov!|ei&?;oo/2VwtF ޤm t R8vkW0{[&:u ]cuh/s8zꝙZ v&4jॣ]"[,'TV iVWk휫B$}(AJxŦ$7:IAa[g?\ذr_Xa'ְR'NWy}]pqoFwiyySKimJ$)V! }aofQ6Nր'Y޹cbBd;n;Eqzȑp*<i[:qbz%~n6M '(7#=ky0|otj`,AǚR&XO,?>:?@g2H8S#y"\/Z_ *G^9n*!Y:| B R[z4_<$zyF@LZXR~4Ƹ'D4fVaOiLhN \:ԎB&T߬2?}:K!ʟ@E6̌oF4|s|+}hU]V@#-$z5LVHCѳ=79]Ϯ*4Ԡ}nբ@Q㪯=cPÃZ?Baҷ˗>F_:fYVԃ*] 9`ynT6rš8Еzh/V&.AQ wyiU0Ȅ5L'ana-qe/JUuV0yn\3?* H_IqKL[Cwl}͘M.)>ySGhd#+D0fq%0Lk OU; W(FYU(^6>p}MH^3o1;^ݾ5C6{,ݹU N. nu%0KihwbbOdO~qN\E>}Ϊn qs'2055Kpo^'G K!:ع0W|;GGfȝ=4qECWп|* q~N~ 5uȢn-\OQY &(kMqr3,|ɖd*1o" 4#4,Pu_[;2">Qnf /xho1lO6+x!+ &48Z| RZ3)Sg~]#= &iND4۪qo"J˗\m~?u`1K}f-T^j*^QskUPh%}vǪcŎŝh\]C<8t!Þx*B@7 73HٹYKDl;o J2rz^WIA}@ O*y {TZ*~mDNGzM,2Izy m63\&A O#Lr{Sq+fM8"eX/fI[S%hO&՚6rbApiTNpOG꩓(or̄Xix9Fc.3X(9ҙDw?0ZzOh1)O8)=n a? 7HU¦khS遱*0 Ī|Sxc翙7*~ 4>P~CЛ=%$% j6f)aHm8@߽Mi[L]<)?y{*Đ6 ?Bᠬ7"wVP{/+ѩ,gݓEz{B'gDw9˯+% 1`(͏b% FgOd)ZߓG-y11E'|S0񘠹^-dCD!UH;4/loI6&)!o\;XLƬRGnz36~iq*j-NSD},u>53>(J[P6r5$Gtәčd1Nlȥ, }CЇ6j^GY&ݩC6OV$ק5u6:pZLzP4=-zO "; L?Y֩9rk#Kú\L 9"2 Mx:irҶQe g?Àq0^&RVHON#V|YHHrhEb֥\BV!\Su2["DS: kAg_r-0`S._HM"d-kЖ7cLx:Ttl ]K4.k)ܻ89NPߤ6\EhM3ݛFDkn''-Dn:'ϣ2mI夻qsruodYzJq yA&/9{nnbH+4Q9yC!PMzX2x1`zmS.pb)@එ8exd5>y~1;5wu RYIfT,,"#]ٍ_n*<2,}h3jteX]o89md^.vXGO5U}-㻦#䵉F.U!K@W* O~2H]h2M'_H)33eL#H7- ,z @kʕt!Ť9) )cz ԡu<sHݏ41DAȈR:u焥Ԅv|c@i!::}`3NZߚTrY눳o-|+CL{oE.Wukh^UWP-]c)YOZ?G`[ W~D=&l.Hmd7Z$|(UtX7-ͭ2[~ing1J@أ{\UE!8͚%pGH X&xWyK+k7Èţxsܦ*?&<%w6G)Y} OWnPYWћaݿB@qd4c~[W)uB-ASi,T(m 8ȯ*ژ)IS ͧ[3IjLaL Mg#jN}бvK͞=t`:nO'Kk$mGdB]8}oLع5O+Dm-0t;^B A^1kZ$['"&_Js:o^y 2wRI ็OxA/$(e\q,X-CgXqO&]wYf`'Q>?+,K0ťG)OVkվ>ab5@$d|TdC5T4y  :؆n+JX.ux=A} ( G,'xtohʝk`ן$W*RҡJ*2~\@Nwŏ(9g\@:EpJe_}ߚlv ϰOSqVVT0ßE5Re2u +_q,16f9hRT#[<,, @|#&ZuafZe+ KS9kZѝ"OeЅ뿜MILYwP.tQ3H^LVf,E+vQdoiQ|Nn2Y wc,d-IpB쵦I> nrؕw1cc4`U=.x~F7o)d^Y$}Պwu^XjY1h:׌ ܈DI3:oȭD,]˗ԛ 'Z0[6Z@8jo©]DlDN"S񙛏!j%pɩCK9oƟ.w(6SLMx6dW듇9KG'[^&6UJbl"^e,/&n{u:pX6bq %hy=2 @5^X"CTɕUgoqCRP=ǃg`,^+PCrFNpȥNGSq{`2S" wêCJ#覇8j,eO/ͳ8C 2,w ^l 3Ew30z57zӈƕvSǒmr\~֟G&^Ny Jra"k؀V7{NGo |6y74ϔoqy=S^-Pش,Kn6] Q(AFv'q}WO ׀of_2Dp\%jwi \6ZXK _"N*ζW~%e8m؇LXOu҃stBVK܄̣Z.dZ,AV'vi .ոƁ0(R+lA9_ 6z%ؽFe7[%5ep :Gu2F B ?cGއ.֒B vX e5ЃJDCm!Gs(ј}$kyX>},WZԠ5+-qxMF2n@yX(Zj*"U~^Nw}VE׆2J 2~4_MJ@ 5.iz=6> JlTP,NGY<տ4$rCtߗ\NCex?jxqC߶fAn[oRRJIl-6O'4Ɯ{*i$ H$ ⠩=O!t Nꞗ=w|׫1{be(: & S"eF߅GZlRÆy]^.])*ZJR '{T% $I-B1[]\WY#2_*A1g%_?BwޑԀ_΂/09QiJOH9ڱ h4^io( U=Pp/Xδk 6LEPU]T{%t/Bn`6˧ӄ{ DSdHQjz u;EnO{dR'i?BREhz!P:D6# < poyҌ$Nv_Yvy:)Q$G ]Ac!嚧 &4=r#%ǖN O;`"8*Ԕh,Xf<Խ9U{r9D r4Y!s>}`,Zx7?mMGЭ,> pl@ l\~>1-;w*C>#8|bQ(d鍶F]^.6x>!L/r 7VlW@w#ָy6|oӳmt~ŀzU ,2X5y؍CrXnJEɷƂB6aӗ].|گ92:iigCN[[fBqLk6bwqF[yT?g]{ӘI =F>pIgF:^ U#xoo'*[.YJd̈6ewQzTް_;Df DdU|amLDD_Z/jq`p9/wq7O9$7vQi r$+MH0؛jİ}ԚlAb1O&;@1 lQҀ#ԇY -UqԢ}8Q"</@E3: .x{߅_bF*@tPp ms_*11|/-oV'iK(M(X**IJ.$Hu0odTD_c7} X O>U/8i͸BRFzcq|̾PNn1\6g˦7V\2yСˏ+/ tܧyL1ޚW8ـP}%x;}ACN*՝=M]Ҁlzv`AuM^A7_̈/p:J-bR*Hz8*%V.rVVV $Y0#gh69x6jaᠠk4.+1(V`K6H"]}]0@.ve_ j7yT,ӿ_UCa~R xf:B5HAq9 73Ռh zҒ7w1_D!Wߊ',iSNzkHK+GVVuLOôcg8Eb|8KE2?{% yMN>X2[Z C1[2fccܾ e=;vqd>]DA萉X[gFpR63~9-JƝ_*w5mjo+ g.0\@)~lyDsK'C NllL`h{ K}?'T<"Re0=1"{zxi39QE}Oey&d5jQw97}Tm2yd2~^kSzJ_ ~G}C}G%f.ww[oHa 2Z?v!̰~{zr%br;0{ &3=Jw\Wo'ޗcٴ4O;TΘ -| ؽ+zj!7G܅q=iπKb9%,VD+I>tXI79nM t6~[vN~D.EYwȇmѸi% 햆A #ޫE]dWb GX~nq.[B~Ig50LMOFHTpQ,ąO(II~Ad՟tXǣG>F zojoF6PcTkH.r1`,Y@^k$`to[tTiO[R}##,{:m=(FuZb8$+%7CEM*Aq:4̎zK(alt$RU ^<* 4 Sg#D7lPO'@={Prޞ.F_YhyϺt$&g7/$ ޢǼDe,5ww4H-DfZ̠0ِYDd<8૗X(2er?z*Kq5{[`>7V|ױgq=1%KB!W}M9.38]Nj SԯW^l44M53<{FLû]FL!uroqJ4@49m. hĥUc66|7,#IE!QVKwr {4@p%xǡ<:R$͝<|DC]d f] t2toaR\`?w4r$L)1mwaxJ|covu(<)5XQ-8 *i< VJ!3K->vS~y$=<;D H8Gg8"jjm3{ ȭtCހzz9PՁ'qj*ޮc[Je/(MW]+eƧa@n0LX+h٭[6=rk!)u rF. *b=Zpĕ&2èXݚD/` ,)0|U+O *eL2%\LԖʧFHIĥ4 Z5PBʉM & hQCH^*$XaO C}˜ﻉaBXA6^c}r n+OԈJxcJ{OvvsC8G[/P#6iMcw@']KbS 29}[u9}"dBC %* TJ oUy0O=*?rbcYj\w.:e0=x̪ݿ(Q\&~d}BEBQ}18J#7N 6r`Qm/i#hhAq%WDYZ+ry VgotAas,>4vE42urDԈd#N%O84^`i=}_3ȋ"SM mvh]> #>M=jZ?v65t[jwaN:JHqH}0brգVŷw_)gf$,"?xX uU59oi %JEyg7l\ g]9"(0Řj㩡{lJ_.]Zq(LFg,H<1\pY[F!8_7&EiCzhV ǸxR-Y'D|s$û T}_4:/ 0 &AXϯPJ"}z2п_Bj 2-gL"$1] xHHQw`\krw]5BXقfoFߞt-C?lUO/>.%N罚7eMw01)N؂et7xAh@>\p ta,*]Dʜ㙲Kc2vrݝJ] I:Pv'L\E=/6Q!(/_@]I7k̞i 3o"/}|nڷs\ w6~dY䒏ANlLXv& MEm@aᜌ361lQ8mqעh$CJ0`n=S5`}j.ObtVlrm{P<#{t7٥8Wd/zi|1<48*"?:@bΎmdhKu%qJ`@*&"7vxd D8 $^A3:XVw}hb$;, atA<J9(~HyHepb"TmXw)[*{B 9Qf8>1p%ܿBKF̓X UuOTKg>8pNYd_ǤfU3Ad2 9d)SH.*;dI}M ="*_31xk ]1\_D\2u߼ЅLm]Z4;y$Mƴf֕ںg9@b߳tJhY;A-8 T(=:AhëFxU~m򣜬~0.dk ,-6Es+<:n9 K@>s{`mu|ܗ&&v.r5?,xw lW=iS #) /$$M&CF%Ar @)=\UX6C R* ,Sa.& y]Q.)t.!'ӪNGCF- hA{4:sZdF9JӨ3%2 ?=)¨OĨlZ #knGKhO#%W[Xҝ!ѿwEIlN٠[;)gNG=BcWŻ 4\ͥ?'|h`C6p0Ov^ؔ *%6nF{9XvuLUj]ǶGzA0c4(^nwgmD7yP&"CH$ nU<ᥨɯ*NjQ7Fٔ'^c?iJ VԟV+@b]11pH׮̄[Vf?Gp<_xL,w/|F.BAQ  ?^h2X^1F8bV0-s$ GrmvbK~vG ꁡAH^Kև;qua]`֋s+v>j}";%.5wx<8Yq|_[jƳ PsvXLA1C>N9=1j;N{+n䡿LcimBr4?ӫeoe:r*^t֭,NڑG_wPl W/~'qK >bV+n^%9`9#r[N^ﯧ넅=՞ݾEF< m>sq[#'m!yjkbتX7Vq 'SxY?ڵi9GEL֢$D1Q}@T.|hLgR0V տH"<5gI"۶ z yBa'!XBLo_A~x랧ɍ}5)U~@X@_`<7;Z S7{Ǿ 3-gKۈh:IyH>s;>tq$n4LQ!B7W8Y  ֜NZ:s!_ usHƲʶuXțQv׾^ #oxAK>r:m(aK0S`[+Af]UJe_V?IyM"kAS X|Հd2Tk]bb8q"4{'bҔgSxgg\Kp~r;Ҭj5QNZs~q'zCf'ccGziA+`$d}%nuGx<;Cb^p$G5ty$&=sdLMuG~.nJɊF} <(H:0MْByv@zgb43 +vu:ҩ3nFH Ư'_ڪpް8kàTS;b*[DۡOm zqz)ケt{>N2A]Tw<*!=f?%`I>b|BKF4W?NL<:Q ~u`MV(]>ف$TQ-m#1q?Dz?!KL杍TBJ:1^LaD$Ѣn{9wЫ$H uje,~R @q|F_=U¯?gCp  =:Nӽ0d!\Y8h>j hwt ]4[ JjuNweN(? z'#('.H6X-`鹃:&Ž$#uU,W 0miyb^/*ZKޢ[yUi`~_va WYv<#&clQQ"zy4$,-ogHqR2D ĊOQf"-3ʤ"|TǤne|`uoWqyf.:BlEu (P{[0"OԚ~-24V."_%^~yANd5w(>zwN9l+'W40^ԜB9HWËSRU7>ofzbg[\5;;#+2pz#78L񸾢: P;<>KћV.wǫշDo7ίz{XJ b](b-_U!,a<9 =w{jz"Ϊ.1!HDpp ׾"}%HS-Ûʔѵgb8o5JxT $1;22ua6Vܗdzjt6ð!wO\(NRäoŲ6'SvGQQm $x:ݧz{{DrS3y/zQсkC9 )S[K-*T|f2WS…c^;Tj=#R-Y,M(TԢu&1jv$9~pn["dX)]vpd-WI(fҔAk}#v罯 u1zGJ\#d;V{UijeV SZ%DtwHADi8Uٰ墶qaXʛ0%xi}56_& 2Cmq'ǛZTM*:]:tO !n 1.Rdr6_ Y>:p9BIu£D  m/ $Cpp +䷾MFlz9jJLjy( #S^ .;@6:Hp:^򮯂/.Xg"4(S?K &0ldǂ&!k?YMvj.@RIWu!`ͩ cdEHc&x +iv9%l{H,GxY&gۯU?m*o֙ԅ 2Hu8{A7g6V$ͼ)`(Zexmqr*",>r+ZljpmM㺧-PR@ʶ_-mT6z ZҚSX!mHenp&a_e]I 5aޕYH0^H_P)fBʵ$k0+{$9 LҶMmu}CȾ >3%T^E&avtlm۫c?Au_ںIo۝L_v3<ˍ)7U=g6#.shi"r=$utTY*#'%QGv>мQ%&mɸ UvFOAq·Hm/WD(n_Y سe^!_,*O>Hq@[dr2Zr'ʟt?+PUjD,Lldc%ǀ-^T>76T0LK@>܂!6d~!de>e*Y@kLM::z5 i6aUmwc=f>zF+eJ :߆my}׈b`;ERK2qr̎C_f,UU/6+ճש$l"{e,n_T]3$&-/jxi«Z547 }+ *$ 0B㛟]X[F5]Xo y}7`Rmj#z=}=m^@2jom+#*7)o%V16cݐKuuO@04Z+/Z!+֊e0ʆP =Op@p2wYBI0/9 i?a*sV|vp]{ Tme==Z7KG^z;݊?dž .ǬGRn%tIB㶮ݎ Ȥ4_bIsgdSy,QYհ-xHۣjQEC}Сu"C~wDw®K#6\ɝxc-H?,[X$j+vmߝg\XmH_1jי* 6o† _|ÙCOiZҏP+g1`!}FOݑFp/q#SE:U/RPn.),5>Ŵ6Z#dhh -@j%j3; 6Nܚ,KL9[0Xr%:0WJ. {S>OԄsRؒf#; rSd1#aHZ7p>G_E)Yu?+%s-΍ASLU㌯823k'{2KFWcmtB``)F@sӷF,YSbf)ƫ2>Jk 'úvm/1j`#pJ8. :6ݤv=iW;DBg5 }rzW;Jİ5 94TDľM?g$Pli`Ȇb9{JIZ([`9IDV]!S+kޭjzynj;Y/"l+Ϊ/}PHΤ.!\۽$n @*iUet}hA&1d!P(1Dij񮾢!}i6f`gw5V#{wsgz1%3YuB%_PҞp\r_pX!hɜ&$-xb#N;g>H8`M뺴YrgJӲotn* dOBqvD. ?x+#F4;3ZjM\xa_U_Um، DUoCT 3FrFTB_NÿfP'%ߔ|+;*DO'+cЧK$'CU g3}. %j}Mg{Zm5R,$\oNv,3Z80 lq)eWԩIzLI(b@ʢnmٔҩfԽ8A6A8 # VB)KԣjgٔFޓt~A)N8a6J0˙N< bMONKjR*qgYDHYJx"G1W eoA&HMiiS:iHWʊ=ux|pŤ#w( -FA VNA&Oqx5*v!7X7E Kuc-aYt?Q|IGv+:_L;@#A{0a*/BWIwQ;bvxpl"g5DYb/1l$xMQaEln#„|!dl}&tGsXlNbYDnBu,jB)&dl?@? 7qot$ķޙQpFJ}6Lӿ+Tݴ^RXZ -gxf-a儿Lh=žVWXg<cfϊrR0ݟ0gFeFZrtcw{eՄW-ZexAdR#0C K=AǍ$g*R/ O[ . F Q%*P͈%YԿmD߱š:-٤4#ͅxGlՅ|M>trE7[b5ON :Ex^i(|v}iA ԝ,WH!Xkt*xnA 7\g}Kĺus0A) d[D'SylqìheqI#]#/?bVPl~ԟ\x'-ȱQ>qQR ~7kzL =q[3Zw) }bɛӅ " ,N{1eCNwaEK\IˣJ[lLH.=;p (Z0?ISjV6ɜ4;y65=e\G>>C͓qI+Lr}1%a _=?"]`яUb7Qd4*ys-cZ|hJmKlMihg7pЃtAerm/A_es' m2~I P*zbGA&m@` HH>;Ti v[ SWGu g>1LkG!Hbr4/m #|zB@g8Ws͟lFR6 Nkma8wl2g0jK$CaPWWq8|F)e*ilrsTSlmB 牌:9W)>]=ʝ9-vYWTGu ׃6rL1םl\` mob=޶\krZ|H8SfdN9\+=#cn٠!@H/rP$Bykq/N_$4Լ偿 .j^7݃>Hl?-u(] Dx"t&T5"sҭ(kI{ѥ3ݏ]Vʃ Gմ?V3=rU. k?|! E/@ LwRjȔ?Kz3g˺Fubi.cڠbã)4y,4rp%E2SAivvWݢ6L* x0. Fȴ>ݲ 8ܖuTsn{bACMvՀ\9^-?ܼVF*eQ4G\i^:5[7 wi i>K dd1vuRx^bf"k!*2(k0mNjV{ n|S͊9N4Gvs:uJ0ψ+@׆TW'  ̵Kjv[Idz:s2)K1fN Df4omdMQGPG6blfKH3CڬHSWu4( 9S[_^qttgiZ/>s]"EvZt iGjFG9*A,$aA1ɩ*N{:,gzBI ?⣫\$)I&Q==oy8/ޏ!|xK, }>DЫH? #6{* FOVp8%MO:QQ+ZX4ɗu4sp%S֟-$!)jw_F'\߃@^JF t]eM̯ncv 8ʭO p@ƁVV.˄5]N DAҀeKv'[.v>tI»s0^}.Das=dz%Ds'Rfe17 fQZ@}HS0E ,wDn֦󵱭gGi<^OC~RCF΍Fj&Y#BFkG%x ȾgUw=NzE}fVd M6N/bKkwqR`m-֦Uiu4U{f$'b9|;]E)9O_tqqjqG vނgI3˃1S!x!Xw D VHh`wy<׽R?Pe{U~aOO`)]g9V?"łmՔCa@fE$v]pTrӤD怅]?(@S۪/9cF

9TY +`H1n&Ug-9^f_nvhDH1xyaVŹ9v#nGIC6%>ĔL73UR=Ov#t߻GS\yvRIw4(#I Gױ5Ba I^.Yp>>&^1> lݔ4㹆~U;^ʆJAl'CNӝR%q̺ X΄HP駋¸8,vOQV}d)[g#6 38݄f^u5|QhecaH7{ZB 8)qT@e B1W#lezp '֥ih_ g _oUZ@1*=vQK-AJ`!'ٷI=~xItrL[2H쁯*YVw'@ QgLIiIJ8G'd|5 ! rƳ~?U  =7J \ 5*I_A)Ȣ|=Y $1OMAKk>7)aˈqY޿f@ "\ؽ[͟6[t\ն)|y͆",YwW07ORph4`]-F@Q@<xO\>HjU0;xFeCFslgX7S1-|>#0(Oc^CS>2Kf ěZ4 H%S큫+S[xNOջ1pjj΍TaG!#ja!g < F#0jmnϷk궋^I5omTUw*FeC{FOf|G$O]u%h Z՟pLܒXmzLbus¬#b4zKG!IŤ|wh`5xL4*^4ˢvk{=cmRˢ!Sd>{͒91XbjEz)&MW x(HvLu̜=;ANI<ޝ#=6۪ oe݄bs,WOCMyq xcJsF?1?l t1~ ~AN*~f I :ɢ,([8QH졬 J5 r) 9awW+sSFw)JBÚrǍn|q)eT*JHC[(<ηUb5i#%ff]$7-r !qMތik jUE / Or~?Dr'(5-T+0pr'jjУ3KwXM!. 𠧯+&sBmlnEiq!Q50L dF, 4S&sՠGUKO z_}W饣f<hiM1M%tD!%Ĵ^e,\uz}*,o  NR`oPc#ʎ kF 2gE1`iGeFs ܌wu"g/;LذL!&Nh1~&q附@I!&V KqCih5* ^L5Ѡd Z:gA͖z#+rky&iMQzvNTԇJq(֬X*Cl8.T ~Z ~7o6`8^7ߤw@ 2]PQ@%<꿊cȝ^%9rƜOTqp>L:ʇQǫ{d(')F``8 3#p |;Iyz{Peٮjg>6EeRZ=OmA:g>.Dam'9[ ]v_fdtX]S)ːf 5yS@(B߇a&)_GhbLWv3ee788DC-WO4NTc#GL& Pof.A-GXEKzO6W28-/K "  l#CY9V88h&l~tIPl砝V%ڵ扺lrɁwf_pL|𼻡|E1*F,8+W6k\ Gbxgh(v_XY#S GdHX F!qOnHԣl o6*Ø)r3%^SϽo8r7Hs&G+ Ǔq {G3ݪ}f0/zǫ7&4 2uCujF1gűiyJHH6s !b/X٘܃VFi-5B89!:Ser!dnV.hBb==7n_arK!,IyM9;ڕ0% Mqpd:3Yfl鯞#n]QkÉ)8h A.M +$l4iʥt㑧ojNcYW[=ފ?XJ(9fR"93 Z gAJ/ܥdUP']f[#Pm@^P:% "Q_5&~8XMR /U?BD3,ɼv!]eXjR9oVRSv? 뱵!r ] ` lbCl9Xg9&Pb)nBQUHΨ[Y~?l1gl}k% e3Q#ZYx,e95Q3Jn?`SԽH R_drE3\jceI5J@dHNWe,u9_cI[p]1o>ֶ\ue\q|| EVuGco pdgtͦFQߐDzvL zzz^_Y:d9 Gxzy({8V=y?hJ$slѝIΐBXMu&gj57Tt?qV c33 =Sr3kw;T-DT!e3P*&D1B])- IX^ 3'M6"ډ@µb=T$*Yy'OMEuB\Y[Z 8_aKN/TF "f^0i'Lj);OW >9 ^|$9|yF9oY/ 9wj'5*# iI ' x|}-,`IS%#;H4Eus&91`0{С"+S,RCY_"Fz3YrжB\AA4]@^񌈉 ) /ȃ:EKH lU_n8+?!g<׆L"׸bF؎QdAM=xXFMpv MЫ&kD%ʟ5q+W`wyh+mF9U4Y9r=$x>Im9.'ÛNY *M.}7j^-9h}=] fG,;_~%0tFm)a%[mHu-Nt.hKUMw̥e˙`QǼKRe`` DQ,ӥڴg,V֡4dTX_iQOk "B/ fx:e&hh1Vki*]_+^'չKșPoSD? +Gqv.O9ܠ G'2UT>yXբz#ˉ}&4h\QnZɥNAM~+$q !Ș/!hR1ɦi1pZpc%^E"T%svг˯aY}[7O<-fsC&0p^bLUP[9;y&բ@|D3eq^>I |PLVd]p Vg#9l=Llah-;3l3?b;S=bC3L[rBȖ>4*\?7# 26@["CѶB M=F!@I!(uSj79UN~;=I6؁)g֙%YhVfH'>'@k}j'x_tf7 nd#lm7}}nDѬ=Nn9sl}6dς b8*<}5k=?r/0 %.s&k y G%fQDֶ%.#(Do=s(lbd 22l%Z%sF{,@(dz:/<f݃jچ^rs rSJ0T q0) .SoN (v B788V]Y?J1! zc]05 &4͔w|d;1!ΈqAōV€]0jcs#ZK^,uo4_7| Uy_˱:Ԋ憸dɒD24}sϵwb;O{?3@+ԙ9{)bd]ox_+;tjH+ޏA6 w%Ir ~Լhg$WE䣊I!PCΫ$tN4ݶ5iGsNUwsC d%Og/>;}%5.TY{[q\iPmXCU3ZAt_h'o~(>V.bV+|͕kQ \i!=}p{I$piAi&&3^ye5Q6jyl0TP+0NwpGwUh {C#Kʁ$;m:4 6W#h6F ~nSKm*;U/$?߲!)uޕT‰[™UAknO2%;E֟og6Ѝ mzG7lj ֗A`|vё)ͼݒ-YjGgJ.Q}7鮐` m/q$G] 0QzdTkeGVHث#C:fԈB2ޢ4' LYp-kbU@^sI @%(^]62n`'+k>#?T+2I3CC?4fUzq+ѪwK_Xbmbw]rrG: jA[Ɣ9u2ez/ + 6Q} [K6jb"oKeWL෡sGVEcr~|VFEJKsD']̊قy{8yk(8M4q"n \"$aŊ1\'1n@<9~W,1AϽKG^Aٜ!iH^4@5O'؀՗cR*VA|:D\ng:lOwSˌ~HW^V/ x&U*|bYaO5(}'KֶPB)2T& Y@G/ݿP9d}X5L!ȌO:Q`'m1Xi<dA&xXH0&Jhr&&XM|mDO;QWG%-Ӝ1 9[aǐ'0Es3":9qk'U$|4dӂ{K薼3 CR$_L $uGmVmqW}T\ 8Έc_( `{x2"< 5a).r}Slu= X/ɻK">Ta j?yܤR$%vZgSrC,`RS1z\7Q:Qi-#}[tAȧ4X,rR3ەRHLLXg~?HVEM~x DV*K4Af#Nnj|@T3&BzY7E͹o8֤-x c6:xv 1ރhG؈^!x\Lu/wLf`si '튾{{R}!)ld^/`D74ոei癆MB~|]t8NqkG^_/L'5W)ެy3br㝝 hqc<`$\ +>cUv"N7*w|f2M%|Uhȯ_ZH=;b@[G=bt%E9L"tfq8UUv_]ᰋvdM>oȝ$ѽ2fN !\1c ?:#0@d}0"#2!od L4iPݸ3a\.ٸ"Vڙ#GM˒J[4TP`(np.Br>n҃Y: QaӢ{]2Z9paFi1PIj_O^Җ UaZN NQn4G>A'ڔē=s+pK_&Ɩ[Q܊4nr7pFLCyˍ*Dym3&&\]"spiv%ld`gx;T6рB8pbordRgWR=jCn|ɺ6(|xZkjZ%US `9[$!*Ї؅שbnهbsO Id^Đ׹y͞;|+'$O3YdķxZ\f(GQ~Zl6D+,~D a92Ϧ C!o*AfdRJ =]=_BY =5ٻ.ӂ000AE:k΄{قny򔢞_v.5s4J[9\b7!K?c"ON>GR(!`,74\A;I&sP J Kʖe$(L1耋>0`ގry 焀,x(u9[D;A)Im|]CQ J"/ xo(U}C72ROkaT)e>qUS{& 6ៜDl5F%?nOXbkR ֟Dt"۷: &L6Y`Cc qOLr'mjBxჩ "wIډӠږ=Y$L@R;}S bI~5)ϸF/~BV!5ɚc/fZr\RG>?zn9a̴@KvkSV[`bu9mP;qW_89}lυv% jҾ|0#3;^4:@ũcQCA5Q5ӨFg5we6x,C2ldߝV0p:Wh ɋ߀(wiy\4;_ (^EA.{%:t[EC JCS6SLAs>K+6~:I7G訌s|H^u/נ uPu{5k/}دD-ϗ=fn$TK;͔ţzp[3$`T*K\2wK*ʩ<8`01+lv0Be&c2D&ӍAQP.U:x}hZoU?17}쩸X#U} | e#ͅ/_QXw.Ō6I۞,4-M <06` V{Mv.&QڦTIU{XF`#&0R  : 'dkXunɕVL,ϝU^|0h6Y v~R2=: 7 Gcb>ɾ̫P݉m+FA1WKLu>CsR 7Dd;Ό 1|UG0 (@ėtޏӜtS1JZE#S>,/6?2J^z#O:Q? ~2'n׋(̦z2جS]Ru-0oo<)aȷ~~/#2l*tɗ#C!w@mew(7-tJoQkツ*mt#CR~F $Ǔ2CNtzşx[S|u?rI+~sAH:*CPa:0K*sQ|\MhRD>jd6[F_)1%T$_X>6c-ۉ'}ᵘL~V"GW5k@|\#섾3”=.tUϕM&] [F"`ۓsv3M]2dzI ֢\(3ߓ5PY])Y0Eu RX Pk˸Ω|.E.:L1(ݡcqbk =MrƁXIw҂}ݽa .%25dKnCjd(IEq> 2r%E3׮}`h^@WضAbPsn; v#v}en 2hWђ;eɍn>|yaJTj NR5=1#|^ D_!C:2p1̖V>BWEEeF)L2H=GrK'O]USW ?X2phg ֶ&r%`rBq N.].hI3,RO<~ojz4o FnI%w=27>BY|Jڑ%)֊Òpu4 ._՘;XxB3oAL^1#"xsZL@wU`=&|V`QFd3\J0<E Ѩ5~6% Gɦ Eչ>1*-&+" BJ?ȼX\yXerxfKwSpߴ*=R/WU}@hCrv'LÃ._N4 fsa= AGY =jZs( Uw%;HjH!+2*<&KJ0˓ NW;] eh7:z`x!6dd˼I,6 =Qx{j-[9>U8-,SmcJQ_Z8c;W@AZ$+cu }v}e. Qg"5 B>?ՖDwybjuHw%=:gCb x >8IԙŐRo'z[]=h ЧPC'tc7 ;[#1ރ işPjY/yeC{Ǎ98yvmetދ M?N0'{r6LH,Q!*99 T*u[ueYӷ#UL%>H f'CXfnNX=mLq|wU¼SI&aܛ^|9Bhdg2Osx6%0Y&^CR2OGMx 0i nY'#,~D& kBrhwE8 q%0ZRXmk@6Z- Jc- 7AJ@vbmQj]mX]s畻eF$2HVI[zWeRӬ.Cؔ3yCsl-4yU]b%`%ՁThE,9\> ֖0@퓛<<%sՒYѮM#۽=Jj_;(f K(E}0nNGMesZIӶf vdsA3bTi(.#{yFU+%CqW@Do c`!bP`ƻ`MzKN8nYۮqCZʒH$qR|4a nJ3 XH\WLK+g 1 88>EF gF /tiRzz`۽EAfjj-- gxVi0 IJ= = Tbq!Ȗ(X9FŮ p\\OO:3Gr~djD=;yhJ ᝭@(O5H3jOQ.'O옴ѡ^qKQZ9CL*o)qQCsՂ,JXc2Lǭ6>zj68(ыs0NC_QQμaiNXױt)vE1&{R?qE&pJUf,[M]mtJ <n/]#;Tu2!hS&&9ߠ^1Faaۜc^X)h<(үֆ3hzX މn޹O:u& U̱6h1<6V<{&D.ܾҷ:DozX2 1ߥ/ :-1D"Q%#ȤMT5<~Zs{+5sӲdB 58+艌A ,B/~J~@& `z9e&ՅJq[|P%|(U81mɨn#5DKZLmV%ci`#r`Fݗ^jES)AÜN4}.YUE=䊱rjJ7tlG7QCCR cK̀0|Jb>+b"(i|As{_QJކhS [C"{xưD *2A-(+OWJfTxp0؎ ,LҿUwÜ҄tOhkU' {̄sE-@͒ HrEyj]t²:)7q?gf&ͯ7p%~_Ɲ-<:62[ɑW٣10iOM-2)Ǚ3 ˆ+uVi |9x^>40[un>4IX')B7B-zxzg[B6) YܾC3lI4ݗj`aױ[c7&kbyFЬW~ 4rнX vPLvVFh)Gp\.ʢ0F+.㴭6ڛפ!.wї_,*nAŨ'PCVJE,dkBZG~CO+ҤW c$ 2}o7kr I^YN}#v٬yńb.VR[9ܽ5DՏo$a18w+ެ[r.:KJgOBnZzhM^/!PCyQE\Kr"U8cl1l%;yt7@VU 8ʳYZ.q癥U|[r팾NcN]I#9RpC!߫Z{:7Ydx*2Qܘr|>.^!swcpXN>L?MDқ@|M( &9ȵlEb*F|ț%$.|vaѮǩJяPV T]6\a78V@Vz3l֚)ђOfy6- m=&nxc,_n+bo `Os}qƌԘxx_q\I+ twCl|Ŗw%0 BYb{F5 #Ez Hx\ *+|( y'SC- ɼ!_\jl!.u18 mڟ#Ýj`O09WKfh;4z/7i\rZ37;gK+߾i;-9g:ؾhAE5Ӯ{Xfب$faAxXqaj cHU^'&Rg!A8âTxP 灜@Ǟ,s7Cte?/h4#uU]aN2p ֎kϜn(d/T/SsCpBHcm4oJ͗'=niAW`ilZPZ-%Z$w2RIHISKL6)hA5<DZ/l1ԍ Vt6GKR@$s/DJQ\Gԯ{E8_nKE$]a0NbL!(Xi$hXe/OHtSEAY p1c-\NۙZC@o@ 棳Iq(}MtMZ$2vZ1#dw:Jmu7,v: XQd]^`K#*43n..A\mRg/B/,\pxmƟv)D~)ي[mDmncg;fS^/@]\qʮMD>ykqHK l*h^H3[~к}Is@) 9"OGD !@^8 CaX- {h%tX}zW)lB=*:w;&S& E7xۃ/:*L[}5ޣ)lG]Jt+YP`p?gtm㚱~\M|$0!bFtv_e btq ^{Vҡ!k@2RB JJC(D?n}zEIs_@o9wM./HT A\p?y2pls^W'By6!)BL~J솾mR8bWHu{X@g]YSC^sRFYޑu c,eLM)!Dd_:׋ K}#X]b ūeӭPx6ąDS4֙oΏ/K֏:38ĝʔs[}b~ޥf>mriU_Ykym%k`_R$ixhZ~fàYvz΍an~gP d\"z`?_J@8%E`;ۄ),@>(fW QhVR&~1닕PRz 5 4\|~%Ҁx/irMڄA8mw!1׺% w.1 ,6[YqYm?нw;d'~n3eŸT߬RX6Rnp k()ʫMQgzvT})NnYP:_)t7^X:2MV 6^d9`ؗȅ T#Gw5-nqVaȨ NRkž"XSL`M ('dCfa&ϽŸ(( f*q] &"!ύ_Jokp{ Jv<~=jm%Sx #,ҍC̈wDvЗLWCc>ZS;=&bz4ۻ<< E+t3Y6^D%vt"4(?ޟmgpwr{ǜqbFǀdEeW`ʆ*cA M+h, 3pgszh~T 졹WvGC_ݞj|ĖqkH(cP)]娴nK_E&NIABv$L-!s\[|2>#;;.|^h$K|>k;iW-bCCt21=1Olև,Y-p 6'p^ca52j?My G#& 8n>T<u1'9TrP|pbЍP?Tn&B\9]rS*MKɠҾ%iD 4nFw|Rv44(+5Rl0c·˕_ ۬qд Z0W`:/$ S[C iaטݪbtr@v2^SZܧ 9Þng"יGx3<]oL}Hw~tB+Dda}#/v{xr4Ða<|=V1ַeGKujuGD"E58Ճo YS jU!We"/ ~3`PCqv vFr/zQߏHy#(J MH<.M*op- Fp-dl)V!U D4۶+D^L4e r2(^KChgo[ެ^s7?d _CS^(L/>Xm<9`+x!M刌,bgu㸉>>e">GZt},1w oؽ\<0Hi:رe.h9絠U95°>%G# fZO>B}ɠj&*<,k2}dLÖd+S)fIya0!w2+!ds, I] ?d|^xD^5^-J_GU`-S1%݀x5Ac{\~b1: &i;Ug 3=::gaFye$>l՜1hJ?u.G"xqn9Q<7tXf;q, ̚ bUʖ"'?CwCU(l)?~l(fZp@dTGTҌzǁ->EO,r<jMA)nM%U?X*ܵyq",bTƏQE !q$_}G&)ɩa%t~*6$`Y+mΨ$!'Z(`Yuan ݣhSg]}E6WĥRM^dihӓghE]G!) ɞEV$|B u)vmZ{ i Cf/4sz=`wa@-nZ5+03k~.؛ܐ3=eKv5_y[IhK8m~KZ;]T8AFmR_c̝S:QD:&wwqK[:ȦYgPQW'kNOXvP KE[M{^9Tő׊7I%RszZol&g:WHb(6WR uc#{HvQjЇ{.'A) ܈EjxQ}OFu+g##*WhD0l&vHv(q5EG7Zғ2`Usj5o;PXޯ>8}{? Dg&:!Q1൙[۸S?% )M@1չ#r[ܽ[a]Py;>1𹔄Fkh\?َF__0|z݉ACkHc'X!ڪ"+mh:\Ջ~篤8b&`E*yr.΅B '[Iyy-j)Ϳ׿/r0fG5=c+&nkQRp+zT``ݙJ$r5]cNco qQyɻ-U+/K24txx95X>_QJB y?3hV/'|բOr2Pz^w٭4A2rsi=_O'Q ɤ}"Czd̦jep%737H)+=d|AyWێ("I0|ygP! ':&qqhY_bN=U?DO:.7YU?,n^L&5>΋Ȕ_"\o`20wbհ"5뿴vxuvέhDKnV\dشuuO鍓tYY)aiW|*GRL9AӁ6(`A)e(i"1 Y-Փrj9"OiG+jxYsq$٤S'[ )H`PN }4S]Yҹg8cby+޳gj&{z5+S.-|AUDE ׼q;5یv6ǫ+ &`j&;(3VV\>naT.m=DJOzc#(liuD{ײɠxŸ iHa|̽ rg8[9Lձ1̿͞GLY#zǙ;MS'1ե$$1YGGS)~8a9'8d?;6-H1*ð'X͂bt+g_Xɿ[9j#<l1ATi>4?_V]=Y~,Rjxơ뮷&åI*Kien4C~'_<؟<K4 6m@CL 5&Ⱦ;s}yWV a i^o݀crPR{TPpضr-)nKC˴FCҁ$^Ň)OƹjC]qg|dl3ͨô]10 IE$oONj U,( %O~;RRi3(”o/v1Q撡1L'cf5HX-}@*?~nv8mG0ŷđ[=P¯!"/oy+̤~6ro8r$B|cKJ*B <{A#08 DW1Kp y KH6ωWPdsVajFs|Xtpa؝K3j*L)[5-qT#~[ؾxAaUKR"Jt˗U6է _fk-J- 絗3"s!"ўz3D̀9zĒ7`9|Ŝ3p h 08 OY $DDgX>N[cIcL_,BX5qL˨Qh8Tw@x(|WRX[D5S(Ko:@w.d*4f0#  MǞYW!Rp=_V+ɤXоON8'Zdx]v!x3D@2OCPhnt[evò"@mc+$lTaco/\ _^, K55@ 6z{U[ VnfO.l'eaC+|l,KcD(sde$YrC4☏s  EO v!h0Jaz>X2wYl F:6:"sk{syV/@ӟ-%Lu KԯcN&~ wTS{5RsL@?!0vA^+0*Q6 0}Ez-.`ɗeOuXK hH]X->)pIb ^8VH(q J匹AB3?vL g($KWQa Pˠ; \G=8d~`wI+`@w qY]._i~x} d@ꄧQӯ 3;IoΛdpb|(1WtAHGd}CwEz 7ϑ{ݩ\v4]G %j QA> wQHBM|PpI'C_ O%3 hN}iT2/AEzA4̵Ё#pM  ;"jN:R}W8ĸN2n][S^oL+_62 #ػs2+g:EcUv\ٽ9NpX#n@зF]¥m[$v"f$R2>Ρ/6bsv7j.-uN?wh/emѲWPmLch()3JPG"_1B)1r/Yӯ޿kXEd-=N{ll =I NTe,a,o\ڝ ԸJ)_ XW%aA¼Seyz +kē:`as .,>T;/ϳ^DA4oPל2Sr揢YХg`+h:r`V#K)?fNFoT(257Wsp^Uvt ",'IM h8`}#cFbR^qZ ݺB#kJU` 5 #;P"N뉥i#  ~'R.Eŧ$Ē{,Ӌ sG]uͪ] u_k#a9;ggWy:l-$tee! W&9 Qճ5,&yx&cE6#w(q@hH# 4G0hn<~* ǐ>3]+-QX A ӎƷ>Yx rօp|>_3&G%rT1  dޘ/]匼ʯ7yǟr_Z/SZb:ƉؗT<־^~ 0k-W43:^*ضtuN6&[}Zm>& gQΧ%yyTvG!w=>r$F8lQ VU0̨<|T8/ѹ vQN 3|=IDsq7 3qTrALuuWAZ P&exDXe*t(&`]Ş7q??;rŃaܗ-[*-|\St=0x-Q +sDEjВeiڱR{ׁh]P^_mxBg@v|iAaQ#B[♅LE`)z(H9釯p(y_+N{hӳig]9Ҹԟ5J^crGą: PqsP0*GPFOy#htT 0㣁v4M" )n {,L\LGӉ=əR;5dxfQ5$Nna 6Đ םD_,oFh[K!-0WN_%xN肯BGLlX̞*q6ꇜne%p}۱nUQHP;akЏW8Wtq-RHm@ +jݢ}1YC`UsTMiCԨ?cp-z}ܭ剡DSwIh\fSxMr<Ϙt55,̇}O;oa[et:  uD MH{` _Nd@ Eyۤ`: Q5E?,_3a^ jdN` jT]+< T{Y?RMu&Zj$? 7@dDh~sC|4-5wvck2Q F$rg`Q T4L&DLS gDtLg>Vg?,c|w/vߝ /NՇ7 n Nsbe *EJb2<.&^t/vKOCeJqL2 $.g%DTeRh_1ṏS!Ԙlu/1Q#hژ ADMK]+) +0hbu${_v@G?òIVW% 3L@c+9Q}k1bF } B[%}r&FʽJ[2!+$ i;I(K7ڠC~ڲ2^CScW{c{({)ؼ1q/Ǖ⨃IZEKIat6\o !(xf'ۓbQ.Z2|"^BgD*Jс74|#;xaVzf=Dlp=A:z|m(NJ{q'ȹT )Ǽ9t#EiqE擧S1o6 `" .6"a8dPKXW {@e0ZOH./Axm~Kиٹ[FO yʣ[yA41zs:(x(q--A;/#t &\(cٗ/'! '/|㾀Ou<%Pإùt3jEPYA.GDm `ܾI$U4)%10 u%NJ<Ǹ< ]K|jsm{trD7nj~Z.PaEB \\#⑗MjM·O"#3J223p008OPKH ߧ=r&t[j⬪zT JK5ftARhc') RMgB^G?]̇d^+9>_B&6~BX]jxc;6hU+K(_#S?;6`MfUpsg'09";^6}Hbpa`dfۺY_L3'ziU.̠~8Gr2vW Ww9B?HYj~auKqgQ|5Q&g2|ejŠ*sj/x:Vb3]=t cxzt)עS:!Rȿp-suFHl?i韍?z=u286ZMBF6)fGu [)AjauC.䈳|LfYZ,RJ,Vũe/&:8~‹1Q,/gӳ5TBJӓ$Z@Mk8E'yhR.M(f xwBETqrͅغ7xR)moҐRbu\aC2wg ;OZP6m*&7r2y0Z1=pJf5sKavJˠ}e}q~U_ *?N $坳:M̵R4G^OZFLh*H7!5 ԑ] jSB"TZqi@IOiu[_a> vT>ۉA? pBDX\ck|v <3r!į} nb63egDv\Fsy<]-Co՟"SSlOj% R;\7@GXl&GW6ļ fDt|әTʏgѰ :LE"h-…^&=[VP͎C?8ۍ5;½έDI&?j.A}`4äkD~/B_?ha/+^P51CO`AfHJg~%MCK%=8M8hG0n3! 80om kcΦ7+'C,)$PdpXYHJ"W{u^n\G~ۿb9'\aDhJowY= bْA|zM_&[k,#} O_ "IM*'8cA.b GL4O9dIr)A`^L{dC䤆uf0R1f>{\MKr1^.|n+{6A= kSϿ ucWPw S@ʤId%K۳FiIw/'ٌȧ\vXOɥqƦೌ Ҷq][*--p޼cJK k y{Fi;ݙתH@SfG"S s -" ]Mo2HiIgpfib`Lq.(yA@Tiri0Uj` igWDQ xl+sL^}fxw˃O~(K,1Zh$#3AF4 ,]ϙ$]0"*‰ͽs KkHxA{U¹'l-H|_:'>YT[)kS-rKp$Ga<#lźj2h#2Bn'pCu$z6rf[U`le[^_ˋN?.a7̲.b _2Cw|H׮ n@RCR{gaqN=[xw>z 6cizR;N8N8pv| \Wpﷻ ޮQzxz4< `͘#Ҕa7?/E'S[wn(&&阬 P/X\%pb4qkÊY:fu,T6-ra-~te.\=ztL{oO]ٗט/p9%sCuWӔF5a^DMTe |jx[d(` Hg@2fI)k̯{*d~K]gRb9{_vvos d⹬CCg/^bTQ) ?8_)UZ>soEE}t|JW@21MQȭq4W/#f$#sZ| 4,b JPJb#?v2+#)6#ڭghkuF)LRe€ɬK\]Рpqn!/]L&;sy[.nC)Js,=+ [OZcL;&w̋RΥX,Ü`_%- KCk4?9ߏ\IuPiHLYT >tnWH=T@@1"z? h{'=y8%妠'wux>*՚ʄyFpmp:96 خzgWRC,]U z҂O9,rӀ! 5o z#~_X'G |O`uygJcEN;.){5c.YijS2lRR*ژ|V^Z?~jm5 )fZ:ko֋PWyL+Tɧ hBetjedS l׎]bTV0~nW=b@iDﲬP&?geIy7 H!{,?(drzt!@acnsfP.+w~g63_S')x{zoJCt0y!.T/B.²Zp"'Y2 wt)#4t=΁ZHH@YB1 Ɍ-YG2,;Ig4 E/\?žҿ;\ْ}i+qr%f_58Daz^;]@&qO>3P$ 8~dI:V @Sz"4 D6ϗ,$t!ŒsOS8LA6_Xx_}da:5SGrӓ[U3(#0_4ۉY^ԁwbi%lyGa㭍'.s1_X3?sj|j jx^m y,jg!ֳh,+3ILdE5{20 ]Bfxo10a6Pz/[;bΥGܝ2̙yb|/c{ i iۼ.3 CGU;4kkgջ*WՅgb=oƕ~ñOY @w«ZSF;Ap,҉Q%+Ext{.ӯ~Etx g=NW7/ȴK #3QY5ZT͇!"}`+}w1Ͳv~[}.vlkgrLxG|0ԸTw>a6v6"{>qXtɠ VsE:Մ{C]yaBK0%ڼLN@= gX[dѵU♏Gߐ}r ]X(N*M6 ^FWy]lY|\S)2IRY|fws'#_:R{`fݽg:j*6`u,RzusO_*Ke?RJ'ccdюOs@f Tc"kQ"7R{6E/`+)gRv{_?pR~W50 V^tyC.SIRy*.Wqe\"B?6Lbwh6Q*T`ZOFU zb궱GzDWeE|Kەbt^?9KXA x afu|x'ã]Fk5%D*R39XJl$[Jg>s.޲>L&S{MW|i׾sy@or&PWIؖr,!e{(I#*&/x9ٰ0Ĕ)ȺK0!qavaN"P <@{f]A4*Ygyw$%M=YDʅWuB('RMl담l:QUE_]m*Ka xQy{#v{K4!|wEΆAbDYRDM>xyl]G.I0R"Qgb/k2'QB̮bx%{B7)MSN}~GEiCIw./nsX/t]y0 #dz3 ]"[]Bl,uI,'=ffJ# d9`&n-i(~X*~(ѻDM'R҇" ❱1彼+ookR?܃3V_j ,m>z/uh2;wP9D80>6EsEL4yvD G}u>StbVmO"ݮtxq[uM94{sHB2׻UgW9kX%r!}(QixID@I83MwA 7~'t*@/wRgC{sF6Yp= 4쩦6\:(X4١C\`ŲU`M\fn!I!.JdWbH[ޗF̒CfbRn1/ ՝,Ng٩p2u Uū[Xo-vmz>`tjR2R*#N(0{ efzYwk@ ~H[Gzk̜ $ ~tR={V0I}Õj7̢ I0[j(8[2㕲RD`ĔUY,q,콺Z\O s^^I]Ш5jҕR>*1 G4z^wכ(M?i40>=l*./4lBn4P" 'f'$F MĈyٱڔI`ND*3QJ٢8L,x {P%ZӲ-x)%bE`͓eDG2b[5w*pX\g&jYv=bK:(zcߕj/LOpPuGr/e}y$n >,ݼ`<4!{bXsD[ FfS:ŏ.atHɫ9QL&(zu_w,yKm;fnioOKj|%'Ր#Aݡʁꭌ1J4hЦ^$?A !HTpP^I!6m", ;7XF̪|2Gh6(DٴgWVz`B|Q;9K2m}$"(Ƴ~z<کRW-1+'OJzϭ6y2%T'muPd!__nȮ@'!CI/jN/VTq!Km/ \aI^n6bU 9 awOEv>{sۢAFz9쌅%Lݱ=,)\?Ze6qA6s=I֤͠SVSf]v f"DM] =SMr.r²'+Y%tr01~b-l. w #y05Q";}MZuA`/۾Hy4:F.Gw`1ኛKAkM= 6O %dVT;v&ygFD 6"ID%ܳosP =?is#iQ\zlAJCLohmW!$d<@I|&6$!U@ҩGZ.;n3q T] t[n(jr*_g49}cދbʡܖ3Ry.a{ g:XIs_yy+.X6'f i[ sSc{&]xr׾ 5L|3elX@uee4AV y' -VM/ŘO2kSI2A.xvҢ9?T= T5V7!#V~"X"eƍXF1OY''ͣeYk/'[ \Em[Ofv;諜~{]gCiuX# o;oy5A%NA/#ASIa .t0x[dg^4na:֯C㿑 Z0Ƿ0Sq2*Aejm'M_lǮz"_]E`\7Єf6 "jsYz,N̼.&玱 yO@W1;;uH.,wd{%eT !ɼ/|St~'-YfώeKB`iz,Pnx 9hwF*=6KlDMNOdosoԜx<[%,Q4Wr`L>gWq:$BXv3)j.:;cE;݋oDpɥY:EۖZ[3at(c$o3p fč9Ї3œ.Y~` YŧіZNZJ3$9Ny$MNrx"#Gv/uEm}X灻q81}8<,N=_Ш7^"i ?+?A]JR8( 3l<Ф ߩ=-A0@0369%2T}zϕm09lvu$پʑ\!~ %y̦̏vi8-jM/^COw8v̭LM>^5eT[# FSRiug^/|Iaw\;\p(X&yp"sO$MW_|N6@U%urtxϣjp<:4:FkoqOºyhpއQ2$% î[xRx(eNB*͠-> nw٢C`]Rp8 g}؄ŏȅΈb,ro`NHfG|nSf~@/6-4LgsINB% 7mw&D 9T57**0uLB>CP93({_`Tecq FWVȞ,z Ԙ~Gxe.vNijv@'>N c7>(=G^*;LT&G$fݮ!(NKZ6YKSq,d_kca%dZ#9l'aQ]Eu/?3jW%AR67pIz Y]S֜x҆+| L`*%J>eN ~YP ]a*pXV,:Z9k-ک\fuQ2}t´+(bRM7Rȵ,hZ_Hjpt px"G^ ) "u(5 (pI=Ya-mfcP!kl~L:~'`;k-b1Ni<+Yщ~l\p0BJlI3/_ eOUIIG\jP!{o,2*4Q9şNRr4"V]F:Ϟ= 3^"vaΤIJ.'U-e~$5w @l~1LmupZ`dVaH 8hkV0;K J@CA@%O I)$jcE[*Im|Sab#3zyGtBP) >ꯎᆭG@:Ϛ/l<A ';wdTMCa k2l:b&Vn[$$,;K+q@~4W/1°'5'hw=mR$SMXٯؓaGĆ+=(ʨKx=!NVDhK0vxxZ轞 ni]bfDի/w&x^@ fڡu!Xս_%8$hw.َ"ӠՖ_|FSRg.Rt 7oul=bu0BU k #d IJnxݡz0ITnUG>Rg'd%+":"ǎ6PB Y'ُ68kotfHx+-I,Kuz:?HfPc -ۇ@hЫc; i6bf[އ$81.`$BP!w&bHԲ1*`R"4,_$NZ#%H|WCj3ҨW<+[]рh@ R]:P='5an6Aј\CI>lDǂ87ӄ\#R`P xa;@5@q%"&`DTF8آ`+kF2/T{f@']oދGhoВw$apc)zCxƊfS 78бa0}/qa"h; y֔2MQOS?nu Iŗ,3@1o>e/ÑY;S ^bD%^&V%(x'G°Ͳ4Ig哺g[|t(N$Di+E?f`P7`tN+Pbiꡌ''E:)̊ȧ %!끽hxACy6LJQ<(B|ư%p T kf~_}P=%}:W$wpהT=䣍c <;w^s.1gIkC+K`wG\$RnLTN*۬xѽ2L)j^י 0]ZFAz9X @F]ζ(U2rPcqG\F{h ʼ0Cș(Ȕm PuvϚ"ݾd^5]̠Tzp]b#BLD{R0)Bvkf04So#FpސZčМ?.zC}";}/۶?>}zN7-];.Y+" rȷ=S ,SȒxSXGOsJ"f-bk BBdQ8\Jо=m~%Ay3^GZS"=xh-;bI0G.tK/4X4a vr9WV_kI'aX=*+w~iQ2I:,!6,("&ټgƵ9ZmF+WI3&ӿ &m2Di@ S6X gvM||/l0#nQ޺,{]$WF։ŸJ2Pd;O,%lg%JUkk.R,0^<Go$`/y*'0y٢e4 U[~7X|~~CY)o6zVPN#lq>̱K> X6rJ#LUI8oX,KB5x)I'rEp g՞(g2t$!Yu%XYVl yPqt^)ĸ[iITy8$9!j޴m k4F#ں3ɿ!VPAmhQժ.|FZDīs $"!lLC2|W㍲$aqD)b \]Ioy]Ѣd"]]u⣡w<ݱ u4aeeͫ:;=Q㭧5V30?aOMawF`$upqCMdnvWvjMGٺ2554"iDi(aaTody(=b`,vKX5w JiزQ@]D :GHh{L4SseuĬCs{#.J5试l4f!`@"Vgb=UϮT_ɑɋ0@cұ#jH1:,%M𿳖GK|{ό^$o0Ӿ4hdd>Y v/衺sH0MQ"jsyКwjS'\=~!8L0*S63 ;)=Yn#"F5nwŊT N @QnX,0J|7$CpCDzяp,"נEr9 ~]?T2< 6ӑ}s\Vwdx6%f>^q BbJ^gX|J;:.<:?icҞG+& HӲv'O]h ({), Htf B'1 jj-3'4W,pD?q^mHz=k][>S :d Uج%obo_46rC G|sPK4q"i/5suEBy87*7Yߍ?Š(^+Ew5 bPy{h?ϵ,Uz;3jsȭ9~%>ow ŞJQ/ #2;JkW-y§{]Lew R:r2KJB54c6?)_'J;%iTy{3Q2<T|D}@6HKxeǛ;]$uJZb7?ZW$_qm4DV k%:JQ(`AVL X=FJuʖ#;e9"j\i*l̪9sKjw3E0I)Fꎉ~[y#  "cFB<W- Yڰ}2 ]m@ɉ߮bpajDQq=]bBwp1'&E!{T[1kvSO$b-_E~6̉~7jh";-485pD;pC]ELA(Cw"ػA@ jT"mP+(ȓpU?:dr)o BlrVSWVlWy _7,ͧ#)^d QiĥvL4Wwm nxi7?a%d02Pa}B@mUK1 "TTsN:|TtG@֚G%TV-E wR8+ 唵[8T\V&M?:A^_ߒ)cZ$ꤽY;_Ϯwno}l]BI|@0;1!m v3VCC`\Oك5R]g:=W\3Ֆ~$ "?W?m}N#sdWK$] DXI,Bc]+&MF߹ Ojum;"{0*&7pՎ\B lqO8l$2q@ cV]kpx`g,`4sebR 5 `TR2m#X Rɱb2 IXEz"p[~56t T^2&zKE,e\i1l3<: ݞDZ1H{0d?|ihegclS$ D`e=$A+2t`65v}'6:5Z0ۏEr?(9B<$ʮ %n`:qDI$LnQ ^s][ .0IQS*#R{ }2L?gA?:)=7T1z? Mn Y.$ |$u񿫷+&w¥Xg"?G "{-8JW{}<"j4 K 2tr(8vJ ClU4Kϔ <~BOh {f_?JK%Mm NGYk!5W'f U!n.zM:ƳǏr 6aɟ^30ʿDy\Z 6 #u>3'VJ]jb@ӢаtNdd4z8Hv(2"=k Do-,&{?`@>ubQ.lHff?!'aaKg?NŨiOyK?MvpF@p]z,\ RJ*8UFT퓀Rpj!eq:Ȅ[a KoOr\:c4@C~iBc}FMו1.rp>,Ϸc@ H_ʕ[ pS쒹Yq>R?$go?-e ]UGdrD4rGp5>y=(XA"eh-C& +?1W%j{Ϣu״z{+?\Q h&@cǽO^c7& (SZ- 5%Nb,dq>߹DQiri(pUKڑT3\H4wj31ؙ.R"О W^ƕC'XUՈg qm?/}7 aZ.Z:lAum7=,{1_͍%ɒ10X8#`j9XmgEx޸w\-FMK +|Six@$$L[MFOu&0nLWƐ+bQ"Xcl? MEJB#AM;c' Շ/_mu'?(/kR[eAXF.Nfܓ%UwC7M3b,~."ٚi(Kݬ&0m M7W1p]oCyX_루9k` Uw] P K}f2nxu6Pv9&ɕȪ*a.LF(nn|._ϛ`K ˭{lPĠC6; 5ݲRʯ9$ߪ_݁%$OtO*q{ tV,g"U{ɝ $68cQq~kaGRr#(C# P ,8˰Ia4:md3.7y_J#.ywkT#@wf2- %k=OeB PܐZ-}l+~"]U=L@yFWuqmE:xIEXhi߄M%fqh8>utX6Ez%rcfPfjq!5fU^ڌ*gÔWn,4;lY)@" hB8`!ēt $y^"mF,V/ل~uG jCޒ !Uqy{#y~yGp*#wG)\'kn0uѶeu3g \i!!#|46MmIÔ?[oCϝ\9)yMc!\EiptSS=Yd34ϵ`M>ɐ|E _tZSK,nY pZ2&!V4RMZ ~@)sAcESBB2u2毓8j&E Iĕz[9>Z>rO#Ń|e 8B3 B{(1ݘ0*g}cT۞N. :Mډ:,8y8V5ժ}dƚa^Pj7U%X ;Zv.>=Yۃ(ma<>; wH؈1&r|:MbJf#᯷㳈s7 qH7?>\ѝ޵uz36νˍm9O`o{ŷAu0fƛs LI8KՒ UMuq=X8Tj\#fQp$îN{q6RظǍԽ(xI?<$NA5J²!Su,  :i8-;^OeKK3W0)q7D/nCPC^tQ}XfL3E*wRQN eF{[v2VQ9Ivmu2mAW[qf{!˵ `g$RtWqe"Zf>Hn# :,UT6]u> R[yՎO\lHN=ܜ#k嫳I9igPO]ߵ 8oV ?:s/vS=}Z6TQsjLbgix͂ҎHƁǶe}SxPn6#lQٌJҊLYJfj:-6S 2SΞ26+GA;VrJ$_Ő40b3 }yuH&qPHҗ>ݲL m@}FN+o<^VZt*9i?ݰ8 ޭ"Z"L r̒};{hs."c \|vC-`dCa[p&E|*k| =(G8(T2,n/S<#YZIB65C_.95S2/ETR&XsŅL7tkY@l-?t֧R2L/ T+f3'e/t2C̦UI3%׉P<*4N<Q{4f94><1^o)P*4$0ta$YHV$ ~r\X$*1ֺM,DN50 Kxm˫|73s-zwQg@C1"zɩՃ X,Z]Ke^YG Pn{ȓť`q @eEJ_ `ADYfP ޔށjbec-t @w6qQ2QU|F{}%.\kSg0qmLa QsF`o#+gJڗ&V$u\,YDKsꭤ?ZTȟ崇Cyɛ,K9|B!l S>jhKD|J28(-bMN[Q;t-b>蜏K":)pn Dhh\?z@s',$%;z~:@ T7WBKP$⠻"=j#g 8eɟnv=mmA䰍g bD~V; a4E80wak>S] 1r-H6c;|7a[%Ę)Lxs/(͇jgjMvM}TwyO%)s#>MFҀYYF\qr<Ft&eOGeIg&9Ҷ&{e!l=*Њap"T{5-\m匎~\WvLX]a>@{R@?'I} ;4m,;Mjv'5D؜BfiM0PP4<">@7ر)i $q(2|{td퇤3hsҰF ? nG)QH`2Zpӧ K6mؼv[- woQqx-&^\& z>P' q"Q{e$?F %gL9b6ӟ2#q[~G3vV2 U[Lz\d+{B JİC=$64mF@]X7{ %]Q ȺF;;J7ŭ?* XrȬs[/a*O+ݓֈ$Llln8]aj9WaK8!3xεr^ͩTмKnE YI !u c7$bB1!fB+g7 >F5 A9h=R!m|+M,vvSG碩p9%LM)֘j\x<ݡP'FiyJgůgge,jG/^Ƭ l!nڕoeG3j 4tu;!P;APƟ d E1FlcafYՒ7򷰥<#7lPV)|e V(&vN*2{Zݤpҥkb24nX"}zi E23ՒR\lu&AlwDyT^C;>*AB . 9`ؼiI>jb(O o٪س\a|$퀈!*m'Vm` Fqvoh|IY8Úe8$j;/.kZ9&ϤqMn>"veҐ3:B3l 3 r䷀= wg8fQ$L{%2ZmMwo'K0<혁0קFYֈ-4D MXQC-ZWۍ80e !WA]zQ_<<~TT/p Ԓ4J @_/…w!2.w5C2jFn{e{:vqaވAӒY"0NW^h'( 7Pk[4Q<0߸©3&ǤJڴtl\MTFSfῄȳb{rPJ~kWfaFJ5j]* )!I(9tCkA7vr)dAʑٰK?E7]|1ܨDBQH"Rb* *b^u7CYXISEEϝhnV'a=QR르jL%M:.{ \-P'FV^ xxhd}`Ĕ3[ V蜆STkaW{$)[O;ưa АCYMqL j>κ9S;~dW^AН\V'-_EdzM#u{5Б'fGKn\q*9rvc${Nn$eI-Q? y]{H9!dFj zI}H40jC6Kt(#y vlJ(z bXZ8 '$/:Pջ8qj?optS1a!_,V&&ڵmxqkZ$m+ Gb~VL>cЈz[RJS~HwXKAPqJV0@s+(^зqeic4WVRT:A< `1`SޑIϴм??ĚRͼbVނYS΋?o_|-<dX$&3rqѕ ;8?.ٜ&Yێ[y,_$͎^P0gV'1EgJm] Fe| ^|ŏBze,x}^=g*̼B{&L*$;9'`.+48WRua  ߠ)uF(%7[`}ۻf^A :YPM?Z)zm?GQ+tev)S?Q+OlFE "`JR“پPbjI۔L}8{P߰\ͷ eT/ j-`lQ-[Q''U&p`+{AyF+7ڙ.z4 Y@gcE7~[ |DhE>=i,mOcLpU:hb-Fل6B#s򳎛nfs`_E4&?r]][p/\Q%W硅ó噙uq6!რ%{ qdQjQb8s"W/17TsD{;9G6;E]Ö2yH͐J^uO'>J2Gh U|{B /OcO$VF͡v< Yv*'LW3@Y$u֣I硭 nh['G%S-Hw k iD?+pQ|,j-)/g ",(+;EdlWO M݌k.7o(9W6EN7סW 8YH5Tj[pT=Z>r@RjCE~wꃩwo8ċFt.{鯼{.a94Qgyga')F˓'CIqOj ͕a(m(YۿatKtkĔgvoyc (j4d}uޙAѥ%!D;RNzw5PU &͟34pnt*J S~>;qtLE?\}#B˕[cۇq<HT- lxVG߆\[?Dh<>K[;t6D-@qb4Rpea&ey"PxkP](˾@=CϨxWFM˃UGSJ~};`k/]!,nh)?&)NXmu2Ȭ?Ĥ.9Ed! &56<:r6]I@5ki}w(N(2 x J S 4~Ec6rswfT.*k#Yh.bR-l~JL6JGe>~jDhԶm241k#\Xs|ǨGcע<4!G-|q&Z /,V1HݸypvjyʴI +O0w7S:5!b~#6P~QUw' i?Å:˃HQ Bo ew"(LӐC%}M*Uj_/G[$ʸ6O' jym ,<'y,G:D nC4gKu0(-8'#Ҏ {hx[vvz2ygl5kۓ% ѐ3OU$|r*p]ӚUs.*< Aܓ!X}uo551. 7sJ.0\YEO=uskm}BU°=_z!ώf@cKO79ρ l؝si_; 4VL~  pQ_YqVo^KϼQU[)Z#﬽k9ڔ?k]pS-xR +roK-1aT/#kzt!Go ކs ZRfiNuP9נd+\Мl38nOtYڡ6H_p/sO'K,ՐmOI>MUʡ_%*S=^a_NH3$ZeVâyuJKm"J$rgO@$Mm$XMM?`T,<9Ʃ! ߳cgm"s=Nh; Ӛc0ϯ77NQ%dbvs(PK4nd;il !C3D,4uK1$i ҵ߂W eaWv`$0ï.hϸ>L t,Y|'_e+QnU.`X 㳻k<&f`;= cگ"3o|ׇl]T-O7oZ:U@/Od6hp+zQcߗ-dQ,DFjHѠm~ Wj=uJɭ͜dcb NpdNǤџ\dBUCVC|ɐ54^H>(xb5 }L?rN?13M 4)aGpT{.s$D$3BYTUErQ+tL$2a=QR*P٘"ܸ4ʒGhue&7,rcmN$NɥXM7>Smʉi YWk%{}sINRwm~>^a&OLI6GE]ʜ_4UVt5 qbise{be =Un0ZVz(؄+_i)Lӕx;t䲻;xSdo_;.DZay4SbcC| X{uDJs[AKY 7bE]OloQΕCp}CKm2fDNF&Ҩ̸ (r4yE56&|0XsN <1]&u>Q !hBZԣ+ t.D˦Or) tm]Iኆ &x_%e#3az\וS'$RI@kҒOE6b^h @qR(pV d ]9?Li zjY pChh6.A۴+uly;6yvX/AdjߔgCGI#y^h,tj޳RixldžeddōRߐ k|աlW%-swQ5 ƘS6Fsz4䜨JrG;sQ\J'-g8. ؔ G1ȳ|,R$w6hĀ*:4ƑG3]'Kd]~Λ@dM KQ"bʳT܋nO݁m) \uPe q݈daNK#߫**lM!~m"'߀WT B\ wk>luSv?gӆ!ę= h1iq3ZSd#]ԦlFBp~zd(`{?9`C. ]AXMx@P~^UQSŧ uh/U2XpKH 4=>E-D#g|4'e){VT)ubNGU >vn8JOYUj~ r|pȿ&4*< As ]LxCr2ZXPcX`:*=QՔ3ٶ_X8mkOrܢ-8-&Hqzcr= #-cmd_OZk'gߝ*F}!.@`_#BN@l,(IIV8z"V˫x؈nkVsBC -V%;rzC .3rf\/WϨ1ޗeM--pKܧRɌWjcԓ27FQzk45aKdu; Yhv_zYaolًebc[}s'-GCy0T,8sAR4&LaI%ã[zgwqM`ߔ8jQ uaYƮ+Bl#!_1,gw#kAC J(D*eFR=V:GX5AEQIwz'_P },^ V ^-uu?)%7>2 WƅsyxSԙ@G_dk>N;pp |Yeys}04!{-23S+myuavi&iX~GK ׬E  {T=|.hR1`JX7{R!3d {9Obӿaj=(O8$B93rt*j.S|vR\Gp(1b n.V?D ڳ.qҫe|2fP|'gJ^!NzdtDmВ;+) 0b+"(mrFa#7<7!hLP OƢgPN ̐o漢!@5arrU),+MW:=x-Dn<D֬u5m~Ø# 5pˀt HO^ K}b *ÿ.1M|?nAT(LܵatnWKTlfº‡Hrd׌s~{42#Y2θ@b^Ul7]nURn\ n`;˩C6GYP{p\Y 5BNp)00+8EPXTVԎ˶ᎌrg]R9Q'/X3e mmoa.VH: \ !FsP&lD0jL2fG\^9c%0+*'LW,43~)BcGl%yFȬ/ډOV.z3_x][7c"2]hДT G̡7MIzsN͗u9𱤨Y4/cdj'pA FIXe cKG0Wu|iYˢ8gHs: R#b$~:eš-GbGɅҌ\U!BUp=/aD6WͼTٯrТ?p YP Z c\% em'xKZaD'ш5h@3*{&pF+aKGUPr P= 7vʹx5>V|Uz\Y 1]U1])/<}pG/6qFp5Ó`eYt?YNHqyoA Pi!n08t_Rտ|2.粥db3Lc *nīdJ. ʏ 3i採,l< Tem'O4VclܕRRR0._ BmZ y`DH NnV׏k^D ؈=[۱TcR*m2݂*Zɹ2-#Z] $ɞH H(Sq_({( ϞPA49*2gڡ;YK2AaqBnB(B, &ݫT_A4Xʬ{*(6ƣ k:kPsL2^Iun^( SsJ S:UKt# pKe4f7B@iЂӁAV˜&S8hȺB$yƥXvdC@&K<,<}J:\”O:Ź <#fP+VB7Eswr/,{J`Q!P:xK _B!n4*Ae&uDTAdBƳN;wuIp}] ǖ yPn&Zn?@*OtgcCO=ҘR1^1;t?@㔀Pv1ZǥPZ<_[<'xG&w'=A;ő-/Dku;~D߆ k"bh3ո^vdaLA`x|?SG8荜iY܈_t9Ɠx.N 9 qB,qvfysK2*}$1@ѥd7lqiqSkP֍Skb"{ndA"|uFF)%޲c2XP@v9űEڮ*޻ypPB|Կ+A#Lo%Y.SzoZ'*'hptbu9XW-4Px3y-Luk.``JiR4̺ `s⚂2@|6e#V07s ]F,t~a;j:IG+NˏuUPAۥVo:ljIN#ZJG&b)ݦ9"E$ae4XAKɣGM*S=m#Tm[9*J&ΝTϐ92g)J=Ԍ0l!x7E#+ t S!/\cy:YBы=P&}< =WYPzLb AG{ {~ _>DLԂ`:k5Suid`ElP5KRb[#Lڋ!n-?ra dXB \ds6J<\LVIbHrFyٗk~t"vi^mGϤK5*SQV!Vyad$停tt/ k'y]yQ%O#t\ i *o8*Ȫt[Eg}_\>XU nm&FίRVnbr7 i'J|a#1e*|RJ@E &PFyK$ZÙӽ,@93uwgjRU 5L"nv5=ŽZhP⚙TC"Ǹ4ieUןCVLTҀaGpd}n >ʀZ@U2\^?O)F"8!#yza/L٬>Lv[|ixZnc!&n3+h?'P͋dB$r~5G/QWĈF 2gƱ @uBw$ʯԔ}y9Eu# z<H Tzlss/u>:ؔo"#F=*=I *-IQ :AkM%Jm9ÔrtҤ&b07(k63 @lk\-e&jK^7Z.q6?ۢh0Y }|aNEX]4hz7l! tTۗq$f \I0gsg1%B 7/Ub@ /?Yu'M0 (WZ0br&_(w(VryAӖu̦L#Gt$>CJeV?I!tsk1A*z(ۥvʳK a!Zdh 8l5 emoV Qvnm֙ `#҂5.Z9y <St|7Z) ЇO'6Wo 7PocٟLDP@ӲkT.ɅK7iJ99vajE6L4!+ a;\֮z|l0A45+ى/qe:X_vj QEDGzPŝTh%q^Y_(+ACQƽY$p67KCsln*`A&:?܋wWX~9AM|!ɔؓ5cyJى_QV)HFaHEG?&g !}p8DB f|,^AX}f%oǀfc{w[_uOo;aRTInQjB4:ӎE !?rۍҲG]'J>S[%_pe4{7[MK CbT-$9AtsfXUP Fo|毣n77xjcԂIv]|$z/s~5\6N ʭ;vL`Lu+CȕÊk]-cZ,F/MfE64`DM8 Nfܙ9<3l|Ev֙ȩ;H#{n#A$E˹B#\M͑-\6 #?7"0X\ՎùDg(AO+M)"_76wyY{nlSlour2Omp>vN {9m*A!$J7%ɬp7Uqܬi3ύ1:r:誴 55=࢚0{83˝Coآ&*N9CRZ#64 F!E OUaks1J=UT5I NCL~6yڽɊ2 ^{$/m;&^Hb',''D'̌$anLAI ) -l}}AHs]=|>q[UBGsם%$+%6{Yg " ~P'ϸ,h)6Ԓ"š 0pjcOg05#1`` ګQ,/ SlVtK; l\OBUśUS ) j38M./toJ ՊƄ7b݇.W _i ^`[8͑#}B _9`Zs2ANr_ pF|{_}3 pG)i9ca^- hoU=cU?FXgDTV0Yu`F}sZo̍JuVxn+ h{d~&:tI AζQi r#ܕ ՜Sh9ܺMz"IB9}"Q8~ &K(s@l?;{}Ś&OFu߭,׵֖%\kRսGL]^WX:UrU!D[̼8"f U Гϗ,IX 8i%V_<:- mK( FJgA l''ѱ" V9{<CK9 Oܡ1XdT`gsG:[(gFI/E9j1g62y_, NW2_֛C=xڝ<=gv@_ClFΈfUVQnaNyXt+ J~;ڛϻ7mxIè5/WiC8 R8WՀK %bWix [aſ 1tc%|p/oA*8d(PN~KdhѶlGN9\8WE/ Ӆ)?`xAft  U$]bӫ獡fe39Lvlc3mYۯʊ+/Cg5|<@$B 9A3V;xOIv]e&k-kѶVCwlr^'̼P.6ڈ,H(p G8|9"ֹe|%ЕlЙX &@FoEFTJf)>rg4ŠrQ z/Z" !FcU}|Ҙ;ş\m<,qIRKrs?OK-7FV2ꛤ^"pI+[G`%¦/%0кĜ'slGpGBW?t!-Lb6 Ci8X8g3Vv3q5U)s5|Ё_,-u}W:ʡuCv+ vqW׵L'oe"T}6 Y`<́?>cFބ `֖wҒʗ'ZR?\khZgvyy-K8QBcTz,hqդmi60<H]/[H8ilKdʂz?i Ѹ"(V͙UD~JjFsU0Nm\_{p~ 1Mw,5{|iFfEqbyԞ)Z$v01vd#ۃYe2E#`$֥V8.:1y!!T9DN z̬>!= Za4>a`d8 H%|zJ>1pS.(w%6UqxtKS{}]qapMUznmd6Q t +>t~pR(^~ZIKmdsL&qQޠ}}Sj;lz0|ݪ&ڸR\4:tٳo?쁎{Yf*/ӡ-E  8X/yf+LHdU6*B=/lfaӟ%D_Qx= \7<d2W|b1QddY4 -լVItG2$,T)փcq 'xg+&NbwY@2P{N^i+fc+y8 )%B&+uYF^ÂyVO֥u|O"a.ڣ! o6EKCC3OXX%֭xmWO39}_h'B;~E,$cKȔL 4h༼sD6"{S#LH<#YR~'f!D)?F.*(^IbOOv hx!'ݶ]nc!VC`΂)iǩz;'d>C, ,"|#֤Fv+N# SVf5q͙HFU3"۹=$ʜ/O|N>2XvϘR9HAODdת&>+/A\34)Km_2RݛezJdZK?ü̉!mw)G:VvE}+/eSU^BӕKۯ74&yk8YLudm εy\jU1jkxL#peЁ+q0/F!}"ē QCOe$mNZ&9+0fCoa!+k9YL6)IH O+)ҧe==Or IOE4b?Aohet(V&6JQ=i`u~fpIrP:Ñ.HA*8}h,ZLP<}K5l;U&۸V]u^`lݲ0E@"鹜3џ'#v%]]wN$#E Yx-!;L]w-;`.#=(o h(=)7lU4` hQU%YG k ! ]/eg{+Y_=Î /f[8dsъLE 3ޑ̃"V)y. [^鮄KHm`~ j%/).7EŅ׷q9"]v*+_-ΨF%Z4$jzՀ"b;}@sMb "nqrq? YC]kchm΍Q} .&dtyXLļį@'lĤ?NOD$Etp+T }IF%*A# <%aG Ws/\Ɍ59}0S(P\4TY̗M}F=:0ش8{y/y![r00;1}OoG͑զOEXBäHqYu"sPQW~(P/&q!R [y &H [*ZZwM~ qĺSPzɅ31ݨǧzȪQ'sKJ>3#WF6 @iqS*yNx@+*̅ko,UqE[톑G_ fD$pa]'"yǡ _)^#:k*%ѠJ{j~gyjHW5|uٹhtzc˩1Z (v[rD3i|ְgv-<j9=B kB.99(uTz t_=7̚>hkc݃5v-W<_8QUKҾ]mL-SV_U^R V} NvfE4;6Fj #{΋1K1WZ2u1-HvCZzۚ yC=dG^u7%b?́w?^D~-6PeG2 }8kh9SȪ T%S,`>Ru/s@* _V/ X8߾IP)s#$)pmfz3%ڢswVG$`VeSjha7(SCSL:%u{!դE-W0k3Y4G󹤋h(8:xwL-$1xC(EZr1]Uhl=j_ZТ]kK<)uUgF)mY9X cMkߔ+ĀTb/VUG7DXDd5$`0äܶ#m,=9By/%rsm ,m Ǧ\..8=bq>yv{ e=B167p3Z/1FV.YW,aJv;bY"kɶB;VX f@̜q3FxmP8g9eg,EE/嗪+)WjWG&Vϴv1kAbADRsNSx(@t# IX?ҪڥToCYut`dG9n G10mvWFJ y@f_*`qWa4>gyyَ8"WZWpvW8gÂU|R&u%x> Ҟ XA0@I;-ErW2|R"@@ #wۧ9>t8[6MZBQ{ĈMh͡^@iU~ ͬ#Nz"\fuwzu`H9Tih`YpTJh~M z(7.rfVYAK|&:w <ógn~-!W&{րF#Mj,1\*1:9%Zaq3FFx>"^J.Y+Nn}b, _ ࣹ/~-'cGgp@%̲ گ hRͣOb66w.1 ŵfJpF FZU'fL|9k|H)J%uݿ1GN\PԂCB}T'fnM-*7qgFMAf}pp78#oT 79Ŀڞ&z|nW1= tqJ߭bvFjFRLL"Vpi)X$7ץAV |k%VE+Tu tfm,CDv4h%D^/c&dޢWeIR]bH{$iWA"9wUw$Ar'lybL)NFpruRfHoӴNJineB w腦f}R\\\:q(4N'5D~ncL#9@@8ϑ\{/\ih_rZ16d/Tme΀Qن&[Qw+])4dH_|ߒC{T{A Ht+ԾeJc^u Nt:z<~ȹRvIphnR{ K-@Q\(w$L8yZ^FGKt-.aEQ^_Ӎ QnԛJaRjy{&?aZ'찰^ Bx8awBE=lV+k300ho"Ŋ:Vfiow0@6jqh+-_1-5Q3U9#x It 2b$RH|B2Z S bT+CMm uo@SڝDˌIj:VʞEruN;e]qttNZQe; %nQ Io[oC3VrW; 6#Z{T\=A/nۢ`a}Zv{Tu+f6žPoRSveK4$`Ȳmĺk<ݛvB9a Q~I*O~>hf ReWab5:^/`/ј3(lƒOq_ħƪf gs:T*&Pp'm~"na@L wH()CBӱ 8B)3(MW,F-uTX;\aI otA7WsAɫlw?/XK6*y4R_ (3Uk4$2 ,[@%"GN8_ L;;RG EbC71+U9jAG,9Afbp^ ʦ:U}  }c{mj# Kt:oS?ڤy`V)5"pZ\ ,i}Ěa#c[yN&()+|{q2dKiPB GnuYǺ& @7%^p\{jĂ~ZCu]٬A )X߮T8#i{&'׆!?_ij 5̣րj*w?4`|$q(͖Imjb6ʼn(#TO< _́_W+6 y`wĶH4&qE~r\DILuDo3"pł0lxmTDpk-L-ή χJJCfg`˿ːEYJjT79֮8FrFDeЖ&9oD9/ӍPju |dޑHQZtK͹?'>v_I,tAqhܩȀ㗂]%нSY'|TR`wn,E 8RA>P,9\uZ2y+/﹭ 4?!ȽA {g[%nHcFOvJC OgŐ7678˜ТYgT5j U4i&̚do OA7g\A-72!3b,+/yDb|d :|zUEdz%oxLR={QR>voe'<Å|sR3~Qۅt_€Ϩ<煛[i2 /,<'[IHS#*i{+xEZ|YmfbHbO&!j1'*k`tH7Y^DnY2Gb~UzKW+e]})*5,E1v)CIg0l}o7YUŁ).洣 :t#Gz_i< R o&G @Fx۹g( |]d]Sң"2ū!"DhQ痮;C(*(9K웇Nc*E2-+2s=h|f_Ğ\ZgLL u=<j4& n&ҫ P{yEo iˎ )G>]#*=,S Ck<(ZBn'MvVBNbʫ@T]ckzL !]ck, M,V7Ny#yЌp-X|ħw6l^ T>I͖,SVm]YdU44y*U _뤼e gTq-H_:zrLzT,uh 2 `̼f%{6 0s;sʩB#BcQeS~ԫ ˴פ5{f=}gu{_D>E`0XVkaXGpݩ ݏ-v+k־ԗ0|#6W;؋ /v4Ut] Z`Rmٝ_!U&猱diͭd,7g`m_ *RO_~#L&ml5hi8ȥt{{^~~ %.DOYj=F;7qo=KDܒ.昬If!F2-jV mk̳A৒fWFN)߫< ~@-`H@>KݞN6 967SKjgT+% Zf3wG~.]a@lEq ªj齉x~_B6PX+<ДI1}(k{n6d[ObrO7oQxn,g4\-2qYnluzw8lŌ@*?^(̳ԆD7'HOO[2Ijr0c l=@D"$X-2WL"E_]ʈawOr9'Z5ׂd2\T9SIK-|= X.nUr Yg#%fq+N@U?t1Si[i*b؅Ӝ^.YRl6-ݩVU&RPC'櫏V~G3N#8L: SP͇nl`,A?] qF5a_OgS!|)lѳpOK@P>RR[mpϙVqܾe|̥6mF/ rBzb.D;PRL8}DG'2wa1a"w%õ)n =㞴]BX S/"SӉp4S$o-纩[dU;CƳ{Roke_]$Mbc",ZZdL:S}b:$(Cb*RgVH1Q{5޴"Q~Ma̟͍@m! %-ۂhWE `iғ-;H_GN?P3'Mh:M_؆Jκ.'L z=z}bpֆEd'޳h.W B1s:i8 4E_i%cE@Em)_5_GǹV7~pqvÌmM wy!u^-H[e,6ʓKRb5˃n8u lQy$#Mg0nWo)_d7w޵zG|t'XͲ,kNfQƴĠ:%G~0*Im⨩<&T}u+De{2cC H/lÑ>XNJk͘Չ</vQͽKdM"΢,1磌KIݚyק'Zba' _5'"IIڅZh\ZYx&k5z4hGiqε+ 9OIEĚP|4\1i˫,]{7 ^rBfAAГEz{Hm˘2”um2x,)bR=Ҭ;dRKyk|XVl#DxKC*1d8qΉUa.XHb*AXS]K`kaMl0RǪ%b~VSMYIc̽~@ue0N!?[k7AxbE}߉+~6i<EE@[$Z/IT% RȜG/_ "C/Le޸JWd(n%F%m,4dXGqhA.rňz >.<`Q;VV[ "YE:fΫPx2DfUTOJ;OЖH?:{}dU.E"K-7sL^ GB/N#:<&9|lK9!:}doH"wvr~0-\(2&ct);t&->} ȘZqHb3jn}MEPWxf|8n/wL&d}M8 00W.4k֌ x (PsZj.[~)G_!q[)"w1$b{-i.k>h?W'Gtobna *N:t~-g2ҷn?UMnsnxzCxT1{(;Cc2[[O/)cj}]:pN:nH?kw\XS[hY$65O2jcɹL%!4xڴYhqsLx;E}[վa ʫ3'N3)[BrtȓxX>Jt3?yMPqJ{l4a {ﭜ ?m!YSGͭ(\k*-2]0\z\;(y`6 ~5Fi EhEX'? \u@$,|--+|ίf{6es$R 4+xJjTc0C5'2MU/ }-$=kzEa2M&+KjӬRmwSP閣5#;vN'RO:F?jT9iUA%!_(!\(y8@ToEA 8) 5iN`P<ȣQK&iIK.deD@-of<5:zѬOg$< zo[݅wx[B#zBfKZNlY$ *꺛˧vբC(jpAV lzJ|oW]Q <@TzBODsBc!OAg`r\z?oyj/eb7)Q;8#EqM $>dE7yj83'ZNA?~ez|tqړ%{3*Dr( m-2>I.ǐ%)༛׾m@F\MzHhI7G 7}_R!{tHd"-Krϭٱ)0WKhN.#Vh4W<41Κ:y=(ܧae5:zd\3IC4( Co>MvRj`G{P9BKAF {S&4b;CR$VI@:]g_'[2+辒~ ; VG 20;J))$ 8)/(`FCĞA`Iّ %?,$Һ=z@Pe!Wz>QD`uOojRNƤǽcB>}2 z]ׇp0Ϩ ʅJ0$ruix呴Z囷A({>R4Pݡ?I'&BHγяyOWt bJl JّT;1}ulGj^X7@ֽ+~#DƄ+tBbyOY[||%KeD\kp 9bWBDsfy?ӒHkm\ 0_BCÆބgO"{~rF 6-&dDnJ;ZYwugW5=}ALT6A7xցc?C)Z1b1 "9/TB㚿c}ʀyhzK#}XJ2?,<RTˈ!.0>5+Qt7 _k kz7QtD5p@Z~AijnzOȩkjN r^ǵG>%Dֶ:IKU"[2[Ƌ[pA^ %Dzxrlh%88&(1:xY50ys~rC눎Tp999袨̧98:c0ldRiQN$?G~U!j>;%0Fmiv#`„9^[^Om/.l/5&evR>}Ig~yÄT&9]W@f5PLƥS!`+g* oY씇߬ce aӎC_215˜QbFv@ 6˫cVv+$ yK3(vYDT ~6 oM Ob;N(k3p@ $*[=eFh5 .\t-;hM"~!Qv(F;}6". XfNXT/Q \)P8l7*6.%yI B2;.oU'WQEpe;鈅 cBB] vˆA-L Ž#F`\N \ ١QzeS1C%e~40;*M SP:tZ-RAac]nr\x8f0BqAY 4X}Nvq#*7r`rs Ln'N^Xl|9Q3-eL!Jy-`c~P+R2&u wA$<)5[xжbSZW 9Ѡa1.쏅 5փ/@}\lM|TA`>zX O* }#P!;9[ʐT-bF6(BNݜc&{.ӻmRk6gm٪R[PaJQ2!ȭY %th1 LѾNZ}~_"RZ K128Rhe=?-Bb{-i+8]:׭0ŠNʹ7K/Y`E!G`h:U5bLBAw$G#b W8` Z6#F" T =6-= ZnJnYo؀{k$pJhz (~=Ͽf1sM'  X&4{KAVM3=[| [YCq"j{v#2c{0@>s$!r?\w6D-Lv9&/mAI'BXg *6Ho{2n74I0 Tb{s {)iw21ڤgC@YVa ʈpWr&sv,%whE^2/8 ᘭ.NX"2.vZkqW*o] %@NɷBx$$i-q_nnwoPʈ H?I;˺=/  ;E_ 5KUt׸+6@gA$+>kI:6 Ԩ%C, N,?U7<ª td.bATEje \7Q)Ô uO)WYi=ѕ-:6R!u2 WW<v"7m^+3y4cgi"uqoOE18EX3B VW{ʔ,J(y|HuxOq&AmUMXa9Oԟ&}[s7(QQ#toۿcјn88[NJa\іlRZeî8aF$IEL/ۧ$pr1HB4g*@>ϱiērHC/Y +geAkThuimRuN##E{Tvo{V_>- .v#dfaBVR^v%(}{\LX1Eq ۺ'qgwlJgl$?@<lװ[PlG{Sb:?.wDnrpy̻SM\u ; Y%(E,1eqhGE0NgI K?|Ș87赨jozW^Pcj%Ӈ;SvP6ow8'(.jr,y?LIxbwu*7o(Vا+Աҽ'gХ*H;#+vEd(#c9&6MfXNfzB.u{b cjhBtn .\#'FȠY^հZ,?N7@TU7o8(@K`{ߣ}⇃4tX]zb!-P@bȸgtqa'Szhaʓ=o k:O=ӅYq*zb%)}H]͘E(vז'lvU~I&w.c.,O2~=i^zzA)٭qCg/X{ZkBkLDMF"K.`&d1p/l zs=B ṕ"H0 w BVp#hf%H *R7cM ;_0&ŭ,Gw}FCG>szDwPY˻\QQs-DA%^À?~E]mqs~YFP7]0#]>S+b/K4#46BqNbi5NSR;l[pj9׃Qm]иe[?yC+! g%$;ޘoj; ͬ#]gP/\,G oH$҂|Tj ˛ C?~0_GtQ^ݡl&#(U61bhX,}y_02S><"1" ߢ|g0?9(. e0#?_1{Unц'P<.[V_@ќ6b Qz1MH)Ta|Aa?9dxeo+q8Bߝ13lY˥Rr94b} \}ݡsE=j#Hڸ nMp" b\=0DD{dw|Lͣ\P6pt̩6d]oM.ayef ]? C7rքX,~-Ny+9ZG^נ̎@aiLL@b ^_`C$)~ >dU>G@Qq}5BdDF ΊOS'Q,!<4Hĺ1-f' ܦ 4 cLsUt̵u@7DagϊZ@Ѹ9kzoκG]#';ߟlNr}B}Oiȟb #(^C! NdAݖ#栝6/#ԨPs@!oH[\c,PeL Z_Y!`~kxmmWCS܉dלIû'WQаq>i} *[.۝hePo%#P)R581+hm0:Af䡝cm87buϙD$ bbhukNEW9x|Zh9",_s6[bU!rYkhc |ZwSQaxx`Y ʫ}tnS)(IƍUji0>s8 VsZкRmeOд] -=` 3C"y3_RCZǃe " 7PI0` ynsx3/"#iylrʕU#)3^ivYJ.>fdllhTOo++K6cKF.֞HCgOL04y \&oP9᩼_Zork0ҨƐT!av!hNsF9~>z5 VtoS"^8G; akT+m谂{/vp,xWua8߅Q6/k}_)CP{$Xeb9m#{xlRV20j݄6.q3nYnyE*ZHc>iH{Uxm:GijN%iC5#UK<.?F3Q%{jɑFE.7I&53Z/|$Fq E-B8h$B@;SbU7CRB۲+LXB,fH?A b8XUٽoE@nXQwdͱ1ͪLXҿT}Cw)@185xq;\ xσ;4&¾}(]XȂ,g*vTbj'vƖ=QNhc!_U!`Y%L͢AC޲KDG泺$[ީMq K.#\ǽpWNN_w# wxxM b!kVz7ݘtA+I`%ŁY?lwq..!|!3{}y8my+U_6HFs:6t5X}P2;h ]=.x<,6>ei%t?A;p/(zuP2!C%izHF_)jaBJLSYAA1_ǯA^h{ӝ֩ ʣ%|6ݶ9/yS͇º&|1 FݷBD o 2ЫvеN4]`/woMۚ7;U{ą `ֳVO,W~g! 4ˏ#CRx҈6TFoӈW#DX 7%oqr(O1D[[~outɉ83)[Dz5ȏɨ}72 bUEi{ k$D|6~;䴿SMK;gOkBSu.>{(Y?]UTpL]+WU΍u|w>.mTa7re8x4({Y^S'e;q['k5aFr?juP| nN7fU j@Ȃl!/eO0@^buåJ:Za|w&GՠTΛS*&>}r`n :$T>gs^=t7>MEUN{xq㺒K.D$0 3jϊIYnJ0O2#';EkpV b$)o -֫-vyC j6dnp닧ZX/^se +j~[-UhfZ'cJ^u A#/LDcgM( 2ﴶק5հ' ZF/{hBLUhWa'^ /..q?;@`%ϝ-wWn,.-\;p"AU3 Ҳ=V!N;$Mnw_䔷L9=E Omw 88sO3ENm|,$?+BKӄKwg&η[hyFIWaa1mRs][mWI.^9Ŧyp>r=̆髉 \ ("(;"M1_ سolPX S/IyP&5uXvҿXʭR;:<:/Qpf#ܴLsf"(jƙq@;0kj:HS1,ռum[Gݗo ,+nLk w5)UAF:mblΚOvl\B*'.8eI3L}pՇb {$!J"ckj5Ƕed]_LZckORktp a +Q#ۉ⦵mj"e Vz,j&k8< ͽm1/׎Yɸ-#5V C,s#J=(jCwx=6-K98GEӅc(of[:|́.53gbm0[FKdX@wK蹐\GABO !Sؓ{(3L u0e'Q њw@x:zgGMţ{RCBhưZ%2q@ԶDd!^]>&ЯlԀn}A!j҇HJh3ĦS1g8byү[7Tgv@?bR&MH:P Ǧk$\>΢˼8 ܍EG"R  Vi.`{<8%vI|]lŰw.b78@R8%w5Pcvġe{{1=xh{{bi.Z20Sr8 >ʛЉLxūC߅fWWCKw㏲uJx6vl|%xRtxc2a;4W{  \XW`$jyDXVCy4S=08B ZŀpU'IO}ଆĜAnO8_iKp1*+)X‚(čh2.pmj(%lUatX Yez(2dTDX:`ba1ͩRwDPcr)EE*OUcX!e raio,Rv,bZ\S:-ׯsqnQ+٨ux*yY;g;_zM cDC'I\v4踕 4^hk - [ slS_i2$~;ڏY ׉ 4!/" 1W{/!Oe`<x}l*JMz#QTVYg'!D#3YdjͿÏ̦782 r?TSeO]ㄙP.KвK4qT?f|[ BZIK=75)pMAQ>Os /*'v%wudFB*؋Un7ܘٷs@+Q1 +A>U N[B+Y㐨lӿ&#_ NgF)G~ȖPB>[4>ygOV[tiO/ \ "_Ǐab~ô ~46,I<>-C]Wvnxg=Ij g2Mn&kf8 1*$]ܢqM|2ќpv3'7 -Z yD:ff aiOn DkXr9Dz=YXA#7͉m3'~79CD$tQa¬Wpv},oC]Gx8f\5@? ѐV̯E:ewhVᨼBsFd_Z(:-vc~$]Z mO2#NT7%-Ak3v >V_μV*\g+r]v[ ӧuu0pkW9V GÞ߰WiᒿK*9srtSdkKMGf(@_j`IP{ʚht23(ԪCvXm#458xՓ 5P/h;wqpn2I#b aY[O\i".U;mQ,cch6aDhM~{(]6 ^.P8/&t1x9nIQw_*ԧ%l `JY0R pol 8I_]_hmSGyG0㴘*@ q2Dn3A\?_no4VX 7:X? 1 c)(d}_!NAm_TbD6JuAyLg6kI:qlW^t:#'ٞ]-ic sV$&Ơ5K z ߕ?i0b11!rZa >O;V[#mPN=jclܯM(F.ܡr,!2&W\dk^-GgȮWc}z)(i#KRœlMv^%4vf v֒JDN()λ:yp(WgfS U},zd *Bb37&\yŜ@emƟ!8hV]y?)rr5zF4/{܄O9/̣܌MJI:Vi}y8/+єH=F[NhV5[ʝ;DL#f:OZ:YL砢 }@# c)U%GVUMp?fNS8Od6&@.! J*m^G3dғ9w)GzxC{F* &mYN xbqgeg^7*{#,c&ޠv,B {b}?$vj*C6!hZvGlbH!2jhzZHxذVxuriD[#i,L+cH :o!q6 5%(σ t^V觙 f[˱tkH^'~2͏MoUi nR-Ouyr9P넫&j{۱HLXT`&FgSײMꅕ >edhN8J퍺:Ñ%֋7v;PZUdr~b{xѱuI'p56ѽro 0Yٖf;-K f4-;+|h78o]'55 ^$]-mQ۾\z!IhKB&~ӭ0[<Ui ktQ.(Bgu-v@&>}ViB_<]kGЇIoj2|Y^'TX-}n$=u}tfuN CĈjbЗ$\),Y?uhFb8IUVm~ƛGU1o+?'^^-\%~~H \=c EP3%5=p*J@rl3*Mc I}LE0!Nдqҵ߁wGoc/҆~'ɪ12bDp[@z9L՘DAG:,Xt+ĕ@T0fLTJkO OdB8v9DQ)a Y6y/iI𻀙7=Q!,$tlv҅19Utb,˜'l"=ӏ/m mr`0Ʊ#U֐}۱j6R`7]NUJxK籴ǖt958)֝_#p{1`!IVS HͨF5,ej{}V.dKNY1Y38xdǟss ^Jq%֕ (þhgݒ\;a{ TzH C$AKW[SDɮje7_^&%駳 +N΃ӵޔ8oIY 8cSSQ]Ƿq01pߑ~FBYIҊj(?`G`9232p;oS=tYӫ}&Xx]S5.b_;bɼI.^2+ř#4M4+X -[@a #ֻ"&bQߪ$P qӲaf:`zQʭa_Ln>d8Vh9ۅiʊNQ@8v+fGx?FjXІZKSE?Ĩ ޳L!rP sWU|[a~(m-QCz{;v)sE1%:/=Nh÷ұ[9]}ikgrl04JJ$QjGGɳY]/~}rZa$.^*x|2%ʂKb:wHHEg/4tMȵØMS@ h{d04W(P T>?" Mo[({3hƓB [GnjE+DJHXɩ&@.}~iP@.Ukc$^e[NϬaܗ ҏ&48M֧:4;+?;襇)64B@ņb}'mN[ȿiъAARL D~asG%6qgH~`9\O֫#+)Q&-QI &=hp;8'uk (rK}hsix QWr\A`D>1R2|cO?#ToTJtbF7GF@PTam'[UqQ‹emГH[1 ƨ,Nc)!; ?08T˞8 юźl €# ImWd£~&j/ZL\:Y+X )#ϕoߤGypn'JmӊԨ=w. 4t8bIyz7ϯ} dͿ((ZA]X{s-_2_}:Sfg/ M;ɛX/M kVЫ*[ WȠvrJc+ױEWq[k/Ez)F |F5w)R+򸨗z@AsT l={yuGmN?u+E3(Ǘ?Y'†`q6zjXkh8t{<]?xK {ہF^8&EͦdeH#fUsPPw&!V@t>I/љ&w[:'ڟ[jn'b.&>҄]&3'Zl*>ExrO0\KyOUz$kM:³kG~iij!Q-J;ffYXsl)c7R⤣A̲h> cS<]p~D}N|A{)"")~98mi =aD@tT+pɤġ5l&#p&ZGʮeAa iߔj`Lr) Ǎr/gJ1i#TU5 &.oC JO8Kc/,8hnXDE^hW#دfySLKd_LC~3v3+°aԺZc< 4z::gF?$jDaERVPz/@85@{O'+Nx JktŅaL}))g~Խ5lS?20W D}[ %.^䏝SU'.X$ #l|;¥؂̓$h2b m`>F6yDg 7cDˣCMkҵf(".=9@Vr&etqjA,>9E9~5d8'KP~! , :}m!(;yq4Hj,#͓sLhë>;cGnQhUhL1Qlt[k9lJ&[U͜7GWkn*ux|~vRYph[62=+ne1|RՃCXE,u͔D)JRv6>kpzɬS[ʛbo27D,Q$3zCƝnS?LofxQ΋)+:m煗T*U!֚)ɺw['й@siX]OaǏU(Ax5!L=#@%M0>Heq.2Y8@]GIo<2B $`ekro/5HA7އ#VFPȑߓv/uFf-7~ᤥ㝵,kу&r&:ј3XOvO k<Lzdw| 釞jVrq,׮Fo GL+tΞw(lA'y;ې-(u:M@F+W',26NmM Bgln F6>|S7~7٘O+%9kʕZ\x) pڪ1zlI 07SX `$x<@2x a+)RgЊ}k58ח`醸jn;zسcXkv%'*9w7֒Tiv)_)ˡ;#Ԁ8IQ* d%R{I!#KyI7yрc0bce 2=4.eEÑ╣(Fi2!`Vk\d$]Tɨbfz@daن5Y̕>gL0CӞ=vyhn#>j}մOGր4vN"Q`?&* ?Do$1 19JXZѕu'񫤹rd5x݇9A' ג5$0 {IE`7gV[hjomTݽh{ݡ~>Ҥt)U>qϩҕK5 ( Y50f|} GxX ¦\Šd^vCP$ޘ}rK&? E ".hzuދ03ʝ,Ox&w^tb0DFQ2]ڨP0;\ia>j•D T;o. [NDHNZ{GEAt%I`m J.}^Q~(|W.,b;3(ɰQԿ~{\rk+s޿=d>H=<&TD3َ@#٪}$*/.p=+ke测YQn.nTpf9`"\<e[[c;C[ NYR1*qq;J*qC?|4 3Dˀ7>57|!./lhR{.i) dNܙѹ6hI$X z Pxxf~6!}rrZD3kC)0Q*n|ZB<3?vjZy;'Asʰ?h(&5C+eM=<,Q: } }^+pH1+\h2t,5 T ma\.O$]$NޘR;9S$^񖨲"3/8 * k9O %+77J Ф;$'}^z֍]| <@MV˹R)## 0gH gly7eU8q%^E{R*1Th}2nƀF`o{}bޙ]˩\o_ \a:E d /_O0#GOyځӃuMHes )CrL VeEag]LĊs{[b/+a c:Wtpu<)<,yCmN)ZO 3~ W{IQ }[⟫ I5tE)T7xlgR*!„YF)B>E``Q"smrٛ&@M۫O)%oZf^;`t2{X⋡+ҭn&˜(.Fa Mn vVn"˗Hvj 3 3Ӈ.QdF2sR`8,#u) IgcQ#dqVpzqS.dM3,"3L굸k6[Uw"^NV%[<%ܞ@}p}x09mL^l d\"E 1et8t#tXT`J+4h5}OD數V71wB?2v;vipHv "!w1:R_Ca%dQ0hH&F)͞UD5 7dD rZxMdiLݭ#XRc K Y[S/QOpPp,4ax0_V`4KϺG͚bU 0Kɧ0-T];=ga5,`ş`1(宅v2ϰ} * ^N%Sh4^5ݭD^̆A܏_O鏮zLj,J sQF*j"Y8a'#= zH59Sߵ;Z4勄51Qu:#͌9kY7Zh੐>߳] O_W9:yyy{εf51mӺ o N{_P|[E܏ GŢ&a "_qε%n[w@$Y1h GKhUׄ0DB|SG|sj~͚2-2iu>&?/`'3! 1$tZ%},VڸEw䂻g9gnd tOxOڷ2s4X"Ю(}?Ȭ1+.:٬Lwoqb>O+hҞf ҃H/QOk>Zk?7w@|H!0s9yuPțG:8wJMy;S &!׫Z0R:uSͬ WTbj+|2@X(_8lkwGM{u8N: }P/ ?CGԎ/jGkd|Y:xƴHm[Ӯy|V(Pk.nEU%L\fQ^fXєK[JȐБ.ϥ3gPzO!Hh6d`^ecd ^@T^L Y wxlcUZVLDiHGc0+.~)uQ6YcѳFV&QۮD+WRˁ^@衋솓g«qm)>֭1SMiV,iW) w&l¢qwѓTtӋ'9sLMX:nLTIq |1(7O8 TFv. cP/K`rxȲd"P`\jalԆ[|g]d:FNZ)|_f}Ӈ A*§UCdovGO!qR%L@~Pх85!#91c=ϢNuPCX"h;0߼̋>&!f;3Ѝ> n[,xP '+lqtq`hGEӃcN  -{1ԊCiz}:]*M$LEAX) nb6!)펏W.:Rl& fN~`0׽j@aq(^Wo/P|yt`#mw128!g!B c\?_FMr 0KtWqL$T8cJk ,h*8,* 1my֜Pwe¯pCX)QXH-")tHWOi~S)9{*EwsɷhPn21@ӁbS.9X W9ʀ{5ͷ(.AK !o,! D$iX=Q.rut7nvK|0Z3J TL^AR. f6 ;oW/ =ݝڳgȋqtpDʥKy<rԞ}]e$+7䕲ӯڀ+uP" 9"VJ@zDoJ,DTHҧ:r+{GEעvS :mA/ pǮ|-Q6ȺuKuyhuS/5 #ġYxш:jh QJ-(,ZOT.`Xv3Dvw8TjTj sF߫9~['~ 9r2 .Ԃ2*2䌀h3WYva|_ Y>.C V=iKUnONB_I<u@G;POkS)%)A};N ɕfw$  oKBH`O{. Ix6 %Y7{b343nj"ǁ6]ip?[؁yU-R|5`Bi`mc-9h!xBBXMA1BW7t2:ɴ2ۍw=iU$.bϖVon\zJL(}WѢEߚq9p HBGl (W 썉3U‘iJ[?3" S 6l{j2(`\:ͧمҟ.5|ؗ _^Z}O񐒐,py?A IL ٝ^S8CgRHdXr,x{,X c[O[#]סHXBQ7%Fmv)8JkCFebwVbk∼~Ï 98ÂtBxAGm!L6 $>5ǮFiBYr09U K +p`83 SP_]2"+m³AS/PEYF6Bq/h ?eGwus@$].>']䔵@ *at-aNN^-=ǝ3w (Wو].|R=v|lfީ@'חp39reTF .ʆnr#OdG2*נNArWj՘òl;&*x3efIy֓ʔ퀭tfƪpˣd͊TKJT(䁳XBͰM͖^Yݫ~g G1xg]V0/[ߙ Ce}aF|2,)i^k6*uD{-gÈ(Ν1H܋Bh bkzH.*4LyvҚOtlԅZdvwLo)ޫ+FSHIC꣭|U -Kf7`D6HXir ĮiLIx18TJיY!%6LltX錛zr+pGF~̷^)oCthW fAXƚ >NdjI+XWds\Rgz23&b=~Oc*E;H8Dje4D$b"ḑ&-ӑ~V@>6DfZԄV@&Y9`aKF9V :Qt[i5OdCBk)OVbTQ>)Jxfc:ll6ڡ=;OWI*HbUXNL (!e{䔓nƪh O/(^+ Jމ$@'4-ܑBR#BRn=B*,Ө?v,"1҂/t5= ic y"ݥY\:sHh =7iPnjq}Yl\3#'gG9GxlAիg#~&5c#AV+=#McyZCvQdژ@ 8ͬRҐ~Vnf`<. m\TJ mO%I?ޗ/(Fv 3Ghګ_0 #/K>~U>szҜ>m,g!dXMvŢe^1pi)Nv{F,.) ¯״K1n+I Xkj웆zB8oey~/Q𙾜T6/>J7m)7 wZp%^hXy3WrJ126iNO*sWEAI7qZB?$[8aLWԓmbU,?0Anby`+ xq\`*OY L XJ*ۺ[N[$7xdWVe׎u床B ] /)N*镂ZHb bS/Z=GҀ(@3  ~_KW;C@*:z):@*.D~e [}O?UtVY@P,pY.vhl{eHX!nP֕諒C*Sk7H~hׄMt0ۨMR~9 LH^JT@BZru<0޾-,f[9SB.Èbtb) #tR.Qfm, #K ND&IW#>*۷%% U>d_yVGײET?"AkjJ$>Q~tX C$03h(g⧎+_ov \w=ٖQE[wڀ@E 越YƜ\N>3 kNЍoٮE_(ڦoC?IS8_䁫&N&r=$bQIE%h5Zx>2?8Sہrʬ#Q$eIDmx$5TgY7AnKd ۇ Hc Z{޵VW:h0}%୕k;:_fvR'օ ?OuΑe^WnX'3 E+ɤD8(O?\l07ºN6:Ÿ 5=JidT wĿ gt)I\z*XoN~!J]~Q"~`3{#.u.I`6Tl`*"n dj\25}F$ 4[ϣ=) E^yDo XH l`@+ۡȜmOQ8f 5za:7>aS]Xj)'͓p1C Fj~_xn,i3O8d0=z#^^3#|~ 4!'uY1fJq7/Zy|OZe p ֌`;ݦ~950r=!1s")At'Ejsx 質dSbd{QO^}uU%!o~7dWPHn~ \6лTh֍e@{~" ? H*>-_a;LZX0P~1w9>&{4n{Z0@q'̏L^/^<8e##xٗK&KBl3kRv4Y_iiA-B?!F@ dObЗ5~&iM~ǜy wť"?aB,=ó5sJx0{MVrܩك=jXPo"}A_c bʿ&h@"A>R# a#$ǢDhYB[~L]Kd It:;Blʬltj`N·t YHo߄ FcMʸ7ń71b\QK͖S%[4..d2nxK$)4]ŐU) ttz03o#D3n4YLZ^ϗѾdsjKKNK*C>h}P\ \(N'EPy`bފӔ,v^W`+N\GF۾3fk [KY]t "HտEn-9-m PBK$ d&˚/ Nl*? KAu6s8G3e;Eϛr\ pS ޠ ǣӚ YA=ms_>S]SA`;l߱$Ŧ*%(nWJ mvשׂ)9F|*Gz 0/9Kf>ERPfД ufW<ׄ+7dGh.s{$Tkw4PoM,sc켟! G- =ex]yyý:!\?cL RWք6eԌC SQŷﵸH$EO:\y˜+Dڇ %S)0Y(= |w..DxuH *w/,ek8`I.|ػc|謜(B="p`G;lY*4l4)xpj8z}J}0#+Ckͦ+keIGF_zS]jvT">+AkưDZ*n#[1},>mb%v֨׆OO.Qpn뎡i t˿*ע[[>״ePborľg.5~I 8Ti(sk9Ri".S1vdx#`$I"ؼX{jfFMrV].(zԘXθ;{2o0HWU] MMB"G7tb^q!Gb@c;_sPNh}x:XLGzT֣)kt*]u{;6K*"._5mQCN^ UZ g8 tPT, 5{+s@jB2讗T"~9cަW=b>Yz:v6޽WVyp+!oҹHwBq'q%T1gIbzecK<^ +_e /ϡ1M,hYBO&9_/,~}d͹b0 |'df^<>:c`_Ih iPk{` &>bqeٻ:FG|[JX άj|п~ U 5;ZS1xA57BV4؉im&R{ LENa.HSnO̢*ўsB9'yx9&[F6SE*{wzKuiNmQ2> s1DE@DAMv[8\(+}wA~F ZB?LsZl%*&]ׂc#9:[ANǧ&u+25 Jȟ9#|]֚H@hmb.қ{'l^WƷkv!C x;k7oa?XF y X ڂ to:QCo z s GGaAu7of~@g _#]ԎæW) q)rMD5B3ZICiRPQnz ^ɠG{o帨<ʚi]HϫBr%N.wrɨFBSIG z.&Wwk(^;['i94w+Ʉ %$,*(O7ۖ4\J7x9EsK3UPRԚ6Myp!4_V8^=p0'GFW)xQ˵޼߁<~mj: >Ic^e9P :]o\&g߱\*)6|9BN^$M" ;^^ ;)&jS3_hQǺǭIO@p-Xfj%9F D 7AkuFzK*B:K{r,?&\Cj{u '툠?;:,9Pp{vLOl]^:? oXqzM5k;W_cѴP=TFWie_[m"34Iqn.3$7RܦtV3ڕpQl,/~oCDZi5:8ֻ>JّŭI u̱ܯ)!v,>#{.܆  K(6.ت\c5ʞ,󭚥'vxo_;( Ӱv(f(^Df}$RqùOj ẁclJ43-~R8[cEtz 2ON `Ljx+}\ L'ܴKwҐ„tMV'+C#TA8]ȰWW^ΧQ3\L~6u5ՋHKf;k& tzGs"K"YeܚSI(R{[1SR׎=`W9`cFG Llfr(!ʼGqHuj>)]wjɜm'Db&,] qY.ou"лp8ރ=&w_^!ثv֐ *[L7?;u2]SMCn9g}U'\roIs5?+:v)ˠ|V)UM?g8\?%4҅ͳק8sCHJ'hWk&|'N105Aal|\R۹S>j |ZLO3#5f!aY}+>[F\Smr:꧌|VϠN]ok1O$(֊!WyWNP pJ@5u3r-'~@a65EbKɲ azKIIIS]r=h!a=vb#I3ff d$FWbor-m17^1Ɯ ҏOY2iDGPhL$ '9ӬzP<*Ab8 ,;Ds5Aզ~i'N$6lM66{"MP 5\v-F({>J\D&c{kLa79lAuW ȒvͥPtRW״FSA!GLL )dz81fCFZlgr$F<$H/7 ɱzCBF$'x@yG,{\ikDĊS\9<iڻP@/rAnllvZRiv2v~ C"T`뜆" s&O%q+GFk極qEGs*b/gȍ6HZ~N\~TgعY={e謞"l~Zl%h[Jc/߾=fW!la3? [Ndfm%tvfдq&R@c&Hr1"!WD1(*OxE3Af[yL݁;@ntsvQԼEMKn;5LMg5=spL0O?Aٜ&Hf ?wW1G(]lP=o%'ԕRB5D{wd*SҜK>]Va>ш Գ_$BiM=w?@҄_Yx%6&O,as?c(x<6ˠ_?"'Jyp[F25]5٦ڿ`lKe9] ˟_Ѱ|w# vB$8qiY6a,Wx,dS' CxM9ݢ 9B>`d@w+i,r};pPR[gրI両G7ýv%~.dzO,`=ƘgpGK}Z2+' G+‚>f%\*X#>?. +'5)p&qM"!8wy2] L)cέF=;XJF޿;_wjkj}-1 B3ZrDn۹7πA'*w/si3W |f:E7~.Vt s.ܑVY8dlN7!:#8"_KPVvHZB{Tԍ{/"]Tx0)_@Е yrnV? |&<`Y 'nϛj_fy~^>;ɔΐ[ Mxe:-hHA]-8&Z_ l8(7ğcFtD&>hT33(k_7OD:*KRtB+@gŒ"(F5J3`6,mczZCWoHJ'r?][e_*)v w̎—eށ,*q-0Ϡӗ%%ſ]q8=ߌI.І_e(bxMV Ya}̶fiDEAh _Y /,ꅆSIV1͛drVZDJHpFsB}e ZApÔ;Q_L1#)d+9l۝htQXhFysvQcx@x\&MT:Z\ʦW,UTR@d)(,SJ0d̆k9*K  _:qۺ)d~MxĒ,Z3o T6#H8g)mGk3MP+`%d^l?Fh&uHkJna et >.4"oжiSzmc%#rne"$ECHzjWzB qK'L.<;g~0716n0R&ꍽ~}%'+~/ʵdcVmyI>ĢYuxoK 7w:S){NXVfu,"W&ۓ~X2Liy.CeEf'T b4YfvN|)d#CfРe7ZC岻haKxxx_xx&K%*[c.qLa H+`Q ]L.J9HEo6׾KnY2HF`;29Ț@97[_oTZ0:Do$ԚQA7MjFP Ẁs8hiA|P}xxd&tZ.y|qIEtяV[f'UQh%y ȂU{NV|,&R w\ Gtq`[KUsf86D2>j؜gPhs->BQWWu?XRnj qk3q&,$-Hh<0jW) 5E'eR"A?8; !c)%4VGca4oLXb:v4Ekō̪v. 3s-oj9ɣJ2(k1뱔A/D:)Ocn^ !_P]ңLHI\F3+`!+Z==(`Pu")Hӕ.TL#yuautXuI3Q/]!~aB~c KQm[z:$S_Y2c8S 6?\o"FԼj7;0Ko::h+967'-1]"ǓEV#]rCM@pclhb4|J'j77?(?+(܍!Ƶ͙5Ea%pM;01_c X`%4lH)MBEb~C]隤@3m:m/gx^0 RW4$utݞM}.pr%dQ|Ēk͢D rӗnӘK Esm{K5Tl(,l AC%➠wEMo}&c  -tVԁ()hJM _1-SKscoI#4z_Xܨ{ͥ7΁}#xgβ/e" 8p>1x|.^L1J])4Hr 6Sqs"MXJ @!B<4ʭq g.?#`$ @eP& L3 Xϴ"1܏,1wCb*S^]PgGEW kPy6&$aT[& ,D;M4K WFPkdw>-VV^_]-p`a2JHg!9|y*-ah#>1Hq {3ɚWmR]se.^bGgͨ $O#K( TQM"GTb4GnaP <e9<O,ͯOC lNrmL&'O_ȡgdlMt vOUV{D#u=lu< ɕBc˜I9|x犽QT^ARtƽ!#m[totE 9tr~,daaOY- t50-Va\@؆ L0޽ɾi,HHj?@0 GF ȶƥe 1VD(جnd'J 6-b;8wNY|퐤".nM鼗vJx[3"D dQ9l+S_ 8KwWȑ2!]w0iiV;LpJxh1ͶjmSF-w4Huhd/864Zx=!L J\*!1!0|w*e2^w;œN兿h9 '9Qaӧ V1؂Qrh:-:n#ӉȾq!fS@&_Bzz!@نhgilYk"ODE{=hܔS$ӑf\Ko :V^$C۬9Pa.!5”:vS>W27*;ʠ;rXYJe>n'0exqXPtgxcT2@u^z*Xq4jHy@=2 \'Ty80֍kپ@/W6Z(e K~L?qSo?I3l3BUw6QqxJC59Ғ@v xU*t[ˆkD sT@q'h{778uQnQ[JUyWH\sUkL>:OusdW?T^eiٰ&_s[ky 0\?L 7:iޔJ0=,i(^\B@4ma".0{J4g>$jM:1&Oo 3 M!zv:vuW"`,r/VQ` n+s~D(lo~%WЃMRF͸!2 s8GMVLP9Uk DQ:~\SfsХ;Kt8&-4(ר, h~g&&$n%)0,7Sr{}•*3.DV#w %ao楀VmR iԱ)O(xˣ&$'&td [>̐"{ _e!C3axȫ[؟?'&` ݋` `HjK1s4gʺ>1_<+Bۀ9Ƕ*6wi.x7%ʨ靆Ŕ9lݺ䫽QOm*/Ziu {fKKHAIh>3R3ӏb`1)i!gh䊚 0k|h(jnmLa~\wHY[XƎ.@[e( ` b|j*^b(QApd}+ @.B)ˇ̑X ,uq$rsц)&Rp>1IO$"[tK@"NBL <"&47 ,E@,,Tc##OK!uvx~SA6`8R tXzǍENkŶ:B{AK; }V Q%/gу[5ݰQvdL^+iM7(x%imauCe g?e˹jAagXD 4W;wĵS'^+1z'?Tw?ʍ(@,8^z0 Dy[p9Vp\t"F?>5mU~zgXWCѩŒhrZwwX z2o\RDtWEU8%/^˰f12-b4C!?&a<QSá62`FY(;R̦.C9 t OSxRfj?#;=ES=,D,+_J4^(7b?`/{F=ԈH:>FAD agC7.|xvtSTcWqWoTL97,BmbniyJc7J;N˾bܦQ|ҔJ+pԑn`.ѳ fsJ l42h5qrP,c~ML:BwctVӲ)Lzy1El p>r蚥%*9sB4٭v`C> ;׷"T<8Z|H] 7d|-_S;n:I YwLn᧝ehXB@L>!s냏u(ͤ#8xP'oH>CCg h+Ӊ5ܱxT쓑Miٵ!t їoqk>Ȝx$?؜뗰%!i|Z&_{利w^UNF1WӨVeK/lƂLdf1d#|FZ7L.<;zzS`lsQw30oK >b o^APKUgbӵ ϡw%B?崧S@jyhU` dBmk-)ʤL}Od]okh2ŜoG GVAS֦VB0*̈́I"a>̴JIa/& /|>h߮1gvdz5}x5D tC=UI&A /aF"E҄)G:sWMPY\ ѳ)kC5b)6 x ;=˭X O1PIӊI4R!smҶTD[};+f#HCs dD82YIQX=-Sj\aEL!K4.P/0q"Ґ Vq438dqwI u &CBpt5xIoTl)C,d8 ' DP8f=VN\Å3Jlq"4upUR;.Е%4B"z'kKqйu>ns—8-@ H} ݻt˫#T$:؂oO}JWz0x+Īl!s b W4⡁ƋbZ# S~S}y9nj Rq.>&1eT#ț6BU˜2xNH:!p1{hRg[ӻd` G F3 5hS;fy fiB,9Ƕirz-j0@r?- \9ep89Hy+V%48v~߬":kvV5hs0Q+ b̳žcd~'Ef %R%Θۙ3`O&i0>Ǖ^9G܅uݰWL,P.Q,AJ0ϫ~E h10D:lTDn!&6晏;x,mUqG&5.NVҾ{ &Yޮy"XUDG&ZqTZ 59a)AQyud~\"J7,}6=_Tutr+ *,Q?L{2 R$ŚNV[4HƷ= s4QF%W3h<B.~Xܖr({W#Znf=Gsz]$'̀NS?'2)ZTkv !d>)4a@;s6z}i z}Ŏfr{Btҧ(D*^ikrnxE*[z{DNWs\RqFcG*^AS̡*㷂= %sYe׋[ރV'WkX]樂7ʁAqo ?00;S 2r C z=~ '];e1UQ`pS;!uĠe($s1!S% )idEnzpRyۋ ~[xTYCwh2\UҵHYW?Lc|Cv*`tvoVV!MG 4 [Ҳ +%l6VѺYxz0r29N0ז!dԊQ1\$/%5t6l6CU݆ sqFqz&ġ;mBO\С!mCL_2MN3`X?/ī7\K+(aů]wL˧[V{+ aީHѶGw^qIn׵~֮L=bO5j֗՛ lLE$e!nZt[^f:0iժWx.t8n.ɢ^Z>z %8ziơP=h(a5H8l5sU3aPXVZ͍)ox,v,-&y1xTK$rM +gkgn&ң!o]#6BM@oWv :\M3Ld|7 g'!uBP^M|ڿċ9RBzm2 *T =e1&5LiHRq=Y"JYC6̒4Ɩ^9l士fJ6%^!&PbOJE~?*M Juh[) ș-b$Ԗ-YAix;ozh1gR՝ȡu=2Y҆=۬Bx&\Q:)KIjUҗuܑXg6]],$st/\B 6>ɃDT_Y u53H(;wy!./>T}c'؟OfiCAy# ~V%U =+p+'dX]Id&hPٻ`qEy^cDք%}qs\s b@ԏ-tK)#{Z'a570_!6},-1SMj:´QӠd2=_DZW|;`9".NkӬ`t.-Nz`qoDfU$8z.UWjzbLYXV.5 \I/J^H!>+oC9*BؖgM<<&g)Nxb_p$lYM';S{d:@2ezj!$gP X%a2ʃ5X2,TWaBl:9jϙ:KDĸqюŀ~{ey-| Y+3&0 3{~fg컱0%Гd;7w ӡ>b? Av}UGӃ1ym|($7GH yQnk<-h<]+`-==rO[L`5ǦC&dό׶?~+}d-Khq| HUm^dZ=ǰ?nG67 @`B eK lPO[^Q~{ Z>g˖71A9]3A:~4 s\_b\ J%Hk)P)B[b= [xY%|7YT=˦q>΂M <]`l3:. Şz(8 cQز TGutgy\E<n|^x+qf'#g4*8_6\O^pGDqIVb.^uDDNx, } %7$ZRw gs9wPJ^9<G\u$+Osʊ' A!ig-{R+[)8;"V+R,%]6v>ܻBGH3*jb9,ISQVưsQ=M1I7ZZgZ5I10IR4)`8yYiЫMnzLZ}4/h*.0UlWԐc=&/MG>qÖ$ۊ9[MD3L-u+%3С,[Mߵא:=mү .]@2),x-a,$rB 3B*c R6 Z-;k~LԔS9b CvL.l+?q?O#u[Vf8/0H8|5ox'u6bKYf-w */+j$i9CHq%l&߱ 1l4R68<(F MNL#N+#>apۏ&ђd0y3 䦧+ !7.N?e 7EPzmZv{A4ڸc(`?&MYm|u.!$ۦҶ޼2gjy/zAnی2l.26 ""1L:}"W7CkHU,ɵN# oB+-"!4s)1σR>5x9k˳>C9_K0o9hf^> 80 $u#Hk9RR҂4@!/ܮo x0P}kk)Ѓyw@37Z?_J*p *ȧ81 d.%{ Vs5}8$TGt:@^ /(D d  6 vs4ͫ55)˱GĦRA*/iaE07b5ĥ&> E rZn99] (Y""#85ڂx _1V-ϥӳU+:(O>20P]Kh\~0ʌ/ϙ[QOg뭲jȤ}2^OlӴ_yNx$v큾).g+:ŗۢ>Ur" /7pxlv Z91O7.ɔ[=Ʒòf_jKrIIll~sj'n)tyՆo(e%t7yNkg*}qռC58+cG%@^*$buY(vЩaDLA*p+%&1!2S$HOC ]ڨ]3ކ48r9hy/a'w iRa]PlVG(OnLu(\\ƚOA'ק}U^Re.CyeChqrNHsʂGՍ5BҺʿlf UboZutg}Z\m .d6up$5]nf t%,mn&8q娂 >jזւג$ m]e{v;X/t묘1:0ʁ P+7[KT:c)-tۿrs4`H8ْUj _V3kM/nP¼+,_3~+uu d r5W"s?5On9V<2PUF/G8?&.(w#zirMO;lEv yH\+[m3w+Tc7lP@f1"ڭiqYz2LV0vq\0[muP*ҬMtFS.zu&kD83 oMq{pWjw?$G"vdL?*Aҷ겗CXR Qrr0E7s%d04LVADd%.޷@ 6Q7joDCir1M0tl`'m$Z4MSQM/%;@~|6B#'ՄG'L fQeldIRuD`,fjAa`6C W2` k*ᆡY"2tED"fV-0exKK]WҪQguf;l/nfigE&"u ={FScYeLL/3W򧉤,(zPr6+Y({4T|`aj>j8sqN'7ҪWF&juw9Kry8V;"˶iEV@[X쩤:66>DmÂ$Τ, 7mf JB~$RV_XYsoz9J7;^ CU -ӭks 1B J[O[tn`OE˯ozyk[4Vے=4DGsSqr:⃪ϛ+jzZJL2!Wލ+ۮnhOO_LXX$[u@ *4 o!s5zld}"b,+@3i"oo >J@J9X+@vpQ"_}a'<3~oh>{ZAq9]2l"! WjZڶy&vO L3)O^^ʹ$v8c}Qa}pT3jSCL"rGjB=}"n/% kg47"9 x&hMEwþײB8/P*Q?(nO6ƛ&BZuBH3v\)=:/ /Ip^V7l rZ}G@I-,m-;\ 1;I(O,WJ MQۚx&!N\'o0V:.Sm[=%C5iD{83["yjLɵ"+;=1̫齏-j]bbm2U˴Hu8}&I?>]Ymq *pn딝Xɂcry~.'kҶ,{p{,Rn#7)YBf‡U^'v5\"WmRj4''6ZtslՂ!LXw, ܆x_;"= eAgT%^&g]l%V>F) l3.dTj]@6- %c~U &Ҙ zBZ=C&_I R YCK/'B?}Ͱ⿿-@\,J7-4ɑ%Fi4OOc;Oe:0‰7uA .VΑj ;7 !sg~*88xIiRԜ!q_|`%np^~<^Hh}"4rƝ4qxk+Tp?4 [wn `G:=K4pD% q$XĈnT7?f'{L |4/jsD̺9D׋2nN9I(ax2n&4įώi @dHn%z GC\n|Uz[@hEؘ#9?'DJ4qRLd%qb1G?z ytK_RI=\i)#`Uw|= P+Zz{F [9e W"b6#~pSzV^b]p Ag-)]څqK4U5ؿ6IfˑaOX'l)uZtXiX:)4 SQ4>Jt3]Jncte%߇|p!*!UFJ"tVuobrDZX|BN==bunZl[ ( J(ua)23*cیD1sW|'ޡjv*Y,͛^QSƎ^ZA7ʿ)W[+ɏ,s@CfUx::_xVOI]PLCsJ:}d!Bb|˳ >ӟiTK\?9\3V">'tAR"أQP.Ӌe?ԉ֎C fF `@NX}؆1Wj}BIf2T!@N1֪= NQ3*y֠|zMBnﻗAב bQ'rEhI1 Kb=wcB1biSn<{L5(KuʴGP14]AFUNHAݡ *u. wxŗ4aI{]KŮ;rCBL+{ӭsޕ*AܷкhK] nth)Js AF( c?_?@ 1#ȓmQHfmN\PB|FZy1ppz[dkj[Da7CV՜rbώJGL?g^-RrCpLygԡV\ tD|U3_7(t9k-EWCE=7 qNf"G|v%Q]ݻ',OofSxD2+"/WLJu V]عXlk {3iv{w`RZvdk&h-Ias mK̷:冚!8͌G`AV!*ŕɢEߒ|zݦS$H~R1 @;VDI4R>,I=t-\$nz-|4F޹$i<U ^z0`԰Pc8Zl둅 ,r@۩ l|3)]*qxDӰa*0>BXA'-d;EDЌC!l196gg PmS'N 4HmzH8ˎ ^g: AUu*{¡Kiɦ˅MR4i{rc7LEGqSfidxDiym& PqWU ل4coxڨ wۙu>zaL4kBWT蠻[JVAD_l."zr(t6*hi]m}`Mg+Yruy7⭬luG&͌<_w]_TsBbYhfٶ 40\}"7"7$@4}z*r/8H(o-bJqqLRפ;cOB *2\p0&X3u'BX{afjyulf oXzbh5Uk uj`6댨V(,eP^Ԃy. C%8:Z#_ x&~08>-Pp_f^%OjJ8sؕgNV=~3@+o0Qx.2.$1 b[)嵨.oC2ґfFetkꅵ_Flcaö]DsΡD+8bDž8Ozn߷ mc3)ь7lB\?|Z5[` ѧBϺ\熼Tm{3clMY5Fs1N=o]Htk )֗* T6oy&wLGREp,UN2è; 3JmWWw (;þՓE) HHt;[ߋ"j~/u0;ZCc 8ی5 H+ P?P,.wMGcG'7)`W<…_}0urV+}/O#⚫͂&?]3- c'n`$ #@y *m{TX@"!(( ANZT%ŵ[*`j ao>,}qwͪ]xZ0Վ!_l'#rTht"5e~f E [|AxO-T9De'I%V=Հ\Vw‡Y?e5#h܍]{LdsM59{ch 8:L~.&-ssx|7hd4@+µ%0a< D+B&!c Z;<–s-c|[1O1)Ѐ]\1L9ks=gu( 96я[if.v',|l߱Ox^aFe6o.A1e?K]A0՞:)ɑNhI&T>P0 (C{m +=%1HMu MA(*Π7DGmە8)9}IB{W;KlcvGpIQ̄TRK:`族4JL`(I`߼-{\=N\PHNFSݷפ ` HMx]cotwmOb遪uGk |>Vyk*h>f, uf mPoJ[۵qx?n `L~鿏, Ax"pUc㇏?c9O2~:b&oOkZIM%T j d`[Gs8xHI'(kLa&U&^4¸Hme*YG,=g-(I&.aC~'{ʕyR/]8 sXYStUfI~ "; [oi9^ ,V"cҎa_o`HiN7cY{ٸTN SyJf2NX=Ij^e%1~W 7L{\K35w\\寈1}#h9gRiXG_~QlmeB.ms*_4C . rOIE6U|_N C$jh+?%Y I?t;7~\15s}i@N JfRc}>q舵q',#К4K} ڡs-K^Rqa?GBf3C9A 㐯*ܝtK< MXf#@M h-?Ogl0!7vMiq25RGyGҝljqMȉw\H92dO|NH)E2[(y k*q6a\EDYh~d-haуE{<!8q;#X"a3Gc6[s"sO5T*@<ZcsW4Hl\qZHAj $zA8آfryzXt%"t fb/Vk.ؿDEhlu4v9~1T^>eђ8>}b"rӝ)dTsLS摏iRkRCXS} != IQ' 7Fz*ȼ\sB&7ތ)M_&l5_ BXp{@3Y x6EJUDy͞N XC|5ZBlanMO3cߙ4%ZqgS9B0*=w7"=ydy){bTwن<ı-x7gh%YMuWv>]{}@>푛@'Nxȏ}"ʟEU:H5E[v^sdڲק.:-;VG5"#Iw9x!ꪁfv)Lfsչ7zf1=qDŽۛmxJdQxBkQLm/2v4y=߯UIꊣ- &r[nI6 5"׍|9QIZ!sgذ(: ُ-Ïf.ՙ~TIN>↟Hz@c H yl9t| Kk=iw}R8qвP,.IT^b2rE3t6DEtL W{ ]}JA8VuL u8AzH$.t [8_ꢖ,m+L~T?Tdp`^p> hj L$4H (nwsg$'\+~u> VM>vՓι(w"q/yfZwh\6n~Wg=\w˕_e>Yh{P$wiN9cB&VX뺸:_q%?Nj#pG'tH-?Xa|U 7J+ g[;,F懓wN*VtZ8:;,i{Nsvf@BGCvƃhUJ)zO휖\bϒBeYt'8/s@5S)XpA 섛⁾_W0Y)y~cmƣ{܋h8N}.Aք}[8R6 hwDс$!Ѩ#c.86Cwl M2lM⑽ΐN__m9j ~&#]8|Hj\P;O*:8D,5Trrɫ&[ (|TDa};w/"Y#7Ё5$@<Y=6u¡gs>(UN9h3vt<4<~pˏ5$.6@Dm#pf4nck5 EoQc+43Graڸ]s[k1Nnpa_ﶗBzzhx&z侉!*L`G"!૽x[̬V04F n)Bv_a!zVV`V;d6e0Ɖ[9[ VDǮ#rT#eьⷾeҕt<[95{LkQ@U!2D]TU@ǟTuu۴۟zW8 1<#Ůi~XP~Bjøf-V@Wvsl9b957aH{a?襽aØ$v]Yl&ܙwapPw21HûxBs56xh:yjHo>Ă12vڞ1 AESIX;Dhk(D;:=.*֊O8Q c[&Atj!Jc {,y8Ⱥ"LpQގT2%Uh_8PȂλ,ʘkd 祷LUcP-`0t01{Au)*abl n+{r "/|jq<볁H!yH0_E• PL4iGܿ&K-ЮtitJ BTn۴];M:]ya>2JLFT(bTފs {l7!\x`+4+anxjj$4v}7R9h!iCYlJ4?:cLzm̫sͱdA6 ykڃfb]yh@{t'{N\huɜjjJ7&:"X=袎c鐧[ j-aIuxr㈎s6!#R ݁$@x [>G<'au#.O8k6Ⴣ.9{D,chIú?`>hh*k(9uE. qhfԨg?dk"rSU_Ks%:yctjv B@-*PP1`=1 T)$:d;d?rD D I$axV9F_W6,uJ*^1ȟޙcr(p&Lh<m,{g81YMyn}nGBIY#g>wRݥ7Uir.~Ӝ<= 91 FMC^$Ѕ Boa`@a OwJpzi_p`5_ jӉx1@jdda˖C{.cl0IA >Ҭ/`f[xŀPxDǓZvj,XiŦ|%9/"aho73 ڵ,wbLS7Jw*0x׫I7Sl3Ki]"EzFMH!U0X/O= ~ $mxeju]\fIC Pa6#oX;95deg*%]UnhUo'wkYf$t,f7;6+B8;Ήno׊ܺQ,Oi|N'J.K[|n>A=wEX!{ jz_PDwvkWl=c|oU݆\֫ {/-#MQ ;(U튽&皜t۽:xZ9BX@Y_=A˞Ӯ}60!C&BKkV$mHik 'N5`ITP%Eה~ymQ}F8p,I Wtg@ϭ;Ya\7q?CN0Rt<=l# VOƣs_A\뫂DM%C_Y\;RTC_'9oEѱՓkQ>)0# >P+[#Q V~2ǡއ#7۹)rqa^ :b:{vs-;ZYP'7B: ԰m\0 C+9FccdDVxQMEA:Xݾ1mgR%H8gNo \۵k\stMY_r/׍c;0ں ffUa6R-QQQy^"a;|<#kШJnBXSlK?ypһ.wRV%-o%;6 1sϑQgz w"QF-)̓l :C-''~ݦ'g*Ci)9daT6!{£0]7VVg Ƅ&R\ uQS3p?Ƈ[ӑ cH|8_|*\hXp[qW!6ף=3.Efx,:s]&%Y!W(IfY!J}?Mf >S^s ? oSDNnZuGhZFװ\|Jkb:E5j6Ț)z5  YT}RnL-;Ϣv:1äJ(1lɠq~,p? #?!aoXv5ŢJ-ZlEp1_m-UqJT)oGSBlV^%wk#V-Jv8wt7stqQgl`xMW$3#cb/!Ztku@'bh\tQjJ2uG^9šk]rěKs*{yML<9"CޟbReo~>aoֻ&ۆY΅x%^ep{"^YOƄ:eAi9:{m}h70~YZ ['7bLw=b #XO)Yg#QRX]8ppzmz/̥[:]qDml|v Eafq5/8>v?2V X8ovxsA~y@۝B f)Op#eovdģe%@&zT BK֩%Qmxv.$gWC}}/.wcۯh)ν/?mxC^엳)n/1"W!Y>cws_*q,)FǬN \na'Z+ d`d"xZh[$O̒6=aM=Ezp?kx!=DN޳ z=܊gg7ϼ~$nqҠXt-Pǵ\ezCo٣\I0`YagDnR=@2pqoxoq`8~e\RlWFGXVBҤ*D*Ji'$uv4r|+aO`:bBpڞ-8fĠk3pY3ӥW+Oͺ/ai)1`+t0OX.):w$d]qjgs;xбvM./lNj_Z5%ҺG<L7ڷap?E5"YcL%{Ir[o_1<4wxr5Řx&f=#%"*f#c6zs2LPwf?u\.!A5Q?ahIu'ʯPzG/"{&<b;~irS O Hme2cKv9ZK~ee }O“ iƯMbvX cj9'>tղ'K|u%/EewaH,K=YzT5SPyBE5 .Yx{@ZױGWpT -qز} ~h]e!"Y~o\xGx릘~*$ncTڒIW C@NK5ڂ鹳G5#rS\0bWɼGMo h?^Ȅdɷ1 `g:kSnb/X ΪYҺE&lgUyޒa٩0cL^5'>QkV>& eaFNK5%yyfnnjI2%_%Y$-?ʲJDMPLwܴXӘ2 lG~CKb$ƥ7b?Y32U7Bq(}@#f| p4 Cݘ895&-c瓎004gQJ=l98jQS<=:L"Dbp'kPh 3XRS^BJ$aU>Q#ëmݚQ6]&ƈrSn6n ng5)? _Q>MQ$qQ@>'^(AN;F,cϑy˸C`>6?&hCng6j ?ȜٱBŀL:a^:-S`0NmtUy TwWl h6 O6-D#B{ou9z(A*Np8}fӫƇcJOK+νD% Y#g[joW4N+K(2Ķ|q,~nc'{ {SdͲDت7Y*wg.\trZCpsk[4A( +'ar;ɬ:1f 8:om fsl֜a'VOvYDIsv0ݳ~X|2)vKN<е#+7::lMklf0<4 `u(2[c|4s{@A^Or.ꞹ,ޏCA2/hH'i:MY4"+ !jjv,A 3Ep*鶅QH3}AW~-_#fcv0 |\!XN:N @ 3s;0t![ʐ0t8@YB4RLHoOIYB25`@vf?M% ^G h҈FeE]͛35Ph6BwH@ 2N@S?نy$ żN0?m A‹Z=7/3?ZGeA; z^X/Y= }) 瀡gE{z {q~"lN&]:LϙL2/uzkjNJSW㼥 5sv'& br6oٷUĹ QĝH7_y9cP+LxmR {KE_<7D#ĉ}2sʎ-hą~N6*Rur$=|T"\,qCtD)I>+&Η4x$iH~;q KZГ)cD`(l|lmqWÝƫø=\u$/.E:5ڲe(~> U^4U F)4pg(>싻_e,lG^hT *Z2 $;Jýew6`!zA IZ#ë%az&ГnISRg)ڙ% Ce*ə{ܱ*K$ĶbbXWnk0fÿsW)|y83cAq-́]A |[0zKZ*C6 V`Ok*)h- %^0pz%rqrJϢ/:vߡS[ݾdBjnQ'n`ߒ{#]ՅwqdWvy4sбgVR;¹gIeB1w&%SR*e΢螾nf`-V6ڬFr"=ۢlW:P1M9 d{"W՟llt7[I*-ԖlDD ͘ Mi"1?Bi$3LГ%ʰ8ܭ]a_N<=T6ݰȼ]xaر6"=,v #p=t**OuV=QL?+6B%c98>fM*vC>5n̕w(XRI@M8@j X*cWa="|d|:Gy:SG`\;'=Vn2[<\+ u>Up@䳂8dItKk9@Z |*:x'~` =NX~\һ,@ nG9\@FT(_gdFA=rRM~UlɼրyNr`rz5/Է l] \w)_1qmjw]b`|@ѱ۹o3K0bqcj?Bc& W OH.B4k,^AVл8ڭ=Xl $5671E /[ǠSwXG $'k2yIy6HI[sXÛ%FC֝'c(:uB*DJypKn᥹mhv7bI01{Wxw um?n[N2U;v-5-)]uZ4IaGFRPD_J\jؿq]2lpPSY/\L<0Xα9@;CIU 2DVr)mϊe|(B͝=03 QbD+S%oIZHvhN~8M_魘6/!qR^pm$CzXR]~t\iu?kx.;Ffaf|дpϏF1eH"/IH~ EfvhXcg*:Q8/I,k=Fb/ܿ=i8$;WV}5tzNd)Uɛy@uaHzc$q -gCjEC4_  ~bYyRh5CDJA[έ2G*bD9[^U]N3PtblUx7[raRۈ~Ω*3nƅ R w:Sٿ)Jl-&;aRD4!/=J `$.]%c tpw[4ZpfD)vT,c6[Q;h!$^%l6꿰'D]pt,xAJh0NG Y⍳CTPˀrՊ]L6#+LElE"y GqIOba':oՆH 0fǝ _AKaĎv'$e,aй1ALYw &3FCE*fo..MuۇvgX\| .Ta+ÿ<@>x‚_"Ήha_a`gLvEΦ\@.,wE4`Ҭg߳5֮j^\R9JL *NpWB'?M,gO*DN%uo]PtDA=>my.ʫ)'F$"nk`ɳ;ITK3hz N kפ4V4(ޑ~ȂT]p"OA0Yt7ed 8q@ 90=hwlcs`׾cwnIztg~,lJyK@/UJd<4B*UPސQhJLtK8=-uڔ?;p|srQߔ2(eeM&=R[^I@g ?;3_}NS3.wmJ: OQùLSWCVFK=EwSL4r:Cسr^s7 4d{r|)YwiC)ӃZOQs{[/wej8OãZn⃯ Zx!#^[Kj1MȕvNDR(eL%h M{8ixz% b/qXGD?7J-XW3X`s483`ΚApi$''1rJ4kn#!o/n@]x| fE3X17"Kӵ@rK8 Y<{F)ZQ^R-IДHƎn/۳ExcEgutl*釸tCf3EEHZ{"%'(E~P\F-]0x @[s5~S (D,zP+/"zt?}QqU]sPޙ,brߌx>-@Vk+cSROZUŐ.=`\II-)~ , 2[J;~i:[(T46Ԥ=+\z8<I=2 YcYm0r2XHYl%A7&aNG?"K ԕ`һ4cX2 PeS]ZLd 6 Ȟpp|ftLLOvgI#Gus^I  NH-.V?6SЊa#'E%ζ+͠!y5sLHDJ&t|ҩ>\w~6fen'钫}xxnVGq f9 #1Rj?qk>؟Gj M.@[g=7{d >ⳑLwfq1DE皰N̸JRH%zZ`zd_-.S5_J8|}q31P~Iԣ*YQ0;RV󾂛4Zp_m@{dW>" *amYEpr,t*2|R!)Y)^bdl]?rf'<ʥE >dٶ-&yi lICg[: 2 gr ބ8\۶"_)3:aqXBj&,ݙ=,{v^d%y:#!-Q}^l#ΑHILhMٔrf}|8 :楥4'bb ^\׽Fȶ;4`^܉!P%<n@s&.x+2+'EtmC/djuDFLЙ,9"I,ug:[LPq'uӟőxGV9PgeR WG@]%hNduF&sR$cU0wT_VNbS+ˢl z%'硛t|:ࠅ fy=ߗ;`ψt?hDT8o-ұC,A& 2hjs:Rwz)P7O:sȰIԂF'9;F`{ sl` 7 dxS4J@S? *V4sZJ^\%>nנ/.t8 H4oDp1W#![#ŗi޶1XAGĶ_#s [?vf.><)`0*S6"֕Bj{)Gt2(5Ғ}(c mJj>WXIڛ2j'KL?p^@| c!n3przQJy.n{Y0d+tgGt.󙛾=ZUO*-eĞNL\bb{got$Y#z} H%x { 'DC%7[:je~N`= š jLE5 BjS5%Gmn*zB{>2-j-:$p_xazַ6^rO+CVIyRh[VS\]џ#3/ ~Q'Ev6.>6WٞD$Ka0_MnH AƏey)ߩ 5uK|8?xԦ[;< TDG5 p9 . hزN5Jis3>Hzڌ[͏Щ凄>ua0qr9n"`B0HW:: 21e@E X9OJgs=Y[A1b)/kuMFE)P(G)bKÍOiLYQ % vPbGK@ b|;hf<$KϹ Kπߒ2C==bNiMMI}h"OѤ>50 lt J?08A4(ēHO&gx ^uxHb4h*S Œ%'.y'V&Yw/2\%nOH8k<R֓\P)}aZj~=(W5ҀrdJ8}B{'Sx6e8]̝͡l gvDoW a 2:~{O-6rXٲ5^#|yZ$ͥQz E+r&݋"ІS<[ L|9{wW[cyrA(mO3tکtLm]:潰a `=RڐWyV~ UQA|p/mhw\3Guhv w3@n[HO]&^i/{kgaq|0Qr7b%>X͇>酮y>RԀ\Ktq<&sCeS&F*jq @K"#3RZVcC ^J<(R$ I6^Dc7({g]i y 岤l.X2A).@ʂߟyAEx~ۙsC!`\Evfb-)ul$n+ )Bg@[Va?DW=t"/gBSbLAqLH<[a'ԐuOĈMړDA)Kq_Xx{bd&)ɍ'(0=C4 Ҝf]I=Cv~x40]kFaꛀ =hZZپߓەkf*\K#݅U }ӼzOGWQ$:ji* ZX'90郕ٍ{]WCG!]~b1<3ھHJV:- :{۷$>U BiRKuS՛6_ŮT=~ӰfXDHߍ&4fdų5,YajP1$94ߌaֲ-TZFپ jb ?oõBCf-ʰv>xܖ 'Ki' Lׁni rM? oU vhǎc~΀jD^{js2Zu3xbpИ80C&ELgPQ"u¨hٺNg]P[  20 &K)7eq7"OGU(Y 뾒0)uj0GXσ溌Q(b X^"TBs)Y}M6(02rjLpE5(f>*A oiȹ߮~`mAG溓L7A@*BQU;?DzbZ,ͥ.)[1}Y_=XELr i6RK GSk{ݢHz+He厈i. bON)_Ɖi2zVk?{BU[? .qofԛ3I9I ¸)&bvwC)q=;0YZsquashfs-tools-ng-1.1.3/tests/libfstream/000077500000000000000000000000001410627516300204105ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libfstream/Makemodule.am000066400000000000000000000053311410627516300230140ustar00rootroot00000000000000test_get_line_SOURCES = tests/libfstream/get_line.c tests/test.h test_get_line_LDADD = libfstream.a libcompat.a test_get_line_CPPFLAGS = $(AM_CPPFLAGS) test_get_line_CPPFLAGS += -DTESTFILE=$(top_srcdir)/tests/libfstream/get_line.txt test_xfrm_bzip2_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_bzip2_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_bzip2_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_bzip2_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_BZIP2=1 test_xfrm_bzip22_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_bzip22_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_bzip22_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_bzip22_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_BZIP22=1 test_xfrm_xz_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_xz_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_xz_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_xz_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_XZ=1 test_xfrm_xz2_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_xz2_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_xz2_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_xz2_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_XZ2=1 test_xfrm_gzip_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_gzip_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_gzip_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_gzip_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_GZIP=1 test_xfrm_zstd_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_zstd_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_zstd_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_zstd_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_ZSTD=1 test_xfrm_zstd2_SOURCES = tests/libfstream/uncompress.c tests/test.h test_xfrm_zstd2_LDADD = libfstream.a libcompat.a $(BZIP2_LIBS) $(ZLIB_LIBS) test_xfrm_zstd2_LDADD += $(XZ_LIBS) $(ZSTD_LIBS) test_xfrm_zstd2_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_ZSTD2=1 if WITH_OWN_ZLIB test_xfrm_bzip2_LDADD += libz.la test_xfrm_bzip22_LDADD += libz.la test_xfrm_xz_LDADD += libz.la test_xfrm_xz2_LDADD += libz.la test_xfrm_gzip_LDADD += libz.la test_xfrm_zstd_LDADD += libz.la test_xfrm_zstd2_LDADD += libz.la endif if BUILD_TOOLS check_PROGRAMS += test_get_line TESTS += test_get_line if WITH_BZIP2 check_PROGRAMS += test_xfrm_bzip2 test_xfrm_bzip22 TESTS += test_xfrm_bzip2 test_xfrm_bzip22 endif if WITH_XZ check_PROGRAMS += test_xfrm_xz test_xfrm_xz2 TESTS += test_xfrm_xz test_xfrm_xz2 endif if WITH_GZIP check_PROGRAMS += test_xfrm_gzip TESTS += test_xfrm_gzip endif if WITH_ZSTD if HAVE_ZSTD_STREAM check_PROGRAMS += test_xfrm_zstd test_xfrm_zstd2 TESTS += test_xfrm_zstd test_xfrm_zstd2 endif endif endif EXTRA_DIST += $(top_srcdir)/tests/libfstream/get_line.txt squashfs-tools-ng-1.1.3/tests/libfstream/get_line.c000066400000000000000000000060541410627516300223470ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_line.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstream.h" #include "../test.h" typedef struct { size_t line_num; const char *str; } line_t; static void run_test_case(const line_t *lines, size_t count, int flags) { size_t i, line_num, old_line_num; istream_t *fp; char *line; int ret; fp = istream_open_file(STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); line_num = 1; line = NULL; for (i = 0; i < count; ++i) { old_line_num = line_num; ret = istream_get_line(fp, &line, &line_num, flags); TEST_ASSERT(line_num >= old_line_num); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(line); TEST_EQUAL_UI(line_num, lines[i].line_num); TEST_STR_EQUAL(line, lines[i].str); free(line); line = NULL; line_num += 1; } ret = istream_get_line(fp, &line, &line_num, flags); TEST_ASSERT(ret > 0); sqfs_destroy(fp); } static const line_t lines_raw[] = { { 1, "" }, { 2, "The quick" }, { 3, " " }, { 4, " brown fox " }, { 5, "" }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 9, "" }, { 10, "dog" }, { 11, "" }, }; static const line_t lines_ltrim[] = { { 1, "" }, { 2, "The quick" }, { 3, "" }, { 4, "brown fox " }, { 5, "" }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 9, "" }, { 10, "dog" }, { 11, "" }, }; static const line_t lines_rtrim[] = { { 1, "" }, { 2, "The quick" }, { 3, "" }, { 4, " brown fox" }, { 5, "" }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 9, "" }, { 10, "dog" }, { 11, "" }, }; static const line_t lines_trim[] = { { 1, "" }, { 2, "The quick" }, { 3, "" }, { 4, "brown fox" }, { 5, "" }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 9, "" }, { 10, "dog" }, { 11, "" }, }; static const line_t lines_no_empty[] = { { 2, "The quick" }, { 3, " " }, { 4, " brown fox " }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 10, "dog" }, }; static const line_t lines_no_empty_ltrim[] = { { 2, "The quick" }, { 4, "brown fox " }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 10, "dog" }, }; static const line_t lines_no_empty_rtrim[] = { { 2, "The quick" }, { 4, " brown fox" }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 10, "dog" }, }; static const line_t lines_no_empty_trim[] = { { 2, "The quick" }, { 4, "brown fox" }, { 6, "jumps over" }, { 7, "the" }, { 8, "lazy" }, { 10, "dog" }, }; int main(void) { run_test_case(lines_raw, 11, 0); run_test_case(lines_ltrim, 11, ISTREAM_LINE_LTRIM); run_test_case(lines_rtrim, 11, ISTREAM_LINE_RTRIM); run_test_case(lines_trim, 11, ISTREAM_LINE_LTRIM | ISTREAM_LINE_RTRIM); run_test_case(lines_no_empty, 7, ISTREAM_LINE_SKIP_EMPTY); run_test_case(lines_no_empty_ltrim, 6, ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_LTRIM); run_test_case(lines_no_empty_rtrim, 6, ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_RTRIM); run_test_case(lines_no_empty_trim, 6, ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_LTRIM | ISTREAM_LINE_RTRIM); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstream/get_line.txt000066400000000000000000000001021410627516300227300ustar00rootroot00000000000000 The quick brown fox jumps over the lazy dog squashfs-tools-ng-1.1.3/tests/libfstream/uncompress.c000066400000000000000000000427621410627516300227650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * uncompress.c * * Copyright (C) 2021 David Oberhollenzer */ #include "fstream.h" #include "../test.h" static sqfs_u8 data_in[] = { #if defined(TEST_BZIP2) 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x05, 0x24, 0x28, 0x04, 0x00, 0x00, 0x27, 0xd7, 0x80, 0x00, 0x10, 0x40, 0x05, 0x06, 0x04, 0x02, 0x00, 0x3f, 0xe7, 0xff, 0x40, 0x30, 0x01, 0x2d, 0x23, 0x62, 0x26, 0x05, 0x3d, 0x03, 0x54, 0xfd, 0x53, 0x4c, 0x86, 0x9e, 0x90, 0x6a, 0x9e, 0x9e, 0x85, 0x3c, 0xa0, 0x00, 0x00, 0x1a, 0x9e, 0x41, 0x13, 0x13, 0x28, 0x69, 0x03, 0xd4, 0x0f, 0x1c, 0x70, 0xd0, 0xb4, 0xe3, 0xe4, 0x75, 0x4e, 0x8b, 0x67, 0x43, 0x7b, 0x38, 0x27, 0x77, 0xe4, 0xc1, 0x98, 0x3a, 0x2d, 0x3a, 0xe4, 0x44, 0x98, 0xdc, 0x49, 0x8b, 0x22, 0x48, 0xfc, 0xc8, 0xe7, 0x57, 0x05, 0x3c, 0x5a, 0xee, 0x5a, 0x84, 0xcd, 0x7c, 0x8f, 0x26, 0x6b, 0x6e, 0xf7, 0xb5, 0x49, 0x1f, 0x79, 0x42, 0x5d, 0x09, 0x8c, 0xc6, 0xde, 0x0c, 0x0d, 0xb1, 0x46, 0xb4, 0xee, 0xd9, 0x8f, 0x33, 0x37, 0x04, 0xa9, 0x05, 0x49, 0xe3, 0x04, 0x16, 0x62, 0x36, 0x3a, 0x01, 0xda, 0xd4, 0xc8, 0x8a, 0x32, 0x02, 0x1f, 0x62, 0x4b, 0xa4, 0x49, 0x59, 0xda, 0x50, 0x85, 0x69, 0x35, 0x21, 0x10, 0xc6, 0x8a, 0x3c, 0x44, 0x95, 0xb0, 0xbc, 0xc5, 0x6b, 0xea, 0xfb, 0x40, 0xbd, 0x14, 0x01, 0x6a, 0xfa, 0xcd, 0x67, 0xd8, 0x2d, 0x93, 0x8b, 0xda, 0x44, 0x1b, 0xe9, 0x5a, 0x87, 0x60, 0xb0, 0xe0, 0x73, 0xd1, 0x01, 0x3a, 0x66, 0x05, 0xcc, 0x34, 0xa0, 0x63, 0x8d, 0x35, 0x5e, 0xa0, 0x9f, 0x05, 0x89, 0x15, 0x51, 0x48, 0x16, 0x0c, 0x61, 0xf4, 0x30, 0xb8, 0x07, 0x29, 0xc0, 0xf5, 0x1a, 0xe1, 0x0d, 0x6c, 0xfe, 0x91, 0xda, 0x13, 0x2f, 0x8e, 0x5b, 0x1c, 0xfc, 0xb3, 0xb2, 0x30, 0x9d, 0xf6, 0x09, 0x30, 0x55, 0x30, 0x67, 0xc2, 0x87, 0xe9, 0x9a, 0xd4, 0x1d, 0x66, 0x11, 0x54, 0x89, 0x21, 0xe1, 0x55, 0x84, 0xbf, 0xa6, 0x11, 0xa4, 0xb8, 0x40, 0xed, 0x42, 0x20, 0xb9, 0xb7, 0x26, 0x31, 0x14, 0x4f, 0x86, 0xdc, 0x50, 0x34, 0x38, 0x8b, 0x57, 0x77, 0x21, 0xf6, 0x89, 0xbd, 0xc5, 0x65, 0xc3, 0x23, 0x45, 0xec, 0x7f, 0x8b, 0xb9, 0x22, 0x9c, 0x28, 0x48, 0x02, 0x92, 0x14, 0x02, 0x00, #elif defined(TEST_BZIP22) 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x5d, 0x09, 0x24, 0x1d, 0x00, 0x00, 0x13, 0xd7, 0x80, 0x00, 0x10, 0x40, 0x05, 0x00, 0x04, 0x02, 0x00, 0x3e, 0xa7, 0xff, 0x40, 0x30, 0x00, 0xac, 0x43, 0x54, 0xf5, 0x36, 0x4c, 0xa7, 0xa8, 0xd3, 0x6a, 0x60, 0x81, 0x40, 0x00, 0xd0, 0x32, 0x64, 0x0d, 0x53, 0xda, 0x02, 0x09, 0xa2, 0x68, 0x34, 0xd1, 0x27, 0x4a, 0xdd, 0xf2, 0x0a, 0x73, 0x43, 0xf9, 0xa2, 0x51, 0x85, 0x76, 0x45, 0x9a, 0x68, 0x3a, 0xe7, 0x0d, 0xc0, 0x21, 0x4a, 0xc4, 0xf9, 0xf7, 0x40, 0xc3, 0x10, 0xb2, 0x9b, 0x58, 0x56, 0x71, 0x50, 0x2f, 0xa4, 0xc5, 0x61, 0x19, 0xf6, 0x59, 0x06, 0x82, 0x03, 0x7f, 0xeb, 0xd2, 0x61, 0x88, 0xcd, 0xe8, 0xf7, 0xe8, 0x87, 0x59, 0x9d, 0xe1, 0xf8, 0x19, 0x6e, 0xad, 0x77, 0xbf, 0x34, 0x17, 0x21, 0x6b, 0x91, 0xc9, 0x52, 0xd0, 0x81, 0x1e, 0xb5, 0x0b, 0xee, 0x42, 0x84, 0x80, 0xd5, 0xa1, 0x8a, 0x04, 0x18, 0x4d, 0xf3, 0xda, 0x7e, 0x3c, 0x40, 0xa4, 0xdb, 0xe5, 0xf0, 0x37, 0x40, 0x3a, 0x7d, 0xa7, 0x45, 0x21, 0xf2, 0x5a, 0x7b, 0x59, 0x56, 0x16, 0xd5, 0xac, 0x9f, 0x60, 0x85, 0x0e, 0xf5, 0x73, 0xd9, 0x47, 0xe2, 0xee, 0x48, 0xa7, 0x0a, 0x12, 0x0b, 0xa1, 0x24, 0x83, 0xa0, 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x2c, 0x24, 0x39, 0xa0, 0x00, 0x00, 0x1f, 0x55, 0x80, 0x00, 0x10, 0x40, 0x05, 0x06, 0x00, 0x3f, 0xe7, 0xff, 0x40, 0x30, 0x00, 0xb5, 0x91, 0x13, 0x4f, 0x54, 0x7a, 0x6a, 0x6d, 0x4d, 0xa2, 0x68, 0x0c, 0x84, 0x53, 0xf5, 0x30, 0x89, 0xa3, 0xd4, 0x0d, 0x0f, 0x49, 0xa0, 0xd4, 0xf4, 0xd1, 0x53, 0xf4, 0x93, 0x69, 0x3c, 0x81, 0x1a, 0x65, 0x53, 0x90, 0x51, 0x07, 0x2a, 0xad, 0x8f, 0x63, 0xba, 0x25, 0xc2, 0x0c, 0x8b, 0xb9, 0x95, 0x15, 0xd8, 0xda, 0x61, 0x5c, 0xa9, 0xe4, 0x0b, 0x21, 0xc9, 0x97, 0x57, 0x01, 0x28, 0x9b, 0xfb, 0x94, 0xb9, 0x48, 0xa3, 0x0a, 0xc6, 0x1c, 0x54, 0x98, 0x9a, 0x39, 0xc3, 0x87, 0x90, 0x33, 0x58, 0x2d, 0x3e, 0x16, 0xb1, 0xae, 0x26, 0x89, 0x75, 0xf5, 0x77, 0xa5, 0x8e, 0x5b, 0x8c, 0x8a, 0x39, 0xbd, 0x75, 0x21, 0x9d, 0x99, 0x18, 0x4a, 0x91, 0xab, 0xbc, 0x08, 0x87, 0xa4, 0xf1, 0x81, 0xb5, 0xb4, 0xb0, 0xfe, 0x6b, 0x9f, 0xbe, 0x19, 0x82, 0xd1, 0x50, 0xe1, 0x5e, 0x13, 0xb5, 0xc6, 0x2c, 0xa4, 0x82, 0xf2, 0x5c, 0xc3, 0x20, 0x41, 0x13, 0x56, 0x63, 0x3d, 0xec, 0x71, 0x2a, 0xbf, 0x2c, 0x60, 0x2f, 0x7a, 0x4d, 0xcb, 0x3f, 0x8b, 0xb9, 0x22, 0x9c, 0x28, 0x48, 0x16, 0x12, 0x1c, 0xd0, 0x00, #elif defined(TEST_XZ) 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00, 0xff, 0x12, 0xd9, 0x41, 0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc, 0xe0, 0x01, 0xbd, 0x01, 0x43, 0x5d, 0x00, 0x26, 0x1b, 0xca, 0x46, 0x67, 0x5a, 0xf2, 0x77, 0xb8, 0x7d, 0x86, 0xd8, 0x41, 0xdb, 0x05, 0x35, 0xcd, 0x83, 0xa5, 0x7c, 0x12, 0xa5, 0x05, 0xdb, 0x90, 0xbd, 0x2f, 0x14, 0xd3, 0x71, 0x72, 0x96, 0xa8, 0x8a, 0x7d, 0x84, 0x56, 0x71, 0x8d, 0x6a, 0x22, 0x98, 0xab, 0x9e, 0x3d, 0xc3, 0x55, 0xef, 0xcc, 0xa5, 0xc3, 0xdd, 0x5b, 0x8e, 0xbf, 0x03, 0x81, 0x21, 0x40, 0xd6, 0x26, 0x91, 0x02, 0x45, 0x4e, 0x20, 0x91, 0xcf, 0x8c, 0x51, 0x22, 0x02, 0x70, 0xba, 0x05, 0x6b, 0x83, 0xef, 0x3f, 0x8e, 0x09, 0xef, 0x88, 0xf5, 0x37, 0x1b, 0x89, 0x8d, 0xff, 0x1e, 0xee, 0xe8, 0xb0, 0xac, 0xf2, 0x6e, 0xd4, 0x3e, 0x25, 0xaf, 0xa0, 0x6d, 0x2e, 0xc0, 0x7f, 0xb5, 0xa0, 0xcb, 0x90, 0x1f, 0x08, 0x1a, 0xe2, 0x90, 0x20, 0x19, 0x71, 0x0c, 0xe8, 0x3f, 0xe5, 0x39, 0xeb, 0x9a, 0x62, 0x4f, 0x06, 0xda, 0x3c, 0x32, 0x59, 0xcc, 0x83, 0xe3, 0x83, 0x0f, 0x38, 0x7d, 0x43, 0x37, 0x6c, 0x0b, 0x05, 0x65, 0x98, 0x25, 0xdb, 0xf2, 0xc0, 0x2d, 0x39, 0x36, 0x5d, 0xd4, 0xb6, 0xc2, 0x79, 0x73, 0x3e, 0xc2, 0x6e, 0x54, 0xec, 0x78, 0x2b, 0x5d, 0xf1, 0xd1, 0xb4, 0xb3, 0xcd, 0xf3, 0x89, 0xf5, 0x81, 0x3e, 0x2c, 0x65, 0xd6, 0x73, 0xd3, 0x1b, 0x20, 0x68, 0x0c, 0x93, 0xd4, 0xfc, 0x9f, 0xf8, 0xa7, 0xd4, 0xfa, 0x3a, 0xb1, 0x13, 0x93, 0x4b, 0xec, 0x78, 0x7d, 0x5c, 0x81, 0x80, 0xe5, 0x14, 0x78, 0xfe, 0x7e, 0xde, 0xf7, 0xad, 0x9e, 0x84, 0xba, 0xf1, 0x00, 0xe9, 0xbd, 0x2c, 0xf4, 0x70, 0x7d, 0xbe, 0x29, 0xb9, 0xf0, 0x10, 0xb9, 0x01, 0xf1, 0x76, 0x8a, 0x5a, 0xad, 0x02, 0xa1, 0x32, 0xc8, 0x53, 0x59, 0x11, 0x4c, 0xe2, 0x98, 0x34, 0xd9, 0x23, 0x51, 0x4a, 0x40, 0x2b, 0x87, 0x41, 0xdd, 0x50, 0xcd, 0x98, 0x1e, 0x29, 0x86, 0x23, 0x93, 0x3e, 0x9b, 0x6b, 0x16, 0xa1, 0x40, 0xac, 0xe7, 0x40, 0xfe, 0xa9, 0x87, 0x48, 0x25, 0x52, 0x02, 0x8b, 0xc4, 0x68, 0x08, 0x5a, 0x62, 0xc1, 0xb2, 0x07, 0x3b, 0x26, 0x1e, 0x59, 0x5c, 0x47, 0x24, 0xae, 0x8e, 0xe5, 0xf7, 0xe6, 0x4b, 0x13, 0xb4, 0x6d, 0x46, 0x65, 0x4f, 0xd0, 0x48, 0xcc, 0x51, 0x4b, 0x80, 0xcb, 0xf1, 0xd4, 0x6c, 0x45, 0x98, 0x92, 0x47, 0xeb, 0x60, 0x00, 0x00, 0x00, 0x01, 0xd7, 0x02, 0xbe, 0x03, 0x00, 0x00, 0xda, 0x2c, 0x45, 0x49, 0xa8, 0x00, 0x0a, 0xfc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x5a #elif defined(TEST_XZ2) 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x04, 0xe6, 0xd6, 0xb4, 0x46, 0x02, 0x00, 0x21, 0x01, 0x16, 0x00, 0x00, 0x00, 0x74, 0x2f, 0xe5, 0xa3, 0xe0, 0x00, 0xdc, 0x00, 0xb3, 0x5d, 0x00, 0x26, 0x1b, 0xca, 0x46, 0x67, 0x5a, 0xf2, 0x77, 0xb8, 0x7d, 0x86, 0xd8, 0x41, 0xdb, 0x05, 0x35, 0xcd, 0x83, 0xa5, 0x7c, 0x12, 0xa5, 0x05, 0xdb, 0x90, 0xbd, 0x2f, 0x14, 0xd3, 0x71, 0x72, 0x96, 0xa8, 0x8a, 0x7d, 0x84, 0x56, 0x71, 0x8d, 0x6a, 0x22, 0x98, 0xab, 0x9e, 0x3d, 0xc3, 0x55, 0xef, 0xcc, 0xa5, 0xc3, 0xdd, 0x5b, 0x8e, 0xbf, 0x03, 0x81, 0x21, 0x40, 0xd6, 0x26, 0x91, 0x02, 0x45, 0x4e, 0x20, 0x91, 0xcf, 0x8c, 0x51, 0x22, 0x02, 0x70, 0xba, 0x05, 0x6b, 0x83, 0xef, 0x3f, 0x8e, 0x09, 0xef, 0x88, 0xf5, 0x37, 0x1b, 0x89, 0x8d, 0xff, 0x1e, 0xee, 0xe8, 0xb0, 0xac, 0xf2, 0x6e, 0xd4, 0x3e, 0x25, 0xaf, 0xa0, 0x6d, 0x2e, 0xc0, 0x7f, 0xb5, 0xa0, 0xcb, 0x90, 0x1f, 0x08, 0x1a, 0xe2, 0x90, 0x20, 0x19, 0x71, 0x0c, 0xe8, 0x3f, 0xe5, 0x39, 0xeb, 0x9a, 0x62, 0x4f, 0x06, 0xda, 0x3c, 0x32, 0x59, 0xcc, 0x83, 0xe3, 0x83, 0x0f, 0x38, 0x7d, 0x43, 0x37, 0x6c, 0x0b, 0x05, 0x65, 0x98, 0x25, 0xdb, 0xf2, 0xc0, 0x2d, 0x39, 0x36, 0x5d, 0xd4, 0xb6, 0xc2, 0x79, 0x73, 0x3e, 0xc2, 0x6e, 0x54, 0xec, 0x78, 0x2b, 0x5d, 0xf1, 0xd1, 0xb4, 0xb3, 0xcd, 0xf3, 0x89, 0xf5, 0x80, 0x79, 0x46, 0xc0, 0x00, 0x00, 0x00, 0xc4, 0xf5, 0x1d, 0x08, 0xf0, 0x34, 0x3a, 0x59, 0x00, 0x01, 0xcf, 0x01, 0xdd, 0x01, 0x00, 0x00, 0x7f, 0x5a, 0x77, 0xcb, 0xb1, 0xc4, 0x67, 0xfb, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x59, 0x5a, 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x04, 0xe6, 0xd6, 0xb4, 0x46, 0x02, 0x00, 0x21, 0x01, 0x16, 0x00, 0x00, 0x00, 0x74, 0x2f, 0xe5, 0xa3, 0xe0, 0x00, 0xe0, 0x00, 0xb7, 0x5d, 0x00, 0x31, 0x9b, 0xca, 0x19, 0xc5, 0x54, 0xec, 0xb6, 0x54, 0xe7, 0xb1, 0x7d, 0xc4, 0x57, 0x9e, 0x6c, 0x89, 0xad, 0x4a, 0x6d, 0x16, 0xd8, 0x3c, 0x05, 0x94, 0x10, 0x16, 0x99, 0x38, 0x21, 0xa3, 0xb9, 0xc5, 0x80, 0xff, 0xfc, 0xee, 0xd4, 0xd5, 0x3f, 0xdd, 0x8c, 0xd7, 0x3d, 0x8f, 0x76, 0xec, 0x96, 0x9d, 0x20, 0xac, 0xcb, 0x18, 0xf5, 0xb2, 0x9c, 0x12, 0xf6, 0x7c, 0x33, 0xdc, 0x4f, 0x9a, 0xe5, 0x2d, 0x63, 0x68, 0xa4, 0x2b, 0x1d, 0x0a, 0x1e, 0xf0, 0xfe, 0x73, 0xf2, 0x5f, 0x7b, 0xb4, 0xea, 0x54, 0xad, 0x27, 0xd1, 0xff, 0xb6, 0x50, 0x06, 0x7b, 0x51, 0x3f, 0x25, 0x8a, 0xcf, 0x4c, 0x03, 0x3e, 0xc3, 0xad, 0x47, 0x34, 0xcf, 0xba, 0x45, 0x79, 0xd0, 0x7b, 0xf6, 0x66, 0x63, 0xc0, 0xc6, 0x69, 0xa7, 0x51, 0x84, 0xa8, 0xa0, 0x0b, 0xbc, 0x6f, 0x13, 0x89, 0xd6, 0x5e, 0xac, 0xca, 0x2f, 0xd2, 0xe7, 0xe1, 0x1e, 0x78, 0x22, 0x3a, 0x59, 0x6c, 0x9c, 0x8c, 0x65, 0xf1, 0x5b, 0xf4, 0xbf, 0xd5, 0xdc, 0x05, 0xeb, 0x70, 0x10, 0xb8, 0x6c, 0xf2, 0x13, 0x20, 0xb0, 0xdd, 0x3e, 0xb2, 0x92, 0x5b, 0xa3, 0xf7, 0x94, 0xa1, 0xa1, 0x74, 0x36, 0x9a, 0xf1, 0xd8, 0xc2, 0xf0, 0xc6, 0x29, 0x7e, 0x85, 0x28, 0xf5, 0xf2, 0x21, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x80, 0x67, 0x40, 0xc3, 0xaa, 0x17, 0x57, 0x00, 0x01, 0xd3, 0x01, 0xe1, 0x01, 0x00, 0x00, 0x86, 0xdf, 0x9e, 0x05, 0xb1, 0xc4, 0x67, 0xfb, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x59, 0x5a #elif defined(TEST_GZIP) 0x1f, 0x8b, 0x08, 0x08, 0x82, 0xd4, 0x97, 0x60, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x35, 0x90, 0xc1, 0x71, 0x43, 0x31, 0x08, 0x44, 0xef, 0xbf, 0x8a, 0x2d, 0x20, 0xf3, 0xab, 0x48, 0x6e, 0xb9, 0xa6, 0x00, 0x82, 0xb0, 0xc3, 0x8c, 0x24, 0x64, 0x09, 0x3c, 0x2e, 0x3f, 0xc8, 0x4e, 0x6e, 0x42, 0xc0, 0xb2, 0xfb, 0x3e, 0x6d, 0x4a, 0x83, 0x8e, 0x15, 0x0d, 0xc5, 0xaa, 0x4d, 0x2c, 0x75, 0x50, 0x13, 0x7f, 0x03, 0x5b, 0x5f, 0xc2, 0x2e, 0x1e, 0x13, 0x54, 0x74, 0xe8, 0x62, 0xed, 0x57, 0x48, 0xd5, 0x6c, 0x2e, 0x29, 0xb9, 0x00, 0xd1, 0x58, 0xcd, 0xca, 0xe1, 0xd2, 0x46, 0x2e, 0x6b, 0x67, 0x2d, 0x5a, 0xa2, 0x3b, 0xc2, 0x51, 0xe9, 0x3b, 0xe5, 0x21, 0xfe, 0x92, 0x16, 0x34, 0xba, 0x76, 0x02, 0x55, 0xbd, 0x05, 0x9d, 0xf8, 0x72, 0x48, 0xd7, 0x96, 0xda, 0x68, 0xba, 0x1f, 0xf7, 0x2c, 0xa9, 0xbd, 0x1d, 0xb7, 0xd0, 0x85, 0x6e, 0xcb, 0x67, 0x14, 0xc8, 0x43, 0x26, 0xab, 0x93, 0xab, 0x75, 0x44, 0xad, 0xd4, 0xd8, 0x5e, 0xca, 0x7b, 0x48, 0x97, 0xee, 0x4b, 0x4f, 0x49, 0x1d, 0x39, 0x0c, 0xa1, 0x34, 0xde, 0xd2, 0x93, 0x1d, 0xcf, 0x00, 0x79, 0xca, 0x4f, 0xbc, 0x6f, 0x49, 0x0a, 0x17, 0xe8, 0x8c, 0x74, 0xf2, 0xca, 0xaa, 0x1d, 0x53, 0xc6, 0x94, 0x1f, 0xe9, 0x45, 0x66, 0x06, 0xcf, 0x8f, 0xbb, 0xd5, 0x18, 0x79, 0x4e, 0xd2, 0x4e, 0x26, 0x85, 0xac, 0x25, 0x07, 0x6b, 0xad, 0xff, 0x84, 0x32, 0x50, 0xe0, 0x12, 0x57, 0x25, 0x47, 0xdf, 0x86, 0x30, 0x68, 0x66, 0x11, 0xf3, 0xc4, 0xc7, 0x83, 0x65, 0xb8, 0xc4, 0xc6, 0x98, 0x0c, 0x8c, 0x99, 0x84, 0x73, 0x8e, 0x63, 0x68, 0x21, 0xdf, 0x1b, 0xd6, 0x8f, 0x31, 0x4d, 0x8b, 0xf4, 0x4d, 0x71, 0x93, 0xca, 0xa3, 0x1c, 0x75, 0x10, 0x32, 0x02, 0xec, 0x72, 0x51, 0x56, 0x42, 0x91, 0x25, 0x73, 0x77, 0x9b, 0xd5, 0x6d, 0x83, 0x36, 0x20, 0x4d, 0x1c, 0xeb, 0x8f, 0x6b, 0xb4, 0xf3, 0xf8, 0x05, 0x6b, 0x8b, 0x8b, 0x20, 0xbe, 0x01, 0x00, 0x00 #elif defined(TEST_ZSTD) 0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x88, 0xa5, 0x08, 0x00, 0x46, 0x97, 0x3a, 0x1a, 0x80, 0x37, 0xcd, 0x01, 0xc0, 0x8a, 0xec, 0xfe, 0x2d, 0xf2, 0xb9, 0x44, 0x6b, 0xb9, 0x24, 0x77, 0x56, 0x5a, 0x33, 0x17, 0x0b, 0x67, 0x83, 0x2e, 0x47, 0x07, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0xc5, 0x2c, 0x5a, 0x92, 0x93, 0x0f, 0x7b, 0xd1, 0x1d, 0x63, 0x2c, 0xc8, 0x99, 0x94, 0x77, 0x8f, 0x94, 0x38, 0x75, 0x80, 0x2f, 0xae, 0xc1, 0x3e, 0xd2, 0xcf, 0x49, 0x15, 0x25, 0x1a, 0x87, 0x93, 0xdd, 0xe8, 0x00, 0x6d, 0xaa, 0xf8, 0x54, 0x74, 0xe5, 0x48, 0x4d, 0xa6, 0xf3, 0x1a, 0xa3, 0x13, 0x08, 0xe5, 0x26, 0xdc, 0x73, 0xcc, 0x3e, 0xfd, 0x86, 0xa9, 0x52, 0xb2, 0x76, 0xc7, 0xc2, 0x0f, 0xe4, 0x84, 0x4b, 0x12, 0x61, 0x3a, 0x6b, 0x7a, 0x1e, 0x8a, 0x81, 0xa9, 0x9b, 0x11, 0x37, 0x25, 0x55, 0x73, 0x73, 0x71, 0xa0, 0x84, 0xca, 0xc3, 0x4b, 0xb5, 0xcc, 0x50, 0xa6, 0x46, 0xd7, 0xe8, 0x08, 0xaa, 0x04, 0x28, 0xb1, 0x8e, 0xea, 0xb4, 0x4a, 0x49, 0x2b, 0xd6, 0x0d, 0x59, 0x68, 0xda, 0x64, 0x29, 0x1f, 0x85, 0x53, 0x72, 0xf1, 0xc5, 0x88, 0x1a, 0x0b, 0x4f, 0x96, 0x43, 0xe0, 0x91, 0x89, 0xb9, 0xc0, 0xe8, 0x18, 0xd5, 0x6e, 0x94, 0xe8, 0x35, 0x66, 0x01, 0x94, 0x80, 0x95, 0x87, 0xe2, 0xc8, 0x19, 0x73, 0xa3, 0x01, 0x05, 0xc1, 0x64, 0x72, 0xc9, 0x6b, 0x6e, 0x55, 0x7c, 0x29, 0x67, 0x90, 0x93, 0x49, 0xeb, 0xe3, 0x85, 0xc2, 0xf5, 0x79, 0x68, 0x9d, 0x92, 0xc3, 0x32, 0x75, 0x80, 0x66, 0xf2, 0x43, 0xa7, 0xb0, 0xc3, 0x22, 0x3f, 0x39, 0x8a, 0x35, 0x5c, 0x63, 0x5c, 0xd1, 0x9e, 0x8a, 0xd2, 0x78, 0x3c, 0x12, 0x01, 0x25, 0x04, 0x0e, 0x08, 0x10, 0x88, 0xb6, 0x1b, 0xb7, 0x96, 0x35, 0xa8, 0x0d, 0x1e, 0xae, 0xac, 0x4a, 0x70, 0xa5, 0x31, 0xd0, 0x0c, 0x78, 0xbf, 0xdd, 0xc5, 0x24, 0x3e, 0xcb, 0x0a, 0x0a, 0x69, 0x40, 0xba, 0xb0, 0xc4, 0x2a, 0x9b, 0x1e, 0x0a, 0x51, 0xa6, 0x16, 0x98, 0x76 #elif defined(TEST_ZSTD2) 0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x58, 0x75, 0x04, 0x00, 0xb2, 0x4c, 0x20, 0x17, 0xa0, 0x25, 0x69, 0x03, 0xf0, 0xb2, 0x37, 0xb1, 0x5e, 0xb9, 0x24, 0x56, 0x5b, 0x52, 0x22, 0x39, 0x01, 0x44, 0x2b, 0x03, 0x55, 0xe3, 0x47, 0x03, 0x12, 0x9a, 0xe1, 0xf0, 0x94, 0x0b, 0xe5, 0xe2, 0xba, 0x7e, 0xfe, 0x9c, 0xc7, 0x61, 0x43, 0xc8, 0xfa, 0xf0, 0x3a, 0xfa, 0x51, 0xaa, 0x50, 0xa6, 0x2d, 0x9a, 0x78, 0xce, 0x2f, 0x61, 0x20, 0x6c, 0x7e, 0x35, 0x60, 0xfb, 0xdd, 0x4c, 0x63, 0xfb, 0x95, 0x35, 0xc0, 0x82, 0x59, 0xc2, 0xc9, 0x78, 0x6e, 0x30, 0xe6, 0xd2, 0x72, 0x15, 0x14, 0x18, 0x62, 0x5d, 0xeb, 0x2d, 0x9d, 0x3e, 0xee, 0x2e, 0x58, 0x58, 0xe9, 0x40, 0x68, 0xb9, 0x2f, 0x23, 0x99, 0x2a, 0x4d, 0xe8, 0x49, 0x79, 0x70, 0x1f, 0xf9, 0xe2, 0x34, 0x2e, 0xab, 0xa5, 0xa3, 0xf2, 0x70, 0x98, 0xd0, 0xb2, 0xb1, 0x3e, 0x5d, 0x90, 0x20, 0xd9, 0x36, 0x8b, 0xdb, 0xaa, 0x20, 0x40, 0x03, 0x14, 0x06, 0x03, 0x16, 0x2a, 0x9d, 0x31, 0xbd, 0x28, 0x3b, 0x0c, 0xac, 0x41, 0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x58, 0xbd, 0x04, 0x00, 0x62, 0xcd, 0x22, 0x19, 0xa0, 0x25, 0x69, 0x03, 0x60, 0x72, 0xc9, 0x36, 0xda, 0xd2, 0x8b, 0xfc, 0xbf, 0x25, 0x42, 0xa9, 0x82, 0x38, 0x70, 0x1a, 0x2e, 0x54, 0x95, 0x33, 0x02, 0x03, 0x51, 0x36, 0x51, 0x80, 0xcc, 0x7a, 0x6e, 0x52, 0x2e, 0x75, 0x64, 0x2d, 0x33, 0x2c, 0xd6, 0xdb, 0xfc, 0x39, 0x31, 0xd5, 0xa8, 0xa2, 0x40, 0xd7, 0x12, 0x4c, 0xc6, 0x76, 0xdc, 0x1e, 0x0f, 0xf4, 0x4e, 0x0a, 0xd3, 0x0c, 0x87, 0x67, 0x25, 0x25, 0x52, 0x66, 0x87, 0x95, 0xc6, 0x69, 0x0c, 0xb4, 0x5e, 0x1d, 0xe7, 0x5e, 0xcd, 0x47, 0x41, 0x80, 0x89, 0x5c, 0xa5, 0x4a, 0x32, 0x26, 0xb3, 0x3d, 0x2b, 0xd5, 0xc0, 0x16, 0xde, 0xfb, 0x65, 0xcd, 0x6a, 0x0c, 0x3f, 0xe7, 0xd6, 0xb2, 0x17, 0x7c, 0x25, 0x35, 0x6b, 0x58, 0xf0, 0x95, 0xb5, 0xf2, 0xe4, 0x4e, 0xf0, 0x34, 0x4f, 0x5f, 0x39, 0xd1, 0x90, 0xf8, 0xb9, 0x59, 0xbe, 0x2e, 0xf9, 0xd4, 0x02, 0x98, 0x50, 0x5a, 0xc2, 0xcf, 0xe1, 0x08, 0x02, 0x00, 0x0f, 0x1e, 0x44, 0x40, 0x79, 0x50, 0x67, 0x3d, 0xd3, 0x35, 0x8f #endif }; static const char orig[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n" "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n" "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n" "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n" "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\n" "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; #if defined(TEST_BZIP2) || defined(TEST_BZIP22) #define COMP_NAME "bzip2" #define COMP_ID FSTREAM_COMPRESSOR_BZIP2 #elif defined(TEST_XZ) || defined(TEST_XZ2) #define COMP_NAME "xz" #define COMP_ID FSTREAM_COMPRESSOR_XZ #elif defined(TEST_GZIP) #define COMP_NAME "gzip" #define COMP_ID FSTREAM_COMPRESSOR_GZIP #elif defined(TEST_ZSTD) || defined(TEST_ZSTD2) #define COMP_NAME "zstd" #define COMP_ID FSTREAM_COMPRESSOR_ZSTD #endif static void destroy_noop(sqfs_object_t *obj) { (void)obj; } static int precache_noop(istream_t *strm) { (void)strm; return 0; } static const char *get_filename(istream_t *strm) { (void)strm; return "memstream"; } static istream_t memstream = { .base = { .destroy = destroy_noop, }, .buffer_used = sizeof(data_in) / sizeof(data_in[0]), .buffer_offset = 0, .eof = true, .buffer = data_in, .precache = precache_noop, .get_filename = get_filename, }; int main(void) { char buffer[2 * (sizeof(orig) / sizeof(orig[0]))]; const char *name; istream_t *xfrm; size_t orig_sz; int ret; /* XXX: null terminator not included in the compressed blob */ orig_sz = (sizeof(orig) / sizeof(orig[0])) - 1; /* generic API test */ TEST_ASSERT(fstream_compressor_exists(COMP_ID)); name = fstream_compressor_name_from_id(COMP_ID); TEST_STR_EQUAL(name, COMP_NAME); ret = fstream_compressor_id_from_name(name); TEST_EQUAL_I(ret, COMP_ID); ret = istream_detect_compressor(&memstream, NULL); TEST_EQUAL_I(ret, COMP_ID); /* decoder test */ xfrm = istream_compressor_create(&memstream, COMP_ID); TEST_NOT_NULL(xfrm); name = istream_get_filename(xfrm); TEST_STR_EQUAL(name, "memstream"); ret = istream_read(xfrm, buffer, sizeof(buffer)); TEST_ASSERT(ret > 0); TEST_EQUAL_UI((size_t)ret, orig_sz); ret = memcmp(buffer, orig, ret); TEST_EQUAL_I(ret, 0); ret = istream_read(xfrm, buffer, sizeof(buffer)); TEST_EQUAL_I(ret, 0); /* cleanup */ sqfs_destroy(xfrm); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/000077500000000000000000000000001410627516300202375ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libfstree/Makemodule.am000066400000000000000000000062351410627516300226470ustar00rootroot00000000000000FSTDATADIR=$(top_srcdir)/tests/libfstree test_canonicalize_name_SOURCES = tests/libfstree/canonicalize_name.c test_canonicalize_name_SOURCES += tests/test.h test_canonicalize_name_LDADD = libfstree.a test_mknode_simple_SOURCES = tests/libfstree/mknode_simple.c tests/test.h test_mknode_simple_LDADD = libfstree.a libcompat.a test_mknode_slink_SOURCES = tests/libfstree/mknode_slink.c tests/test.h test_mknode_slink_LDADD = libfstree.a libcompat.a test_mknode_reg_SOURCES = tests/libfstree/mknode_reg.c tests/test.h test_mknode_reg_LDADD = libfstree.a libcompat.a test_mknode_dir_SOURCES = tests/libfstree/mknode_dir.c tests/test.h test_mknode_dir_LDADD = libfstree.a libcompat.a test_gen_inode_numbers_SOURCES = tests/libfstree/gen_inode_numbers.c test_gen_inode_numbers_SOURCES += tests/test.h test_gen_inode_numbers_LDADD = libfstree.a libcompat.a test_add_by_path_SOURCES = tests/libfstree/add_by_path.c tests/test.h test_add_by_path_LDADD = libfstree.a libcompat.a test_get_path_SOURCES = tests/libfstree/get_path.c tests/test.h test_get_path_LDADD = libfstree.a libcompat.a test_fstree_sort_SOURCES = tests/libfstree/fstree_sort.c tests/test.h test_fstree_sort_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/lib/fstree test_fstree_sort_LDADD = libfstree.a libfstream.a libcompat.a test_fstree_from_file_SOURCES = tests/libfstree/fstree_from_file.c tests/test.h test_fstree_from_file_CPPFLAGS = $(AM_CPPFLAGS) test_fstree_from_file_CPPFLAGS += -DTESTPATH=$(FSTDATADIR)/fstree1.txt test_fstree_from_file_LDADD = libfstree.a libfstream.a libcompat.a test_fstree_glob1_SOURCES = tests/libfstree/fstree_glob1.c tests/test.h test_fstree_glob1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(FSTDATADIR) test_fstree_glob1_LDADD = libfstree.a libfstream.a libcompat.a test_fstree_from_dir_SOURCES = tests/libfstree/fstree_from_dir.c tests/test.h test_fstree_from_dir_CPPFLAGS = $(AM_CPPFLAGS) test_fstree_from_dir_CPPFLAGS += -DTESTPATH=$(top_srcdir)/tests/libtar/data test_fstree_from_dir_LDADD = libfstree.a libcompat.a test_fstree_init_SOURCES = tests/libfstree/fstree_init.c tests/test.h test_fstree_init_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/lib/fstree test_fstree_init_LDADD = libfstree.a libfstream.a libcompat.a test_filename_sane_SOURCES = tests/libfstree/filename_sane.c test_filename_sane_SOURCES += lib/fstree/filename_sane.c test_filename_sane_w32_SOURCES = tests/libfstree/filename_sane.c test_filename_sane_w32_SOURCES += lib/fstree/filename_sane.c test_filename_sane_w32_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_WIN32=1 fstree_fuzz_SOURCES = tests/libfstree/fstree_fuzz.c fstree_fuzz_LDADD = libfstree.a libfstream.a libcompat.a FSTREE_TESTS = \ test_canonicalize_name test_mknode_simple test_mknode_slink \ test_mknode_reg test_mknode_dir test_gen_inode_numbers \ test_add_by_path test_get_path test_fstree_sort test_fstree_from_file \ test_fstree_init test_filename_sane test_filename_sane_w32 \ test_fstree_from_dir test_fstree_glob1 if BUILD_TOOLS check_PROGRAMS += $(FSTREE_TESTS) noinst_PROGRAMS += fstree_fuzz TESTS += $(FSTREE_TESTS) endif EXTRA_DIST += $(FSTDATADIR)/fstree1.txt EXTRA_DIST += $(FSTDATADIR)/fstree_glob1.txt $(FSTDATADIR)/fstree_glob2.txt EXTRA_DIST += $(FSTDATADIR)/fstree_glob3.txt squashfs-tools-ng-1.1.3/tests/libfstree/add_by_path.c000066400000000000000000000070141410627516300226430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * add_by_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *a, *b; struct stat sb; fstree_t fs; char *opts; opts = strdup("mode=0755,uid=21,gid=42"); TEST_ASSERT(fstree_init(&fs, opts) == 0); free(opts); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0750; sb.st_uid = 1000; sb.st_gid = 100; TEST_EQUAL_UI(fs.root->link_count, 2); a = fstree_add_generic(&fs, "dir", &sb, NULL); TEST_NOT_NULL(a); TEST_STR_EQUAL(a->name, "dir"); TEST_EQUAL_UI(a->mode, sb.st_mode); TEST_EQUAL_UI(a->uid, sb.st_uid); TEST_EQUAL_UI(a->gid, sb.st_gid); TEST_ASSERT(a->parent == fs.root); TEST_EQUAL_UI(a->link_count, 2); TEST_NULL(a->next); TEST_ASSERT(fs.root->data.dir.children == a); TEST_EQUAL_UI(fs.root->link_count, 3); TEST_ASSERT(!a->data.dir.created_implicitly); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFBLK | 0640; sb.st_rdev = 1234; b = fstree_add_generic(&fs, "blkdev", &sb, NULL); TEST_NOT_NULL(b); TEST_ASSERT(b != a); TEST_STR_EQUAL(b->name, "blkdev"); TEST_EQUAL_UI(b->mode, sb.st_mode); TEST_EQUAL_UI(b->uid, sb.st_uid); TEST_EQUAL_UI(b->gid, sb.st_gid); TEST_ASSERT(b->parent == fs.root); TEST_EQUAL_UI(b->link_count, 1); TEST_EQUAL_UI(b->data.devno, sb.st_rdev); TEST_ASSERT(b->next == a); TEST_EQUAL_UI(fs.root->link_count, 4); TEST_ASSERT(fs.root->data.dir.children == b); TEST_NULL(fstree_add_generic(&fs, "blkdev/foo", &sb, NULL)); TEST_EQUAL_UI(errno, ENOTDIR); TEST_NULL(fstree_add_generic(&fs, "dir", &sb, NULL)); TEST_EQUAL_UI(errno, EEXIST); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0755; TEST_NULL(fstree_add_generic(&fs, "dir", &sb, NULL)); TEST_EQUAL_UI(errno, EEXIST); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFCHR | 0444; b = fstree_add_generic(&fs, "dir/chrdev", &sb, NULL); TEST_NOT_NULL(b); TEST_EQUAL_UI(b->mode, sb.st_mode); TEST_EQUAL_UI(b->uid, sb.st_uid); TEST_EQUAL_UI(b->gid, sb.st_gid); TEST_EQUAL_UI(b->link_count, 1); TEST_ASSERT(b->parent == a); TEST_EQUAL_UI(b->data.devno, sb.st_rdev); TEST_NULL(b->next); TEST_ASSERT(a->data.dir.children == b); TEST_EQUAL_UI(a->link_count, 3); TEST_EQUAL_UI(fs.root->link_count, 4); b = fstree_add_generic(&fs, "dir/foo/chrdev", &sb, NULL); TEST_NOT_NULL(b); TEST_NULL(b->next); TEST_EQUAL_UI(b->mode, sb.st_mode); TEST_EQUAL_UI(b->uid, sb.st_uid); TEST_EQUAL_UI(b->gid, sb.st_gid); TEST_EQUAL_UI(b->link_count, 1); TEST_ASSERT(b->parent != a); TEST_ASSERT(b->parent->parent == a); TEST_EQUAL_UI(b->data.devno, sb.st_rdev); TEST_NULL(b->next); TEST_EQUAL_UI(a->link_count, 4); TEST_EQUAL_UI(fs.root->link_count, 4); TEST_ASSERT(a->data.dir.children != b); b = b->parent; TEST_ASSERT(b->data.dir.created_implicitly); TEST_EQUAL_UI(b->mode, S_IFDIR | 0755); TEST_EQUAL_UI(b->uid, 21); TEST_EQUAL_UI(b->gid, 42); TEST_EQUAL_UI(b->link_count, 3); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0750; sb.st_uid = 1000; sb.st_gid = 100; a = fstree_add_generic(&fs, "dir/foo", &sb, NULL); TEST_NOT_NULL(a); TEST_ASSERT(a == b); TEST_ASSERT(!a->data.dir.created_implicitly); TEST_EQUAL_UI(a->mode, sb.st_mode); TEST_EQUAL_UI(a->uid, sb.st_uid); TEST_EQUAL_UI(a->gid, sb.st_gid); TEST_EQUAL_UI(a->link_count, 3); TEST_EQUAL_UI(a->parent->link_count, 4); TEST_EQUAL_UI(fs.root->link_count, 4); TEST_NULL(fstree_add_generic(&fs, "dir/foo", &sb, NULL)); TEST_EQUAL_UI(errno, EEXIST); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/canonicalize_name.c000066400000000000000000000033051410627516300240430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * canonicalize_name.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" static const struct { const char *in; const char *out; } must_work[] = { { "", "" }, { "/", "" }, { "\\", "\\" }, { "///", "" }, { "\\\\\\", "\\\\\\" }, { "/\\//\\\\/", "\\/\\\\" }, { "foo/bar/test", "foo/bar/test" }, { "foo\\bar\\test", "foo\\bar\\test" }, { "/foo/bar/test/", "foo/bar/test" }, { "\\foo\\bar\\test\\", "\\foo\\bar\\test\\" }, { "///foo//bar//test///", "foo/bar/test" }, { "./foo/././bar/test/./.", "foo/bar/test" }, { "./foo/././", "foo" }, { ".", "" }, { "./", "" }, { "./.", "" }, { "foo/.../bar", "foo/.../bar" }, { "foo/.test/bar", "foo/.test/bar" }, }; static const char *must_not_work[] = { "..", "foo/../bar", "../foo/bar", "foo/bar/..", "foo/bar/../", }; int main(void) { char buffer[512]; size_t i; for (i = 0; i < sizeof(must_work) / sizeof(must_work[0]); ++i) { strcpy(buffer, must_work[i].in); if (canonicalize_name(buffer)) { fprintf(stderr, "Test case rejected: '%s'\n", must_work[i].in); return EXIT_FAILURE; } if (strcmp(buffer, must_work[i].out) != 0) { fprintf(stderr, "Expected result: %s\n", must_work[i].out); fprintf(stderr, "Actual result: %s\n", buffer); return EXIT_FAILURE; } } for (i = 0; i < sizeof(must_not_work) / sizeof(must_not_work[0]); ++i) { strcpy(buffer, must_not_work[i]); if (canonicalize_name(buffer) == 0) { fprintf(stderr, "Test case accepted: '%s'\n", must_not_work[i]); fprintf(stderr, "Transformed into: '%s'\n", buffer); return EXIT_FAILURE; } } return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/filename_sane.c000066400000000000000000000040471410627516300231760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * filename_sane.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" static const char *must_work[] = { "foobar", "test.txt", #if !defined(_WIN32) && !defined(__WINDOWS__) && !defined(TEST_WIN32) "\\foo", "foo\\", "foo\\bar", #endif NULL, }; static const char *must_not_work[] = { ".", "..", "/foo", "foo/", "foo/bar", NULL, }; static const char *must_not_work_here[] = { #if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) "\\foo", "foo\\", "foo\\bar", "foo", "fo:o", "fo\"o", "fo|o", "fo?o", "fo*o", "fo\ro", "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "LPT1", "LPT2", "con", "prn", "aux", "nul", "com1", "com2", "lpt1", "lpt2", "AUX.txt", "aux.txt", "NUL.txt", "nul.txt", #endif NULL, }; int main(void) { size_t i; for (i = 0; must_work[i] != NULL; ++i) { if (!is_filename_sane(must_work[i], false)) { fprintf(stderr, "%s was rejected!\n", must_work[i]); return EXIT_FAILURE; } if (!is_filename_sane(must_work[i], true)) { fprintf(stderr, "%s was rejected when testing for " "OS specific stuff!\n", must_work[i]); return EXIT_FAILURE; } } for (i = 0; must_not_work[i] != NULL; ++i) { if (is_filename_sane(must_not_work[i], false)) { fprintf(stderr, "%s was accepted!\n", must_not_work[i]); return EXIT_FAILURE; } if (is_filename_sane(must_not_work[i], true)) { fprintf(stderr, "%s was accepted when testing for " "OS specific stuff!\n", must_not_work[i]); return EXIT_FAILURE; } } for (i = 0; must_not_work_here[i] != NULL; ++i) { if (!is_filename_sane(must_not_work_here[i], false)) { fprintf(stderr, "%s was rejected in the generic test!\n", must_not_work_here[i]); return EXIT_FAILURE; } if (is_filename_sane(must_not_work_here[i], true)) { fprintf(stderr, "%s was accepted when testing for " "OS specific stuff!\n", must_not_work_here[i]); return EXIT_FAILURE; } } return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/fstree1.txt000066400000000000000000000003771410627516300223600ustar00rootroot00000000000000# comment line slink /slink 0644 2 3 slinktarget dir /dir 0755 4 5 nod /chardev 0600 6 7 c 13 37 nod /blkdev 0600 8 9 b 42 21 pipe /pipe 0644 10 11 dir / 0755 1000 100 dir "/foo bar" 0755 0 0 dir "/foo bar/ test \"/" 0755 0 0 sock /sock 0555 12 13 squashfs-tools-ng-1.1.3/tests/libfstree/fstree_from_dir.c000066400000000000000000000204351410627516300235600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" static void check_hierarchy(tree_node_t *root, bool recursive) { tree_node_t *n, *m; n = root->data.dir.children; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "CREDITS"); TEST_ASSERT(S_ISREG(n->mode)); TEST_ASSERT(n->parent == root); n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "file-size"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "12-digit.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "format-acceptance"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu-g.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "link_filled.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "ustar-pre-posix.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "ustar.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "v7.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "large-mtime"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "12-digit.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "long-paths"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "ustar.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "negative-mtime"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "sparse-files"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu-small.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax-gnu0-0.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax-gnu0-1.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax-gnu1-0.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "sqfs.sha512"); TEST_ASSERT(S_ISREG(n->mode)); TEST_ASSERT(n->parent == root); n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "user-group-largenum"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "8-digit.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "xattr"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "acl.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "xattr-libarchive.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "xattr-schily-binary.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "xattr-schily.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NULL(n); } int main(void) { struct stat sb; tree_node_t *n; fstree_t fs; /* recursively scan into root */ TEST_ASSERT(fstree_init(&fs, NULL) == 0); TEST_ASSERT(fstree_from_dir(&fs, fs.root, TEST_PATH, NULL, NULL, 0) == 0); fstree_post_process(&fs); check_hierarchy(fs.root, true); fstree_cleanup(&fs); /* non-recursively scan into root */ TEST_ASSERT(fstree_init(&fs, NULL) == 0); TEST_ASSERT(fstree_from_dir(&fs, fs.root, TEST_PATH, NULL, NULL, DIR_SCAN_NO_RECURSION) == 0); fstree_post_process(&fs); check_hierarchy(fs.root, false); fstree_cleanup(&fs); /* recursively scan into a sub-directory of root */ memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0755; TEST_ASSERT(fstree_init(&fs, NULL) == 0); n = fstree_mknode(fs.root, "foodir", 6, NULL, &sb); TEST_NOT_NULL(n); fs.root->data.dir.children = n; TEST_ASSERT(fstree_from_dir(&fs, n, TEST_PATH, NULL, NULL, 0) == 0); TEST_ASSERT(fs.root->data.dir.children == n); TEST_NULL(n->next); fstree_post_process(&fs); check_hierarchy(n, true); fstree_cleanup(&fs); /* non-recursively scan into a sub-directory of root */ memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0755; TEST_ASSERT(fstree_init(&fs, NULL) == 0); n = fstree_mknode(fs.root, "foodir", 6, NULL, &sb); TEST_NOT_NULL(n); fs.root->data.dir.children = n; TEST_ASSERT(fstree_from_dir(&fs, n, TEST_PATH, NULL, NULL, DIR_SCAN_NO_RECURSION) == 0); TEST_ASSERT(fs.root->data.dir.children == n); TEST_NULL(n->next); fstree_post_process(&fs); check_hierarchy(n, false); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/fstree_from_file.c000066400000000000000000000046401410627516300237210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *n; fstree_t fs; TEST_ASSERT(fstree_init(&fs, NULL) == 0); TEST_ASSERT(fstree_from_file(&fs, TEST_PATH, NULL) == 0); fstree_post_process(&fs); n = fs.root->data.dir.children; TEST_EQUAL_UI(fs.root->link_count, 9); TEST_EQUAL_UI(fs.root->mode, S_IFDIR | 0755); TEST_EQUAL_UI(fs.root->uid, 1000); TEST_EQUAL_UI(fs.root->gid, 100); TEST_EQUAL_UI(n->mode, S_IFBLK | 0600); TEST_EQUAL_UI(n->uid, 8); TEST_EQUAL_UI(n->gid, 9); TEST_EQUAL_UI(n->link_count, 1); TEST_STR_EQUAL(n->name, "blkdev"); TEST_EQUAL_UI(n->data.devno, makedev(42, 21)); n = n->next; TEST_EQUAL_UI(n->mode, S_IFCHR | 0600); TEST_EQUAL_UI(n->uid, 6); TEST_EQUAL_UI(n->gid, 7); TEST_EQUAL_UI(n->link_count, 1); TEST_STR_EQUAL(n->name, "chardev"); TEST_EQUAL_UI(n->data.devno, makedev(13, 37)); n = n->next; TEST_EQUAL_UI(n->mode, S_IFDIR | 0755); TEST_EQUAL_UI(n->uid, 4); TEST_EQUAL_UI(n->gid, 5); TEST_EQUAL_UI(n->link_count, 2); TEST_STR_EQUAL(n->name, "dir"); TEST_NULL(n->data.dir.children); n = n->next; TEST_EQUAL_UI(n->mode, S_IFDIR | 0755); TEST_EQUAL_UI(n->uid, 0); TEST_EQUAL_UI(n->gid, 0); TEST_EQUAL_UI(n->link_count, 3); TEST_STR_EQUAL(n->name, "foo bar"); TEST_NOT_NULL(n->data.dir.children); TEST_NULL(n->data.dir.children->next); TEST_EQUAL_UI(n->data.dir.children->mode, S_IFDIR | 0755); TEST_EQUAL_UI(n->data.dir.children->uid, 0); TEST_EQUAL_UI(n->data.dir.children->gid, 0); TEST_EQUAL_UI(n->data.dir.children->link_count, 2); TEST_STR_EQUAL(n->data.dir.children->name, " test \""); TEST_NULL(n->data.dir.children->data.dir.children); n = n->next; TEST_EQUAL_UI(n->mode, S_IFIFO | 0644); TEST_EQUAL_UI(n->uid, 10); TEST_EQUAL_UI(n->gid, 11); TEST_EQUAL_UI(n->link_count, 1); TEST_STR_EQUAL(n->name, "pipe"); n = n->next; TEST_EQUAL_UI(n->mode, S_IFLNK | 0777); TEST_EQUAL_UI(n->uid, 2); TEST_EQUAL_UI(n->gid, 3); TEST_EQUAL_UI(n->link_count, 1); TEST_STR_EQUAL(n->name, "slink"); TEST_STR_EQUAL(n->data.target, "slinktarget"); n = n->next; TEST_EQUAL_UI(n->mode, S_IFSOCK | 0555); TEST_EQUAL_UI(n->uid, 12); TEST_EQUAL_UI(n->gid, 13); TEST_EQUAL_UI(n->link_count, 1); TEST_STR_EQUAL(n->name, "sock"); TEST_NULL(n->next); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/fstree_fuzz.c000066400000000000000000000010751410627516300227540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_fuzz.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include int main(int argc, char **argv) { int ret = EXIT_FAILURE; fstree_t fs; if (argc != 2) { fputs("Usage: fstree_fuzz \n", stderr); return EXIT_FAILURE; } if (fstree_init(&fs, NULL)) return EXIT_FAILURE; if (fstree_from_file(&fs, argv[1], NULL)) goto out_fs; ret = EXIT_SUCCESS; out_fs: fstree_cleanup(&fs); return ret; } squashfs-tools-ng-1.1.3/tests/libfstree/fstree_glob1.c000066400000000000000000000123571410627516300227670ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_glob1.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" static void check_hierarchy(tree_node_t *root, bool subdir, bool recursive) { tree_node_t *n, *m, *parentdir; if (subdir) { n = root->data.dir.children; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "tarcorpus"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == root); TEST_NULL(n->next); } else { n = root; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, ""); TEST_ASSERT(S_ISDIR(n->mode)); TEST_NULL(n->parent); TEST_NULL(n->next); } parentdir = n; n = n->data.dir.children; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "file-size"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "format-acceptance"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu-g.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "large-mtime"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "long-paths"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "negative-mtime"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "sparse-files"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu-small.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax-gnu0-0.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax-gnu0-1.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "pax-gnu1-0.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "user-group-largenum"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); if (recursive) { m = n->data.dir.children; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "gnu.tar"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NULL(m); } else { TEST_NULL(n->data.dir.children); } n = n->next; TEST_NOT_NULL(n); TEST_STR_EQUAL(n->name, "xattr"); TEST_ASSERT(S_ISDIR(n->mode)); TEST_ASSERT(n->parent == parentdir); TEST_NULL(n->data.dir.children); n = n->next; TEST_NULL(n); } int main(void) { fstree_t fs; int ret; /* first test case, directory tree only */ ret = fstree_init(&fs, NULL); TEST_EQUAL_I(ret, 0); ret = fstree_from_file(&fs, TEST_PATH "/fstree_glob1.txt", TEST_PATH); TEST_EQUAL_I(ret, 0); fstree_post_process(&fs); check_hierarchy(fs.root, true, false); fstree_cleanup(&fs); /* first test case, directory tree plus fnmatch()ed files */ ret = fstree_init(&fs, NULL); TEST_EQUAL_I(ret, 0); ret = fstree_from_file(&fs, TEST_PATH "/fstree_glob2.txt", TEST_PATH); TEST_EQUAL_I(ret, 0); fstree_post_process(&fs); check_hierarchy(fs.root, true, true); fstree_cleanup(&fs); /* third test case, same as second, but entries directly at the root */ ret = fstree_init(&fs, NULL); TEST_EQUAL_I(ret, 0); ret = fstree_from_file(&fs, TEST_PATH "/fstree_glob3.txt", TEST_PATH); TEST_EQUAL_I(ret, 0); fstree_post_process(&fs); check_hierarchy(fs.root, false, true); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/fstree_glob1.txt000066400000000000000000000001131410627516300233470ustar00rootroot00000000000000dir /tarcorpus 0755 0 0 glob /tarcorpus 0755 0 0 -type d -- ../libtar/data squashfs-tools-ng-1.1.3/tests/libfstree/fstree_glob2.txt000066400000000000000000000002151410627516300233530ustar00rootroot00000000000000dir /tarcorpus 0755 0 0 glob /tarcorpus 0755 0 0 -type d ../libtar/data glob /tarcorpus 0644 0 0 -type f -name "*gnu*.tar" -- ../libtar/data squashfs-tools-ng-1.1.3/tests/libfstree/fstree_glob3.txt000066400000000000000000000001431410627516300233540ustar00rootroot00000000000000glob / 0755 0 0 -type d ../libtar/data glob / 0644 0 0 -type f -name "*gnu*.tar" -- ../libtar/data squashfs-tools-ng-1.1.3/tests/libfstree/fstree_init.c000066400000000000000000000022061410627516300227160ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_init.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "internal.h" #include "../test.h" int main(void) { fstree_t fs; char *str; str = strdup("mtime=1337,uid=1000,gid=100,mode=0321"); TEST_NOT_NULL(str); TEST_ASSERT(fstree_init(&fs, str) == 0); free(str); TEST_EQUAL_UI(fs.defaults.st_mtime, 1337); TEST_EQUAL_UI(fs.defaults.st_uid, 1000); TEST_EQUAL_UI(fs.defaults.st_gid, 100); TEST_EQUAL_UI(fs.defaults.st_mode, S_IFDIR | 0321); fstree_cleanup(&fs); TEST_ASSERT(fstree_init(&fs, NULL) == 0); if (fs.defaults.st_mtime != 0) { TEST_EQUAL_UI(fs.defaults.st_mtime, get_source_date_epoch()); } TEST_EQUAL_UI(fs.defaults.st_uid, 0); TEST_EQUAL_UI(fs.defaults.st_gid, 0); TEST_EQUAL_UI(fs.defaults.st_mode, S_IFDIR | 0755); fstree_cleanup(&fs); str = strdup("mode=07777"); TEST_NOT_NULL(str); TEST_ASSERT(fstree_init(&fs, str) == 0); free(str); fstree_cleanup(&fs); str = strdup("mode=017777"); TEST_NOT_NULL(str); TEST_ASSERT(fstree_init(&fs, str) != 0); free(str); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/fstree_sort.c000066400000000000000000000042341410627516300227450ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_sort.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "internal.h" #include "../test.h" int main(void) { tree_node_t *a, *b, *c, *d; struct stat sb; fstree_t fs; memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFBLK | 0600; sb.st_rdev = 1337; a = fstree_mknode(NULL, "a", 1, NULL, &sb); b = fstree_mknode(NULL, "b", 1, NULL, &sb); c = fstree_mknode(NULL, "c", 1, NULL, &sb); d = fstree_mknode(NULL, "d", 1, NULL, &sb); TEST_ASSERT(a != NULL && b != NULL && c != NULL && d != NULL); /* empty list */ TEST_NULL(tree_node_list_sort(NULL)); /* single element */ TEST_ASSERT(tree_node_list_sort(a) == a); TEST_NULL(a->next); /* two elements, reverse order */ b->next = a; TEST_ASSERT(tree_node_list_sort(b) == a); TEST_ASSERT(a->next == b); TEST_NULL(b->next); /* two elements, sorted order */ TEST_ASSERT(tree_node_list_sort(a) == a); TEST_ASSERT(a->next == b); TEST_NULL(b->next); /* three elements, reverse order */ c->next = b; b->next = a; a->next = NULL; TEST_ASSERT(tree_node_list_sort(c) == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_NULL(c->next); /* three elements, ordered */ TEST_ASSERT(tree_node_list_sort(a) == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_NULL(c->next); /* four elements, reverse order */ d->next = c; c->next = b; b->next = a; a->next = NULL; TEST_ASSERT(tree_node_list_sort(d) == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); /* four elements, sorted order */ TEST_ASSERT(tree_node_list_sort(a) == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); /* force merge sort to go through LRLR pattern */ b->next = a; a->next = d; d->next = c; c->next = NULL; TEST_ASSERT(tree_node_list_sort(b) == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); /* cleanup and done */ free(a); free(b); free(c); free(d); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/gen_inode_numbers.c000066400000000000000000000040001410627516300240570ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * gen_inode_table.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" static tree_node_t *gen_node(tree_node_t *parent, const char *name) { struct stat sb; memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0755; return fstree_mknode(parent, name, strlen(name), NULL, &sb); } static void check_children_before_root(tree_node_t *root) { tree_node_t *n; for (n = root->data.dir.children; n != NULL; n = n->next) TEST_LESS_THAN_UI(n->inode_num, root->inode_num); for (n = root->data.dir.children; n != NULL; n = n->next) check_children_before_root(n); } static void check_children_continuous(tree_node_t *root) { tree_node_t *n; for (n = root->data.dir.children; n != NULL; n = n->next) { if (n->next != NULL) { TEST_EQUAL_UI(n->next->inode_num, (n->inode_num + 1)); } } for (n = root->data.dir.children; n != NULL; n = n->next) check_children_continuous(n); } int main(void) { tree_node_t *a, *b, *c; fstree_t fs; // inode table for the empty tree TEST_ASSERT(fstree_init(&fs, NULL) == 0); fstree_post_process(&fs); TEST_EQUAL_UI(fs.unique_inode_count, 1); TEST_EQUAL_UI(fs.root->inode_num, 1); fstree_cleanup(&fs); // tree with 2 levels under root, fan out 3 TEST_ASSERT(fstree_init(&fs, NULL) == 0); a = gen_node(fs.root, "a"); b = gen_node(fs.root, "b"); c = gen_node(fs.root, "c"); TEST_NOT_NULL(a); TEST_NOT_NULL(b); TEST_NOT_NULL(c); TEST_NOT_NULL(gen_node(a, "a_a")); TEST_NOT_NULL(gen_node(a, "a_b")); TEST_NOT_NULL(gen_node(a, "a_c")); TEST_NOT_NULL(gen_node(b, "b_a")); TEST_NOT_NULL(gen_node(b, "b_b")); TEST_NOT_NULL(gen_node(b, "b_c")); TEST_NOT_NULL(gen_node(c, "c_a")); TEST_NOT_NULL(gen_node(c, "c_b")); TEST_NOT_NULL(gen_node(c, "c_c")); fstree_post_process(&fs); TEST_EQUAL_UI(fs.unique_inode_count, 13); check_children_before_root(fs.root); check_children_continuous(fs.root); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/get_path.c000066400000000000000000000022301410627516300221730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *a, *b, *c, *d; struct stat sb; fstree_t fs; char *str; TEST_ASSERT(fstree_init(&fs, NULL) == 0); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0750; sb.st_uid = 1000; sb.st_gid = 100; a = fstree_add_generic(&fs, "foo", &sb, NULL); b = fstree_add_generic(&fs, "foo/bar", &sb, NULL); c = fstree_add_generic(&fs, "foo/bar/baz", &sb, NULL); d = fstree_add_generic(&fs, "foo/bar/baz/dir", &sb, NULL); str = fstree_get_path(fs.root); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/"); free(str); str = fstree_get_path(a); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/foo"); free(str); str = fstree_get_path(b); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/foo/bar"); free(str); str = fstree_get_path(c); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/foo/bar/baz"); free(str); str = fstree_get_path(d); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/foo/bar/baz/dir"); free(str); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/mknode_dir.c000066400000000000000000000027141410627516300225220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *root, *a, *b; struct stat sb; fstree_t fs; memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFDIR | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 4096; root = fstree_mknode(NULL, "rootdir", 7, NULL, &sb); TEST_EQUAL_UI(root->uid, sb.st_uid); TEST_EQUAL_UI(root->gid, sb.st_gid); TEST_EQUAL_UI(root->mode, sb.st_mode); TEST_EQUAL_UI(root->link_count, 2); TEST_ASSERT(root->name >= (char *)root->payload); TEST_STR_EQUAL(root->name, "rootdir"); TEST_NULL(root->data.dir.children); TEST_NULL(root->parent); TEST_NULL(root->next); a = fstree_mknode(root, "adir", 4, NULL, &sb); TEST_ASSERT(a->parent == root); TEST_NULL(a->next); TEST_EQUAL_UI(a->link_count, 2); TEST_EQUAL_UI(root->link_count, 3); TEST_ASSERT(root->data.dir.children == a); TEST_NULL(root->parent); TEST_NULL(root->next); b = fstree_mknode(root, "bdir", 4, NULL, &sb); TEST_ASSERT(a->parent == root); TEST_ASSERT(b->parent == root); TEST_EQUAL_UI(b->link_count, 2); TEST_ASSERT(root->data.dir.children == b); TEST_EQUAL_UI(root->link_count, 4); TEST_ASSERT(b->next == a); TEST_NULL(a->next); TEST_NULL(root->parent); TEST_NULL(root->next); free(root); free(a); free(b); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/mknode_reg.c000066400000000000000000000017401410627516300225170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_reg.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *node; struct stat sb; fstree_t fs; memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFREG | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 4096; node = fstree_mknode(NULL, "filename", 8, "input", &sb); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, sb.st_mode); TEST_NULL(node->parent); TEST_EQUAL_UI(node->link_count, 1); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_ASSERT(node->data.file.input_file >= (char *)node->payload); TEST_ASSERT(node->data.file.input_file >= node->name + 8); TEST_STR_EQUAL(node->name, "filename"); TEST_STR_EQUAL(node->data.file.input_file, "input"); free(node); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/mknode_simple.c000066400000000000000000000050011410627516300232250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_simple.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *node; struct stat sb; fstree_t fs; memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFSOCK | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 1337; node = fstree_mknode(NULL, "sockfile", 8, NULL, &sb); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_STR_EQUAL(node->name, "sockfile"); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, sb.st_mode); TEST_EQUAL_UI(node->link_count, 1); TEST_NULL(node->parent); TEST_NULL(node->data.target); TEST_EQUAL_UI(node->data.devno, 0); free(node); memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFIFO | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 1337; node = fstree_mknode(NULL, "fifo", 4, NULL, &sb); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_STR_EQUAL(node->name, "fifo"); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, sb.st_mode); TEST_EQUAL_UI(node->link_count, 1); TEST_NULL(node->parent); TEST_NULL(node->data.target); TEST_EQUAL_UI(node->data.devno, 0); free(node); memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFBLK | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 1337; node = fstree_mknode(NULL, "blkdev", 6, NULL, &sb); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_STR_EQUAL(node->name, "blkdev"); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, sb.st_mode); TEST_EQUAL_UI(node->link_count, 1); TEST_EQUAL_UI(node->data.devno, sb.st_rdev); TEST_NULL(node->parent); free(node); memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFCHR | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 1337; node = fstree_mknode(NULL, "chardev", 7, NULL, &sb); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_STR_EQUAL(node->name, "chardev"); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, sb.st_mode); TEST_EQUAL_UI(node->link_count, 1); TEST_EQUAL_UI(node->data.devno, sb.st_rdev); TEST_NULL(node->parent); free(node); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libfstree/mknode_slink.c000066400000000000000000000026731410627516300230700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_slink.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "../test.h" int main(void) { tree_node_t *node; struct stat sb; fstree_t fs; memset(&fs, 0, sizeof(fs)); memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFLNK | 0654; sb.st_uid = 123; sb.st_gid = 456; sb.st_rdev = 789; sb.st_size = 1337; node = fstree_mknode(NULL, "symlink", 7, "target", &sb); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, S_IFLNK | 0777); TEST_EQUAL_UI(node->link_count, 1); TEST_NULL(node->parent); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_ASSERT(node->data.target >= (char *)node->payload); TEST_ASSERT(node->data.target >= node->name + 8); TEST_STR_EQUAL(node->name, "symlink"); TEST_STR_EQUAL(node->data.target, "target"); free(node); node = fstree_mknode(NULL, "symlink", 7, "", &sb); TEST_EQUAL_UI(node->uid, sb.st_uid); TEST_EQUAL_UI(node->gid, sb.st_gid); TEST_EQUAL_UI(node->mode, S_IFLNK | 0777); TEST_EQUAL_UI(node->link_count, 1); TEST_NULL(node->parent); TEST_ASSERT((char *)node->name >= (char *)node->payload); TEST_ASSERT(node->data.target >= (char *)node->payload); TEST_ASSERT(node->data.target >= node->name + 8); TEST_STR_EQUAL(node->name, "symlink"); TEST_STR_EQUAL(node->data.target, ""); free(node); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libsqfs/000077500000000000000000000000001410627516300177235ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libsqfs/Makemodule.am000066400000000000000000000011121410627516300223200ustar00rootroot00000000000000test_abi_SOURCES = tests/libsqfs/abi.c tests/test.h test_abi_LDADD = libsquashfs.la test_table_SOURCES = tests/libsqfs/table.c tests/test.h test_table_LDADD = libsquashfs.la test_xattr_writer_SOURCES = tests/libsqfs/xattr_writer.c tests/test.h test_xattr_writer_LDADD = libsquashfs.la xattr_benchmark_SOURCES = tests/libsqfs/xattr_benchmark.c xattr_benchmark_LDADD = libcommon.a libsquashfs.la libcompat.a LIBSQFS_TESTS = \ test_abi test_table test_xattr_writer if BUILD_TOOLS noinst_PROGRAMS += xattr_benchmark endif check_PROGRAMS += $(LIBSQFS_TESTS) TESTS += $(LIBSQFS_TESTS) squashfs-tools-ng-1.1.3/tests/libsqfs/abi.c000066400000000000000000000114611410627516300206250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * abi.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "sqfs/block_processor.h" #include "sqfs/compressor.h" #include "sqfs/block.h" #include "../test.h" #include static void test_compressor_opt_struct(void) { sqfs_compressor_config_t cfg; TEST_EQUAL_UI(sizeof(cfg.id), sizeof(sqfs_u16)); TEST_EQUAL_UI(sizeof(cfg.flags), sizeof(sqfs_u16)); TEST_EQUAL_UI(sizeof(cfg.block_size), sizeof(sqfs_u32)); TEST_EQUAL_UI(sizeof(cfg.level), sizeof(sqfs_u32)); TEST_EQUAL_UI(sizeof(cfg.opt), (2 * sizeof(sqfs_u64))); TEST_EQUAL_UI(sizeof(cfg.opt.gzip), sizeof(cfg.opt)); TEST_EQUAL_UI(sizeof(cfg.opt.lzo), sizeof(cfg.opt)); TEST_EQUAL_UI(sizeof(cfg.opt.xz), sizeof(cfg.opt)); TEST_EQUAL_UI(sizeof(cfg.opt.padd0), sizeof(cfg.opt)); TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, id), 0); TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, flags), sizeof(sqfs_u16)); TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, block_size), sizeof(sqfs_u32)); TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, level), (2 * sizeof(sqfs_u32))); TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, opt), (4 * sizeof(sqfs_u32))); } static const char *names[] = { [SQFS_COMP_GZIP] = "gzip", [SQFS_COMP_LZMA] = "lzma", [SQFS_COMP_LZO] = "lzo", [SQFS_COMP_XZ] = "xz", [SQFS_COMP_LZ4] = "lz4", [SQFS_COMP_ZSTD] = "zstd", }; static void test_compressor_names(void) { const char *str; int i, id; for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) { str = sqfs_compressor_name_from_id(i); TEST_STR_EQUAL(str, names[i]); id = sqfs_compressor_id_from_name(str); TEST_EQUAL_I(id, i); } } static void test_blockproc_stats(void) { sqfs_block_processor_stats_t stats; TEST_ASSERT(sizeof(stats) >= (8 * sizeof(sqfs_u64))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, size), 0); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, input_bytes_read), sizeof(sqfs_u64)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, output_bytes_generated), 2 * sizeof(sqfs_u64)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, data_block_count), 3 * sizeof(sqfs_u64)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, frag_block_count), 4 * sizeof(sqfs_u64)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, sparse_block_count), 5 * sizeof(sqfs_u64)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, total_frag_count), 6 * sizeof(sqfs_u64)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, actual_frag_count), 7 * sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.size), sizeof(size_t)); TEST_EQUAL_UI(sizeof(stats.input_bytes_read), sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.output_bytes_generated), sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.data_block_count), sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.frag_block_count), sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.sparse_block_count), sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.total_frag_count), sizeof(sqfs_u64)); TEST_EQUAL_UI(sizeof(stats.actual_frag_count), sizeof(sqfs_u64)); } static void test_blockproc_desc(void) { sqfs_block_processor_desc_t desc; TEST_ASSERT(sizeof(desc) >= (4 * sizeof(sqfs_u32) + 5 * sizeof(void *))); TEST_EQUAL_UI(sizeof(desc.size), sizeof(sqfs_u32)); TEST_EQUAL_UI(sizeof(desc.max_block_size), sizeof(sqfs_u32)); TEST_EQUAL_UI(sizeof(desc.num_workers), sizeof(sqfs_u32)); TEST_EQUAL_UI(sizeof(desc.max_backlog), sizeof(sqfs_u32)); TEST_EQUAL_UI(sizeof(desc.cmp), sizeof(void *)); TEST_EQUAL_UI(sizeof(desc.wr), sizeof(void *)); TEST_EQUAL_UI(sizeof(desc.tbl), sizeof(void *)); TEST_EQUAL_UI(sizeof(desc.file), sizeof(void *)); TEST_EQUAL_UI(sizeof(desc.uncmp), sizeof(void *)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, size), 0); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, max_block_size), sizeof(sqfs_u32)); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, num_workers), (2 * sizeof(sqfs_u32))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, max_backlog), (3 * sizeof(sqfs_u32))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, cmp), (4 * sizeof(sqfs_u32))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, wr), (4 * sizeof(sqfs_u32) + sizeof(void *))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, tbl), (4 * sizeof(sqfs_u32) + 2 * sizeof(void *))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, file), (4 * sizeof(sqfs_u32) + 3 * sizeof(void *))); TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, uncmp), (4 * sizeof(sqfs_u32) + 4 * sizeof(void *))); } int main(void) { test_compressor_opt_struct(); test_compressor_names(); test_blockproc_stats(); test_blockproc_desc(); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libsqfs/table.c000066400000000000000000000106671410627516300211700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * table.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "../test.h" #include "sqfs/compressor.h" #include "sqfs/error.h" #include "sqfs/table.h" #include "sqfs/io.h" static sqfs_u8 file_data[32768]; static size_t file_used = 0; static int dummy_read_at(sqfs_file_t *file, sqfs_u64 offset, void *buffer, size_t size) { (void)file; if (offset >= sizeof(file_data)) return SQFS_ERROR_OUT_OF_BOUNDS; if (size > (sizeof(file_data) - offset)) return SQFS_ERROR_OUT_OF_BOUNDS; memset(buffer, 0, size); if (offset < file_used) { if (size > (file_used - offset)) size = file_used - offset; memcpy(buffer, file_data + offset, size); } return 0; } static int dummy_write_at(sqfs_file_t *file, sqfs_u64 offset, const void *buffer, size_t size) { (void)file; if (offset >= sizeof(file_data)) return SQFS_ERROR_OUT_OF_BOUNDS; if (size > (sizeof(file_data) - offset)) return SQFS_ERROR_OUT_OF_BOUNDS; if (offset > file_used) memset(file_data + file_used, 0, offset - file_used); if ((offset + size) > file_used) file_used = offset + size; memcpy(file_data + offset, buffer, size); return 0; } static sqfs_u64 dummy_get_size(const sqfs_file_t *file) { (void)file; return file_used; } static sqfs_s32 dummy_compress(sqfs_compressor_t *cmp, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { (void)cmp; memcpy(out, in, outsize < size ? outsize : size); return 0; } static sqfs_s32 dummy_uncompress(sqfs_compressor_t *cmp, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { (void)cmp; if (outsize < size) return 0; memcpy(out, in, size); return size; } static sqfs_file_t dummy_file = { { NULL, NULL }, dummy_read_at, dummy_write_at, dummy_get_size, NULL, }; static sqfs_compressor_t dummy_compressor = { { NULL, NULL }, NULL, NULL, NULL, dummy_compress, }; static sqfs_compressor_t dummy_uncompressor = { { NULL, NULL }, NULL, NULL, NULL, dummy_uncompress, }; /*****************************************************************************/ static sqfs_u64 table[4000]; int main(void) { sqfs_u64 start, value, locations[4], *copy; sqfs_u16 hdr; size_t i; int ret; /* fill the table with data */ for (i = 0; i < sizeof(table) / sizeof(table[0]); ++i) table[i] = i; /* serialize the table */ ret = sqfs_write_table(&dummy_file, &dummy_compressor, table, sizeof(table), &start); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(file_used, (3 * (8192 + 2) + (7424 + 2) + 4 * sizeof(sqfs_u64))); TEST_EQUAL_UI(start, (3 * (8192 + 2) + (7424 + 2))); /* check the location list */ memcpy(locations, file_data + start, sizeof(locations)); for (i = 0; i < 4; ++i) locations[i] = le64toh(locations[i]); TEST_EQUAL_UI(locations[0], 0); TEST_EQUAL_UI(locations[1], (1 * (8192 + 2))); TEST_EQUAL_UI(locations[2], (2 * (8192 + 2))); TEST_EQUAL_UI(locations[3], (3 * (8192 + 2))); /* check the individual blocks */ memcpy(&hdr, file_data + locations[0], sizeof(hdr)); hdr = le16toh(hdr); TEST_EQUAL_UI(hdr, (0x8000 | 8192)); for (i = 0; i < 8192; i += sizeof(sqfs_u64)) { memcpy(&value, (file_data + locations[0] + 2) + i, sizeof(value)); TEST_EQUAL_UI(value, i / sizeof(sqfs_u64)); } memcpy(&hdr, file_data + locations[1], sizeof(hdr)); hdr = le16toh(hdr); TEST_EQUAL_UI(hdr, (0x8000 | 8192)); for (i = 0; i < 8192; i += sizeof(sqfs_u64)) { memcpy(&value, (file_data + locations[1] + 2) + i, sizeof(value)); TEST_EQUAL_UI(value, (1024 + i / sizeof(sqfs_u64))); } memcpy(&hdr, file_data + locations[2], sizeof(hdr)); hdr = le16toh(hdr); TEST_EQUAL_UI(hdr, (0x8000 | 8192)); for (i = 0; i < 8192; i += sizeof(sqfs_u64)) { memcpy(&value, (file_data + locations[2] + 2) + i, sizeof(value)); TEST_EQUAL_UI(value, (2048 + i / sizeof(sqfs_u64))); } memcpy(&hdr, file_data + locations[3], sizeof(hdr)); hdr = le16toh(hdr); TEST_EQUAL_UI(hdr, (0x8000 | 7424)); for (i = 0; i < 7424; i += sizeof(sqfs_u64)) { memcpy(&value, (file_data + locations[3] + 2) + i, sizeof(value)); TEST_EQUAL_UI(value, (3072 + i / sizeof(sqfs_u64))); } /* read the table back */ ret = sqfs_read_table(&dummy_file, &dummy_uncompressor, sizeof(table), start, 0, start, (void **)©); TEST_EQUAL_I(ret, 0); ret = memcmp(copy, table, sizeof(table)); TEST_EQUAL_I(ret, 0); free(copy); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libsqfs/xattr_benchmark.c000066400000000000000000000052221410627516300232440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xattr_benchmark.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "common.h" #include "sqfs/xattr_writer.h" #include "sqfs/xattr.h" #include #include #include #include static struct option long_opts[] = { { "block-count", required_argument, NULL, 'b' }, { "groups-size", required_argument, NULL, 'g' }, { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; static const char *short_opts = "g:b:hV"; static const char *help_string = "Usage: xattr_benchmark [OPTIONS...]\n" "\n" "Possible options:\n" "\n" " --block-count, -b How many unique xattr blocks to generate.\n" " --group-size, -g Number of key-value pairs to generate for each\n" " xattr block.\n" "\n"; int main(int argc, char **argv) { long blkidx, grpidx, block_count = 0, group_size = 0; sqfs_xattr_writer_t *xwr; sqfs_u32 id; int ret; for (;;) { int i = getopt_long(argc, argv, short_opts, long_opts, NULL); if (i == -1) break; switch (i) { case 'b': block_count = strtol(optarg, NULL, 0); break; case 'g': group_size = strtol(optarg, NULL, 0); break; case 'h': fputs(help_string, stdout); return EXIT_SUCCESS; case 'V': print_version("xattr_benchmark"); return EXIT_SUCCESS; default: goto fail_arg; } } if (block_count <= 0) { fputs("A block count > 0 must be specified.\n", stderr); goto fail_arg; } if (group_size <= 0) { fputs("A group size > 0 must be specified.\n", stderr); goto fail_arg; } /* setup writer */ xwr = sqfs_xattr_writer_create(0); /* generate blocks */ for (blkidx = 0; blkidx < block_count; ++blkidx) { ret = sqfs_xattr_writer_begin(xwr, 0); if (ret < 0) { sqfs_perror(NULL, "begin xattr block", ret); goto fail; } for (grpidx = 0; grpidx < group_size; ++grpidx) { char key[64], value[64]; snprintf(key, sizeof(key), "user.group%ld.key%ld", blkidx, grpidx); snprintf(value, sizeof(value), "group%ld/value%ld", blkidx, grpidx); ret = sqfs_xattr_writer_add(xwr, key, value, strlen(value)); if (ret < 0) { sqfs_perror(NULL, "add to xattr block", ret); goto fail; } } ret = sqfs_xattr_writer_end(xwr, &id); if (ret < 0) { sqfs_perror(NULL, "end xattr block", ret); goto fail; } } /* cleanup */ sqfs_destroy(xwr); return EXIT_SUCCESS; fail: sqfs_destroy(xwr); return EXIT_FAILURE; fail_arg: fputs("Try `xattr_benchmark --help' for more information.\n", stderr); return EXIT_FAILURE; } squashfs-tools-ng-1.1.3/tests/libsqfs/xattr_writer.c000066400000000000000000000207471410627516300226370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xattr_writer.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "../test.h" #include "sqfs/xattr_writer.h" #include "sqfs/compressor.h" #include "sqfs/xattr.h" #include "sqfs/error.h" #include "sqfs/super.h" #include "sqfs/io.h" static sqfs_u8 file_data[1024]; static size_t file_used = 0; static int dummy_write_at(sqfs_file_t *file, sqfs_u64 offset, const void *buffer, size_t size) { (void)file; if (offset >= sizeof(file_data)) return SQFS_ERROR_OUT_OF_BOUNDS; if (size > (sizeof(file_data) - offset)) return SQFS_ERROR_OUT_OF_BOUNDS; if (offset > file_used) memset(file_data + file_used, 0, offset - file_used); if ((offset + size) > file_used) file_used = offset + size; memcpy(file_data + offset, buffer, size); return 0; } static sqfs_u64 dummy_get_size(const sqfs_file_t *file) { (void)file; return file_used; } static sqfs_s32 dummy_compress(sqfs_compressor_t *cmp, const sqfs_u8 *in, sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) { (void)cmp; memcpy(out, in, outsize < size ? outsize : size); return 0; } static sqfs_file_t dummy_file = { { NULL, NULL }, NULL, dummy_write_at, dummy_get_size, NULL, }; static sqfs_compressor_t dummy_compressor = { { NULL, NULL }, NULL, NULL, NULL, dummy_compress, }; /*****************************************************************************/ int main(void) { size_t offset, ool_value_offset, id_offset; sqfs_xattr_id_table_t idtbl; sqfs_xattr_writer_t *xwr; sqfs_xattr_value_t value; sqfs_xattr_entry_t key; sqfs_xattr_id_t desc; sqfs_super_t super; char strbuf[32]; sqfs_u16 hdr; sqfs_u64 ref; sqfs_u32 id; int ret; /* setup */ xwr = sqfs_xattr_writer_create(0); TEST_NOT_NULL(xwr); /* record a block of key/value pairs */ ret = sqfs_xattr_writer_begin(xwr, 0); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "user.foobar", "test", 4); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "security.selinux", "Xwhatever", 9); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_end(xwr, &id); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(id, 0); /* record a second, different block */ ret = sqfs_xattr_writer_begin(xwr, 0); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "user.foobar", "bla", 3); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "security.selinux", "blub", 4); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_end(xwr, &id); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(id, 1); /* same as first block after sorting and gets the same ID */ ret = sqfs_xattr_writer_begin(xwr, 0); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "security.selinux", "Xwhatever", 9); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "user.foobar", "test", 4); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_end(xwr, &id); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(id, 0); /* the third assignment overwrites the first, making the block identical to the second one */ ret = sqfs_xattr_writer_begin(xwr, 0); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "user.foobar", "mimimi", 6); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "security.selinux", "blub", 4); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "user.foobar", "bla", 3); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_end(xwr, &id); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(id, 1); /* add another block with the same value, so it gets stored OOL */ ret = sqfs_xattr_writer_begin(xwr, 0); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add(xwr, "security.selinux", "Xwhatever", 9); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_end(xwr, &id); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(id, 2); /* serialize */ sqfs_super_init(&super, 131072, 0, SQFS_COMP_GZIP); ret = sqfs_xattr_writer_flush(xwr, &dummy_file, &super, &dummy_compressor); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(file_used, 177); /* meta data block holding the key-value-pairs */ memcpy(&hdr, file_data, sizeof(hdr)); hdr = le16toh(hdr); TEST_EQUAL_UI(hdr, (0x8000 | 101)); offset = 2; memcpy(&key, file_data + offset, sizeof(key)); key.type = le16toh(key.type); key.size = le16toh(key.size); offset += sizeof(key); TEST_EQUAL_UI(key.type, SQFS_XATTR_USER); TEST_EQUAL_UI(key.size, 6); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, key.size); TEST_STR_EQUAL(strbuf, "foobar"); offset += key.size; memcpy(&value, file_data + offset, sizeof(value)); value.size = le32toh(value.size); offset += sizeof(value); TEST_EQUAL_UI(value.size, 4); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, value.size); TEST_STR_EQUAL(strbuf, "test"); offset += value.size; memcpy(&key, file_data + offset, sizeof(key)); key.type = le16toh(key.type); key.size = le16toh(key.size); offset += sizeof(key); TEST_EQUAL_UI(key.type, SQFS_XATTR_SECURITY); TEST_EQUAL_UI(key.size, 7); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, key.size); TEST_STR_EQUAL(strbuf, "selinux"); offset += key.size; ool_value_offset = offset; memcpy(&value, file_data + offset, sizeof(value)); value.size = le32toh(value.size); offset += sizeof(value); TEST_EQUAL_UI(value.size, 9); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, value.size); TEST_STR_EQUAL(strbuf, "Xwhatever"); offset += value.size; memcpy(&key, file_data + offset, sizeof(key)); key.type = le16toh(key.type); key.size = le16toh(key.size); offset += sizeof(key); TEST_EQUAL_UI(key.type, SQFS_XATTR_USER); TEST_EQUAL_UI(key.size, 6); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, key.size); TEST_STR_EQUAL(strbuf, "foobar"); offset += key.size; memcpy(&value, file_data + offset, sizeof(value)); value.size = le32toh(value.size); offset += sizeof(value); TEST_EQUAL_UI(value.size, 3); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, value.size); TEST_STR_EQUAL(strbuf, "bla"); offset += value.size; memcpy(&key, file_data + offset, sizeof(key)); key.type = le16toh(key.type); key.size = le16toh(key.size); offset += sizeof(key); TEST_EQUAL_UI(key.type, SQFS_XATTR_SECURITY); TEST_EQUAL_UI(key.size, 7); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, key.size); TEST_STR_EQUAL(strbuf, "selinux"); offset += key.size; memcpy(&value, file_data + offset, sizeof(value)); value.size = le32toh(value.size); offset += sizeof(value); TEST_EQUAL_UI(value.size, 4); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, value.size); TEST_STR_EQUAL(strbuf, "blub"); offset += value.size; memcpy(&key, file_data + offset, sizeof(key)); key.type = le16toh(key.type); key.size = le16toh(key.size); offset += sizeof(key); TEST_EQUAL_UI(key.type, (SQFS_XATTR_SECURITY | SQFS_XATTR_FLAG_OOL)); TEST_EQUAL_UI(key.size, 7); memset(strbuf, '\0', sizeof(strbuf)); memcpy(strbuf, file_data + offset, key.size); TEST_STR_EQUAL(strbuf, "selinux"); offset += key.size; memcpy(&value, file_data + offset, sizeof(value)); value.size = le32toh(value.size); offset += sizeof(value); TEST_EQUAL_UI(value.size, 8); memcpy(&ref, file_data + offset, sizeof(ref)); ref = le64toh(ref); TEST_EQUAL_UI(ref, (ool_value_offset - 2)); offset += value.size; /* meta data block holding the ID descriptions */ id_offset = offset; memcpy(&hdr, file_data + offset, sizeof(hdr)); TEST_EQUAL_UI(le16toh(hdr), (0x8000 | (16 * 3))); offset += sizeof(hdr); memcpy(&desc, file_data + offset, sizeof(desc)); TEST_EQUAL_UI(le64toh(desc.xattr), 0); TEST_EQUAL_UI(le32toh(desc.count), 2); TEST_EQUAL_UI(le32toh(desc.size), 42); offset += sizeof(desc); memcpy(&desc, file_data + offset, sizeof(desc)); TEST_EQUAL_UI(le64toh(desc.xattr), 42); TEST_EQUAL_UI(le32toh(desc.count), 2); TEST_EQUAL_UI(le32toh(desc.size), 36); offset += sizeof(desc); memcpy(&desc, file_data + offset, sizeof(desc)); TEST_EQUAL_UI(le64toh(desc.xattr), 78); TEST_EQUAL_UI(le32toh(desc.count), 1); TEST_EQUAL_UI(le32toh(desc.size), 23); offset += sizeof(desc); /* the xattr table itself */ TEST_EQUAL_UI(super.xattr_id_table_start, offset); memcpy(&idtbl, file_data + offset, sizeof(idtbl)); TEST_EQUAL_UI(le64toh(idtbl.xattr_table_start), 0); TEST_EQUAL_UI(le32toh(idtbl.xattr_ids), 3); offset += sizeof(idtbl); memcpy(&ref, file_data + offset, sizeof(ref)); TEST_EQUAL_UI(le64toh(ref), id_offset); offset += sizeof(ref); TEST_EQUAL_UI(offset, file_used); /* cleanup */ sqfs_destroy(xwr); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/000077500000000000000000000000001410627516300175355ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/Makemodule.am000066400000000000000000000212161410627516300221410ustar00rootroot00000000000000TARDATADIR=$(top_srcdir)/tests/libtar/data test_tar_gnu0_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_gnu0_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu0_CPPFLAGS += -DTESTFILE=format-acceptance/gnu.tar test_tar_gnu1_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_gnu1_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu1_CPPFLAGS += -DTESTFILE=format-acceptance/gnu-g.tar test_tar_gnu2_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_gnu2_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu2_CPPFLAGS += -DTESTFILE=user-group-largenum/gnu.tar test_tar_gnu2_CPPFLAGS += -DTESTUID=0x80000000 -DTESTGID=0x80000000 test_tar_gnu2_CPPFLAGS += -DTESTTS=1542995392 test_tar_gnu3_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_gnu3_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu3_CPPFLAGS += -DTESTFILE=negative-mtime/gnu.tar -DTESTTS=-315622800 test_tar_gnu4_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_gnu4_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu4_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu4_CPPFLAGS += -DTESTFILE=long-paths/gnu.tar -DLONG_NAME_TEST test_tar_gnu4_CPPFLAGS += -DTESTTS=1542909670 test_tar_gnu5_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_gnu5_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu5_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu5_CPPFLAGS += -DTESTFILE=large-mtime/gnu.tar -DTESTTS=8589934592L test_tar_gnu6_SOURCES = tests/libtar/tar_big_file.c tests/test.h test_tar_gnu6_LDADD = libtar.a libfstream.a libcompat.a test_tar_gnu6_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_gnu6_CPPFLAGS += -DTESTFILE=file-size/gnu.tar test_tar_pax0_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_pax0_LDADD = libtar.a libfstream.a libcompat.a test_tar_pax0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_pax0_CPPFLAGS += -DTESTFILE=format-acceptance/pax.tar test_tar_pax1_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_pax1_LDADD = libtar.a libfstream.a libcompat.a test_tar_pax1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_pax1_CPPFLAGS += -DTESTFILE=user-group-largenum/pax.tar test_tar_pax1_CPPFLAGS += -DTESTUID=2147483648UL -DTESTGID=2147483648UL test_tar_pax1_CPPFLAGS += -DTESTTS=1542995392 test_tar_pax2_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_pax2_LDADD = libtar.a libfstream.a libcompat.a test_tar_pax2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_pax2_CPPFLAGS += -DTESTFILE=large-mtime/pax.tar -DTESTTS=8589934592L test_tar_pax3_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_pax3_LDADD = libtar.a libfstream.a libcompat.a test_tar_pax3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_pax3_CPPFLAGS += -DTESTFILE=negative-mtime/pax.tar -DTESTTS=-315622800 test_tar_pax4_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_pax4_LDADD = libtar.a libfstream.a libcompat.a test_tar_pax4_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_pax4_CPPFLAGS += -DTESTFILE=long-paths/pax.tar test_tar_pax4_CPPFLAGS += -DLONG_NAME_TEST -DTESTTS=1542909670 test_tar_pax5_SOURCES = tests/libtar/tar_big_file.c tests/test.h test_tar_pax5_LDADD = libtar.a libfstream.a libcompat.a test_tar_pax5_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_pax5_CPPFLAGS += -DTESTFILE=file-size/pax.tar test_tar_ustar0_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_ustar0_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar0_CPPFLAGS += -DTESTFILE=format-acceptance/ustar.tar test_tar_ustar1_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_ustar1_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar1_CPPFLAGS += -DTESTFILE=format-acceptance/ustar-pre-posix.tar test_tar_ustar2_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_ustar2_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar2_CPPFLAGS += -DTESTFILE=format-acceptance/v7.tar test_tar_ustar3_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_ustar3_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar3_CPPFLAGS += -DTESTFILE=user-group-largenum/8-digit.tar test_tar_ustar3_CPPFLAGS += -DTESTUID=8388608 -DTESTGID=8388608 test_tar_ustar3_CPPFLAGS += -DTESTTS=1542995392 test_tar_ustar4_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_ustar4_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar4_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar4_CPPFLAGS += -DTESTFILE=large-mtime/12-digit.tar test_tar_ustar4_CPPFLAGS += -DTESTTS=8589934592L test_tar_ustar5_SOURCES = tests/libtar/tar_simple.c tests/test.h test_tar_ustar5_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar5_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar5_CPPFLAGS += -DTESTFILE=long-paths/ustar.tar test_tar_ustar5_CPPFLAGS += -DLONG_NAME_TEST -DTESTTS=1542909670 test_tar_ustar6_SOURCES = tests/libtar/tar_big_file.c tests/test.h test_tar_ustar6_LDADD = libtar.a libfstream.a libcompat.a test_tar_ustar6_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_ustar6_CPPFLAGS += -DTESTFILE=file-size/12-digit.tar test_tar_target_filled_SOURCES = tests/libtar/tar_target_filled.c tests/test.h test_tar_target_filled_LDADD = libtar.a libfstream.a libcompat.a test_tar_target_filled_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu_SOURCES = tests/libtar/tar_sparse_gnu.c tests/test.h test_tar_sparse_gnu_LDADD = libtar.a libfstream.a libcompat.a test_tar_sparse_gnu_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu0_SOURCES = tests/libtar/tar_sparse.c tests/test.h test_tar_sparse_gnu0_LDADD = libtar.a libfstream.a libcompat.a test_tar_sparse_gnu0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu0_CPPFLAGS += -DTESTFILE=sparse-files/pax-gnu0-0.tar test_tar_sparse_gnu1_SOURCES = tests/libtar/tar_sparse.c tests/test.h test_tar_sparse_gnu1_LDADD = libtar.a libfstream.a libcompat.a test_tar_sparse_gnu1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu1_CPPFLAGS += -DTESTFILE=sparse-files/pax-gnu0-1.tar test_tar_sparse_gnu2_SOURCES = tests/libtar/tar_sparse.c tests/test.h test_tar_sparse_gnu2_LDADD = libtar.a libfstream.a libcompat.a test_tar_sparse_gnu2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu2_CPPFLAGS += -DTESTFILE=sparse-files/pax-gnu1-0.tar test_tar_sparse_gnu3_SOURCES = tests/libtar/tar_sparse.c tests/test.h test_tar_sparse_gnu3_LDADD = libtar.a libfstream.a libcompat.a test_tar_sparse_gnu3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu3_CPPFLAGS += -DTESTFILE=sparse-files/gnu.tar test_tar_xattr_bsd_SOURCES = tests/libtar/tar_xattr.c tests/test.h test_tar_xattr_bsd_LDADD = libtar.a libfstream.a libcompat.a test_tar_xattr_bsd_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_xattr_bsd_CPPFLAGS += -DTESTFILE=xattr/xattr-libarchive.tar test_tar_xattr_schily_SOURCES = tests/libtar/tar_xattr.c tests/test.h test_tar_xattr_schily_LDADD = libtar.a libfstream.a libcompat.a test_tar_xattr_schily_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_xattr_schily_CPPFLAGS += -DTESTFILE=xattr/xattr-schily.tar test_tar_xattr_schily_bin_SOURCES = tests/libtar/tar_xattr_bin.c tests/test.h test_tar_xattr_schily_bin_LDADD = libtar.a libfstream.a libcompat.a test_tar_xattr_schily_bin_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_xattr_schily_bin_CPPFLAGS += -DTESTFILE=xattr/xattr-schily-binary.tar tar_fuzz_SOURCES = tests/libtar/tar_fuzz.c tar_fuzz_LDADD = libtar.a libfstream.a libcompat.a LIBTAR_TESTS = \ test_tar_ustar0 test_tar_ustar1 test_tar_ustar2 test_tar_ustar3 \ test_tar_ustar4 test_tar_ustar5 test_tar_ustar6 \ test_tar_pax0 test_tar_pax1 test_tar_pax2 test_tar_pax3 test_tar_pax4 \ test_tar_pax5 \ test_tar_gnu0 test_tar_gnu1 test_tar_gnu2 test_tar_gnu3 test_tar_gnu4 \ test_tar_gnu5 test_tar_gnu6 \ test_tar_sparse_gnu test_tar_sparse_gnu0 test_tar_sparse_gnu1 \ test_tar_sparse_gnu2 test_tar_sparse_gnu3 \ test_tar_xattr_bsd test_tar_xattr_schily test_tar_xattr_schily_bin \ test_tar_target_filled if BUILD_TOOLS check_PROGRAMS += $(LIBTAR_TESTS) TESTS += $(LIBTAR_TESTS) noinst_PROGRAMS += tar_fuzz endif EXTRA_DIST += $(TARDATADIR) squashfs-tools-ng-1.1.3/tests/libtar/data/000077500000000000000000000000001410627516300204465ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/CREDITS000066400000000000000000000023021410627516300214630ustar00rootroot00000000000000The tar archives in this directory have been obtained from here: https://github.com/mgorny/tar-test-inputs git commit hash a2110a6 This repository was linked in the following article on interoperability of various different tar programs: https://dev.gentoo.org/~mgorny/articles/portability-of-tar-features.html The original intention of the example archives was to test various tar programs for interoperability with each others extensions and format quirks. The following have been removed since there is no intention in adding support for those features: - volume-label tests - multi-volume tests - longe user + group names - sun tar samples - star samples - file flags tests In addition to that, the files in "file-size" are truncated, since we are only interested in parsing the header. The following addtional files have been added: - xattr/xattr-shily-binary.tar Created from xattr/xattr-shily.tar by manually patching in a capability xattr key/value pair. - tar/format-acceptance/link_filled.tar Contributed in GitHub issue #64. A tar ball that contains a hard link where the 100 byte target field is completely filled without containing a null-terminator. squashfs-tools-ng-1.1.3/tests/libtar/data/file-size/000077500000000000000000000000001410627516300223355ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/file-size/12-digit.tar000066400000000000000000000040001410627516300243570ustar00rootroot00000000000000big-file.bin000644 001750 001750 10000000000013375730126 014134 0ustar00mgornymgorny000000 000000 squashfs-tools-ng-1.1.3/tests/libtar/data/file-size/gnu.tar000066400000000000000000000040001410627516300236300ustar00rootroot00000000000000big-file.bin00006440001750000175013375730126012015 0ustar mgornymgornysquashfs-tools-ng-1.1.3/tests/libtar/data/file-size/pax.tar000066400000000000000000000040001410627516300236270ustar00rootroot00000000000000./PaxHeaders.7839/big-file.bin0000644000000000000000000000015513375730126013066 xustar0019 size=8589934592 30 mtime=1542959190.916897254 30 atime=1542959522.512018391 30 ctime=1542959190.916897254 big-file.bin0000644000175000017500000000000013375730126014133 0ustar00mgornymgorny00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/000077500000000000000000000000001410627516300240225ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/gnu-g.tar000066400000000000000000000040001410627516300255410ustar00rootroot00000000000000input.txt0000644000175000017500000000000513375560044014561 0ustar mgornymgorny1337556176213375561750test squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/gnu.tar000066400000000000000000000040001410627516300253150ustar00rootroot00000000000000input.txt0000644000175000017500000000000513375560044012370 0ustar mgornymgornytest squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/link_filled.tar000066400000000000000000000240001410627516300270020ustar00rootroot0000000000000020_characters_here01/0000777000175000017500000000000013716045455014220 5ustar alexeyalexey20_characters_here01/20_characters_here02/0000777000175000017500000000000013716045463020004 5ustar alexeyalexey20_characters_here01/20_characters_here02/20_characters_here03/0000777000175000017500000000000013716045470023570 5ustar alexeyalexey20_characters_here01/20_characters_here02/20_characters_here03/20_characters_here04/0000777000175000017500000000000013716045572027362 5ustar alexeyalexey20_characters_here01/20_characters_here02/20_characters_here03/20_characters_here04/errored_file_tst0000777000175000017500000000000513716044035032627 0ustar alexeyalexeytest 20_characters_here01/20_characters_here02/20_characters_here03/20_characters_here04/some_test_file0000777000175000017500000000000513716044035032275 0ustar alexeyalexeytest 20CharsForLnkTest001/0000777000175000017500000000000013716046033014024 5ustar alexeyalexey20CharsForLnkTest001/20CharsForLnkTest002/0000777000175000017500000000000013716046037017430 5ustar alexeyalexey20CharsForLnkTest001/20CharsForLnkTest002/20CharsForLnkTest003/0000777000175000017500000000000013716046044023027 5ustar alexeyalexey20CharsForLnkTest001/20CharsForLnkTest002/20CharsForLnkTest003/20CharsForLnkTest004/0000777000175000017500000000000013716045064026432 5ustar alexeyalexey././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootroot20CharsForLnkTest001/20CharsForLnkTest002/20CharsForLnkTest003/20CharsForLnkTest004/0123456789012345678920CharsForLnkTest001/20CharsForLnkTest002/20CharsForLnkTest003/20CharsForLnkTest004/01234567890123450000777000175000017500000000000013716044035052321 120_characters_here01/20_characters_here02/20_characters_here03/20_characters_here04/errored_file_tstustar alexeyalexeysquashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/pax.tar000066400000000000000000000060001410627516300253160ustar00rootroot00000000000000PaxHeader/input.txt000644 001750 001750 00000000162 13375560044 015625 xustar00mgornymgorny000000 000000 30 ctime=1542905892.629967955 30 atime=1542905911.020942023 30 mtime=1542905892.629967955 24 SCHILY.fflags=extent input.txt000644 001750 001750 00000000005 13375560044 013650 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/ustar-pre-posix.tar000066400000000000000000000040001410627516300276060ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 13375560044 013610 0ustar mgornymgorny000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/ustar.tar000066400000000000000000000040001410627516300256620ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 13375560044 013650 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/format-acceptance/v7.tar000066400000000000000000000040001410627516300250600ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 13375560044 006461 test squashfs-tools-ng-1.1.3/tests/libtar/data/large-mtime/000077500000000000000000000000001410627516300226515ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/large-mtime/12-digit.tar000066400000000000000000000040001410627516300246730ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 100000000000013623 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/large-mtime/gnu.tar000066400000000000000000000040001410627516300241440ustar00rootroot00000000000000input.txt00006440001750000175000000000005011504 0ustar mgornymgornytest squashfs-tools-ng-1.1.3/tests/libtar/data/large-mtime/pax.tar000066400000000000000000000060001410627516300241450ustar00rootroot00000000000000./PaxHeaders.11530/input.txt0000644000000000000000000000012077777777777012714 xustar0020 mtime=8589934592 30 atime=1543015522.291866334 30 ctime=1543015033.979919105 input.txt0000644000175000017500000000000500000000000013622 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.1.3/tests/libtar/data/long-paths/000077500000000000000000000000001410627516300225225ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/long-paths/gnu.tar000066400000000000000000000060001410627516300240170ustar00rootroot00000000000000././@LongLink0000000000000000000000000000024500000000000011707 Lustar rootwheel012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/input.txt012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/01234560000644000175000017500000000000513375567346022655 0ustar mgornymgornytest squashfs-tools-ng-1.1.3/tests/libtar/data/long-paths/pax.tar000066400000000000000000000060001410627516300240160ustar00rootroot00000000000000012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/01234560000644000000000000000000000041013375567346020303 xustar00174 path=012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/input.txt 30 mtime=1542909670.281837797 30 atime=1542909708.308731672 30 ctime=1542909670.281837797 012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/01234560000644000175000017500000000000513375567346024155 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.1.3/tests/libtar/data/long-paths/ustar.tar000066400000000000000000000040001410627516300243620ustar00rootroot00000000000000012345678901234567890123456789/012345678901234567890123456789/input.txt000644 001750 001750 00000000005 13375567346 033463 0ustar00mgornymgorny000000 000000 012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789test squashfs-tools-ng-1.1.3/tests/libtar/data/negative-mtime/000077500000000000000000000000001410627516300233615ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/negative-mtime/gnu.tar000066400000000000000000000240001410627516300246560ustar00rootroot00000000000000input.txt00006440001750000175000000000005/p016500 0ustar mgornymgornytest squashfs-tools-ng-1.1.3/tests/libtar/data/negative-mtime/pax.tar000066400000000000000000000240001410627516300246550ustar00rootroot00000000000000./PaxHeaders.12320/input.txt0000644000000000000000000000010600000000000012601 xustar0020 mtime=-315622800 20 atime=-315622800 30 ctime=1543015908.675050405 input.txt0000644000175000017500000000000500000000000013622 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.1.3/tests/libtar/data/sparse-files/000077500000000000000000000000001410627516300230435ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/sparse-files/gnu-small.tar000066400000000000000000000230001410627516300254450ustar00rootroot00000000000000input.bin0000644000175000017500000002000013376224003021541 Sustar mgornymgorny00000000000000000100000000100000000000010000000020000000000000000000002000000test test squashfs-tools-ng-1.1.3/tests/libtar/data/sparse-files/gnu.tar000066400000000000000000001040001410627516300243370ustar00rootroot00000000000000input.bin0000644000175000017500000010000013376223472023616 Sustar mgornymgorny00000000000000000100000000100000000000010000000020000000000001000000003000000000000100000001000000000004000000000000100000000500000000000010000000060000000000001000000007000000000000100000001000000000000000000test test test test test test test test squashfs-tools-ng-1.1.3/tests/libtar/data/sparse-files/pax-gnu0-0.tar000066400000000000000000001060001410627516300253440ustar00rootroot00000000000000./PaxHeaders.21740/input.bin0000644000000000000000000000120413376223472012610 xustar0027 GNU.sparse.size=2097152 26 GNU.sparse.numblocks=9 23 GNU.sparse.offset=0 28 GNU.sparse.numbytes=4096 28 GNU.sparse.offset=262144 28 GNU.sparse.numbytes=4096 28 GNU.sparse.offset=524288 28 GNU.sparse.numbytes=4096 28 GNU.sparse.offset=786432 28 GNU.sparse.numbytes=4096 29 GNU.sparse.offset=1048576 28 GNU.sparse.numbytes=4096 29 GNU.sparse.offset=1310720 28 GNU.sparse.numbytes=4096 29 GNU.sparse.offset=1572864 28 GNU.sparse.numbytes=4096 29 GNU.sparse.offset=1835008 28 GNU.sparse.numbytes=4096 29 GNU.sparse.offset=2097152 25 GNU.sparse.numbytes=0 30 mtime=1543055162.245810774 30 atime=1543055172.420245075 30 ctime=1543055162.245810774 input.bin0000644000175000017500000010000013376223472013617 0ustar00mgornymgorny00000000000000test test test test test test test test squashfs-tools-ng-1.1.3/tests/libtar/data/sparse-files/pax-gnu0-1.tar000066400000000000000000001050001410627516300253440ustar00rootroot00000000000000./PaxHeaders.21748/input.bin0000644000000000000000000000045013376223472012622 xustar0027 GNU.sparse.size=2097152 26 GNU.sparse.numblocks=9 29 GNU.sparse.name=input.bin 124 GNU.sparse.map=0,4096,262144,4096,524288,4096,786432,4096,1048576,4096,1310720,4096,1572864,4096,1835008,4096,2097152,0 30 mtime=1543055162.245810774 30 atime=1543055172.420245075 30 ctime=1543055162.245810774 ./GNUSparseFile.21748/input.bin0000644000175000017500000010000013376223472017047 0ustar00mgornymgorny00000000000000test test test test test test test test squashfs-tools-ng-1.1.3/tests/libtar/data/sparse-files/pax-gnu1-0.tar000066400000000000000000001060001410627516300253450ustar00rootroot00000000000000./PaxHeaders.21754/input.bin0000644000000000000000000000030213376223472012613 xustar0022 GNU.sparse.major=1 22 GNU.sparse.minor=0 29 GNU.sparse.name=input.bin 31 GNU.sparse.realsize=2097152 30 mtime=1543055162.245810774 30 atime=1543055172.420245075 30 ctime=1543055162.245810774 ./GNUSparseFile.21754/input.bin0000644000175000017500000010100013376223472017045 0ustar00mgornymgorny000000000000009 0 4096 262144 4096 524288 4096 786432 4096 1048576 4096 1310720 4096 1572864 4096 1835008 4096 2097152 0 test test test test test test test test squashfs-tools-ng-1.1.3/tests/libtar/data/sqfs.sha512000066400000000000000000000114131410627516300223470ustar00rootroot000000000000006217c207d51e38bd819ff70047a070d0172c2e1cc97ba5feb578a2429d1c911bd7990ea845b28389a956754e52e5693beba38c124bf6d7452522361a81c2da09 tests/libtar/data/long-paths/gnu.sqfs 6217c207d51e38bd819ff70047a070d0172c2e1cc97ba5feb578a2429d1c911bd7990ea845b28389a956754e52e5693beba38c124bf6d7452522361a81c2da09 tests/libtar/data/long-paths/pax.sqfs 6217c207d51e38bd819ff70047a070d0172c2e1cc97ba5feb578a2429d1c911bd7990ea845b28389a956754e52e5693beba38c124bf6d7452522361a81c2da09 tests/libtar/data/long-paths/ustar.sqfs 2e2cd5fa04304c5765d7bb54c30273be6cd7414150ae95d217efcec1fbaa7486b4e752adb95e0dddd5329355132baaa7260dc958eb4418158a249929e89b0581 tests/libtar/data/sparse-files/gnu-small.sqfs f48a79c58db4d3553ffde5bd3780bb34c12fd0ee703aba95a40588c1cf7b5a679ea16d2a842a11e262bd14c2f0b4dd256436dcfc72a177a455427f93b504eae6 tests/libtar/data/sparse-files/gnu.sqfs f48a79c58db4d3553ffde5bd3780bb34c12fd0ee703aba95a40588c1cf7b5a679ea16d2a842a11e262bd14c2f0b4dd256436dcfc72a177a455427f93b504eae6 tests/libtar/data/sparse-files/pax-gnu0-1.sqfs f48a79c58db4d3553ffde5bd3780bb34c12fd0ee703aba95a40588c1cf7b5a679ea16d2a842a11e262bd14c2f0b4dd256436dcfc72a177a455427f93b504eae6 tests/libtar/data/sparse-files/pax-gnu0-0.sqfs f48a79c58db4d3553ffde5bd3780bb34c12fd0ee703aba95a40588c1cf7b5a679ea16d2a842a11e262bd14c2f0b4dd256436dcfc72a177a455427f93b504eae6 tests/libtar/data/sparse-files/pax-gnu1-0.sqfs 194384a9a3683ef751f45ca9f380790b3bd9c234a839ff72b09c778f96fe521be9d816c7d3179edf936ec35cd66fad2485b03482468629baa3290a1475c72147 tests/libtar/data/large-mtime/12-digit.sqfs 194384a9a3683ef751f45ca9f380790b3bd9c234a839ff72b09c778f96fe521be9d816c7d3179edf936ec35cd66fad2485b03482468629baa3290a1475c72147 tests/libtar/data/large-mtime/gnu.sqfs 194384a9a3683ef751f45ca9f380790b3bd9c234a839ff72b09c778f96fe521be9d816c7d3179edf936ec35cd66fad2485b03482468629baa3290a1475c72147 tests/libtar/data/large-mtime/pax.sqfs c40037ae4a4b4224a919cf18c238d5a6b13f17fcca2602810e870e7606435f61426417cda32dd9bca85e74ec6c0fc75c996f60ec8b4560e79c83631937b6cdfa tests/libtar/data/negative-mtime/gnu.sqfs c40037ae4a4b4224a919cf18c238d5a6b13f17fcca2602810e870e7606435f61426417cda32dd9bca85e74ec6c0fc75c996f60ec8b4560e79c83631937b6cdfa tests/libtar/data/negative-mtime/pax.sqfs d3d112eab3537f6784a207d0bfd8f2826908fbddb3cf20e379a646c6c587c8bbaa67a1753a4ee0246c110dc10bd0ce821fa58368936efac3a1bbf8fd3f782e2b tests/libtar/data/format-acceptance/gnu-g.sqfs d3d112eab3537f6784a207d0bfd8f2826908fbddb3cf20e379a646c6c587c8bbaa67a1753a4ee0246c110dc10bd0ce821fa58368936efac3a1bbf8fd3f782e2b tests/libtar/data/format-acceptance/gnu.sqfs d3d112eab3537f6784a207d0bfd8f2826908fbddb3cf20e379a646c6c587c8bbaa67a1753a4ee0246c110dc10bd0ce821fa58368936efac3a1bbf8fd3f782e2b tests/libtar/data/format-acceptance/ustar-pre-posix.sqfs d3d112eab3537f6784a207d0bfd8f2826908fbddb3cf20e379a646c6c587c8bbaa67a1753a4ee0246c110dc10bd0ce821fa58368936efac3a1bbf8fd3f782e2b tests/libtar/data/format-acceptance/v7.sqfs d3d112eab3537f6784a207d0bfd8f2826908fbddb3cf20e379a646c6c587c8bbaa67a1753a4ee0246c110dc10bd0ce821fa58368936efac3a1bbf8fd3f782e2b tests/libtar/data/format-acceptance/pax.sqfs d3d112eab3537f6784a207d0bfd8f2826908fbddb3cf20e379a646c6c587c8bbaa67a1753a4ee0246c110dc10bd0ce821fa58368936efac3a1bbf8fd3f782e2b tests/libtar/data/format-acceptance/ustar.sqfs 20eb111e2019eca4f26535e52823593ebf6a6b2c35d7a8779d6e4f92b90cba8e41edaf1f92425b5084adc680a7c0dac758a2f5170e0b4f19db3ea3357fb3080a tests/libtar/data/format-acceptance/link_filled.sqfs a5e95c464f41249da9a4156db3a23d30e01652881e839912f632f2614f1775e62a3fed184efbec1a25148f43edf7ba2a92e1136416aca4bf8f3e73a3b137162b tests/libtar/data/user-group-largenum/gnu.sqfs 428728b2ca26543a9b0e698d1ae4f54463a7912b51248f2bd34627eb20f3abd8469379a5cd71ab6903aedd198ef48c97e66385c22f128e8aa08571f669c4c6c6 tests/libtar/data/user-group-largenum/8-digit.sqfs a5e95c464f41249da9a4156db3a23d30e01652881e839912f632f2614f1775e62a3fed184efbec1a25148f43edf7ba2a92e1136416aca4bf8f3e73a3b137162b tests/libtar/data/user-group-largenum/pax.sqfs 6ff3ae611d295fc597db088f82ccd0218220ffb51ed901f0d1027c68d687f917b1601bf096421281fb95df48fb2aa6e5f1dc81caccbaa914cb971e17a2c4d5cd tests/libtar/data/xattr/xattr-schily-binary.sqfs 627a69ed25f9b5380d269fbe12603818d73b29d6a1155fcf7bebca2ba30ec1cce7b8405394498333f9847f10f0768b80b94b723551dbff10c633d2887e62b804 tests/libtar/data/xattr/xattr-schily.sqfs 627a69ed25f9b5380d269fbe12603818d73b29d6a1155fcf7bebca2ba30ec1cce7b8405394498333f9847f10f0768b80b94b723551dbff10c633d2887e62b804 tests/libtar/data/xattr/xattr-libarchive.sqfs b8e0e1cb41663c3d6278bf214234ac00ae8b86b9bc16d086bd0a7bfa9b0d28d626f70c6a1bd6f05dbbfa46431ce3f4518a4be38caf87b1f071d57edae24c5b10 tests/libtar/data/xattr/acl.sqfs bae693082a771c500c2d6b52a8eeb91decd98e90eaae379951bcc80589533ff43b58375f8a7f3de77c35456ee7fb269a6b17e4c29b291475578ba8453f152d0e tests/tar2sqfs/root-becomes.sqfs squashfs-tools-ng-1.1.3/tests/libtar/data/user-group-largenum/000077500000000000000000000000001410627516300243665ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/user-group-largenum/8-digit.tar000066400000000000000000000040001410627516300263350ustar00rootroot00000000000000input.txt000644 400000004000000000000000005 13376036700 011334 0ustar00000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/user-group-largenum/gnu.tar000066400000000000000000000040001410627516300256610ustar00rootroot00000000000000input.txt00006440000000000513376036700007404 0ustar test squashfs-tools-ng-1.1.3/tests/libtar/data/user-group-largenum/pax.tar000066400000000000000000000060001410627516300256620ustar00rootroot00000000000000./PaxHeaders.25012/input.txt0000644000000000000000000000014013376036700012645 xustar0018 uid=2147483648 18 gid=2147483648 30 atime=1542999264.054938739 30 ctime=1542999260.518139451 input.txt0000644000000000000000000000000513376036700011144 0ustar0000000000000000test squashfs-tools-ng-1.1.3/tests/libtar/data/xattr/000077500000000000000000000000001410627516300216105ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libtar/data/xattr/acl.tar000066400000000000000000000060001410627516300230530ustar00rootroot00000000000000PaxHeader/input.txt000600 001750 001750 00000000337 13376310551 015616 xustar00mgornymgorny000000 000000 30 ctime=1543082993.344948841 29 atime=1543083015.18807739 30 mtime=1543082345.191275018 24 SCHILY.fflags=extent 110 SCHILY.acl.access=user::rw-,group::---,other::---,user:nobody:rw-:65534,group:nogroup:rw-:65533,mask::r-- input.txt000600 001750 001750 00000000005 13376310551 013635 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/xattr/xattr-libarchive.tar000066400000000000000000000060001410627516300255640ustar00rootroot00000000000000PaxHeader/input.txt000644 001750 001750 00000000243 13376340315 015623 xustar00mgornymgorny000000 000000 30 ctime=1543094606.340136302 29 atime=1543094642.61414619 30 mtime=1543094477.803174898 24 SCHILY.fflags=extent 50 LIBARCHIVE.xattr.user.mime_type=dGV4dC9wbGFpbg input.txt000644 001750 001750 00000000005 13376340315 013646 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.1.3/tests/libtar/data/xattr/xattr-schily-binary.tar000066400000000000000000000060001410627516300262310ustar00rootroot00000000000000./PaxHeaders.31616/input.txt0000644000000000000000000000022213376340315012655 xustar0030 mtime=1543094477.803174898 29 atime=1543094642.61414619 30 ctime=1543094606.340136302 57 SCHILY.xattr.security.capability=0 input.txt0000644000175000017500000000000513376340315013666 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.1.3/tests/libtar/data/xattr/xattr-schily.tar000066400000000000000000000060001410627516300247470ustar00rootroot00000000000000./PaxHeaders.31616/input.txt0000644000000000000000000000020313376340315012654 xustar0030 mtime=1543094477.803174898 29 atime=1543094642.61414619 30 ctime=1543094606.340136302 42 SCHILY.xattr.user.mime_type=text/plain input.txt0000644000175000017500000000000513376340315013666 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.1.3/tests/libtar/tar_big_file.c000066400000000000000000000014211410627516300223050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_big_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" int main(void) { tar_header_decoded_t hdr; istream_t *fp; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.sb.st_uid, 01750); TEST_EQUAL_UI(hdr.sb.st_gid, 01750); TEST_EQUAL_UI(hdr.sb.st_size, 8589934592); TEST_EQUAL_UI(hdr.sb.st_mtime, 1542959190); TEST_EQUAL_UI(hdr.mtime, 1542959190); TEST_STR_EQUAL(hdr.name, "big-file.bin"); TEST_ASSERT(!hdr.unknown_record); clear_header(&hdr); sqfs_destroy(fp); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/tar_fuzz.c000066400000000000000000000013661410627516300215530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_fuzz.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include #include int main(int argc, char **argv) { tar_header_decoded_t hdr; istream_t *fp; int ret; if (argc != 2) { fputs("usage: tar_fuzz \n", stderr); return EXIT_FAILURE; } fp = istream_open_file(argv[1]); if (fp == NULL) return EXIT_FAILURE; for (;;) { ret = read_header(fp, &hdr); if (ret > 0) break; if (ret < 0) goto fail; ret = istream_skip(fp, hdr.sb.st_size); clear_header(&hdr); if (ret < 0) goto fail; } sqfs_destroy(fp); return EXIT_SUCCESS; fail: sqfs_destroy(fp); return EXIT_FAILURE; } squashfs-tools-ng-1.1.3/tests/libtar/tar_simple.c000066400000000000000000000027321410627516300220440ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_simple.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" #ifndef TESTUID #define TESTUID 1000 #endif #ifndef TESTGID #define TESTGID TESTUID #endif #ifndef TESTFNAME #define TESTFNAME input.txt #endif #ifndef TESTTS #define TESTTS 1542905892 #endif #ifdef LONG_NAME_TEST static const char *fname = "012345678901234567890123456789/012345678901234567890123456789/" "012345678901234567890123456789/012345678901234567890123456789/" "012345678901234567890123456789/input.txt"; #else static const char *fname = STRVALUE(TESTFNAME); #endif int main(void) { tar_header_decoded_t hdr; char buffer[6]; sqfs_s64 ts; istream_t *fp; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.sb.st_uid, TESTUID); TEST_EQUAL_UI(hdr.sb.st_gid, TESTGID); TEST_EQUAL_UI(hdr.sb.st_size, 5); ts = TESTTS; if (sizeof(time_t) < sizeof(ts) && ts > INT32_MAX) { TEST_EQUAL_UI(hdr.sb.st_mtime, INT32_MAX); } else { TEST_EQUAL_UI(hdr.sb.st_mtime, ts); } TEST_EQUAL_UI(hdr.mtime, ts); TEST_STR_EQUAL(hdr.name, fname); TEST_ASSERT(!hdr.unknown_record); TEST_ASSERT(istream_read(fp, buffer, 5) == 5); buffer[5] = '\0'; TEST_STR_EQUAL(buffer, "test\n"); clear_header(&hdr); sqfs_destroy(fp); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/tar_sparse.c000066400000000000000000000040041410627516300220420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_sparse.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" static void test_case_sparse(const char *path) { tar_header_decoded_t hdr; sparse_map_t *sparse; istream_t *fp; fp = istream_open_file(path); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.sb.st_uid, 01750); TEST_EQUAL_UI(hdr.sb.st_gid, 01750); TEST_EQUAL_UI(hdr.sb.st_size, 2097152); TEST_EQUAL_UI(hdr.actual_size, 2097152); TEST_EQUAL_UI(hdr.record_size, 32768); TEST_STR_EQUAL(hdr.name, "input.bin"); TEST_ASSERT(!hdr.unknown_record); sparse = hdr.sparse; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 0); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 262144); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 524288); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 786432); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 1048576); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 1310720); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 1572864); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 1835008); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 2097152); TEST_EQUAL_UI(sparse->count, 0); sparse = sparse->next; TEST_NULL(sparse); clear_header(&hdr); sqfs_destroy(fp); } int main(void) { test_case_sparse( STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE) ); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/tar_sparse_gnu.c000066400000000000000000000023041410627516300227140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_sparse_gnu.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" int main(void) { tar_header_decoded_t hdr; sparse_map_t *sparse; istream_t *fp; TEST_ASSERT(chdir(TEST_PATH) == 0); fp = istream_open_file("sparse-files/gnu-small.tar"); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.sb.st_uid, 01750); TEST_EQUAL_UI(hdr.sb.st_gid, 01750); TEST_EQUAL_UI(hdr.sb.st_size, 524288); TEST_EQUAL_UI(hdr.actual_size, 524288); TEST_EQUAL_UI(hdr.record_size, 8192); TEST_STR_EQUAL(hdr.name, "input.bin"); TEST_ASSERT(!hdr.unknown_record); sparse = hdr.sparse; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 0); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 262144); TEST_EQUAL_UI(sparse->count, 4096); sparse = sparse->next; TEST_NOT_NULL(sparse); TEST_EQUAL_UI(sparse->offset, 524288); TEST_EQUAL_UI(sparse->count, 0); TEST_NULL(sparse->next); clear_header(&hdr); sqfs_destroy(fp); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/tar_target_filled.c000066400000000000000000000067131410627516300233630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_target_filled.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" int main(void) { tar_header_decoded_t hdr; char buffer[16]; istream_t *fp; TEST_ASSERT(chdir(TEST_PATH) == 0); fp = istream_open_file("format-acceptance/link_filled.tar"); TEST_NOT_NULL(fp); /* "deep" directory hierarchy containg 2 files */ TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20_characters_here01/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/" "20_characters_here03/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/" "20_characters_here03/20_characters_here04/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0777); TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/" "20_characters_here03/20_characters_here04/" "errored_file_tst"); TEST_EQUAL_UI(hdr.sb.st_size, 5); TEST_ASSERT(istream_read(fp, buffer, 5) == 5); buffer[5] = '\0'; TEST_STR_EQUAL(buffer, "test\n"); TEST_ASSERT(skip_padding(fp, 5) == 0); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0777); TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/" "20_characters_here03/20_characters_here04/" "some_test_file"); TEST_EQUAL_UI(hdr.sb.st_size, 5); TEST_ASSERT(istream_read(fp, buffer, 5) == 5); buffer[5] = '\0'; TEST_STR_EQUAL(buffer, "test\n"); TEST_ASSERT(skip_padding(fp, 5) == 0); clear_header(&hdr); /* "deep" directory hierarchy containg a hard link */ TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/" "20CharsForLnkTest003/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/" "20CharsForLnkTest003/20CharsForLnkTest004/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/" "20CharsForLnkTest003/20CharsForLnkTest004/" "01234567890123456789"); TEST_ASSERT(hdr.is_hard_link); TEST_STR_EQUAL(hdr.link_target, "20_characters_here01/" "20_characters_here02/20_characters_here03/" "20_characters_here04/errored_file_tst"); clear_header(&hdr); /* end of file */ TEST_ASSERT(read_header(fp, &hdr) > 0); sqfs_destroy(fp); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/tar_xattr.c000066400000000000000000000021171410627516300217120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_xattr.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" int main(void) { tar_header_decoded_t hdr; char buffer[6]; istream_t *fp; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.sb.st_uid, 01750); TEST_EQUAL_UI(hdr.sb.st_gid, 01750); TEST_EQUAL_UI(hdr.sb.st_size, 5); TEST_EQUAL_UI(hdr.sb.st_mtime, 1543094477); TEST_EQUAL_UI(hdr.mtime, 1543094477); TEST_STR_EQUAL(hdr.name, "input.txt"); TEST_ASSERT(!hdr.unknown_record); TEST_ASSERT(istream_read(fp, buffer, 5) == 5); buffer[5] = '\0'; TEST_STR_EQUAL(buffer, "test\n"); TEST_NOT_NULL(hdr.xattr); TEST_STR_EQUAL(hdr.xattr->key, "user.mime_type"); TEST_STR_EQUAL((const char *)hdr.xattr->value, "text/plain"); TEST_EQUAL_UI(hdr.xattr->value_len, 10); TEST_NULL(hdr.xattr->next); clear_header(&hdr); sqfs_destroy(fp); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libtar/tar_xattr_bin.c000066400000000000000000000024111410627516300225370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_xattr_bin.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar.h" #include "../test.h" static const uint8_t value[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; int main(void) { tar_header_decoded_t hdr; char buffer[6]; istream_t *fp; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.sb.st_mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.sb.st_uid, 01750); TEST_EQUAL_UI(hdr.sb.st_gid, 01750); TEST_EQUAL_UI(hdr.sb.st_size, 5); TEST_EQUAL_UI(hdr.sb.st_mtime, 1543094477); TEST_EQUAL_UI(hdr.mtime, 1543094477); TEST_STR_EQUAL(hdr.name, "input.txt"); TEST_ASSERT(!hdr.unknown_record); TEST_ASSERT(istream_read(fp, buffer, 5) == 5); buffer[5] = '\0'; TEST_STR_EQUAL(buffer, "test\n"); TEST_NOT_NULL(hdr.xattr); TEST_STR_EQUAL(hdr.xattr->key, "security.capability"); TEST_EQUAL_UI(hdr.xattr->value_len, sizeof(value)); TEST_ASSERT(memcmp(hdr.xattr->value, value, sizeof(value)) == 0); TEST_NULL(hdr.xattr->next); clear_header(&hdr); sqfs_destroy(fp); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libutil/000077500000000000000000000000001410627516300177245ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/libutil/Makemodule.am000066400000000000000000000015451410627516300223330ustar00rootroot00000000000000test_str_table_SOURCES = tests/libutil/str_table.c tests/test.h test_str_table_LDADD = libutil.a libfstream.a libcompat.a test_str_table_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(top_srcdir)/tests/libutil test_rbtree_SOURCES = tests/libutil/rbtree.c tests/test.h test_rbtree_LDADD = libutil.a libcompat.a test_xxhash_SOURCES = tests/libutil/xxhash.c test_xxhash_LDADD = libutil.a libcompat.a test_threadpool_SOURCES = tests/libutil/threadpool.c test_threadpool_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) test_threadpool_LDADD = libutil.a libcompat.a $(PTHREAD_LIBS) test_ismemzero_SOURCES = tests/libutil/is_memory_zero.c test_ismemzero_LDADD = libutil.a libcompat.a LIBUTIL_TESTS = \ test_str_table test_rbtree test_xxhash test_threadpool test_ismemzero check_PROGRAMS += $(LIBUTIL_TESTS) TESTS += $(LIBUTIL_TESTS) EXTRA_DIST += $(top_srcdir)/tests/libutil/words.txt squashfs-tools-ng-1.1.3/tests/libutil/is_memory_zero.c000066400000000000000000000011201410627516300231240ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * is_memory_zero.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "../test.h" #include "util.h" int main(void) { unsigned char temp[1024]; size_t i, j; memset(temp, 0, sizeof(temp)); for (i = 0; i < sizeof(temp); ++i) { TEST_ASSERT(is_memory_zero(temp, i)); for (j = 0; j < i; ++j) { TEST_ASSERT(is_memory_zero(temp, i)); temp[j] = 42; TEST_ASSERT(!is_memory_zero(temp, i)); temp[j] = 0; TEST_ASSERT(is_memory_zero(temp, i)); } } return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libutil/rbtree.c000066400000000000000000000116541410627516300213620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * rbtree.c * * Copyright (C) 2020 David Oberhollenzer */ #include "config.h" #include "rbtree.h" #include "../test.h" static int key_compare(const void *ctx, const void *a, const void *b) { (void)ctx; return *((const sqfs_s32 *)a) - *((const sqfs_s32 *)b); } static size_t count_nodes_dfs(rbtree_node_t *n) { return 1 + (n->left == NULL ? 0 : count_nodes_dfs(n->left)) + (n->right == NULL ? 0 : count_nodes_dfs(n->right)); } static size_t min_depth(rbtree_node_t *n) { size_t lhs, rhs; if (n == NULL) return 0; lhs = min_depth(n->left) + 1; rhs = min_depth(n->right) + 1; return lhs < rhs ? lhs : rhs; } static size_t max_depth(rbtree_node_t *n) { size_t lhs, rhs; if (n == NULL) return 0; lhs = min_depth(n->left) + 1; rhs = min_depth(n->right) + 1; return lhs > rhs ? lhs : rhs; } static size_t get_ref_black_depth(rbtree_t *rb) { rbtree_node_t *n; size_t count = 0; for (n = rb->root; n != NULL; n = n->left) { if (!n->is_red) count += 1; } return count; } static void check_binary_tree_dfs(rbtree_node_t *n) { const void *key = rbtree_node_key(n); const void *cmp; if (n->left != NULL) { cmp = rbtree_node_key(n->left); TEST_ASSERT(key_compare(NULL, cmp, key) < 0); check_binary_tree_dfs(n->left); } if (n->right != NULL) { cmp = rbtree_node_key(n->right); TEST_ASSERT(key_compare(NULL, cmp, key) > 0); check_binary_tree_dfs(n->right); } } static void check_colors_dfs(rbtree_node_t *n) { if (n->is_red) { TEST_ASSERT(n->left == NULL || !n->left->is_red); TEST_ASSERT(n->right == NULL || !n->right->is_red); } if (n->left != NULL) check_colors_dfs(n->left); if (n->right != NULL) check_colors_dfs(n->right); } static void check_black_depth_dfs(rbtree_node_t *n, size_t ref, size_t counter) { if (!n->is_red) counter += 1; if (n->left == NULL || n->right == NULL) TEST_EQUAL_UI(counter, ref); if (n->left != NULL) check_black_depth_dfs(n->left, ref, counter); if (n->right != NULL) check_black_depth_dfs(n->right, ref, counter); } static int check_subtrees_equal(const rbtree_node_t *lhs, const rbtree_node_t *rhs, size_t datasize) { if (lhs == rhs) return -1; if (lhs->value_offset != rhs->value_offset) return -1; if ((lhs->is_red && !rhs->is_red) || (!lhs->is_red && rhs->is_red)) return -1; if (memcmp(lhs->data, rhs->data, datasize) != 0) return -1; if (lhs->left == NULL) { if (rhs->left != NULL) return -1; } else { if (rhs->left == NULL) return -1; if (check_subtrees_equal(lhs->left, rhs->left, datasize)) return -1; } if (lhs->right == NULL) { if (rhs->right != NULL) return -1; } else { if (rhs->right == NULL) return -1; if (check_subtrees_equal(lhs->right, rhs->right, datasize)) return -1; } return 0; } int main(void) { size_t count, blkdepth, mind, maxd; sqfs_s32 key, key2; rbtree_t rb, copy; rbtree_node_t *n; sqfs_u64 value; int ret; TEST_ASSERT(rbtree_init(&rb, sizeof(sqfs_s32), sizeof(sqfs_u64), key_compare) == 0); count = 0; for (key = -1000; key < 1000; ++key) { /* lookup of current key must fail prior to insert */ TEST_NULL(rbtree_lookup(&rb, &key)); /* previous key/value pairs must still be there */ for (key2 = -1000; key2 < key; ++key2) { n = rbtree_lookup(&rb, &key2); TEST_NOT_NULL(n); value = *((sqfs_u64 *)rbtree_node_value(n)); TEST_EQUAL_UI((sqfs_u64)(key2 + 10000), value); } /* insert key value pair */ value = key + 10000; TEST_ASSERT(rbtree_insert(&rb, &key, &value) == 0); count += 1; /* check if the tree has the right number of nodes */ TEST_EQUAL_UI(count_nodes_dfs(rb.root), count); /* check if it is still a binary tree */ check_binary_tree_dfs(rb.root); /* root node must be black. Every red node must have black children. */ TEST_ASSERT(!rb.root->is_red); check_colors_dfs(rb.root); /* every path from the root to a leave must have the same number of black nodes. */ blkdepth = get_ref_black_depth(&rb); check_black_depth_dfs(rb.root, blkdepth, 0); /* longest root to leaf path must be at most twice as long as the shortest. */ mind = min_depth(rb.root); maxd = max_depth(rb.root); TEST_ASSERT(maxd <= mind * 2); /* lookup of current key must work after insert */ n = rbtree_lookup(&rb, &key); TEST_NOT_NULL(n); value = *((sqfs_u64 *)rbtree_node_value(n)); TEST_EQUAL_UI((sqfs_u64)(key + 10000), value); } /* test if copy works */ ret = rbtree_copy(&rb, ©); TEST_EQUAL_I(ret, 0); TEST_EQUAL_UI(rb.key_size, copy.key_size); TEST_EQUAL_UI(rb.key_size_padded, copy.key_size_padded); TEST_EQUAL_UI(rb.value_size, copy.value_size); TEST_ASSERT(rb.key_compare == copy.key_compare); TEST_ASSERT(rb.root != copy.root); ret = check_subtrees_equal(rb.root, copy.root, rb.key_size_padded + rb.value_size); TEST_EQUAL_I(ret, 0); /* cleanup */ rbtree_cleanup(&rb); rbtree_cleanup(©); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libutil/str_table.c000066400000000000000000000027461410627516300220600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * str_table.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "str_table.h" #include "fstream.h" #include "compat.h" #include "../test.h" static char *strings[1000]; static int read_strings(void) { istream_t *fp; ssize_t ret; char *line; int i; fp = istream_open_file("words.txt"); TEST_NOT_NULL(fp); for (i = 0; i < 1000; ++i) { ret = istream_get_line(fp, &line, NULL, 0); TEST_EQUAL_I(ret, 0); strings[i] = line; } sqfs_destroy(fp); return 0; } int main(void) { str_table_t table; size_t i, j, idx; const char *str; TEST_ASSERT(chdir(TEST_PATH) == 0); if (read_strings()) return EXIT_FAILURE; TEST_ASSERT(str_table_init(&table) == 0); for (i = 0; i < 1000; ++i) { TEST_ASSERT(str_table_get_index(&table, strings[i], &idx) == 0); TEST_EQUAL_UI(idx, i); for (j = 0; j <= i; ++j) { str = str_table_get_string(&table, j); TEST_NOT_NULL(str); TEST_ASSERT(str != strings[i]); TEST_STR_EQUAL(str, strings[j]); } for (; j < 1000; ++j) TEST_NULL(str_table_get_string(&table, j)); } for (i = 0; i < 1000; ++i) { TEST_ASSERT(str_table_get_index(&table, strings[i], &idx) == 0); TEST_EQUAL_UI(idx, i); str = str_table_get_string(&table, i); TEST_NOT_NULL(str); TEST_ASSERT(str != strings[i]); TEST_STR_EQUAL(str, strings[i]); } str_table_cleanup(&table); for (i = 0; i < 1000; ++i) free(strings[i]); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libutil/threadpool.c000066400000000000000000000042161410627516300222340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "threadpool.h" #include "../test.h" #if defined(_WIN32) || defined(__WINDOWS__) #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include #endif #include static int worker(void *user, void *work_item) { unsigned int value = *((unsigned int *)work_item); (void)user; #if defined(_WIN32) || defined(__WINDOWS__) Sleep(100 * value); #else { struct timespec sp; sp.tv_sec = value / 10; sp.tv_nsec = 100000000; sp.tv_nsec *= (long)(value % 10); nanosleep(&sp, NULL); } #endif *((unsigned int *)work_item) = 42; return 0; } int main(void) { unsigned int values[10]; thread_pool_t *pool; unsigned int *ptr; size_t i, count; int ret; pool = thread_pool_create(10, worker); TEST_NOT_NULL(pool); count = pool->get_worker_count(pool); TEST_ASSERT(count >= 1); /* dequeue on empty pool MUST NOT deadlock */ ptr = pool->dequeue(pool); TEST_NULL(ptr); for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { values[i] = sizeof(values) / sizeof(values[0]) - i; ret = pool->submit(pool, values + i); TEST_EQUAL_I(ret, 0); } for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { ptr = pool->dequeue(pool); TEST_NOT_NULL(ptr); TEST_ASSERT(ptr == (values + i)); TEST_EQUAL_UI(*ptr, 42); } ptr = pool->dequeue(pool); TEST_NULL(ptr); pool->destroy(pool); /* redo the same test with the serial implementation */ pool = thread_pool_create_serial(worker); TEST_NOT_NULL(pool); ptr = pool->dequeue(pool); TEST_NULL(ptr); count = pool->get_worker_count(pool); TEST_EQUAL_UI(count, 1); for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { values[i] = sizeof(values) / sizeof(values[0]) - i; ret = pool->submit(pool, values + i); TEST_EQUAL_I(ret, 0); } for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { ptr = pool->dequeue(pool); TEST_NOT_NULL(ptr); TEST_ASSERT(ptr == (values + i)); TEST_EQUAL_UI(*ptr, 42); } ptr = pool->dequeue(pool); TEST_NULL(ptr); pool->destroy(pool); return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/libutil/words.txt000066400000000000000000000145721410627516300216340ustar00rootroot00000000000000a ability able about above accept according account across act action activity actually add address administration admit adult affect after again against age agency agent ago agree agreement ahead air all allow almost alone along already also although always American among amount analysis and animal another answer any anyone anything appear apply approach area argue arm around arrive art article artist as ask assume at attack attention attorney audience author authority available avoid away baby back bad bag ball bank bar base be beat beautiful because become bed before begin behavior behind believe benefit best better between beyond big bill billion bit black blood blue board body book born both box boy break bring brother budget build building business but buy by call camera campaign can cancer candidate capital car card care career carry case catch cause cell center central century certain certainly chair challenge chance change character charge check child choice choose church citizen city civil claim class clear clearly close coach cold collection college color come commercial common community company compare computer concern condition conference Congress consider consumer contain continue control cost could country couple course court cover create crime cultural culture cup current customer cut dark data daughter day dead deal death debate decade decide decision deep defense degree Democrat democratic describe design despite detail determine develop development die difference different difficult dinner direction director discover discuss discussion disease do doctor dog door down draw dream drive drop drug during each early east easy eat economic economy edge education effect effort eight either election else employee end energy enjoy enough enter entire environment environmental especially establish even evening event ever every everybody everyone everything evidence exactly example executive exist expect experience expert explain eye face fact factor fail fall family far fast father fear federal feel feeling few field fight figure fill film final finally financial find fine finger finish fire firm first fish five floor fly focus follow food foot for force foreign forget form former forward four free friend from front full fund future game garden gas general generation get girl give glass go goal good government great green ground group grow growth guess gun guy hair half hand hang happen happy hard have he head health hear heart heat heavy help her here herself high him himself his history hit hold home hope hospital hot hotel hour house how however huge human hundred husband I idea identify if image imagine impact important improve in include including increase indeed indicate individual industry information inside instead institution interest interesting international interview into investment involve issue it item its itself job join just keep key kid kill kind kitchen know knowledge land language large last late later laugh law lawyer lay lead leader learn least leave left leg legal less let letter level lie life light like likely line list listen little live local long look lose loss lot love low machine magazine main maintain major majority make man manage management manager many market marriage material matter may maybe me mean measure media medical meet meeting member memory mention message method middle might military million mind minute miss mission model modern moment money month more morning most mother mouth move movement movie Mr Mrs much music must my myself name nation national natural nature near nearly necessary need network never new news newspaper next nice night no none nor north not note nothing notice now n't number occur of off offer office officer official often oh oil ok old on once one only onto open operation opportunity option or order organization other others our out outside over own owner page pain painting paper parent part participant particular particularly partner party pass past patient pattern pay peace people per perform performance perhaps period person personal phone physical pick picture piece place plan plant play player PM point police policy political politics poor popular population position positive possible power practice prepare present president pressure pretty prevent price private probably problem process produce product production professional professor program project property protect prove provide public pull purpose push put quality question quickly quite race radio raise range rate rather reach read ready real reality realize really reason receive recent recently recognize record red reduce reflect region relate relationship religious remain remember remove report represent Republican require research resource respond response responsibility rest result return reveal rich right rise risk road rock role room rule run safe same save say scene school science scientist score sea season seat second section security see seek seem sell send senior sense series serious serve service set seven several sex sexual shake share she shoot short shot should shoulder show side sign significant similar simple simply since sing single sister sit site situation six size skill skin small smile so social society soldier some somebody someone something sometimes son song soon sort sound source south southern space speak special specific speech spend sport spring staff stage stand standard star start state statement station stay step still stock stop store story strategy street strong structure student study stuff style subject success successful such suddenly suffer suggest summer support sure surface system table take talk task tax teach teacher team technology television tell ten tend term test than thank that the their them themselves then theory there these they thing think third this those though thought thousand threat three through throughout throw thus time to today together tonight too top total tough toward town trade traditional training travel treat treatment tree trial trip trouble true truth try turn TV two type under understand unit until up upon us use usually value various very victim view violence visit voice vote wait walk wall want war watch water way we weapon wear week weight well west western what whatever when where whether which while white who whole whom whose why wide wife will win wind window wish with within without woman wonder word work worker world worry would write writer wrong yard yeah year yes yet you young your yourself squashfs-tools-ng-1.1.3/tests/libutil/xxhash.c000066400000000000000000000027251410627516300214010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xxhash.c * * Copyright (C) 2020 David Oberhollenzer */ #include "config.h" #include "util.h" #include "../test.h" static const struct { const char *plaintext; size_t psize; sqfs_u32 digest; } test_vectors[] = { { .plaintext = "\x9e", .psize = 1, .digest = 0xB85CBEE5, }, { .plaintext = "\x9e\xff\x1f\x4b\x5e\x53\x2f\xdd" "\xb5\x54\x4d\x2a\x95\x2b", .psize = 14, .digest = 0xE5AA0AB4, }, { .plaintext = "\x9e\xff\x1f\x4b\x5e\x53\x2f\xdd" "\xb5\x54\x4d\x2a\x95\x2b\x57\xae" "\x5d\xba\x74\xe9\xd3\xa6\x4c\x98" "\x30\x60\xc0\x80\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00", .psize = 101, .digest = 0x018F52BC, }, }; int main(void) { sqfs_u32 hash; size_t i; for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); ++i) { hash = xxh32(test_vectors[i].plaintext, test_vectors[i].psize); if (hash != test_vectors[i].digest) { fprintf(stderr, "Test case " PRI_SZ " failed!\n", i); fprintf(stderr, "Expected result: 0x%08X\n", test_vectors[i].digest); fprintf(stderr, "Actual result: 0x%08X\n", hash); return EXIT_FAILURE; } } return EXIT_SUCCESS; } squashfs-tools-ng-1.1.3/tests/pack_dir_root.sh.in000077500000000000000000000010471410627516300220450ustar00rootroot00000000000000#!/bin/sh set -e LICDIR="@abs_top_srcdir@/licenses" REFFILE="@abs_top_srcdir@/tests/pack_dir_root.txt.ref" GENSQFS="@abs_top_builddir@/gensquashfs" RDSQFS="@abs_top_builddir@/rdsquashfs" IMAGE="pack_dir_root.sqfs" SED="@SED@" if [ ! -f "$GENSQFS" -a -f "${GENSQFS}.exe" ]; then GENSQFS="${GENSQFS}.exe" RDSQFS="${RDSQFS}.exe" fi "$GENSQFS" --all-root --pack-dir "$LICDIR" --defaults mtime=0 \ -c gzip -q "$IMAGE" "$RDSQFS" -l / "$IMAGE" | "$SED" 's/^-[rwx-]* //g' > "${IMAGE}.txt" diff "$REFFILE" "${IMAGE}.txt" rm "$IMAGE" "${IMAGE}.txt" squashfs-tools-ng-1.1.3/tests/pack_dir_root.txt.ref000066400000000000000000000003041410627516300224100ustar00rootroot000000000000000/0 607 0BSD.txt 0/0 18k GPLv2.txt 0/0 34k GPLv3.txt 0/0 7k LGPLv3.txt 0/0 1k LZ4.txt 0/0 1k hash_table.txt 0/0 6k musl.txt 0/0 1k xxhash.txt 0/0 768 xz.txt 0/0 983 zlib.txt 0/0 1k zstd.txt squashfs-tools-ng-1.1.3/tests/rdsquashfs/000077500000000000000000000000001410627516300204435ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/rdsquashfs/pathtraversal.sh.in000066400000000000000000000004171410627516300242660ustar00rootroot00000000000000#!/bin/sh RDSQFS="@abs_top_builddir@/rdsquashfs" REFFILE="@abs_top_srcdir@/tests/rdsquashfs/pathtraversal.sqfs" GOTCHA="/tmp/gotcha.txt" if "$RDSQFS" -u / -p . "$REFFILE"; then if [ -e "$GOTCHA" ]; then echo "Found $GOTCHA which should not be there" exit 1 fi fi squashfs-tools-ng-1.1.3/tests/rdsquashfs/pathtraversal.sqfs000066400000000000000000000100001410627516300242100ustar00rootroot00000000000000hsqs PThis file has been created through a path traversal bug. BxcbX Fհ@if VbV0?\jH~In!HTl v.xc`F(ɐ_WRQeb0Ta squashfs-tools-ng-1.1.3/tests/tar2sqfs/000077500000000000000000000000001410627516300200255ustar00rootroot00000000000000squashfs-tools-ng-1.1.3/tests/tar2sqfs/simple.tar000066400000000000000000000500001410627516300220210ustar00rootroot00000000000000./0000775000175000017500000000000013747277110011066 5ustar goliathgoliath./bar/0000775000175000017500000000000013747277045011641 5ustar goliathgoliath./bar/bar0000664000175000017500000000000413747276672012327 0ustar goliathgoliathbar ./bar/baz0000664000175000017500000000000413747276665012341 0ustar goliathgoliathfoo ./bar/foo0000664000175000017500000000000013747276665013743 1./bar/bazustar goliathgoliath./baz/0000775000175000017500000000000013747277074011653 5ustar goliathgoliath./baz/bar0000664000175000017500000000000413747276672012337 0ustar goliathgoliathbar ./baz/baz0000664000175000017500000000000413747276665012351 0ustar goliathgoliathfoo ./baz/foo0000664000175000017500000000000013747276665013763 1./baz/bazustar goliathgoliath./foo/0000775000175000017500000000000013747277100011650 5ustar goliathgoliath./foo/bar0000664000175000017500000000000413747276672012346 0ustar goliathgoliathbar ./foo/baz0000664000175000017500000000000413747276665012360 0ustar goliathgoliathfoo ./foo/foo0000664000175000017500000000000013747276665014001 1./foo/bazustar goliathgoliathsquashfs-tools-ng-1.1.3/tests/tarcompress.sh.in000066400000000000000000000027441410627516300215720ustar00rootroot00000000000000#!/bin/sh set -e TAR2SQFS="@abs_top_builddir@/tar2sqfs" RDSQFS="@abs_top_builddir@/rdsquashfs" URL="https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/" DIR="/dev/shm/tarcompress/" mkdir -p "$DIR" curl "$URL/linux-3.11.tar.bz2" > "$DIR/linux-3.11.tar.bz2" curl "$URL/linux-3.11.tar.gz" > "$DIR/linux-3.11.tar.gz" curl "$URL/linux-3.11.tar.xz" > "$DIR/linux-3.11.tar.xz" sha256sum -c <<_EOF e7ae11d20aafdc5a2dfd104f198d87e5ce65fa8ada1ce18a263e89ff0148e1fa $DIR/linux-3.11.tar.bz2 59904beb1fdae62eb1991f29926c234283c9e4009b6e437499f13d2749bed6ca $DIR/linux-3.11.tar.gz 803ec8f0ad4b2ddedcb0332a590cd2b5e10dfc57c3b1c95bc9c46af81d51d7f9 $DIR/linux-3.11.tar.xz _EOF "$TAR2SQFS" "$DIR/out1.sqfs" < "$DIR/linux-3.11.tar.bz2" "$TAR2SQFS" "$DIR/out2.sqfs" < "$DIR/linux-3.11.tar.gz" "$TAR2SQFS" "$DIR/out3.sqfs" < "$DIR/linux-3.11.tar.xz" # set -e makes sure this explodes if they aren't equal diff "$DIR/out1.sqfs" "$DIR/out2.sqfs" diff "$DIR/out2.sqfs" "$DIR/out3.sqfs" rm "$DIR/out2.sqfs" "$DIR/out3.sqfs" rm "$DIR/linux-3.11.tar.bz2" rm "$DIR/linux-3.11.tar.gz" # every file that exists in the tar ball MUST exist in the squashfs image # and they MUST be identical tar -C "$DIR" -xf "$DIR/linux-3.11.tar.xz" find "$DIR/linux-3.11" -type f | sed "s#$DIR/##" | \ while read fname; do "$RDSQFS" -c "$fname" "$DIR/out1.sqfs" > "$DIR/temp" diff "$DIR/$fname" "$DIR/temp" done # cleanup rm "$DIR/temp" "$DIR/out1.sqfs" "$DIR/linux-3.11.tar.xz" rm --one-file-system -rf "$DIR/linux-3.11" rmdir "$DIR" squashfs-tools-ng-1.1.3/tests/test.h000066400000000000000000000053621410627516300174160ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * test.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef TEST_H #define TEST_H #include #include #include #include #include #define STR(x) #x #define STRVALUE(x) STR(x) #define TEST_PATH STRVALUE(TESTPATH) #if defined(__GNUC__) || defined(__clang__) # define ATTRIB_UNUSED __attribute__ ((unused)) #else # define ATTRIB_UNUSED #endif static ATTRIB_UNUSED FILE *test_open_read(const char *path) { FILE *fp = fopen(path, "rb"); if (fp == NULL) { perror(path); abort(); } return fp; } static ATTRIB_UNUSED void test_assert(const char *expr, int value, int linenum) { if (value == 0) { fprintf(stderr, "%d: '%s' is false!\n", linenum, expr); abort(); } } static ATTRIB_UNUSED void test_str_equal(const char *expr, const char *value, const char *ref, int linenum) { if (strcmp(value, ref) != 0) { fprintf(stderr, "%d: '%s' should be '%s', but actually is '%s'!\n", linenum, expr, ref, value); abort(); } } static ATTRIB_UNUSED void test_not_null(const void *value, const char *expr, int linenum) { if (value == NULL) { fprintf(stderr, "%d: '%s' should not be NULL, but is!\n", linenum, expr); abort(); } } static ATTRIB_UNUSED void test_null(const void *value, const char *expr, int linenum) { if (value != NULL) { fprintf(stderr, "%d: '%s' should be NULL, but is!\n", linenum, expr); abort(); } } static ATTRIB_UNUSED void test_equal_ul(const char *lhs, const char *rhs, unsigned long lval, unsigned long rval, int linenum) { if (lval != rval) { fprintf(stderr, "%d: %s (%lu) does not equal %s (%lu)!\n", linenum, lhs, lval, rhs, rval); abort(); } } static ATTRIB_UNUSED void test_equal_sl(const char *lhs, const char *rhs, long lval, long rval, int linenum) { if (lval != rval) { fprintf(stderr, "%d: %s (%ld) does not equal %s (%ld)!\n", linenum, lhs, lval, rhs, rval); abort(); } } static ATTRIB_UNUSED void test_lt_ul(const char *lhs, const char *rhs, unsigned long lval, unsigned long rval, int linenum) { if (lval >= rval) { fprintf(stderr, "%d: %s (%lu) is not less than %s (%lu)!\n", linenum, lhs, lval, rhs, rval); abort(); } } #define TEST_STR_EQUAL(str, ref) test_str_equal(#str, str, ref, __LINE__) #define TEST_NULL(expr) test_null(expr, #expr, __LINE__) #define TEST_NOT_NULL(expr) test_not_null(expr, #expr, __LINE__) #define TEST_EQUAL_I(a, b) test_equal_sl(#a, #b, a, b, __LINE__) #define TEST_EQUAL_UI(a, b) test_equal_ul(#a, #b, a, b, __LINE__) #define TEST_LESS_THAN_UI(a, b) test_lt_ul(#a, #b, a, b, __LINE__) #define TEST_ASSERT(expr) test_assert(#expr, (expr), __LINE__) #endif /* TEST_H */ squashfs-tools-ng-1.1.3/tests/test_tar_sqfs.sh.in000077500000000000000000000022441410627516300221070ustar00rootroot00000000000000#!/bin/sh set -e TARDIR="@abs_top_srcdir@/tests/libtar/data" TARDIR2="@abs_top_srcdir@/tests/tar2sqfs" SHA512FILE="@abs_top_srcdir@/tests/libtar/data/sqfs.sha512" TAR2SQFS="@abs_top_builddir@/tar2sqfs" if [ ! -f "$TAR2SQFS" -a -f "${TAR2SQFS}.exe" ]; then TAR2SQFS="${TAR2SQFS}.exe" fi # process tar files used for conformance tests for filename in $(find "$TARDIR" -name "*.tar" | grep -v ".*/file-size/.*"); do dir="$(dirname $filename | sed -n -e 's;.*/tests/;tests/;p')" imgname="$dir/$(basename $filename .tar).sqfs" mkdir -p "$dir" "$TAR2SQFS" --defaults mtime=0 -c gzip -q "$imgname" < "$filename" done # edge case test filename="$TARDIR2/simple.tar" dir="$(dirname $filename | sed -n -e 's;.*/tests/;tests/;p')" imgname="$dir/root-becomes.sqfs" mkdir -p "$dir" "$TAR2SQFS" --root-becomes foo --defaults mtime=0 \ -c gzip -q "$imgname" < "$filename" # verify sha512sum -c "$SHA512FILE" # cleanup rm "$dir/root-becomes.sqfs" for filename in $(find "$TARDIR" -name "*.tar" | grep -v ".*/file-size/.*"); do dir="$(dirname $filename | sed -n -e 's;.*/tests/;tests/;p')" imgname="$dir/$(basename $filename .tar).sqfs" rm "$imgname" rmdir -p "$dir" || true done