pax_global_header00006660000000000000000000000064146147220460014520gustar00rootroot0000000000000052 comment=8f9966c8ea3ea8a854941d041e7fcb9eb4f772fb squashfs-tools-ng-1.3.1/000077500000000000000000000000001461472204600151175ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/.github/000077500000000000000000000000001461472204600164575ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/.github/workflows/000077500000000000000000000000001461472204600205145ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/.github/workflows/codeql-analysis.yml000066400000000000000000000007471461472204600243370ustar00rootroot00000000000000name: "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@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 - name: Autobuild uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 squashfs-tools-ng-1.3.1/.gitignore000066400000000000000000000007731461472204600171160ustar00rootroot00000000000000.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 tests/tar2sqfs/*.sh /mknastyfs /mk42sqfs /extract_one /list_files /sqfsbrowse /xattr_benchmark squashfs-tools-ng-1.3.1/.travis.yml000066400000000000000000000043251461472204600172340ustar00rootroot00000000000000language: 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.3.1/CHANGELOG.md000066400000000000000000000603151461472204600167350ustar00rootroot00000000000000# 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.3.1] - 2024-05-02 ### Fixed - gensquashfs: apply xattr file also when using a pack file (#122) - Fix broken C++ guard in rbtree.h - fixed fragment table size typo (#126) ## [1.3.0] - 2024-03-11 ### Added - rdsquashfs: improve unpacking error message on Windows - rdsquashfs: add a workaround for unpacking forbidden filenames on Windows - tar2sqfs: Add option to exclude files ### Fixed - Fix compressor ID enumerator in format documentation ### Changed - libsqfs: add a threshold for extended directory inodes with index ## [1.2.0] - 2022-12-03 ### Added - Make it possible for `gensquashfs` to add xattrs from a description file - Make it possible for `gensquashfs` to specify file sorting order - Support `.` and `..` directory browsing support in `sqfs_dir_reader_t` - Add `sqfs_tree_node_get_path` to `libsquashfs` ### Fixed - Only use actually available CPUs in `gensquashfs` and `tar2sqfs` - Overzealous bounds check in the `libsquashfs `block processor (#110) - In `sqfs_compressor_create`, clear the output pointer on error (#110) - Typo in `gensquashfs` and `tar2sqfs` block count statistics (#108) - Hard link resolution: report error if path cannonicalization fails - Documentation: signedness of the directory header inode number - Seek position in `libsquashfs` meta data reader erroneously out-of-bounds - Memory leak in `libfstree` error path - Double-free in `libfstree` error path ### Changed - Allow NULL pointer for `sqfs_destroy`, `sqfs_dir_tree_destroy` - Use 32 bit hard link counter in `libfstree` instead of just 16 bit - Internal cleanup and restructuring - Upgrade autotools version - Check in `autogen.sh` if the required tools are available - Trigger a build failure if no compressor library is available ## [1.1.4] - 2022-03-30 ### Added - libsquashfs: A flag for `sqfs_open_file` to *not* perform any local charset based transformations, e.g. UTF-8 to UTF-16 on Windows (#96). ### Fixed - libsquashfs: uninitializeed state in the directory reader caused by rewind call after reading an emtpy directory. - libsquashfs: stricter bounds checking in directory reader. - libsquashfs: Guard against multiplication overflow in the data reader on 32 bit systems. - structure alignment test on 32 bit x86 systems (#93) - Windows: error handling on FlushFileBuffers (#97) - Windows: loop exit condition in mkdir_p (#97) - Windows: error handling when reading from a pipe (#102) - Windows: heap corruption in stdout stream wrapper (#100) - Use the correct printf specifiers for 64 bit types on 32 bit systems. - Miscellaneous static analysis and compiler warnings. ### Changed - Windows: add a wrapper for main() that reads the UTF-16 command line and converts it to UTF-8 for propper Unicode support (#96). - Windows: when opening a file, treat the path as UTF-8, convert it to UTF-16 and use the WCHAR API for propper Unicode support (#96). - Windows: redirect stdio print functions, convert from UTF-8 to UTF-16 if the target is the console for propper Unicode support (#96). - libsquashfs: Windows: when opening files, interpret the API argument path as UTF-8, convert it to UTF-16 and use the WCHAR API, unless explictly told not to (#96). - Upgraded version of the builtin copy of zlib. ## [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.2.0]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.1.4...v1.2.0 [1.1.4]: https://github.com/AgentD/squashfs-tools-ng/compare/v1.1.3...v1.1.4 [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.3.1/COPYING.md000066400000000000000000000110331461472204600165470ustar00rootroot00000000000000# 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/util/hash_table.c`, `include/util/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. Some additional components of `lib/compat` are also LGPL licensed, to allow their inclusion in `libsquashfs`. See SPDX identifiers. 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`. - 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 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.3.1/Doxyfile.in000066400000000000000000000263041461472204600172370ustar00rootroot00000000000000# 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.3.1/Makefile.am000066400000000000000000000023201461472204600171500ustar00rootroot00000000000000ACLOCAL_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 licenses doc TESTS = include lib/sqfs/Makemodule.am include lib/util/Makemodule.am include lib/io/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/libutil/Makemodule.am include tests/libio/Makemodule.am include tests/libfstree/Makemodule.am include tests/libtar/Makemodule.am include tests/libsqfs/Makemodule.am include tests/gensquashfs/Makemodule.am include tests/rdsquashfs/Makemodule.am if HAVE_DOXYGEN @DX_RULES@ MOSTLYCLEANFILES = $(DX_CLEANFILES) endif squashfs-tools-ng-1.3.1/README.md000066400000000000000000000172451461472204600164070ustar00rootroot00000000000000# 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. # Package Versioning and Git Branches This package attempts to use [Semantic Versioning](https://semver.org/spec/v2.0.0.html). A [changelog](CHANGELOG.md) is maintained that summarizes changes between releases. Releases are tagged and gpg signed in the git tree and official release tarballs are generated using Autotools. The git `master` branch currently contains ongoing development for a future `2.0` release. The latest stable version is `1.2.0`, maintained in a `fixes-1.2.0` branch. Bug fixes that also affect the previous `1.1.0` minor version are back-ported to a `fixes-1.1.0` branch with occasionally patch level releases. The latest release from this branch is `1.1.4`. Older versions are no longer supported. Fixes were previously backported to the `fixes-1.0.0` branch, the final release is `1.0.7`. It is likely that support for `1.1.x` will also be dropped after version `2.0`. # Getting and Building the Source Code Official release tarballs can be obtained here: https://infraroot.at/pub/squashfs In between patch level releases, bug fixes are continuously published as individual patch files in `fixes-` directories. 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 announcements and I will continue to do so until I am booted off. squashfs-tools-ng-1.3.1/autogen.sh000077500000000000000000000025651461472204600171300ustar00rootroot00000000000000#!/bin/sh DIE=0 (autoreconf --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have autoconf installed to compile this package." echo "Download the appropriate package for your distribution," echo "or see http://www.gnu.org/software/autoconf" DIE=1 } (libtoolize --version) < /dev/null > /dev/null 2>&1 || (glibtoolize --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have libtool installed to compile this package." echo "Download the appropriate package for your distribution," echo "or see http://www.gnu.org/software/libtool" DIE=1 } (automake --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have automake installed to compile this package." echo "Download the appropriate package for your distribution," echo "or see http://www.gnu.org/software/automake" DIE=1 } if ! test -f "$(aclocal --print-ac-dir)"/pkg.m4; then echo echo "You must have pkg-config installed to compile this package." echo "Download the appropriate package for your distribution," echo "or see https://www.freedesktop.org/wiki/Software/pkg-config/" DIE=1 fi if test "$DIE" -eq 1; then exit 1 fi autoreconf --force --install --symlink if ! grep -q pkg.m4 aclocal.m4; then cat < */ #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, void *xattr_map, tree_node_t *node) { char *path = NULL; 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); path = NULL; if (ret) { ret = -1; goto out; } } #else (void)path_prefix; #endif if (selinux_handle != NULL || xattr_map != NULL) { path = fstree_get_path(node); if (path == NULL) { perror("reconstructing absolute path"); ret = -1; goto out; } } if (xattr_map != NULL) { ret = xattr_apply_map_file(path, xattr_map, xwr); if (ret) { ret = -1; goto out; } } if (selinux_handle != NULL) { ret = selinux_relable_node(selinux_handle, xwr, node, path); if (ret) { ret = -1; goto out; } } if (sqfs_xattr_writer_end(xwr, &node->xattr_idx)) { sqfs_perror(node->name, "completing xattr key-value pairs", ret); ret = -1; goto out; } if (S_ISDIR(node->mode)) { node = node->data.dir.children; while (node != NULL) { if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, scan_xattr, xattr_map, node)) { ret = -1; goto out; } node = node->next; } } out: free(path); return ret; } int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr) { if (xwr == NULL) return 0; if (selinux_handle == NULL && !scan_xattr && xattr_map == NULL) return 0; return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, xattr_map, fs->root); } squashfs-tools-ng-1.3.1/bin/gensquashfs/filemap_xattr.c000066400000000000000000000132261461472204600232250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * filemap_xattr.c * * Copyright (C) 2022 Enno Boland */ #include "fstree.h" #include "mkfs.h" #include #define NEW_FILE_START "# file: " static void print_error(const char *filename, size_t line_num, const char *err) { fprintf(stderr, "%s: " PRI_SZ ": %s\n", filename, line_num, err); } // Taken from attr-2.5.1/tools/setfattr.c static sqfs_u8 *decode(const char *filename, size_t line_num, const char *value, size_t *size) { sqfs_u8 *decoded = NULL; if (*size == 0) { decoded = (sqfs_u8 *)strdup(""); if (decoded == NULL) goto fail_alloc; return decoded; } if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { *size = ((*size) - 2) / 2; decoded = calloc(1, (*size) + 1); if (decoded == NULL) goto fail_alloc; if (hex_decode(value + 2, (*size) * 2, decoded, *size)) goto fail_encode; } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { size_t input_len = *size - 2; *size = (input_len / 4) * 3; decoded = calloc(1, (*size) + 1); if (decoded == NULL) goto fail_alloc; if (base64_decode(value + 2, input_len, decoded, size)) goto fail_encode; } else { const char *v = value, *end = value + *size; sqfs_u8 *d; if (end > v + 1 && *v == '"' && *(end - 1) == '"') { v++; end--; } decoded = calloc(1, (*size) + 1); if (decoded == NULL) goto fail_alloc; d = decoded; while (v < end) { if (v[0] == '\\') { if (v[1] == '\\' || v[1] == '"') { *d++ = *++v; v++; } else if (v[1] >= '0' && v[1] <= '7') { int c = 0; v++; c = (*v++ - '0'); if (*v >= '0' && *v <= '7') c = (c << 3) + (*v++ - '0'); if (*v >= '0' && *v <= '7') c = (c << 3) + (*v++ - '0'); *d++ = c; } else *d++ = *v++; } else *d++ = *v++; } *size = d - decoded; } return decoded; fail_alloc: fprintf(stderr, "out of memory\n"); return NULL; fail_encode: print_error(filename, line_num, "bad input encoding"); free(decoded); return NULL; } static int parse_file_name(const char *filename, size_t line_num, char *line, struct XattrMap *map) { struct XattrMapPattern *current_file; char *file_name = strdup(line + strlen(NEW_FILE_START)); if (file_name == NULL) goto fail_alloc; current_file = calloc(1, sizeof(struct XattrMapPattern)); if (current_file == NULL) goto fail_alloc; current_file->next = map->patterns; map->patterns = current_file; if (canonicalize_name(file_name)) { print_error(filename, line_num, "invalid absolute path"); free(current_file); free(file_name); return -1; } current_file->path = file_name; return 0; fail_alloc: fprintf(stderr, "out of memory\n"); free(file_name); return -1; } static int parse_xattr(const char *filename, size_t line_num, char *key_start, char *value_start, struct XattrMap *map) { size_t len; struct XattrMapPattern *current_pattern = map->patterns; struct XattrMapEntry *current_entry; if (current_pattern == NULL) { print_error(filename, line_num, "no file specified yet"); return -1; } current_entry = calloc(1, sizeof(struct XattrMapEntry)); if (current_entry == NULL) { return -1; } current_entry->next = current_pattern->entries; current_pattern->entries = current_entry; current_entry->key = strdup(key_start); len = strlen(value_start); current_entry->value = decode(filename, line_num, value_start, &len); current_entry->value_len = len; return 0; } void * xattr_open_map_file(const char *path) { struct XattrMap *map; size_t line_num = 1; char *p = NULL; istream_t *file = istream_open_file(path); if (file == NULL) { return NULL; } map = calloc(1, sizeof(struct XattrMap)); if (map == NULL) goto fail_close; for (;;) { char *line = NULL; int ret = istream_get_line(file, &line, &line_num, ISTREAM_LINE_LTRIM | ISTREAM_LINE_RTRIM | ISTREAM_LINE_SKIP_EMPTY); if (ret < 0) goto fail; if (ret > 0) break; if (strncmp(NEW_FILE_START, line, strlen(NEW_FILE_START)) == 0) { ret = parse_file_name(path, line_num, line, map); } else if ((p = strchr(line, '='))) { *(p++) = '\0'; ret = parse_xattr(path, line_num, line, p, map); } else if (line[0] != '#') { print_error(path, line_num, "not a key-value pair"); ret = -1; } ++line_num; free(line); if (ret < 0) goto fail; } sqfs_destroy(file); return map; fail: xattr_close_map_file(map); fail_close: sqfs_destroy(file); return NULL; } void xattr_close_map_file(void *xattr_map) { struct XattrMap *map = xattr_map; while (map->patterns != NULL) { struct XattrMapPattern *file = map->patterns; map->patterns = file->next; while (file->entries != NULL) { struct XattrMapEntry *entry = file->entries; file->entries = entry->next; free(entry->key); free(entry->value); free(entry); } free(file->path); free(file); } free(xattr_map); } int xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr) { struct XattrMap *xattr_map = map; int ret = 0; const struct XattrMapPattern *pat; const struct XattrMapEntry *entry; for (pat = xattr_map->patterns; pat != NULL; pat = pat->next) { char *patstr = pat->path; const char *stripped = path; if (patstr[0] != '/' && stripped[0] == '/') { stripped++; } if (strcmp(patstr, stripped) == 0) { printf("Applying xattrs for %s", path); for (entry = pat->entries; entry != NULL; entry = entry->next) { printf(" %s = \n", entry->key); fwrite(entry->value, entry->value_len, 1, stdout); puts("\n"); ret = sqfs_xattr_writer_add( xwr, entry->key, entry->value, entry->value_len); if (ret < 0) { return ret; } } } } return ret; } squashfs-tools-ng-1.3.1/bin/gensquashfs/fstree_from_dir.c000066400000000000000000000226111461472204600235350ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "mkfs.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; } fstree_insert_sorted(root, 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.3.1/bin/gensquashfs/fstree_from_file.c000066400000000000000000000307071461472204600237030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "io/file.h" #include "compat.h" #include "mkfs.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_stream(fstree_t *fs, istream_t *fp, const char *basepath) { const char *filename; size_t line_num = 1; char *line; int ret; filename = istream_get_filename(fp); 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; } return 0; fail_line: free(line); return -1; } int fstree_from_file(fstree_t *fs, const char *filename, const char *basepath) { istream_t *fp; int ret; fp = istream_open_file(filename); if (fp == NULL) return -1; ret = fstree_from_file_stream(fs, fp, basepath); sqfs_destroy(fp); return ret; } squashfs-tools-ng-1.3.1/bin/gensquashfs/gensquashfs.1000066400000000000000000000316441461472204600226370ustar00rootroot00000000000000.TH GENSQUASHFS "1" "November 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\-\-sort\-file\fR, \fB\-S\fR Specify a file that can override the order in which input files are packed, or affect packing behaviour (e.g. disable compression or fragmentation for certain files). .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 \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\-\-xattr-file\fR, \fB\-A\fR Read extended attributes from a file. The format for the file is identical to the output generated by `\fBgetfattr \-\-dump\fR`. .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 PACK 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 SORT FILE FORMAT The sort file is has one entry per line, consisting of a numeric priority and a filename. The name and the priority are separated by one or more space character (including tabs) and the line can be intended. Any leeding or preceeding spaces are dropped. The priority is a 64 bit number and can be negative. Files not listed in the sort file receive a default priority of 0. After processing the sort file, before packing the input files, the file list is sorted by priority, with lower values preceeding larger ones. The given filename is matched against the actual path of the file in the SquashFS file in the resulting image. It is \fInot\fR matched against the input path, which may differ. Any file is allowed to match only once. The first match encountered in the sort file will be used. Lines can be empty or contain a single line comment, started with \fB#\fR. Filenames can be wrapped can be wrapped in quotation marks ("...") if necessary, with \fB\\\fR serving as an escape character for quotation marks or itself. To control the packing behavior, an optional list of flags can be inserted between the priority and the filename. The flags are wrappe in brackets and comma separated, e.g. [flag1,flag2,...]. The following flags are supported: .TS tab(;) allbox; l l l l l l l l l l l l l l l l rd. \fBFlag\fR;\fBDescription\fR glob;T{ Interpret the filename as a shell glob pattern and match all files that the pattern applies to. This performs path globbing, i.e. a wild card character (\fB*\fR or \fB?\fR) or a bracket range (\fB[]\fR) cannot match a path separator. A slash must always be matched by an explicit slash. T} glob_no_path;T{ Same as \fBglob\fR, but disables path globbing. Wild cards are allowed to match slashes. T} align;T{ Force device block alignment of the matched files, i.e. the compressed output always starts at a multiple of the device block size. Padding is inserted before and after. T} dont_fragment;T{ Do not perform tail-end packing for the matched files, always generate a sequence of independent blocks. T} dont_compress;T{ Do not compress the matched files. Note that if tail-end packing is performed, the entire fragment block is left uncompressed. T} dont_deduplicate;T{ Do not perform deduplication on the matched files. If they are packed and the data blocks or the tail end happens to match a previously packed file, keep them anyway. T} nosparse;T{ Do not perform sparse file detection. If a matched file contians block of zero bytes, pack them as-is. T} .TE .PP .SS Example .PP .nf # Specify a packing order with file globbing -8000 [glob] bin/* -5000 [glob] lib/* # glob_no_path means * is allowed to match / -1000 [glob_no_path] share/* # Our boot loader needs this -100000 [dont_compress,dont_fragment,nosparse] boot/vmlinuz # For demonstration, a quoted filename and no flags 1337 "usr/share/my \\"special\\" file " .fi .SH XATTR FILE FORMAT The format for xattr files tries to be identical to the output of \fBgetfattr\fR. Attributes are listed as key-value pairs with an \fB=\fR sign in between. If a line starts with `\fB# file: \fR`, the rest of the line is interpreted as an absolute path that the following xattrs are applied to. Plain text values are wrapped in quotation marks ("...") and support some escape sequences. Currently supported are \fB\\"\fR, \fB\\\\\fR and \fB\\0\fR. Raw binary values can encoded as hexadecimal or base64, by starting the value with a \fB0x\fR or \fB0s\fR prefix respectively. .SS Example .PP .nf # file: dev/ security.selinux="system_u:object_r:device_t:s0" user.beverage_preference=0xCAFECAFEDECAFBAD # file: dev/rfkill security.selinux="system_u:object_r:wireless_device_t:s0" system.posix_acl_access=0sSGVsbG8gdGhlcmUgOi0pCg== .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.3.1/bin/gensquashfs/mkfs.c000066400000000000000000000107771461472204600213360ustar00rootroot00000000000000/* 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 = fi->flags; 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, void *xattrmap) { 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 (xattrmap != NULL) ret = xattr_apply_map_file(path, xattrmap, xwr); if (ret == 0 && selinux_handle != NULL) ret = selinux_relable_node(selinux_handle, xwr, n, path); free(path); if (ret == 0) ret = sqfs_xattr_writer_end(xwr, &n->xattr_idx); if (ret) { sqfs_perror(filename, "flushing completed key-value pairs", ret); return -1; } 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, xattrmap)) { return -1; } } } return 0; } static int read_fstree(fstree_t *fs, options_t *opt, sqfs_xattr_writer_t *xwr, void *selinux_handle, void *xattrmap) { int ret; ret = fstree_from_file(fs, opt->infile, opt->packdir); if (ret == 0 && (selinux_handle != NULL || xattrmap != NULL)) ret = relabel_tree_dfs(opt->cfg.filename, xwr, fs->root, selinux_handle, xattrmap); 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; istream_t *sortfile = NULL; void *sehnd = NULL; void *xattrmap = 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.xattr_file != NULL) { xattrmap = xattr_open_map_file(opt.xattr_file); if (xattrmap == NULL) goto out; } if (opt.sortfile != NULL) { sortfile = istream_open_file(opt.sortfile); if (sortfile == 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, xattrmap)) 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, xattrmap, sqfs.xwr, opt.scan_xattr)) { goto out; } } if (sortfile != NULL) { if (fstree_sort_files(&sqfs.fs, sortfile)) 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); if (sortfile != NULL) sqfs_destroy(sortfile); free(opt.packdir); return status; } squashfs-tools-ng-1.3.1/bin/gensquashfs/mkfs.h000066400000000000000000000062751461472204600213410ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mkfs.h * * Copyright (C) 2019 David Oberhollenzer * Copyright (C) 2022 Enno Boland */ #ifndef MKFS_H #define MKFS_H #include "config.h" #include "common.h" #include "fstree.h" #include "util/util.h" #include "io/file.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; const char *xattr_file; const char *sortfile; 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; struct XattrMapEntry { char *key; sqfs_u8 *value; size_t value_len; struct XattrMapEntry *next; }; struct XattrMapPattern { char *path; struct XattrMapEntry *entries; struct XattrMapPattern *next; }; struct XattrMap { struct XattrMapPattern *patterns; }; void process_command_line(options_t *opt, int argc, char **argv); int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr); void *xattr_open_map_file(const char *path); int xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr); void xattr_close_map_file(void *xattr_map); 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); /* 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); int fstree_from_file_stream(fstree_t *fs, istream_t *file, const char *basepath); /* 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); int fstree_sort_files(fstree_t *fs, istream_t *sortfile); #endif /* MKFS_H */ squashfs-tools-ng-1.3.1/bin/gensquashfs/options.c000066400000000000000000000300251461472204600220550ustar00rootroot00000000000000/* 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 { "xattr-file", required_argument, NULL, 'A' }, { "sort-file", required_argument, NULL, 'S' }, { "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:S:A: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"; static const char *pack_options = " --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. The directory becomes the\n" " file system root.\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"; const char *extra_options = " --sort-file, -S Specify a \"sort file\" that can be used to\n" " micro manage the order of files during packing\n" " and behaviour (compression, fragmentation, ..)\n" "\n" #ifdef WITH_SELINUX " --selinux, -s Specify an SELinux label file to get context\n" " attributes from.\n" #endif " --xattr-file, -A Specify an Xattr file to get extended attributes\n" " for loading xattrs\n" " --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 *pack_details = "Example of a pack file:\n" "\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" " \n" " # `slink` for symlink, `link` for hard links\n" " slink /lib 0777 0 0 /usr/lib\n" " link /init 0777 0 0 /sbin/init\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"; const char *sort_details = "When using a sort file, the specified paths are within the SquashFS image.\n" "Files with lower priority are packed first, default priority is 0.\n" "The sorting is stable, files with the same priority do not change place\n" "relative to each other.\n" "\n" "Example:\n" " # Specify a packing order with file globbing\n" " -8000 [glob] bin/*\n" " -5000 [glob] lib/*\n" "\n" " # glob_no_path means * is allowed to match /\n" " -1000 [glob_no_path] share/*\n" "\n" " # Our boot loader needs this\n" " -100000 [dont_compress,dont_fragment,nosparse] boot/vmlinuz\n" "\n" " # For demonstration, a quoted filename and no flags\n" " 1337 \"usr/share/my \\\"special\\\" file \"\n" "\n\n"; static const char *xattr_details = "The format of xattr files tries to immitate the format generated\n" "by `getfattr --dump`.\n" "\n" "Example:\n" " # file: dev/\n" " security.selinux=\"system_u:object_r:device_t:s0\"\n" " user.beverage_preference=0xCAFECAFEDECAFBAD\n" "\n" " # file: dev/rfkill\n" " security.selinux=\"system_u:object_r:wireless_device_t:s0\"\n" " system.posix_acl_access=0sSGVsbG8gdGhlcmUgOi0pCg==\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 'A': opt->xattr_file = optarg; break; case 'S': opt->sortfile = optarg; break; case 'h': fputs(help_string, stdout); printf(pack_options, SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE); fputs(extra_options, stdout); fputs(pack_details, stdout); fputs(sort_details, stdout); fputs(xattr_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.3.1/bin/gensquashfs/selinux.c000066400000000000000000000031701461472204600220520ustar00rootroot00000000000000/* 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.3.1/bin/gensquashfs/sort_by_file.c000066400000000000000000000153561461472204600230540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sort_by_file.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "fstree.h" #include "mkfs.h" #include "sqfs/block.h" #include #include #include static int decode_priority(const char *filename, size_t line_no, char *line, sqfs_s64 *priority) { bool negative = false; size_t i = 0; if (line[0] == '-') { negative = true; i = 1; } if (!isdigit(line[i])) goto fail_number; *priority = 0; for (; isdigit(line[i]); ++i) { sqfs_s64 x = line[i] - '0'; if ((*priority) >= ((0x7FFFFFFFFFFFFFFFL - x) / 10L)) goto fail_ov; (*priority) = (*priority) * 10 + x; } if (!isspace(line[i])) goto fail_filename; while (isspace(line[i])) ++i; if (line[i] == '\0') goto fail_filename; if (negative) (*priority) = -(*priority); memmove(line, line + i, strlen(line + i) + 1); return 0; fail_number: fprintf(stderr, "%s: " PRI_SZ ": Line must start with " "numeric sort priority.\n", filename, line_no); return -1; fail_ov: fprintf(stderr, "%s: " PRI_SZ ": Numeric overflow in sort priority.\n", filename, line_no); return -1; fail_filename: fprintf(stderr, "%s: " PRI_SZ ": Expacted ` ` " "after sort priority.\n", filename, line_no); return -1; } static int decode_filename(const char *filename, size_t line_no, char *buffer) { char *src, *dst; if (buffer[0] == '"') { src = buffer + 1; dst = buffer; for (;;) { if (src[0] == '\0') goto fail_match; if (src[0] == '"') { ++src; break; } if (src[0] == '\\') { switch (src[1]) { case '\\': *(dst++) = '\\'; src += 2; break; case '"': *(dst++) = '"'; src += 2; break; default: goto fail_escape; } } else { *(dst++) = *(src++); } } if (*src != '\0') return -1; } if (canonicalize_name(buffer)) goto fail_canon; return 0; fail_canon: fprintf(stderr, "%s: " PRI_SZ ": Malformed filename.\n", filename, line_no); return -1; fail_escape: fprintf(stderr, "%s: " PRI_SZ ": Unknown escape sequence `\\%c` " "in filename.\n", filename, line_no, src[1]); return -1; fail_match: fprintf(stderr, "%s: " PRI_SZ ": Unmatched '\"' in filename.\n", filename, line_no); return -1; } static int decode_flags(const char *filename, size_t line_no, bool *do_glob, bool *path_glob, int *flags, char *line) { char *start = line; *do_glob = false; *path_glob = false; *flags = 0; if (*(line++) != '[') return 0; for (;;) { while (isspace(*line)) ++line; if (*line == ']') { ++line; break; } if (strncmp(line, "glob_no_path", 12) == 0) { line += 12; *do_glob = true; *path_glob = false; } else if (strncmp(line, "glob", 4) == 0) { line += 4; *do_glob = true; *path_glob = true; } else if (strncmp(line, "dont_fragment", 13) == 0) { line += 13; (*flags) |= SQFS_BLK_DONT_FRAGMENT; } else if (strncmp(line, "align", 5) == 0) { line += 5; (*flags) |= SQFS_BLK_ALIGN; } else if (strncmp(line, "dont_compress", 13) == 0) { line += 13; (*flags) |= SQFS_BLK_DONT_COMPRESS; } else if (strncmp(line, "dont_deduplicate", 16) == 0) { line += 16; (*flags) |= SQFS_BLK_DONT_DEDUPLICATE; } else if (strncmp(line, "nosparse", 8) == 0) { line += 8; (*flags) |= SQFS_BLK_IGNORE_SPARSE; } else { goto fail_flag; } while (isspace(*line)) ++line; if (*line == ']') { ++line; break; } if (*(line++) != ',') goto fail_sep; } if (!isspace(*line)) goto fail_fname; while (isspace(*line)) ++line; memmove(start, line, strlen(line) + 1); return 0; fail_fname: fprintf(stderr, "%s: " PRI_SZ ": Expected ` ` " "after flag list.\n", filename, line_no); return -1; fail_sep: fprintf(stderr, "%s: " PRI_SZ ": Unexpected '%c' after flag.\n", filename, line_no, *line); return -1; fail_flag: fprintf(stderr, "%s: " PRI_SZ ": Unknown flag `%.3s...`.\n", filename, line_no, line); return -1; } static void sort_file_list(fstree_t *fs) { file_info_t *out = NULL, *out_last = NULL; while (fs->files != NULL) { sqfs_s64 lowest = fs->files->priority; file_info_t *it, *prev; for (it = fs->files; it != NULL; it = it->next) { if (it->priority < lowest) lowest = it->priority; } it = fs->files; prev = NULL; while (it != NULL) { if (it->priority != lowest) { prev = it; it = it->next; continue; } if (prev == NULL) { fs->files = it->next; } else { prev->next = it->next; } if (out == NULL) { out = it; } else { out_last->next = it; } out_last = it; it = it->next; out_last->next = NULL; } } fs->files = out; } int fstree_sort_files(fstree_t *fs, istream_t *sortfile) { const char *filename; size_t line_num = 1; file_info_t *it; for (it = fs->files; it != NULL; it = it->next) { it->priority = 0; it->flags = 0; it->already_matched = false; } filename = istream_get_filename(sortfile); for (;;) { bool do_glob, path_glob, have_match; char *line = NULL; sqfs_s64 priority; int ret, flags; ret = istream_get_line(sortfile, &line, &line_num, ISTREAM_LINE_LTRIM | ISTREAM_LINE_RTRIM | ISTREAM_LINE_SKIP_EMPTY); if (ret != 0) { free(line); if (ret < 0) return -1; break; } if (line[0] == '#') { free(line); continue; } if (decode_priority(filename, line_num, line, &priority)) { free(line); return -1; } if (decode_flags(filename, line_num, &do_glob, &path_glob, &flags, line)) { free(line); return -1; } if (decode_filename(filename, line_num, line)) { free(line); return -1; } have_match = false; for (it = fs->files; it != NULL; it = it->next) { tree_node_t *node; char *path; if (it->already_matched) continue; node = container_of(it, tree_node_t, data.file); path = fstree_get_path(node); if (path == NULL) { fprintf(stderr, "%s: " PRI_SZ ": out-of-memory\n", filename, line_num); free(line); return -1; } if (canonicalize_name(path)) { fprintf(stderr, "%s: " PRI_SZ ": [BUG] error " "reconstructing node path\n", filename, line_num); free(line); free(path); return -1; } if (do_glob) { ret = fnmatch(line, path, path_glob ? FNM_PATHNAME : 0); } else { ret = strcmp(path, line); } free(path); if (ret == 0) { have_match = true; it->flags = flags; it->priority = priority; it->already_matched = true; if (!do_glob) break; } } if (!have_match) { fprintf(stderr, "WARNING: %s: " PRI_SZ ": no match " "for '%s'.\n", filename, line_num, line); } free(line); } sort_file_list(fs); return 0; } squashfs-tools-ng-1.3.1/bin/rdsquashfs/000077500000000000000000000000001461472204600200525ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/bin/rdsquashfs/Makemodule.am000066400000000000000000000011501461472204600224510ustar00rootroot00000000000000rdsquashfs_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 libio.a libcompat.a libutil.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.3.1/bin/rdsquashfs/describe.c000066400000000000000000000066521461472204600220070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * describe.c * * Copyright (C) 2019 David Oberhollenzer */ #include "rdsquashfs.h" static char *get_name(const sqfs_tree_node_t *n) { char *name; int ret; ret = sqfs_tree_node_get_path(n, &name); if (ret != 0) { sqfs_perror(NULL, "Recovering file path of tree node", ret); return NULL; } if (canonicalize_name(name) != 0) { fprintf(stderr, "Error sanitizing file path '%s'\n", name); sqfs_free(name); return NULL; } return name; } static void print_stub(const char *type, const char *name, const sqfs_tree_node_t *n) { const char *start, *ptr; printf("%s ", type); if (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); } 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) { char *name = get_name(n); if (name == NULL) return -1; print_stub(type, name, n); sqfs_free(name); if (extra != NULL) printf(" %s", extra); fputc('\n', stdout); return 0; } static int print_file(const sqfs_tree_node_t *n, const char *unpack_root) { char *name, *srcpath; name = get_name(n); if (name == NULL) return -1; #if defined(_WIN32) || defined(__WINDOWS__) srcpath = fix_win32_filename(name); if (srcpath == NULL) { fprintf(stderr, "Error sanitizing windows name '%s'\n", name); sqfs_free(name); return -1; } if (strcmp(name, srcpath) == 0) { free(srcpath); srcpath = NULL; } #else srcpath = NULL; #endif print_stub("file", name, n); if (unpack_root == NULL && srcpath != NULL) { printf(" %s", srcpath); } else if (unpack_root != NULL) { printf(" %s/%s", unpack_root, srcpath == NULL ? name : srcpath); } fputc('\n', stdout); free(srcpath); sqfs_free(name); 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)) { 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: return print_file(root, unpack_root); 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.3.1/bin/rdsquashfs/dump_xattrs.c000066400000000000000000000043101461472204600225660ustar00rootroot00000000000000/* 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.3.1/bin/rdsquashfs/fill_files.c000066400000000000000000000102251461472204600223260ustar00rootroot00000000000000/* 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; int ret; if (num_files == max_files) { new_sz = max_files ? max_files * 2 : 256; new = realloc(files, sizeof(files[0]) * new_sz); if (new == NULL) goto fail_oom; files = new; max_files = new_sz; } ret = sqfs_tree_node_get_path(node, &path); if (ret != 0) { sqfs_perror(NULL, "assembling file path", ret); return -1; } if (canonicalize_name(path)) { fprintf(stderr, "Invalid file path '%s'\n", path); sqfs_free(path); return -1; } #if defined(_WIN32) || defined(__WINDOWS__) { char *fixed = fix_win32_filename(path); sqfs_free(path); if (fixed == NULL) goto fail_oom; path = fixed; } #endif files[num_files].path = path; files[num_files].inode = node->inode; num_files++; return 0; fail_oom: fputs("add_file: out of memory!\n", stderr); return -1; } static void clear_file_list(void) { size_t i; for (i = 0; i < num_files; ++i) sqfs_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)) { 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.3.1/bin/rdsquashfs/list_files.c000066400000000000000000000075431461472204600223640ustar00rootroot00000000000000/* 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.3.1/bin/rdsquashfs/options.c000066400000000000000000000146101461472204600217130ustar00rootroot00000000000000/* 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.3.1/bin/rdsquashfs/rdsquashfs.1000066400000000000000000000064011461472204600223200ustar00rootroot00000000000000.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.3.1/bin/rdsquashfs/rdsquashfs.c000066400000000000000000000131661461472204600224100ustar00rootroot00000000000000/* 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; int ret; ret = sqfs_tree_node_get_path(it, &path); if (ret == 0) { fprintf(stderr, "Entry '%s' found more than once!\n", path); } else { fputs("Entry found more than once!\n", stderr); } 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.3.1/bin/rdsquashfs/rdsquashfs.h000066400000000000000000000032641461472204600224130ustar00rootroot00000000000000/* 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" #include "util/util.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.3.1/bin/rdsquashfs/restore_fstree.c000066400000000000000000000161161461472204600232560ustar00rootroot00000000000000/* 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; char *fixed; HANDLE fh; (void)flags; fixed = fix_win32_filename(name); if (fixed == NULL) { fputs("create_node: out of memory!\n", stderr); return -1; } wpath = path_to_windows(fixed); free(fixed); 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: { DWORD err = GetLastError(); free(wpath); SetLastError(err); w32_perror(name); if (err == ERROR_FILE_EXISTS) { fputs("\nHINT: this could be caused by case " "sensitivity on Windows.\n", stderr); } 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)) { fprintf(stderr, "Found an entry named '%s', skipping.\n", n->name); return 0; } ret = sqfs_tree_node_get_path(n, &name); if (ret != 0) { sqfs_perror((const char *)n->name, "constructing full path", ret); return -1; } ret = canonicalize_name(name); assert(ret == 0); if (!(flags & UNPACK_QUIET)) printf("creating %s\n", name); ret = create_node(n, name, flags); sqfs_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)) 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; } } ret = sqfs_tree_node_get_path(n, &path); if (ret != 0) { sqfs_perror(NULL, "reconstructing full path", ret); return -1; } ret = canonicalize_name(path); assert(ret == 0); #if defined(_WIN32) || defined(__WINDOWS__) { char *fixed = fix_win32_filename(path); if (fixed == NULL) { sqfs_perror(path, "fixing Windows path", SQFS_ERROR_ALLOC); sqfs_free(path); return -1; } path = fixed; } #endif #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; } } sqfs_free(path); return 0; fail: sqfs_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.3.1/bin/rdsquashfs/stat.c000066400000000000000000000126371461472204600212020ustar00rootroot00000000000000/* 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: " PRI_U64 "\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.3.1/bin/sqfs2tar/000077500000000000000000000000001461472204600174345ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/bin/sqfs2tar/Makemodule.am000066400000000000000000000010121461472204600220300ustar00rootroot00000000000000sqfs2tar_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 += libio.a libcompat.a libfstree.a sqfs2tar_LDADD += $(ZLIB_LIBS) $(XZ_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(BZIP2_LIBS) sqfs2tar_LDADD += $(PTHREAD_LIBS) dist_man1_MANS += bin/sqfs2tar/sqfs2tar.1 bin_PROGRAMS += sqfs2tar squashfs-tools-ng-1.3.1/bin/sqfs2tar/options.c000066400000000000000000000131731461472204600213000ustar00rootroot00000000000000/* 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 = io_compressor_id_from_name(optarg); if (compressor <= 0) { fprintf(stderr, "unknown compressor '%s'.\n", optarg); goto fail; } if (!io_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 = IO_COMPRESSOR_MIN; while (i <= IO_COMPRESSOR_MAX) { name = io_compressor_name_from_id(i); if (io_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.3.1/bin/sqfs2tar/sqfs2tar.1000066400000000000000000000105311461472204600212630ustar00rootroot00000000000000.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.3.1/bin/sqfs2tar/sqfs2tar.c000066400000000000000000000125621461472204600213530ustar00rootroot00000000000000/* 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.3.1/bin/sqfs2tar/sqfs2tar.h000066400000000000000000000021031461472204600213460ustar00rootroot00000000000000/* 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 "util/util.h" #include "tar/tar.h" #include "io/xfrm.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.3.1/bin/sqfs2tar/write_tree.c000066400000000000000000000103171461472204600217530ustar00rootroot00000000000000/* 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 void 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_mtime = node->inode->base.mod_time; switch (node->inode->base.type) { case SQFS_INODE_BDEV: case SQFS_INODE_CDEV: sb->st_rdev = node->inode->data.dev.devno; break; case SQFS_INODE_EXT_BDEV: case SQFS_INODE_EXT_CDEV: sb->st_rdev = node->inode->data.dev_ext.devno; break; case SQFS_INODE_SLINK: sb->st_size = node->inode->data.slink.target_size; break; case SQFS_INODE_EXT_SLINK: sb->st_size = node->inode->data.slink_ext.target_size; 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; break; case SQFS_INODE_DIR: sb->st_size = node->inode->data.dir.size; break; case SQFS_INODE_EXT_DIR: sb->st_size = node->inode->data.dir_ext.size; break; default: break; } } 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)) { 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; } ret = sqfs_tree_node_get_path(n, &name); if (ret != 0) { sqfs_perror(NULL, "resolving tree node path", ret); 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++); sqfs_free(name); return ret; } } if (!no_xattr) { if (get_xattrs(name, n->inode, &xattr)) { sqfs_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) { sqfs_free(name); return -1; } if (S_ISREG(sb.st_mode)) { if (sqfs_data_reader_dump(name, data, n->inode, out_file, super.block_size)) { sqfs_free(name); return -1; } if (padd_file(out_file, sb.st_size)) { sqfs_free(name); return -1; } } sqfs_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; } sqfs_free(name); return ret; } int write_tree(const sqfs_tree_node_t *n) { sqfs_hard_link_t *lnk; int status = -1; if (!no_links) { int ret = sqfs_tree_find_hard_links(n, &links); if (ret) { sqfs_perror(NULL, "detecting hard links in " "file system tree", ret); 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; sqfs_free(lnk->target); free(lnk); } return status; } squashfs-tools-ng-1.3.1/bin/sqfs2tar/xattr.c000066400000000000000000000034511461472204600207450ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/000077500000000000000000000000001461472204600174745ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/bin/sqfsdiff/Makemodule.am000066400000000000000000000010671461472204600221020ustar00rootroot00000000000000sqfsdiff_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 libio.a libcompat.a libutil.a sqfsdiff_LDADD += $(LZO_LIBS) libfstree.a $(PTHREAD_LIBS) dist_man1_MANS += bin/sqfsdiff/sqfsdiff.1 bin_PROGRAMS += sqfsdiff squashfs-tools-ng-1.3.1/bin/sqfsdiff/compare_dir.c000066400000000000000000000036761461472204600221400ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/compare_files.c000066400000000000000000000031431461472204600224510ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/extract.c000066400000000000000000000022711461472204600213140ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/node_compare.c000066400000000000000000000112221461472204600222710ustar00rootroot00000000000000/* 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) { sqfs_tree_node_t *ait, *bit; bool promoted, demoted; int ret, status = 0; char *path; ret = sqfs_tree_node_get_path(a, &path); if (ret != 0) { sqfs_perror(NULL, "constructing absolute file path", ret); 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); sqfs_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; sqfs_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; } sqfs_free(path); return status; } squashfs-tools-ng-1.3.1/bin/sqfsdiff/options.c000066400000000000000000000074631461472204600213450ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/sqfsdiff.1000066400000000000000000000046331461472204600213710ustar00rootroot00000000000000.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.3.1/bin/sqfsdiff/sqfsdiff.c000066400000000000000000000073101461472204600214460ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/sqfsdiff.h000066400000000000000000000032001461472204600214450ustar00rootroot00000000000000/* 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 "util/util.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.3.1/bin/sqfsdiff/super.c000066400000000000000000000067531461472204600210110ustar00rootroot00000000000000/* 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.3.1/bin/sqfsdiff/util.c000066400000000000000000000007511461472204600206200ustar00rootroot00000000000000/* 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; int ret; ret = sqfs_tree_node_get_path(n, &path); if (ret != 0) { sqfs_perror(NULL, "get path", ret); return NULL; } if (canonicalize_name(path)) { fprintf(stderr, "failed to canonicalization '%s'\n", path); sqfs_free(path); return NULL; } return path; } squashfs-tools-ng-1.3.1/bin/tar2sqfs/000077500000000000000000000000001461472204600174345ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/bin/tar2sqfs/Makemodule.am000066400000000000000000000007621461472204600220430ustar00rootroot00000000000000tar2sqfs_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 libio.a tar2sqfs_LDADD += libfstree.a libcompat.a libfstree.a libutil.a $(LZO_LIBS) tar2sqfs_LDADD += $(ZLIB_LIBS) $(XZ_LIBS) $(ZSTD_LIBS) $(BZIP2_LIBS) tar2sqfs_LDADD += $(PTHREAD_LIBS) dist_man1_MANS += bin/tar2sqfs/tar2sqfs.1 bin_PROGRAMS += tar2sqfs squashfs-tools-ng-1.3.1/bin/tar2sqfs/options.c000066400000000000000000000211251461472204600212740ustar00rootroot00000000000000/* 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' }, { "exclude-dir", required_argument, NULL, 'E' }, { "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:sxekfqE:SThV"; 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" " --exclude-dir, -E Skip tar entry if glob matches.\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; char **excludedirs = NULL; size_t num_excludedirs = 0; static size_t max_excludedirs = 0; static void input_compressor_print_available(void) { int i = IO_COMPRESSOR_MIN; const char *name; fputs("\nSupported tar compression formats:\n", stdout); while (i <= IO_COMPRESSOR_MAX) { name = io_compressor_name_from_id(i); if (io_compressor_exists(i)) printf("\t%s\n", name); ++i; } fputs("\tuncompressed\n", stdout); fputc('\n', stdout); } void process_args(int argc, char **argv) { size_t idx, new_count; bool have_compressor; int i, ret; char **new; 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)) { goto fail; } break; case 'B': if (parse_size("Device block size", &cfg.devblksize, optarg, 0)) { goto fail; } if (cfg.devblksize < 1024) { fputs("Device block size must be at " "least 1024\n", stderr); goto fail; } 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); goto fail; } 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"); goto fail; } 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 'E': if (num_excludedirs == max_excludedirs) { new_count = max_excludedirs ? max_excludedirs * 2 : 16; new = realloc(excludedirs, new_count * sizeof(excludedirs[0])); if (new == NULL) goto fail_errno; max_excludedirs = new_count; excludedirs = new; } excludedirs[num_excludedirs] = strdup(optarg); if (excludedirs[num_excludedirs] == NULL) goto fail_errno; if (canonicalize_name(excludedirs[num_excludedirs])) { perror(optarg); goto fail; } ++num_excludedirs; break; case 'h': printf(usagestr, SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE); compressor_print_available(); input_compressor_print_available(); goto out_success; case 'V': print_version("tar2sqfs"); goto out_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_errno: perror("parsing options"); goto fail; fail_arg: fputs("Try `tar2sqfsr --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_excludedirs; ++idx) free(excludedirs[idx]); free(root_becomes); free(excludedirs); exit(ret); } squashfs-tools-ng-1.3.1/bin/tar2sqfs/process_tarball.c000066400000000000000000000164231461472204600227650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * process_tarball.c * * Copyright (C) 2019 David Oberhollenzer */ #include "tar2sqfs.h" static bool is_excluded(const char *name) { for (size_t i = 0; i < num_excludedirs; ++i) { if (fnmatch(excludedirs[i], name, 0) == 0) return true; } return false; } 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; struct stat sb; 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->mtime = sqfs->fs.defaults.st_mtime; } memset(&sb, 0, sizeof(sb)); sb.st_mode = hdr->mode; sb.st_uid = hdr->uid; sb.st_gid = hdr->gid; sb.st_rdev = hdr->devno; sb.st_size = hdr->actual_size; sb.st_mtime = hdr->mtime; node = fstree_add_generic(&sqfs->fs, hdr->name, &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->mode)) { if (write_file(input_file, sqfs, hdr, &node->data.file, hdr->actual_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->mode)) { fprintf(stderr, "'%s' is not a directory!\n", hdr->name); return -1; } sqfs->fs.root->uid = hdr->uid; sqfs->fs.root->gid = hdr->gid; sqfs->fs.root->mode = hdr->mode; if (keep_time) sqfs->fs.root->mod_time = hdr->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; 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 (is_excluded(hdr.name)) { if (skip_entry(input_file, hdr.record_size)) goto fail; clear_header(&hdr); continue; } 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.record_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.record_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.3.1/bin/tar2sqfs/tar2sqfs.1000066400000000000000000000144501461472204600212670ustar00rootroot00000000000000.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\-\-exclude\fR, \fB\-E\fR Test paths against a given shell-style globbing pattern and exclude all matches, e.g. \fB\-\-exclude boot/*\fR keeps the \fBboot\fR directory in the final SquashFS archive, but drops all of its contents. .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.3.1/bin/tar2sqfs/tar2sqfs.c000066400000000000000000000031011461472204600213400ustar00rootroot00000000000000/* 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 (!io_compressor_exists(ret)) { fprintf(stderr, "%s: %s compression is not supported.\n", istream_get_filename(input_file), io_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.3.1/bin/tar2sqfs/tar2sqfs.h000066400000000000000000000014641461472204600213570ustar00rootroot00000000000000/* 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 "util/util.h" #include "tar/tar.h" #include "tar/format.h" #include "io/xfrm.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; extern char **excludedirs; extern size_t num_excludedirs; 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.3.1/configure.ac000066400000000000000000000227371461472204600174200ustar00rootroot00000000000000AC_PREREQ([2.69]) AC_INIT([squashfs-tools-ng],[1.3.1],[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_INSTALL AC_PROG_SED AC_SYS_LARGEFILE AC_CANONICAL_HOST AC_SUBST([LIBSQUASHFS_SO_VERSION], [5:1:4]) 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([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([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"]) 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"], [ 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_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"], [ 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_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"]) libsqfs_dep_mod="" AS_IF([test "x$with_lz4" = "xyes"], [libsqfs_dep_mod="$libsqfs_dep_mod liblz4"], []) AS_IF([test "x$with_gzip" = "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"]) #### make sure we actually have a compressor #### have_compressor="no" AS_IF([test "x$with_gzip" != "xno"], [have_compressor="yes"]) AS_IF([test "x$with_xz" != "xno"], [have_compressor="yes"]) AS_IF([test "x$with_lzo" != "xno"], [have_compressor="yes"]) AS_IF([test "x$with_lz4" != "xno"], [have_compressor="yes"]) AS_IF([test "x$with_zstd" != "xno"], [have_compressor="yes"]) AS_IF([test "x$have_compressor" != "xyes"], [AC_MSG_FAILURE([no block compression libraries found!])]) ##### additional checks ##### AC_CHECK_HEADERS([sys/xattr.h], [], []) AC_CHECK_HEADERS([alloca.h], [], []) AC_CHECK_FUNCS([strndup getopt getopt_long getsubopt fnmatch strchrnul sched_getaffinity]) ##### 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/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.3.1/doc/000077500000000000000000000000001461472204600156645ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/doc/architecture.md000066400000000000000000000171721461472204600207000ustar00rootroot00000000000000# 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 binary 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 can 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 `destroy` and `copy`. The former destroys and frees the object itself, the later creates an exact copy of the object. The `copy` callback 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 than the 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` continue to work in the future. squashfs-tools-ng-1.3.1/doc/benchmark.ods000066400000000000000000003124651461472204600203400ustar00rootroot00000000000000PK|k|R…l9Š..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK|k|RObject 2/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 2/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 2/content.xmlÝ][sëD~ß_‘2µû&{î—lN(8¨-àØ*Š7Å’-²å•”“„_¿=ºØÓŠ+—Á'Kª Ö7šž¾Ì×Ý#%\|}·ÊÏ>§e•ëw:%“³t=/’l½|7ù÷¯Ÿ"3ùúòoÅb‘ÍÓó¤˜ß¬ÒuÍ‹u ÿ>ƒ»×Õùü:.ë¢(ÞM®ëzs>››tÝÞ2-ÊåŒJfÍ IGUñz;üöövzË›¡¿þOG«ëDÝ>*ê¶Ì púá«´ŽÇÊrcQŒåÙúÃñíÐ]|rWåÑ¢8àÂG·7èÖ~ߎn¾Üí90<0¹ì+€ø¦.@çl5a_]^´¦Û7íÕ³vK¸Õ½›üD&ƒAgÝ·U¶n|±„û’l™Õ°6:™]^ÌöÌyyÑNúPÀüšNº ‹x•å÷î’«5ú[–e¼¹†oÀéiYgiuæâ¦*‹?`‚u±N±ž„ÇıÇÅ5ß|amNu–‹6E•ÕUëò&íç)‹ºÙ(Q¼^:ê"n5-äÒ™?×¢€°€¬Êþ„”oêíbúË„F¼>:vÏÓ»~‚ÖüÕ´ž=É1gPVtW`ñyÜüÕw?“vô"Ëó~ìöÂvhªÜÏhƒ’Ç ºÜ”<Í âY­îWWEÕ÷˜d»'š­çùM’F×Y’¤ëhžæ9lªEœWéä±8ô ÖþåºLã:JW›ú¾Ÿ1OãÏi´Œ7ý˜2[^×mü&Q|—V;_µ‡ÜoîbSã7ÿy¼ò¨©’¬Úäñ}”ÇWiŽUÊ‹e ÅØõ*›ÌS¦ŽøR ¤2·&ê𦲄Ê*®À l¯·Ù«.ïayñÈ ª×h‘•U=˜ÚUe‘§‡3vqSÎæŽï²Êsyt¿Þ_`Ä«/†8í#JïŶ:Û§©¬ŸœšõèX±&´¥í—gjûÚü¨ÖÝ”Dí¥ ÐÎWý÷&.Ót›%®>$S&ç«v:΂ÇHb$lãæÞTдI£ö¥ÌUš,Ó¨tµ|s$$ÿþ% %§õ}’Å«b¼ç/‚‘½õÒy3Î?PþÿUÎ˲¸’âvý–üŸpFöû¿GÞŒÿôc­ÿo6oÇûRÛ„Î÷y¿C¾D/è?ÿZ/7mÎÛq´N atŸ£;äKtô¾ø¯utž.ÞŸ ŸÇ‹Å>?wÈ—èçWï÷Ÿæç+ð2ÜüvœÌ©€Ê|Ÿ“;äKtò‘–ÿØ `UäYòzG€£—ýšmûžÍ›®hv𔽮Šä~û¥YÓåE†Íg³Š.’¨êã¨[ë.4ÏϯËtñn2ößÛ=SeÎÍ}`Ïó¸ª:Í›s¯ívðv¥;|ï—Pgµ³$H„H‘SNe¿‚{ØÝ÷}s0˜£9^Û\þœæ øçôì—Mš&7›³ÏÕÙ¿Š«êbÖ ¸˜yÒzÑyºL×É™ÿÅ;ÈH×] 5ÇÞSK¹·06ÕŒ6ßOt7§w›xÝ>ÿ¸ëí_vs$Ó›¼¨#èÿã½a×¶ÝñiTÆk(«ã$)SgàİÒ¾‡-澃ÈVõöòôJÅùãC¾§œô l‰¨áŸè:®Ú³Ïʱ]}½³ø‚y& Sf¶¬Ó‡ƒ‹tBj*5\Û]Q”ÐÄ5t éÒ­k+A ‹$#‡à¢~ ANµóÎÖ°î4t{ž»J;ŸÜõú¶Þ”Ù*.ý~’Ýå¢(ÚÓÕÝéy¯Šƒ§G5ót¶7¤Õ”3ê)e¦„ÛC!­v!ýSûÔ«XŒä9¬aY”ŽQ^1òHÄ|ã"f«ä²Ì’½:è¬âÿeÃZ;ß<î§ûý~º?ä§½v'S!}»Ë©¡æÝÍa*yÜò¯b…*-½zMcûi>ÇùMZEOwì·Çû­GÍÖoÅŒpŒkÜà ¢7CÃB›"[×Û§*›";qiÉ3]k®Æ£äÅÖ{Üzï_d½÷Ç­÷þDÖ£/¶Þ‡ãÖûð"ë}8n½'²{±õ>·ÞÇYïãqë}<‘õø‹­÷é¸õ>½ÈzŸŽ[ïÓ‰¬'^l½ïŽ[ï»Yï»ãÖûîDÖ“/¶Þ÷Ç­÷ý‹¬÷ý˜ú>œõnã<ßo;µ+9ÔùûGioæmXS»6Ÿ]ÛÞ‘P^Gíë†hTtÆIZºùfµ®0Ö^t‚žvÇÞ‹ýzÚw˺ÑÎX­FG'뤗Åí€+± }Mú`ê\Ù¾‚碶kVªÚ½W²+nû}WÎ6 ËË W'i5¿|•2ïb¶ïbÖ‹x¥Õç®âg¯d¡rýË?³Í³×?²Ô kÿâÙËYk„\þ¿‹g/d²¹ü?«:yöúG¦Ûëw­Ï^ÿÈ„7výèRýû’Ä^šCÜûÌé0®'q/.oÍCŸm›‘G7¯éÛpÊŒ<®xÊŒ<=xÊŒlæß†2#{ë·¡ÌÈV÷m(3²ó|ÊŒlŸŸ¶^/yOÓ˜wæýš-´aŒ)f¨²žH0ùBJN˜æÜ0,ÞBI·Ô€*”f–p_J>a‚ÁV×’("‘÷1ò¨üà­)P²©R’pëÂÜî3Î &_sb­VLhÂ)’PòµæÂØîF#|ù &_­ ·šJc5’Pò9l4C¨’ÌX¦|ù æÓ[¦ASä„„‘O¦–qiˆ6Š@¦#Þ@'£/*…!Ÿ Å%aê)ÑÂçL>BSk¥áòH>BBÉWÔÈôrˆ¾|Œ„’/ ÓiŒI$Ò#Á¶!c\ëþmC„ó¿€LkßZe(ò?B‚Ñ• DtŸ цNF^T C1…}&åÚRøôÜ0DBÉš¾·TQj÷åc$”|nƒ O¹¥L"ù &_3i-1œJˆ5$!á²±°J¸?ÕÇÙØG‚ÅŸ"@ôJHj¥@öÇH(0R1 WÂܧt2ð¸)PQ&¦š/+¨‚à]€€`Ò¹’ÞÎ*$!aäË)…|o±ÆBåã•äC$˜|Qf(T=Üøú‘pµ€‘Ä|@Û1(< ”÷¶@tLJ YŠ‚#Á(€3¥5´=x†£†`Œ¼˜ „ tuj’&äý ÄH0ùTjh8!Rù}á %ß ×þa”6é‘Pò´„S !²¸/#ÁH@×PJZ&$b„„ÒªÍ)l£ 1¾þ EZ[wúÈÀ͈ǧt2ð¬È jÊ9ƒ|§ Q ¶Wz PòYs 5…¨¾|Œ„’¯”Ò•<Ä-}ù &_¸( B”ò“Ñ E’¹£pˆu¢ŒO æn12>4ž(þ0ŒÜá“¢X—JÔ  “Ñ€ÇMއ ؤ¢V ɵ´ƒ0DH8ùÐõkØòJ†·!BÂÈ×SF…0S”CÞõô"¡äk¹Õî– l8DBÑ€€L¯ å’j†ž’`$”ÿ…ÖŒA×­ æ2Ü÷?F‚ч¦ïþæÊÉ6=%ÞóJÊìVJe%¡ÀqŒ2ßì öÔ*.˜$DƒÃ!¡äkÍ ú>hºÑ¶ÇH(ùR@ÇωRÀ¯þ¡ôùÿ|8¨ Årèu,h*(b}Ûô”ù?h÷cèt4à¿¶, 9WB PóÃ#Á²¯ÒPf3àwkü^xˆ„‘o¦úMÃ8SÐêøg!C$”þÖ*M u™NZáë‘P4ÀPÿ®Ê‚¦^qFµ°³Ýa$Ô†WÈL`t×Ü¢t?€N·á½ð —w¡–6äv—{ŠCeÏ ¤Xä#$Ô†orB{K´&mxŒ„’/8Ø—åº*ÿÔeˆ„Úð* C»ô#¡ü/˜æJªÈ€ð0Œ\O»ííÑÛètDà¿°,ó® ˜Í{"F‚ɇüÊ8¡Ì*ß  Œt;gT¹ö^Cwé³ `4Ä,d°ã©ˆ†Šˆ¶Â@¡o¡Ä'¨üÇH°4U…Ä L&J FÂjèr{à($¦ ޼° Ê\WÝñ+âÞC0B‚åC¢Ý;°Ú¨²Q>ÆHù”4t£ûÿä#¡ˆf‡0·ÐòJaüóÞHÿisÀLGÜ 0žÿ‡H¸Ú®”ÒÇ (F‚ý*/f¹   Ó×®„:1î™!ü è´ 6D‚ÉgîmP 9Ÿ(‹å#$(H¹Ò(î^ƒcˆŠ SV¸?êN™O FÒZN8åЉˆ!Á‘r¿àz@I$*1Œ(£R1ë~¢·“@§#ÿ7©‚•ÆÐÿH¢ TÁF+2BBɇ\(n¸Þa¨4ÇH @§²Ž€^\Cíão„Œˆ$ìt)™BøÏå‡H("ÐJè~$5Ðú5è E¨Æý~ †>Ô07DB´Rk¡„6Fsÿ‰èè‰D0¼RáK—ýiêþhpÿ—†_Û¿/Üþï~/ÿ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.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 3/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 3/content.xmlÕ]YsãF’~ß_¡àÄîȺ­Zó°±ûàénÛ3³óE kàPKò¯Ÿ,€ ‘Õ< £ÌV˦M|…ª¬Ì¬/ÀòÍŸŸVÅÕ7WÕy¹þ4¡S2¹rëy™åëå§Éÿþý3ùóí¿Ý”‹E>w×Y9X¹u“ÌËuÿ¼‚»×õõü>­š²,?Mî›fs=›•·în™–ÕrÆ%³vФ¿£®y³þøø8}äíпÿu汤qOͬ½¬²¬84šÂgËY–6iò-wêïxºoVï ÖÚY‹öCeµzú4y¨Ö×{±¯×éÊÕ×îiãªÜï8-®ËnHÊl‘ä°ùªÜ\ï'¸n5·]»ÎÈJgÿøòùoó{·JaŽºI×s×ßUmšãê#rV¹M¹×_6ß ÞŽÅïÞîaìí~ìð^pËù ÅŠÙ<-æ»Í6Ï…½]?8XêñäRUŽÓ_¹&»–‹|¬È׿÷oîý{ì"Ou‘,Ê#&¶;¨à‰{O¯–;º]”kX (ú/ùÛ®Ñ ßiã´ñì¬4<¬çÈÜéÇå+„Þš|02¹:J¡ž„Ý7Ïr;Ï]‘cð2YÕ!}û»1gçù{wˆÓæþÈ94³/¶_>£ÍXïK=¯òÍh"ìF£ù ë#{c³ž\mõ7H øä¶ÏÒ‡¦„=çó¤uûúö¦;0ÛsÓ]½êŽ„—îÓä+™ƒ®¶ßVùºµÅîËòeÞ€lt2»½™˜óö¦›ôûæ÷t²½°HWyñì/ù\£¿eY¥›{xFwU“»úÊû/LU•¿ÁëríÚe+œZŽ^®ý6\¬‹©^sɦ¬ó¦ÕjS=¸~žªlÚƒ’¤ë¥§.â¥é Άs-Jp HÀêüwHù¦Ù Ó_NÀ5Òõ1г{áž¶ð vÍßm׳æ ÒŠí¾(›ÿtÇýϤ½È‹¢»»°ê”ÿ­PrJ¡Á½BÉË*^¥ÐúyuWIó¼Iv§q²Eóõ¼xÈ\rŸg™['sWp¨iQ»É)?@"†—›Ê¥MâV›æ¹Ÿ±pé7—,ÓM?¦Ê—÷Mç¿Y’>¹zoë±ú‡õ±½Øæøí¿&^9©ª,¯7Eúœé+ð–Šr™B2v¿Êçz*ç‰Ï!UnÞ©h‹·™%dV.¹…ü¶»ÞE¯¦zñÒ%d¯É"¯ê&˜ÚgeÉ`^ÙåC5Ô>åõÀ@ääy9~8~@W? qÚ›>ˆíöl_¶eýâÐtÒ¢c—5±5màTP´I£…Ì=…•Ë–.©|.ß¶„ä¿ÿˆÄBÉemŸåéª\gÇø‹…`ä`¾´G>Œñ¤ÿ”ñÓª*“¬|\$ûgœ‘Ãöï‘cÿ#õØkÿ‡ÍDZ¾Ô6£óCÖß"?¢•ÔŸ¬•Û2çãZ;B=dè-ò#úH]üǺp‹dgÃçébqÈÎ[äG´ó»×û/³óXnþ8FæT@f~ÈÈ[äG4ò™’ÿ\°.‹<{¿àh±ß³l? Ѽýó½D³£]ö-pWfÏ»/­L·7¶Ÿ­[O¢ª÷£Þo­¿Ð>¼¾¯ÜâÓd:í¿wg¦Î½™{Çži]owÞö½vÇap*}ó½¡É¯IX¿I{ŸÏkïó…´G߬½/çµ÷åMÚûr^{_.¤=öfí}=¯½¯oÒÞ×óÚûz!íñ7kïçóÚûùMÚûù¼ö~¾öÄ›µ÷Ëyíýò&íýr^{¿\H{òÍÚûõ¼ö~}“ö~“âÇÓÞcZ‡u§ö)⢀Tÿð(=˜yWAÖf¯íç6“íî(JH°“î¥C4*¹wiæ*_'?¬Ö5ƺ‹~¡—Ýqðb/O÷†Ùv`²WV·£³“mW¯ÊÇ€+Á²à }ZúÝÔ\Ù½ˆç½v[®Ô»dŸßþãŸût¶m3,oo|†œ¹z~û.iÞÍl7ßͬ_â¤/~_¥¯–d¢SþåïùæÕòLuâê¿|µø#s˜âþ§xµø#ƒ}Lñ¯›ìÕò ·1å÷íÖWË?2à•]j¹÷P8HÓcˆû: ¦Í!þõåzè«u3²yóž¶·™‘튱™‘݃±™‘ÅüÇØÌÈÚúclfd©û163²òü›Y¾>l½_(x­% ‹QÂ@-ñŽãFX _¢4ÎX1o}¦ Wƒ´PXœ.b(^–¢)ÔˆS&qÙ@ñ$ðÞÆ¥6R…:ÀP, 7Ä å²°’#J  h: *t) Hd·(!p£µ•¥%ø(ÐÅa­âE'È‹cDp¡AñÈŠ'c‚[ Å"7s†âe)P˜ûSYÅ9†âÅGMˆÞã-³8<‘h5´†úˆS8wF(ŽâsEëdXé¸o0ß°@ E# =¨” —’*,A]Œ<µ¥*ˆ€Z«ÐŠ& Ĥ?ù‡ŒÄ;Œ\AR± â3†âIù¸aЍÏLÀŠF´í¡Cø3Ìb>@H¼Æ¦ß$!œjMKb¡&³P"jÜ/„ØÅaàÑ\As ç²Q£ )Dñ9€"J@¬jûùŒ0H0„âå(Tjô ¡Ñø@b()é°Ï…ã³"‚¢5Õ”åþYÔê2dÔTÃP4P«¬oдDV h¤Y¨Õl÷‘B€]Œ(¼€A¢õ™­”ð7¡>á::€¢¹£s¨ Ñ$H0/Jù ”áÌP%†¢I áÀqN”’ãw"(-pæ{TIHGp‹7€¢y¢†|p ›¸~ÄH4RД )ØåHaøVV4wTJYH4ÔÌ qt (žDi®“†BV€%@P´#©(µÆ;³Á *MˆÇ°Q¨“´$glŠF DÈý’ €¢ùÌMp«#ÑHAA¥lý³f©¥ÂÉJˆ]Ž\/NC­Êà/ B GÛCñœò¡ *𤀡hGrJ —DA–Ž»*Š—-1+ l–Bh«Ζ¼ÐÐí~‡ h~U’°ŠjÅÁë|Qpj™0¾‹ƒh!À.G ±h´ hd @߯1,0ÆŠ&£>$\B4D`(b¤ÒÑP¤iñË:MF8yÄ—k!`(-Hk¡FÃzø%ŒÄZ_ ™VÄB¹Š‹¸ŠF ¿&É´ôE¦q«1À.G ±h´!!-âšZj¤ ¡hÂÇA¨Ô|P Ú¾±Íåßdï?‚6׊WDIÇšri•Ö˜1Ñ´­-%¿¡@Ñ?Qmð@ñhжV€ƒ»} @RF¥°Ì?•G´`/¤…ðJ/Ýö¿jûk’ûß­|í~£rÿ-üßþ PKBŒ°J*!yPK|k|Rnb¦Â  Thumbnails/thumbnail.png‰PNG  IHDRÈÁJÉÁPLTE2>'''7772A=U+dl3Dm9y_S&Y7.~!|+!~&KCPMZ'We6nGal:GGG[SKWWWGeHoCQuCNwLnhsGgggwwwD…(a˜5i›3s­Qw–Fx§WYž#_“4\ *ms—/e¡w¦a¢)k§6x¨!r«GR"©Eåybî‡àl†}¢"˲$ê@p"i%lo·"ÅW' YDÖx‰t°nq›ÃÙ1ìDr (ɵ–nÒ/¯¬iRJÁ,»@2˜Ók›Û™<+ùýYÆÐ$m r™°<”LP-Ò°¤P%“lçv"éøš[É,(Öž›+¥I2‰΃˜O™„œg'“ … J ÚZØÁ¾Ú6Çâ „ D’²dÂÁ9Éà˜ž€RRû¦v HÓ:.6Eb^pШÀšëH¨Š¯-[(' ‡M¦#•cÃÒÛ¾f _'©V/°£y)iaXrpC [C´tîŽåèžDF[H+ &àÈšÁ…“´ÐÓ°»Àl!5dÃYWš£™d18(Ÿ8Ú&ŠXBH~›…jhP¥`µHøÂ¬ãù%ÔÚèd`*Èêˆ,‘c D@XR2ó'”©îLH/¡4œZ+§N1É37P-#{’jù#¬„ã˜zZÙt°0ú4¨–ápLÐ.8®(¾Q±ž„bɬlþg2HÆH$Ð0’T"1¨ÖP UùþÎ=ò“…A@$ȉ"ÛW8™™ˆH c—` 4m–'=‘dY‰`T<$‘ˆ±'£(æ–sMCA/á¦}Ç,Á\zµ“j*‰(‹oÊœÄì-E`쎯,(JÌIlãî HÒÏ¢güœ1\…’–ÌÆ˜ñn® 8ŸsÝXà‚’ÖØfÜéPn‘6¢gêN‹HZ8¯ZÜ„Û<æ@A ‡c–qÛÂÉ…egãDóN“+œ¾wAe-äÛ–çío"Áù;ùn[8ÆFLÐ!ú-Ö‰Ùô“ÒœÇÁåíYMøÄAOJ Eâø,Î)˜ã¤W^×fÌB¥F¿Â‘³j=ä¢2shE\—Æã‘$‰Ab$‰Ab$‰Ab$‰A~^Ad‘oÓ%¥jÄGáüôíø´‘§q´ŘÙ—N3%„ª.ˆpü‘s „ "˜§©#˜0s‰Z² þûPL˜mP@ä?8'lhPKÏ÷¸)ì¢zX®U|'Qmø°CÜÈ6w8u¥ «};cóà¢A#:£‰ÃÆ]ø >(â6åX{°dG’ð­ËÍÍèºÕI"äí†ògÈÔÎÒ¶¢´Óa_ .žQ×ÁšPí¹.¡™ìX“AÐþPÊñaÏÛWÕA@gl¥mÖó$H„¹&P;[C•$sŸìê1jb® °õ¸p)ç¢Â°NÛɸCZsuTeç$¸ú€©dõ`께#eA» \iaì°N\I@PŽø€¢õ\ƒjùÌÌhKØ…ri\E²ªìµLMq šf™˜,0'`=f7Kx-P'b ‰‚ª$Æ`üsÊÁ>ppœà+àÔj‡H¦ºy€ã…Õ³óþ©´“Å!J ƒÄ 1ÈB¡úºðA<èZ‰ÆÞ—iH¤ÎÓãÃIéËHòÓ“òÓf˼÷”›SÆ©X¸Ü“E&ƦœE(:•0)¯JÚ{êÍS °N3–.{U2Ùê‚Tâ\AÊfj$£’jU¬*¥5CYAJ3ö¬>GlDfÎÕª!+©MUö\1öÚ‘H‰î·v¼Vö\±‘Ò¼V¢üÜ»_ižÚPåw¿ÕQ<—jÄ\Yö6­vˆ¢8“(ÿ¬dyÛ´Ú6 Ø Üa¨+¢ÃËSþX$?ió¤ìä¼?ýf1×ÍÓ€(¤‰W~c¯²ûåÈíÜc.•[µj(D)±g¯¥´1Qj­g¯¡¥ÄU<‹Rc«šE)±*™sÄk3î·"!Jöè‘…<‹òÊ‘#ÿsä9 ØºysGõÝoù¼V¶Òí@±åÈèÂêÞÑÑ‘£˜÷[¾å9ÃqG@1òðƒÓ`Rt¨[šû-Sˆ2zÇ–Íw|þpÀïoZ’J¥¦q‹¬jÁýŽn¥z^ï»TëE© %& C/ ÈUE†º¾_Ê`u¨„½ùÇ#G^~þöŽŽ-_ÀÀí;r½C¦ô™FFv 6¾Q9$w'v¯³ßþé ·‚»]òÍûW·ïI\Ñ·w*·4;ÕòX—7^QåjÖYdÌ>TÆ1û?Üh:oÑÕw D‘NK1vâäˆLËtaïôì/Os²‡UËÃ^Ù=,Ï£Ÿ2ß/ìZ{ñMeœÄ&ëûÍ0OWÐFŽ~bCàn¯aôfÊ:OΠ ÷#ãò<úQe|hÑe+¢u™.+0_ôW¸ÉeôpT߸宠ã.‹¸«|}$›}9›Íþüí[w_·~o÷Šw¬¨ Hð°Anï¸ñÆÛ–¦Ýô‘[v˜~>Ù[ Ê*4žº-uQêâßrËWã¥ÜæL%@¼€”`ìÙ_4áí» ”þ*s Q’ uç,‘±M¦½åòî‰N£r 1U©1ûئ‹SK ¸…”©€N2v‘¬Ø¹ðWAxþ˜®8/„ŒeU­ÿþp*õ[ܵ¢ûÊ7¬¾¢»·â œa|vcŸÍ˜ý•-[>±å³f‚ÛL:=ôÓ—^ª8ˆâR²²J$³õÆ‹ÆÆ¯Ž3dEôvˆïÎD"³q¿=¿}1„…ÛÀ4NfÊ>Y?xÈ\ñK‹µFŸZœºÐ„…½'Ë5á:Ó¡nš…¯0§”s~Î`7u²l±óŒ½V …ñ‚ ¹ŸkôsKS©\·é7ªr¿£”„$‚Òs¼<ý H#uÙUÝ+~ã‚»®ì~1]®ò ‡º¾àü…BcÛ›íìíóÏþàÙgŸýÛÅ©7môžxñ‘¯ïyä±2^”˜ñ,J4hijžO?ºù†[o]œú½ËWŒ[øØ,š¡|ÑL§ÛüÆO¦Ro¾ìòÐìԼ܉-;ƒq‰Œþùâ ¼}¬¢峂HLõÙ%2uô žê-ÆOU$˜šÕt èìã‘©–ýÜÒ\·1-HUú¢¹wöɇâ~'ûÔÒ%`á÷½ñÝwf©æKµ0vÝ{±6}jé›/ï^ñÈð¿~ ç'ÓÎÞVã¾_ez–YJdtôGGŸºL|ÅéYN™Vp„8“y­ˆ±gÿæÓKoý E.RÚžÖç/®•˜òÁ ÆQíÝÿÃ\~çpdóÖöcáüªc‰ ·¬•§þíw¿ýÞËãù¶ÖUà HûïëkohhXÕþ8|ÝoÖ ·6\“S&»¿¡ášáÐÑÖ›§çGŽoº¼;4{{ú…¾•PóÖ†Hj]9)ŸKáüÚµkux¸­¡-Ìu°}¸*ÉÞ á•o»Ê€¼°çª|…[ÛÙ{eÛãáÍÇZÛ_˜°{uèàJÖfRD`­€¶¶¯¯¯µaåÄv¶(Ãó#Œ~¸+8WÛú]y+> ¦ÕòýÑÍêttó5­í§ ` L¨uçN_[Hd^^c¤«Ú€¥„`W7¸ì·ÌHü¾vÓp­;å,üδÑïþÓQŸ×°s8´£­9h惶rmåªUÓpMÎ8×;>úÉ^¶·»{Ç Xµ¾aý±Y9ÐÙŒG†'õB+Ûþ#œÝ ç?xèÐ@dÓª–0CÝŒ:®$|J5fø¯—˜ÈðýÐmwg^8$‡L< &þ|..ÕP6’=åÃg¨ÈP~¿Hv¼¼T"Èçö•}rHçÊí²óšcþDfZ‰à|àRpUJ·¿”êŠ_mXHVg'µéH`FÙq¿üjâÄÞP:3niÙÐŒS4Ú=õ•#a¹*­ffì6νˆ;TÓ±oó"_^?<¥r¤k/ÖRzÒ«²ßþ—îî¯\°·”‘S-<âú³£µuõå'æ^•ZxÄõ T4´½sa¿(ìßWt¯øòzBßæ¬Zóûˆë‹àª¾¼ÓZTN¸š9©Ö|IdäÕyÃßU÷?ß“ –J½qc4p“aÍ‘'9o³(Å’_ÿËomýÛéƒýÛM:¿q㲺eýýùÊö/×R4Š·—z÷²‰l¿çÚ]80ìáù‘H]ýyõõu&Õ×翌çšZZšë—ml¬Û>‘Îkܸ¼®©³?—tgÓˆÜ^wjâhMýYÕRçàn¨ÊòP(Ù±Á‰ì)#ß®¨¸³™¹J¤î5W¼¶®ó0$c¦Ù–åb°±¥ÿpç¶mÛ–5·4L¡?C¬1ÔA6×-&Õ×77Õ5.ÏSwí®ßݵ¼¾pOórìüíÙ'Z²r{SHÜ#2ÓR¸ér÷ –Ç—ÍX"¿ôê7]öÚ‚ÖØ]Ðó^Vwx"55wö7-ùÜö–úæåuzcss3ȯ®©±®±©~<å ó©±¥±¾ù¼º¦–ñÔTþùõõÁ×¼¾i[sÝ2óí!#ïOt66™ob jðéÃOŸr¤ÿ0ˆ· +zãØ~¡M;—È®úÂîÛäñæͪkîl¬;¼mÛvó¿¼¥¾±1/ÇqMæhóÇE]Ðì@Ô…’3q¿cs·ÇAcìý…®cwÔØû—güÁ°«Ø-eSAÜ KÛÏ+lnÚî?TÿDp\#ïå»A"æÛCÛ¶Íï+@G²°¹à À°Až“yZF};DÚša÷al)u¨[=ûŒ'áåïÒæ£gwåã‘JöÍ1ï×be_ç!úͽñ¬üwb§«û¢0HHU­ïÄž‡¢O*q'ö<¬DŠA ˆŒ>ž¡$¢©Þä;liØÃ*ý¥ E#›Ž>å,&ýZ%áÒLLtÒ Ã‘=>s}VŸuy8\3œq"}³Ãœ>ŽvÄëŠÜ´®ŽôÁŠ„›…²ðfŽtÒÈUL`2 ÕšüÒCQWyׂp¢q ŠNEâH34áÊ Ÿ‰ã኉@™Ä…Â5w™Ë£2R5W IáC¤ëŠ*ªÄu`Ì/Dº›¸—ø2ÒÿŸróCÂC=¥? {tG¤UؤðކWÖÂVŒ¹~Aµ bÊ—ÿÙA!‹ã$j™dì‡#VÁÉPäh“jîDnÝÌ·¸Ã™{|:q6ó«vÞDeâ~$‰Ab$9cD¯¦é×"£šMÄ‘“–èÝ3××&A>ó¨6?>K1¢X˜ßv¥& `¶¹©)Lz\Šy±×ÕˆhÖK¨2’Ì¡Ô<%ÁY6÷H:â\!ãâÕ8ˆän†º‚þ $©Ë½ñhÆ<Ââ,ƒ„û(]Ó \jì á!{aDšŸ6‡æ—g=Ì|ƒõ¡±ûAb$‰Ab$‰Ab$‰Ab$‰Ab$‰Ab¦èË9•œÛ~ó b6õȪüÍFªp‡!Tô ‚=õq©[eÇzÄáwJÝO¹¹óKˆð´ðˆ#2l‡y0Âs¤DÜçœiɘ4J êÙŠ›+?œ›ßRPRš»ëFeª â!Žt:ɘ3èx˜ì9#”š7ï#ùư¼àeXžÅÒg®2Òî¡ÈRÙi‹ob³%üÛÞøÝcÕq=m+TÄ6êB 5 Œ0â[„Z"Àu\á¹ZY¬â%‡¬=d@¤Ù$G˜pµ3qŸZUA¤å¸6•О¶’Ì1w?hTó1d{ƒæQ eÌÄ€ØÂñ¼ÚÀ±WÙ ‘Lbn›.Ù£Uð®9ã/ª b®9~Òí!iú>L…Q-îú.áô$¸“¹ž@ijD§›U$kdÄwZc¶`–4»çìM¡!šE@±ª‚?öES†…¦˜ª,%ÜÜðJI—ØóIî 4ǘ›ˆÇi «Žé!å{ðÆ”ÀЖ»eß4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 6/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 6/content.xmlíZßÛ6~ï_a8¸CïA–dý°ìz]ܸ§äÒ(úFK´Ì % $½¶ó×wHвdËZí&‹-Š&À¶â gøÍ|3.²þùTÐÉ#æ‚°òaêϼé—)ËH™?Lýü_'™þ¼ùaÍv;’âUÆÒCK餬”ðß ì.Å*Ý#.cÓ½”ÕÊuY…K³eÆxîÎ=ßsµÒÔî"úñxœ­úù“«dŽÄ'éZíœgíÓž{^àæn†$r >¾³;N{Yôîð—Ë¥«¥VuÇxqz˜x¹º{U¢‹>U˜…Ñc°ÁaÙÎ!ž³ju1°Ò‘«} r笾ûÛÇ¿¤{\ °!$*SlwñJÞŸ¹Wì¿,m”«§Z-K]L±:¬pý™ßDO¾ˆ56™j¼6¥«–rxÌÇîU FQ!I¶·dÇZQºÀ¾övE†±Û•n{/Ð2l覈¦ Xy¦x4\¥|åê8èêÈ ǪX¢±¾”n‡c””_îó[I/üëä$¨³cwR8XÞ€Í|7Úúk¬ãºæ:„ ²ñ„ ²[Ðv|µr{wy(¶˜ö ç–»ñÀaß¼)T`â…éÕÌ•ŽÕ§ì‡®SÞ²ÐI?+î¶PÕ„ñ£êr à ¦Ù¸ΜB\·oµ»Û¿³§ûwSÄHîïÔaâ~¡þññCû¶ËŽë‹E¤œT£¡ÑîSÅls׈§“:~­¡ ˜n쀒f’:šöb³6S×Y˜’P§{˜þÏ›^)Mꯂ”:9ìËHN$œÍŸº›µÛcs³6Fo¤{Z/ìPAèY-©YÃnÉ9ªöpâ ’Ž¹$XLÁg_À@ÉJ¬Ý¶< ¹›»Ó_mgæNU‘s*&ˆÔQ•ü€­Τ.•¹j]ž:©ë¬mkÇ€0€ òý ’Íaì²Ô@å=¡êîŸjñ3P/BMÊ”2ììI–áÒI1¥å¢Ð¦%2PÛË’c$\Tòl-RŒ±“£Êêp’ï¥ hæ µ‰g û׋zèÔÿë´ˆ>ŒŒˆŠ¢³CÑÓ.$ÊrÓÁ¾ éUx8V•ˆ¡B8NMˆj¹uàªÇÎò¥Y7íTò3åP]0N9;Â…¼2­Æ§…C›xznt"¢• ;{* †Àº°€j”ÁMún¨¿£9í qºWxá´÷©Ykòãv„Q”ˆÉ°„÷À¿Ön­´v[­ûŠ2˜&  ÷º€T˜‡¤šFS>Ë8Vÿ‰ ³þôh®¾q6Q§0‹³GËÕ <Ž'Cò÷OìÇ6(¦yèžáì‘0WoËä~j œ·â £ÿ¼I¶Í~4 nÌÂ8€Å†3ŒqèHBÁ9\*Ö†Þ(JÑ•™Eåèy;F™éU­msv¼ÀÊ•Q €íÉ7¦ìj¥ù¥„âP]¤&Ï—æ®óÝêâ·VÝ›Sõ§ÀHcù 'Ÿß.GÕ×x#´» ‹tó}î~kiíZã/Œ2ÐÉiG¢»øÂ÷“8HæQâ-.ð®/„8¢äÇB¼MùwIã¯eÚœlr/q+¨Ï$õwAøÛ﯃KMÞ"\^¼HæK?º¸¼½núµ@¯‡Üühù¿LºÈ;¢·@ž%Õ«!_,Aàa´˜Ïãnλ¢·@þUÈWªb€y‹¥¿ “pC_ì ïŠÞ†íÃ7æ7ýE”ÄI†‹dÑÁÝ•¼ì¿‡¯;òãd™DË0Ž’0éÔø•è[F3‘´—š‰²~»»Ý§¼ÛyæÛ¯ëϱùPKJߨ"PK|k|RObject 8/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 8/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 8/content.xmlíZ[ã¶~ï¯\´h|‹c'N3)Úsö Ýh·@_KqÔ•-CR&ÉüúCÉ×8NÆ™ÁEwi-R¤>ò#E võý1c“G"$åùƒå;ž5!yÂ1ÍÓëÿ³Ö÷ë/V|»¥ Ybžì3’+;ṂÿN`w.—É Å9°vJK×åÉË-©;õ|Ï5JV½CÊ@5ê‡ÃÁ9FõÃo®–ÙŠ•[k§c6¤=õ¼ÀM]Œ²)9|Yï8îT6¸ÃãØ5ÒZuËEv|°ö"_¶Ç^æ(#rIŽT#FlÉ9l°9ÞÚÀ ^,[K¹Ê·¤WÎ껾ÿù÷dG26¤ByBê]¢P×Ãç…® o㇓F¹Ø fÔpâFôa¥ë;~=ø ’TVØTbðÖ)]v”»häc:v/¨1²)ºa¤k tkEëûºÛ5Æn׺ݽ@ËäF`gn‚XÒ€U'FFÃÕÊ=W‡›®‚qjõŒ(4Ö—Ö=ã£ùÇëüÖÒ–ßc%³·üJ o–7`+¿mó5ÖqUsg„ ðxÂøŒ-h3>‰F¹»;ßg"Fû†ÎsɃíxà°oÚ*0±eºH›v»åû̙֫’{æ§9Åb=âû3õ£$+£·#z¼x°N¼˜éEóä^î·ŸãÔßêTè¨Síº×& IYE`¹A¢^ïÖ†ië“(ªt@Á)ðeêøí! ŽžøÆÿ)Ø07H±þ/Ñ”DJ.&v@ótWìÕ䛟LäTN~$ žEß®ÜjËÊíø¯S0CÜEƒ!9å{Ze¶€ u€±vû`} ¡~×zèä÷‚\ œ‚ùòYÙtòœÎ»vÞͦuÈÊþbÚŠ½C²¼¹u~8Ûg¹Užéa7ðð½h¾kŠøN_Pdî„~ ‹ ³8Ðb‚nCR}²ÊEàѹ‹Ù"蹈pqá!r¢¹öàÖ.ôuÞ $ð$,ßqÇr™®žÅ&ˆã0yfÕ2¼™Ëñ ä².¥ŠaC&ˆ‘Æ{K)ºŠßœ ¦æ[зpÀi8ápzÎ,ô;YŠœy^«À¨­À_÷JÜæ¤ .Ðå&påøv¹¥‚âAÃó^7ÉÐ_\\ †$¢½SÏÍ,j3ˆí‰´_’©;ëí¢ýÕÇ4…Xpš«f,.à™G0ĸƒ¬DSo: Æ‘Åm5lTÜp“õ:†›Ö)3´5?+ —;¢Ûå/ËδìA˜»lò\V.jG÷íefPµ²-ø¡'€•žQHxMÑ Ó¥¬·ÒüzH3¦j pQÃÌÔrý?æP“wr_v/6ŸA cP9gúD‹öŒf®Ha&ÂÆD&ëWêQ0”ÔWníä…‘.#eIàâ˜ypïxóЙÍâh1kö%Ÿvd™{™ýWÉ({šÝ$Ý‹c<{á"˜ûsωÂ(˜·.û’;IÿJ°3ô6¸ÃEDÁ4pscâî¾äóàæo; ^Oç3=“ù]Ø}Éç€}|z«dû³™¿€GÆ’ëEÝdŸK>ê'©ðÛàŽ½ÐAí:SÐuÞM}ɧÜhåÅÕ]jfêééž¿DݳWjýÕÿ7ëÿPKñ•'Ä!PK|k|R styles.xmlÝZësÛ6ÿÞ¿B£LoÚ™£HJvl©¶zm$Q;’ñ†ß¼JTˆÄ}¤ó´ªQ +óØÔ# ߘ5…¨f[5$´ÐvÞÒ×Dõdd_Ñ8ú”Ç1íŠE½ð¡.FÞ Á»WŽƒ²#Ì´AR“Жõ¨ú@ááU«œ ˜ºøª¬ë\”1/É!oY±ªq×ÏÜö$ã¹ÿ&õ?ï~Ú§$ÏÆ†ˆ¢mÔ㈓bt54Ôʱ•Y§·LåP³Ó‰¤¶™.¦k·s& vÍEØ‹qDÅúÂ$@5<1ÏJ­ËéO²IûtòåàØ$iFèÝåô¨`â›:384D+z/Å9¸JºØ!‘øáq¢ãÙ¿G5¶%ròß ¥ÀÓ¯Ûáåî„ÄÙc´»f’i(&oþóßɇ7½Ú~6íü>ÛqÓ89+bœ ’ÚvÊI¶ŠêòâE˜Ò©#/G)GÅÖ+ ±0—z03Ô …^L„D¹ÚF‚Ù)É÷ˆ©=¾Í§õ쉺„­(ÊÓ¥0‹s=A¹‘ÔûùÃôP„™…òN/hB'ÌÑý¾u3Vª›xsÝ–­6XŠo;#°)½¢Ü’CùÕÔ×ÚY^ãíU~±Ï t!î„äì“Ò‡2Ø_-N^Ÿ¢“éDmP)­fÎæË$J4¾;å±Â´Ò9óÔ³e[³$°ôn•ƒÃð<wÍßÕæý1¤Â•’‰©è!1f†Ñb‹’E™G²ÔQ¢uƒ¼ ÓÊ $ÇÞ†cØO"éfT-ª—±Ä੆“IcUŠÕéB QzèSH‚¨ÀÖ.ÐÅÞ]V G})°ý²‚K/n!y J™m’©å¥BñKí˜{Ê/ÔˆdDá,Cù` ©ÖäwÀ)œrt&R,aËõ>ažkäŒ=mulš}ßâÿ£_Êqˆãì«…n~oMBäŒY+ Vµ*§0§âí5¨¢è7©"é4ê±ÕÈ´Ö¶ÃnT#»åÓ¢‰}ÊH®{°øb’)@E½P‡L—/íÞ}¦5°¶=ùýÌ?`¤®˜'UW÷ ^sá¾TÖ9` h ?‡¹3?±¹cÆÌ15‡ÖÑýð“t ­á†Ñ¸æ…{@±vý6~ °óÝy ŠIx”í·£mÏ`»?Æúù°õó²~þ„ÖmÐù­>J¶Vª¶GZŽÔ6(ú”rub¬z>Q´÷"JYލ·¡žä ›·æ$LWsJ*ã±:ý³3ȸ‰`”Ä“Wçúéï?kN\èOˉÁ:ñ;ÆdþyÀî·ËB0Æ.gè(Ÿšw ‡¹ÉzBÃTÎáv¯2ä°šL?­#v4:bzHv$V÷ª3í¡èSêX?H$KñTþ=cñQ²­ZÇäx铊AðúuG=}ÊT¼Æ¥äûy.PláÊrùì ü={”(8FE»îSò+Ò‡šÏʟʰo9güù+‚¶sŒ¯ýy$ë­ÿE`é˞ܬƒ!8òÀ`A;&,Ï M=l=G Ûq'GÀö€þöEa[ ¶xnØbýoÈ{,†ož»¸NýyÚºRÃuÇ?xÃ`Õ:’$òÜ„Ã+Å„+eïwEN;ˆÚ—¨Í ]Ê=¹ÙgÃŽð‡wKv,£üi:B]Vm­}Ál¾<]sÍ™!žÂʼnširKßÝ0)Õ‹Í`,ÏOÌõ·ß¯•Uç%4•¬èT³©’ßòÅÎÿf>¬ß3Ìg'Ëý=C-K ›/.[p:¶Žö'P?D5 ’\­€i™û׈¨?+h÷Ç·ß[½ìD†D%¢ªivPIºÂ®gDGÑ3ê¯/ô—¢ ûWl16Ôë««« ÿpÐŽ ¸_y²¹»×[~3xªÕÿ§l±Jmsg¿Ýzµ±– NTôAüŽ÷AûÞ~ÃnÙy YóÄqªÞ"*… ¶ð×_™ÿI"iÔ<ݤ±bcHgÓ1’ÎVýmœzkq ífEäÝ Zª÷ Á<ô‚…7?Ÿ®ƒÀ׿A`µP„ëNœÂÙý¢‘ž®æ¯W‹ùlž/΂Ó3µÄJÿVFw…aÓ¾—‹Mç1¿Î _„­—Ë:ƒ{¢Xö»«‡ßýõÖõPKeß¾À+PK|k|RObject 4/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 4/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 4/content.xmlÝ]YsãF’~ß_¡àÄÎÁº­Zž¶ÛŽ Û;ÞǼAHa \jIþõ›…ƒ¬„x@¢ªÙ½­¶‰¯PYyÔ—™´|ó·§U~õ9-«¬X˜ÐˆL®Òõ¼H²õòÃä~û45“¿ÝþÇM±Xdóô:)æ«t]O矆¿¯àîuu=¿Ëº(Š“ûºÞ\ÏfÅ&]··DE¹œ1Bɬ4éï¨*^o‡?>>F¼úÛÏ6­Ó§zÖ^–I’ïÍá³å,‰ëxú9KÿÒßñt_¯öÞA­µ³í‡.ŠrõôaòP®¯w˾^Ç«´ºNŸ6i™9ãüº(à†i‘,¦(_›ëÝ×å:ÙUv`­töû/?ÿs~Ÿ®b˜£ªãõ<íï*7õaó9+ÓM±³_2ßÞ<”y3,™ÏÒ`x‚¢%¾ïÄf°÷úau—–£e󼌃ÅxÅá>¶Ý¨‰»H/—[º]k½—Üm×h†Ö8î<;kù›õ™»1ýø¼xâ;—{3 ÷«ƒêH8ýìXnáYš'㼘®ª!}»»1'§ù{»‰ãúþÀ>4³_l>~ùÙÏ6c£c˜Xªy™mFa;)æ&¬èÆf-<¹êìç|rÛWñC]€ÎÙ|Ú„}u{Ón˜nß´W¯Ú-áV÷aò+™ ]ußVÙºñÅîK²eVÃÚèdv{3Û3çíM;éKó{:é.,âU–?»K®ÖèoY–ñæV¼§§e¥Õ•‹_˜ª,þ€ ÖÅ:mÄzމcÇÅ5ß|amNu–›nŠ*««ÖåCÚÏSu³Q¦ñz騋¸Õ´Kgþ\‹Â °*ûR¾©·‹é/O!4âõ!б{ž>uð+´æï¦õìU޹‚²¢»‹Ï àæ¿Üq÷3iG/²<ïÇn/l‡¦ÊýŒ6(9fнàΠäuo2hõ¼º+òiý¼I¶»qÒ¡Ùzž?$éô>K’t=§y›jçU:9‡Ô¿\—i\OÓÕ¦~îgÌÓøs:]Æ›~L™-ïë6~“iü”V;_µ‡ÜoîbSã7ÿ9õx娩’¬Úäñó4ïÒ«”ËбûU6˜§Lñ¥@He:oMÔáMe •U:½ƒü±½Þf¯º|†åÅK 3¨^§‹¬¬êÁÔ®*›zz8cå|`îø)«<‘£ûåðæø #^}5Äi(½Ûêl_§²~uj:êѱbMhKÛ¯ÏÔö½ùQ­»)™¶—&x@;_õ¿q™ Ç,qõ!‰˜œ¯Ø}ê8sž"‰‘d°LŒ›{OPAÓ&Ú—2w4Vi²L§¥«å›#!ùŸ_#±PrYß'Y¼*ÖÉ·ãüÅB0²·^Ú!ߌó”ÿ_ÊùqYÓ¤x\KþO8#ûýß#ߌÿôc_Öÿ›oÇûRÛ„Î÷y¿C¾F/è?¿¬—›6çÛq´N atŸ£;äktô¾øË::Oߟ ŸÇ‹Å>?wÈ×èçwï÷_çç;ð2Üüí8™S•ù>'wÈ×èä-ÿ©ÀªÈ³äýŽG/û=Ûö=+š7^®hvð”½îŠäyû¥YÓíM†Íg³Š.’¨ŠÈ.–úصpQ¹‹Í3Âëû2]|˜DQÿ½Ý;UæÜÝø<«ª³@sþµÝÞît‡ðýRê¬v©16‹îWñìœSk¡9Ìѳmn¿»ß?ÔéÕ?7išWWÿ(Y7àfæIëEçé2]'Wþï@#]w±ÔG–Qoa,ÒL4ßt7§O›xÝ>¹ î_vs4Ó›¼¨§q™Æ{Âîm8ºcÔi¯¡¼Ž“¤LÿÃJÿë#l5÷D¶ª·—£ï(×LJüD%éØRÃCÓû¸jÏ@+ÇzõýÎà æ™€FÌl#¦#í0ŒT$µ £môE =B\C».ݶ"´°H„0r(.ê‘*#͈­iݹèödw•v^yê5nm¼)³U\>OŸö{Jv—‹¢hÏYwçè½.Jœ"Õ<ÎÓÙÞ VgÒÓÊDD jµ ê_Ûç_ÅbD(Ïa Ë¢tÜrFÌÈ1ó‹™­’Ë2Köê d°Šÿ]” í|sÜOÏûýô|ÈO{íN"!ý=+#£Ø!»›ÃdrÜòïb…*-½ÊMcûi>ÇùCZM_ïØ¿Ÿvìß=2h6+f¼€Slã†N½ÚÙºÞ>_٤ىKPžéZs0%g[ïãië}<ËzO[ïã…¬G϶Þ÷§­÷ýYÖûþ´õ¾¿õØÙÖûá´õ~8Ëz?œ¶Þ²?ÛzŸN[ïÓYÖûtÚzŸ.d=q¶õ~“´t½òÃj]a¬½è½î޽ûõ´o™u§;cµœ¬“^® ÄB4ô5é‹©[lpeû2ž‹Ú®Y©j÷†É®¸ýý_»r¶9jXÞÞ¸ú8I«ùí»”y7³í|7³^Ä;­>ÿs¿yý# ­ë_þ™mÞ¼þ‘¥NXûo^þÈZ#äòþ—xóòG&ûËÿ³ª“7¯dº ¹~wäúæõLxc×.5Ü»/Iì¥é1ĽÏãz‚÷ óÖ<ôͶytóž¾ §ÌÈãŠðÊD„H*¬¶FJï¬_?'9WøjRÔáB+¥¸BŠ"äœÄ5æ ¸ª$²Ö ÃW„PƼp}“æÆôì_À¯Œhºûãû#ç¤Ä1 öÙ©„[«9%üˆ÷ªœ“=ÇtÃ_@Un$L+­Á^ÅÈ9‰vLëúöDû~ÉÓceˆß×ÞF mcŠªPha$˜|) Ü0I™H>BBÉ·’Km$cþÒ¾|Œ“O‰$BBhS¢PÆHù,¢ÔH%´`ÒXOý.ú¨=™Æ6GŸ„’/¨1FQ(u„fÈú9*?$ðÝšx¨P 6šUšYk¸Ï9$˜|?R­!à°|„„’eªãLr"ÑÀH(ùZj.ˆ”ÚJ©¸/#Áô'Žç(O++þ çna·3ª¨ûßGB‘ÓVh(*ÀËLKTH"äb$àù$x$—„Aª§D ŸuH0ù’Qi\3&¬_ ‘Pò•’S z"¡æðåc$”|iˆ…öŒHCý<<BI§ Š-Ú}"ñ æ}p.—VkM¬BÎ÷`àxÆtŸ¨ÄÈÅÀ#¥ã,ôvЉ Ü„r Eå ‘Pòƒ—…ºÓøâJ:„™äZS%”BÊ# ”t® ‡(‡BÃhƒ”ÇH¨íeqûÛr :D¾ { ºLj‰ÕœàÐóPÛŸ!f¨6ähûcäbÛßcÄ@(" )ÆBµ+ Þä¾ Lº¢å'¨EâF¾Œ‡úV¥”@!0DBɧœp­åÆ*ŸíH°ü¯™q•6l7ªPõƒ‘Pþ7†Ja5äXžïŒ£É¡¸ãDƒøˆ\Œ¼˜ „Š3å”%ú}æ!F‚ÉB1È´ÔXîWC$Œ|A³k©Ö^ùYxˆ„ÒßHi­Ñðé’½¯?F‚U„H³Æ(m™DUBBé/P­†‚ÃÂPüa$ p²s°‚~Kà§,¹ x1(TÄ94ºÀ·ê.ÿ0lˆ“O4×Àø”(©±|„„’žf–hð6CA8DBÉW\sb,‘Ts_>FB‘Â8¢·œiŽH#¡ô‡†ÚLíJ~M}õŒ8U–PD# j0r1 ðh)P1 &µMÛ+-G>ÀH0ùFQƉ+û™ÄòF¾Ž¸pG¾nZjÿ‘Ü %ŸX©\DC¸/#¡Îã-S”ä|LÐóŒ„ò¿ÐÖP%\Ñu‡ïŒ# Wµ÷ÅŒ\Œ(ñEB¹Á=s²’P!OQ&ÄH°m ¥6ÜJèÀrÐ6@H(ù–‚c ÿWÚ6=DBÉ–uí–E¥ß‘ÿŸ¡Þ ºe(ð­´¨CHù$²FINûO¯x]Ž|v —9ôߨßBÞÇù!ÁäCºsgàPþ$Ý»F¶‰$S¢3Áðû¯Œ„ ßÖ”»¾ÛJT `$ hŸyš¯¡4…‚Bhhm,ôÑ-F‚mw |ª¹…òFbÐvÇÐå¶»ÿ†R°í¦¤1BZrI Úî %_3j¬{›Õ8 _>FÂÈ·ÑÐæK5¾QÊ£ý!ŠrÜó÷܉ØØÔ§Œ„ÚòÒöw?èm(„ò>èÈÝ£&@ ò>F‚ÑÕm{B¿€.G^ ˜ßD„ƒ¶œJ¢ˆÚCŒ„’O!»Zm5¡ŠéËÇH(P”»7_{õÁ ÀH(ý-’jʘ1Ô(_Œ£ öU ©®±ˆ, )I¡Ùwç­‚á4„`D@•\ãrHv†.GþûòÑ@‘µ=QF†ž‘`ùPc4ÕŒ@À1”è8Èý‚Cí^Aˆãþ³ñ!Šˆ8…„C— Ú~áFBtWÌpà`<‰ˆ#áZ ©¡î”JX*-„"£¡äjÖ0ÂQE0€.Gþ[Ë2¢‰ Æ‚jȽ¦ &_5ÿ$‡¹P£^#Áˆ*ʰî%ˆ!¡ˆÀ@û#5ð­{#F‚ô²ÿÀD€`‰H»ów÷RJ‹â#Áˆ@"ô`ÐË`èrDàÿûæ@¥!äb%цRj´Â !¡äCNÜ+yL1濨1D„›1‚3wôéÁb$I.Ü1^èH¡h€rè:Lÿ‰þÉ BBÑ€qþ¥ÆГ 4€‘`4À ×0JÁÃÐãzC¯¤á• _ºí¡T÷«Žûß<øÚþVäþÛðR|ûPK/ùsåxPK|k|RObject 9/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 9/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 9/content.xmlíZYoã6~ï¯0´hÑ>È’,ù¬ãE}ØíÃ@_i‘–Ù¥D¤c;¿¾CJ”%Yv” )Š&€³ä çüf84vùþ²Á’òìÆ †¾3 YÌ1Í’çë—[wæ¼_}·ä› Éóx—’L¹1ÏüÀéL.â-Šs~ãl•ÊžÇs’G†\$ÞÈ|Ï09ö„”¡ªØ÷ûýpÖ/ŸÝìßøáþ]1RÛ u8ó>Ñ||üP¿mú¢£}±ÈXмw#,¸Žiò‚o#¯ ;ƒ2~µ¡ tVv@;ÅÁg»örµ, ¦¬›bwP”„¶îÆùÓwZLƒr•ÒÌä"s˜&Tmã­–^‡ÌÕ²z® ÞN¹±A)eG½¥g {$(ß‚Å9$E‰hü‚(Á¿€ŒgĨ­i¸¦nt]YÕ•wªŽœ›sI•‰ª;bå®L¡¸(Ktëòµ5I_guY°€LÒ{` Â\UÆØm ²KDÝÝ9”äGx>ÉkšÅl‡‰»¥“Ì cå b¼¿˜©pµ¾­AÊ%i®ŽV"#莸 Ê- ÉVÅ.:YŠx„ÇQ·Çå¦:Í?ÝЯS™3ttZÖt‰ñÁt°MiÜ º Tˆ q¢’nF¸ê‰»†€|«ö‹vªÄÌC TŒSî† ©Z¢õ˜àÖüÐÁæ;·ÂTÖTCgGeÁX@q¸Iß­CýÛÓþ5LwO˜ö‡éñÿþgxòè«áª{}ÕN_X%òZÂÎ/»bkC«|„~<›h LÓ'Äz€3ߌ¿ÿ7¦vö¼[_rF±s%ݧ ]Ž™èßÇX=A@v›Ÿsƒ¼‹ó[IXs|¬Ʀղ€žù4Vì)ÖÓn0‰Ó"p[¢oÙg> ƒ©Þ4/ÏÅVx ‡v­Ž¹8Õ‰¶-'fHÊÒûÅ »_/ =ÙY+U:–  Ç£¹5bèÃQ ×]2F Ã4Ò|õ‰0ðýŽ |Í4ò‘’àÁ€#0™R9ø•(xü´ôÊ3K¯f€µ&g† èÉ!3Å»R'®€!jc­ëÆùA£ýùwbµCÇ|Î ÁaøK8]<È…ƒ‡xn{ȹB³¢·˜–ân‘,n0 ÎviæØÈëH×o»²àÃ3|L‡³Pç§‚çZ RÐeH¢­*ÅÃk%š4äGóY[C4G£¶ŠÉp®áYúJ«.exo™ƒu·HUOC·è¡9Q¹ ïÆâŠ,€¬kȺ¢IXû!áíM*í1l%\èò}u$˜b?9}=Çî»0®2–Š;Y&­‚NÑß\\0IqºÑšb¦VÌb;"ݧÄ둈?ë@ÖLS 9‡§i5 åðà €\ó¬ðÆÚ#Æ:=›0¹a€ûN¦yMnÕ[ mì˜ÏGÅÆmnñ­MƒËÝ„áÉ\T­lÒŠM­èq'z‰éd-e ¾o`§%òmÛô™è‚ÖÚ©¾§Ð€)+®IXNýþ7cÔà¶ÖÌÏ{g†uŧӇ>^õ±3¹§ùÉFs«'0@{ÃDÆ«j0X‰KÏ*yb¤ÊH9 оüÉÜGãi0õ'ád6>¹xN{†Ã=+½¯Ãçx‘¬²ûè*ðžçq4MFÕg=Ì-Ò#¡ÿBާèµ<÷ƒ`3?š‚€£ÂZ´·ñ¿–ëAØø©{Þ"½…ã‡ûWLùtŒæóÑ$Ï£vÊ´·ðü^*üj9Ÿ…Ôq8§Ñ¨‘ó&é-<ßÕÞP¯à¤,xÎ-^\Öõ­jÞ*»^óíë5ÞÅvÕþ«PK[éQíìA!PK|k|RObject 7/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 7/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 7/content.xmlíZßÛ6~¿¿ÂpÑÃ݃~[²äóº@8Šl.)Pô+Ñ2JHzmç¯ïeY–µòf)ŠK€MÅÎð›ùf8\týñ ³gÌaåÃܳÝù —)ËH™?Ìùô+žÿ°ùÛšm·$Å«Œ¥û—ÒJY)áßì.Å*Ý!.có”ÕÊqX…Ëz‹Íxîø®ç:ZinvÈVýp8؇@«~úŸ£d–ÄGéíœgÒö]7pr'CYϾ3;Ž;Y îð’$q´Ô¨n/Žó=/Wçc¯JT`±ÂÇ s¢#ºb 6X,ÛZÀsV­ÎV:roAnœÕs~}üð1Ýá !Q™b³‹WòvøÜÐá¸bçøei«\í9ÕjYê`ŠÕa…ãÙ^=ø D4Ødªñš”®:Ê]4â9ŸºTE…$y¢¸k$ãè0ÕŠÒöu·+2LÝ®t»{–éH`NŠhÚ‚•'Š'ÃUÊ=W‡QWN€8F½ÀMõ¥t/8FIùù6¿•ôÌï©NŽ‚Z[v#…£å ØêïV[MuÜÔÜa‚l:a‚ì‚-èizµrww¹/ž0Ÿì:Ï5¶ÓÃ>¿-T`â™éÌÿŽ ¯ýËÙìc…qV/Û?%‹Õ žëÎÆU_4òFL¨ê¾¢Û‰µC¢¾:U^ÝåÜ?žß 8<×ãØ|j,ì úÔXÚa RÕ2Š1­Iè28WGksºð/],â¨ï¿W.";J”¦c|¨ µ à-X?àŽs© ÞÃîðã0kÍ2<–ë º&±ª!ƒE‰2D¤ˆâÖ{ K9ãª|ß—gÀ9'Ù Š°WnúqÝ!ΑÚi8j§á¨EÃõêÚ‹Ðëä6²ß»U¯Ës½þx’Eh†³8ee6^—oùùê½43ψîÏë}µyÕ"ÍuÑVŒ”²a+x â Ê­«†b6¥ƒ°’3¶ªs¸»Ãm„Di†ëŸ Ûë”AMXõ/Ô.´¬FæVÝ`Ä¥¬^TŽîÛ1ÉÌ jc›³CO+=£mCÌ+Óµ¬·Òþ IÑ¥ép™Ã\Õ½‘Ô¡ff_v®6ŸA SPM9ç/eÚ0wªPÏ 9Ì/PÖéæ-ÚL/ÆÜÚ1^n 4’ó ‰ša’(ŽÝ$Pw“ëwúM_òj¤Ó }*Òëü¿INým”u¯ŽoÇ‘ï.=;ö“ÀOâ³Ç¾äNÖ¿ jú¥@ïƒ; îb¹ì0 —‘Ÿœ}ö%ßwþ…TïƒÛ àOÆ/ò¢Å¢SO=É·ÀýEÈìp{¾›,\†Òh/ÃîžäÛðœ½ì$v¡zCr…žß)ï¾ä[ÀþðÛ8˾âÖXúaì%~d//êf»'ù𻼾²»KíÔÕ<ÌËwºsñ†7_ýÿCeóPKÆ¿aøFâ"PK|k|RObject 5/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 5/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 5/content.xmlÕ][sÛ¸~ï¯ðh§}£„;@×q§Ói¶Iv·ÛÎtúF‹Ì.%ª$Ûùõ=àEâAt¡-#JâY튜ËwÎéìÍŸžVùÕ'[VY±~7¡S2¹²ëy‘fëå»É¿þù·ÈLþtû»›b±Èæö:-æ+»®£y±®áßWp÷ººžß'e]Å»É}]o®g³bc×í-Ó¢\ΡdÖ šôwT¯·Ã§¼úÏÌÕö©žõ£—ešæûF3Bøl9K“:‰>eöñ‡þާûzµ÷Çñ¬Aû¡‹¢\=½›<”ëëØ×ëde«kû´±eævœä×E7DEºˆ2Ø|Yl®w\7šëÖ®²²ÒÙ¿?¼ÿu~oW ÌQÕÉznû»ÊM}X}DÎJ»)vúKçÛÁ›‡2o†¥ó™Í­¶šÑ)Ýj¯Q|·Á¤Êªnoõ¼ÙooÒëÁàánªO˱÷ÂPpŒÕ&©³»Ü'IËäqì,n,xßðvç cowc‡÷‚[Î(VÌæI>ßn¶~ÎíèíºÁÞRG—z,3pœ~øÊÖÉØµÜXäcy¶þí°;tçßcyªòhQ0áÑð†½µß·£›ocîb9 OÇ; O‘·$wãØ Þ½~XÝÙrôÚÀ<_úÁbüÆá>¶ Tðħ—Ë-Ý.Ї5¬½—Üm×h†/´qÜxñ¬4 ÖSdîÆôãóâBw&Ì€Ì_¬R¨#aûɱÜÖÃ3›§ã¼ˆV•OßînÌßéiþÞqR߈C3û`óñáý0ÛŒõ?±Tó2ÛŒ&Âv4Ú˜›°:°76káÉU§¿AQÀ'·}<Ôì9›GÛW·7mÀtqÓ^½jCÂI÷nò‘L¼AWÝ·U¶nl±„ûÒl™Õ Ìnof{æ¼½i'ýrù=tÉ*ËŸÝ%Wkô·,ËdsoÀ趬3[]9ÿ…©Êâ7˜`]¬m³ì`…c˱ãË5߆‹µ9Õi.ÚUV7Z­ËÛÏSu(Q²^:ê"Nšrél8×¢·€¬Ê>Ã@Ê7õV˜þr®‘¬ŽÝsûÔÁ/Ø5³]Ï^d˜+(+º+ |^7ÿpÇÝϤ½Èò¼»½°j•û­PrL¡{ÁBÉË*^¥ÐêyuWäQý¼I¶Ñ8éÐl=ÏRÝgij×ÑÜæ9Õ"É+;9懨UÄðr]Ú¤ŽìjS?÷3æ6ùd£e²éÇ”Ùò¾ný7’'[íl=Vr¿>º‹Mßüg4à•£ªJ³j“'ÏQžÜÙo)/– c÷«lî©§´Žø,Riç­Š:¼©,¡²²Ñ(ä·íõ6{Õå3ˆ—,Ì zYYÕÞÔ®*‹ûpÊ.ʹ§îä)«"Gãåpp|ƒ¯¾âŒlz/¶Ýsü²-맦£»¬ ­éøÛSuüÖü¨ÖÝ”Fí¥ ÐÎWýï!)­=f©«É”ÉùÊÃî­ãLÐ]CçvñÙÙðy²Xì³s‡|‹v~ó~ÿev¾+ÃÍß‘9P™ï3r‡|‹F>ÑòŸ:¬ŠÈƦš‰æ{c”îfû´IÖí£{Pâ~ɛәØäE%¥Mö„nŸ9º“Ô¨LÖPa'iZZ§ã?$ éÿÑæ¾Ã’W¿n¬MÛËÓ)×LJüB%él9©¡¢è>©ÚcÐÊ_}¿Ó˜ƒ T@§Ìl¦÷$cß“ÔTjçI[,ŠÚ„¤†ŽÁ.`ÝlJ¸AKˆGôK€ZúKÈ©æn‰­jÝÑèöpwe;«<õ;nu¼)³UR>GOû-%»ËEQ´G­»£ô~/JÝFªy’ÛÙ^¿VSΆ~m¦Dôkµóëí#°b1•ç ò(½œá3ò„Ïüè|f»Ée™¥{÷ =>X%ÿ-ʆÂv¶9n§çývz>d§½z'S!‡1+!†Å!½›£|r\ùo¢ˆÊ–ƒúM÷Ó|Jò[E/·íßOÛöï>hâ¿]fü§Ç 9œ(z54T´)²u½}ʲ±àÜ©KSÕµê¡?ÀP°þ¨—H¨Ê©‚dŒ²£…󨵆ª21Çgü F ÌÄŠ2¥a» pD º% Î<†ˆ‡rÔ(Fê<(˜‚Ac"Ô¤89`(\¹ê !°w¬9ÁÅ¢…¤EèŒ`«$Ö†ù´8€‚Q“R€†jŒ2|¨éAÁtE Ž!Jp®P@zP0J Ð–ʸ`pĹ!P2xL‚(A1ÿJbÃ(rF ÷XB—ÌÝaºÒ)a(h¥Ä è±ÒÆ?aBÁ$ÜÑ_,¸{äŽiCÁÕ qgàõP”àÖ̓‚y¢P…@gDõŽ—=(”àçJrÚ¢’ÙÇ.G ׳‚9¤r–Ž…61á‚¢ð p¢ÁèÆH&1-xP° Ô’Á8·ò ¶>§×ÍSV! :Xó `¤•Ùþ /ð pÏ; *%DÀ†•bÞó#­ç@Àà÷†!Gô±Ë‘ÂðEò`™šuÈ…Í[¾L’ ç\@F6ܽ¡ˆýxP¸L­ÜsF#Ük8&Æ™Aáê%Ð0£Fº'<8OzP0Z P™Óÿ ZÀP0?€ÈSÂcŠ_Rò `´ •€Íº×„øxÍÇ.G ƒ¡ÁBBQˆ}a€¡PÆý¬“€  É…ÒóBBž«ȇJ(¦ðÛ›LP—Ê©Óøe1 ÷â>†æÝ€ßãC> ö(ÔÄŠ5¯-3Âq¢ö `¤ ÜÈ\SE5r») š;¬›“Эr÷;Fº3-d  Âw¹˜E9"h G P¦»§ÿÄ=mñŽ[®‰’Œ2¨„;Ö¸‰BP0Z€ÂXkÅTGø•j 扮&…ŽM]’ÀƒÂÑ´(PŽ@jÜãWL »- ý$˜CBys®˜Ä p÷ZŽ+Ï@å^–@P¸¾žR-¥r¯æHŠiCÁhAùAç ñïõ-`(-H÷ÐoûŸÑ#(˜HC(…L¤×ødÁƒ‚Ñ‚T°Ô§Pº·Ô-xØåhaø›ÛÁÜAj„`îÕјá7ù<(œ e£!ò)q ïAáhARˆ~­ …|ì÷!(-Á)î?µÄ†‚ÑçLP÷"{ó‰hCáh¡yALi©÷Xaˆ„#*Ý™®†ŠˆIŽ=ì…¤à_©ð¥Ûþ/Îêþbçþoƒö¾¶tÿÍÿ_2ßþPKQÔ¬_’ÓyPK|k|R settings.xmlí[ßs¢:~¿EÇ×;])u½‹Óºƒ¶X·­UðWy j¶!á&A´ý ¨½­•ÖEèìÎè‹ÉwNNNÎùr‚gßç>>šAÆ%祓/Jé—zˆLÎKƒ¾qü­ô½þ×}x@.¬yÔ }HÄ1‡BÈ&üHv'¼æRò€d‡‘ñ>ä5áÖhɺ[íeëZ"lygŽyDD”êÊYù-È/ßÀQ òybº º¢V«Õ½á¯ šL·ª^ývR©ìŠìƒàΡ·) FÛg)é#=†-vÑFmoCM.˜tR=vˆ“_Ò4Ýг¤E>Rôu—&õ9—Pz‚;¸b3dœ².åHÈ•0Þ:­Ù¦ô5òýÖ ÍèŒW”¡'JÀV€‘¸¥Üœˆ)e{8;d¹E¡oh¿¶Pž‹õ¥þàë®@3˜ ›€LRÌ£f_ë›s[ÚiÑeOÜ|ùµA… ~ŽÀ6¥~_¢äêÑ1èàp5QôDÉj0q”}=k±¦4j1´ÀJ1¤T,„q¥1¡×‡sq'Æ¦Ñ œw‘&ë`ž"lËÍ÷“ÔG™AúÔ‘@èå“N2š‡ä ’Ã!9’Ã!9’CÁÉấ‡Â²CåÙáÙá²e‡´ÇI!j×uœ”¨Rj`ov{‡<—QŒÀR jEÓ¾½¯oÇ ‚Ç.ƒq!3ƒïí"ƆŒ&úóüRŒß¡¢(èbVŒÚ¤˜²­î¢VOUõkÆòú«i-À(W€KÍCŸ˜4º‚Àƒ¬!ÖB!ƒNèm~ Œ´¾C1·àfªÏEHâñW’Hà˜LÈ@yIâšêN{,²6·úÔ\ÀM§ÊCÀXŽiyxS˜ré\é› õŸŒÑn~ëNc_x+t<4C<ç=ÒðíÊgu%¼>GÜZHVÂ(AOé^úgSžÕ)áöŠÝ‚—7Bboú•áKß^̘0œKžƒaHÎS@˜KéR¼˜i†OsGðbÀ¡÷®ŒƒPÎQâSO¡Ñ¢@âò)Ä¢@¶X,géÊV<­P„›pX­4lQªKãý]V<ìøÃÝNW?‡˜Øè¿åg xF7¬áÇMGº~«Ïÿ¿në†=ßNO×-_CfËPî-}Þ$ 9ö¯Š=nk¦: íñà~Ñè¹>½ÖpÑô5ù|( iawؘ¹Ä\ܰÒô;3·…±û¤ÌãßNk>õZÍö kgd,l‡vËø×w'ƹP¢› ß6£hÙG[Ø­aäµ&ÚíÏÞµ{ÕáöØÆŽl'u{´}{>þi÷×2’ö*up·¯_ŒÆeOÕb]+ɘzÁ¥7nHŒIµïk¡ÝkôîG¨¸j÷ƒ[{lÆcÕõF[ï)Zgpi ÆŠ9\Α¡uúŠi\DÆ´ã¨&¾ûtmÿþü·dk’ˆ0·’ò‚ ¶oÕÿù]©lÑ[^=’4È.€Ÿ@òä²EÒÍô÷À Êäy4§€WĹeß·Ân™„àmY{Uƒ$ã cù)¨+c­0É7ÙÆ µüqÿœ r#™Kf„Kâ½Û_BÿátQà™’VPI! Xóz(h`7Ä@¤Æ”=ÙoQ¬×3ØŸ†¾CÂRmEŠ:ò"e½´ ¹G«–{‰é‚2ƒQ+ ËažƒÓ‹zzî•j¹'½4ýÈ2ãkÑ¥ÇØO‡Ë÷¸ïHS^Dò.¼”ֿבd_Þ ‰+B°åÜ5AËY޶ý ˜¸Ôæ«Wï-é¼…Ô6uŒ¥[ÅKðuš€¸0CŒJ-÷”ßü œö‰úPKêbôòe1PK|k|RObject 1/meta.xml’ÁNÃ0 †ï4[Ë@âËßoû·“®UÐ5ÚšŒ$”‘´J›"#/Ïñ’¬ó›ÔîvZ"WV¶WèEô%5 /œReFöÞ× ë:ÚͨuL›AJx4v·ä¬èÅiáV4ºáFTØp/¹­Ñ -ø7ËÃX§XɱUݺ24R°Ä^Ô@BØc©ÍǵɒÕj!; ÖÚì§8ùlÌá´Ü çÿ”$ 4ÖÉÿš>o;Ø>¿/N4#ùp~;yvT A'¼uùF¿9| ÜSFïèt²Ñ¦=n_—‹íb]ÛÚÙw”›<´ºTñ4…_õRøÑ ®ý…üPKil”Û#IPK|k|RObject 1/styles.xmlUË’Û ¼ç+\ÊYF²’ª˜²½·|@jó,B2Ĩ=ò÷di‘K«¥r´ÕÝôôÌÀåelÄ¡gJs×$?fÉI %—õ5ùýú3ý‘¼Ü¾\ ª8e¸Ú5LšT›¿‚éƒ%K©Ö…¹&wcZŒÐ0 Ç¡8‚ªÑë/ä¾¥†%3úN”€…-““¾'²Ÿ‘ÿ:C5U¼µ^:%1Í5–¤aŠÝ1³¢±¯zú§âL”úbë¡ÁÆ–)îèD`[@Úè”Kô8`‡r%4[¾OY–#‹HYoÕôŒ`sz®ê%ì :Yc´id²(„jU–›ÑY ª‘Õ%iÏÙðu±¬ÈðYwfÆò&Ø.!;#Z€ú…:^)„…BÛt¨,ï4ód×¼1ËuqùÑ_5Ye´€Å®m» â­û^‡üÝà¿¡é÷ªW±‡yðj7 –:j‘VRhZÛÎ'™Qpùçãmv_ƒê†ÝòÅíþ-Ëï:½ûÏm´‡ÑÝÃÜÔ-I¾ðçA>ªÛžøy!ƒ½ŠCºîëèûúƒ&øû5VƃW¼ÕvJøˆJŠ˜`Ž¥Q~Ì—ë^µf'ÕïH±ÜUÿ˜íà)*’ÛüîLÏ º]ÐöCtûPK¨©×(èÈPK|k|RObject 1/content.xmlÕ]Ysã6~ß_áRj·6ïKëq*™³¶&yH²U©¼Ñ$$qCZ’²ìùõ‹ƒ¤2%Ò–ÛG¦j’@4úëMÚ¹üáv]\ÜЪÎYùnfkvAË”ey¹|7ûÏhöÃÕß.Ùb‘§tž±t»¦ec¤¬lø¿/øÓe=OWIÕ0ÆÞÍVM³™›&ÛÐR=BXµ4˶L¹hÖ=Q×nÓ/ßívdçÊ¥¿ÿjŠ9£¡·Ù­^VYV ­v,Ë5—f–4‰q“ÓÝwÝ·«f=ø„DZ)g»¥ V­oßͶU9ß{^&kZÏéí†V¹8)æŒñ –-Œœ _±Í|¿Á\"×ò®ó#gµÍ?~þú[º¢ë„ïQ7I™Òî©jÓ‡ÏòÍŠnØ¿,ío¶U!—e©I *[›6±{ô$ð­€I×­lM*åíT:‹¡4õÍrê³|)7Œõ&iòë‚ÂM²*ÙMÝE¬åÖÆ0õq±>ËÍ2=¬g¦I‘öÂ6w,®X|Àjw’ծʹátË×´I¦òk5+òò¯ãö-f÷ö=•Ém] vD…'ݛ˦è~µ¤¦2n}N37›n0n¦YKr=]‰r1|ºÜ®¯i5™7<÷í`1]pþœÓ;*·Ä½¥WË>Ü.ضäœxˆŠKâ±¹¶Ã=4N+/6å"è¬cÁ\¬éÖì‡nUvÐÔÏÖGC¨ÂôFD¹ÞÂsZdÓ"83ÖõaøOëñ;ß½'ÍêˆFæÏ|RþõóW˜m¦ZÇab©Ó*ßL„jµ&˜Ø°>"›cªéÙE‹( ÜÙUW$Û†q™óÔf__]*‡iýF^(—§{7ûÅš,ºh©u^J],ùsY¾Ì~6{f^]š{^]ªMï3HWö¬X$뼸C¢ÖèYVÉfÅO¼áJ§U“ÓúBØ/ߪbñ JVRÉp8ÅÎ9ÍNR™Ê©9cÃ꼑¨6Õ–vûT¬‘Žb$åR„.KœFM‰t÷Z0n¼«óo|¡ínšþ0ݰÁM#)MŠè^ÐÛvúR»O&µù Å\ð²¢á‡/Íß]»âÏL­^äEÑ­íú¥4&jtpr¨õ0@½GZß­¯Ya4w¾Iï³v6/Ób›Qc•g-”wªERÔtvÊÁ”7Mƒ®7Í]·cA“j,“M·¦Ê—«FÙof$·´Þëz*þ0í ¬ñå ®œ„*ËëM‘ÜErM ]¤‚-^Œ­ÖyzOEEà£< U4Uµó²²ä•5®9 õã*{5Õ?^²äÁŒW¯Æ"¯êæ`kQ•@6ÛVéÜÉm^Y'ýå¸s¼B‹^MàŒO=8×Ë?LäðÁ©é¤F§²°‘Ž_ÔñSÇ-ÔŠ‡2C Íôj¿úÛ¤¢S»<õ¡E?]Ì­¨ˆ™‡“cAbb0è[‘Ø{À¨ø¥Í‚¡”¹Ÿá+š-©Q‰Z^¶„ü¿¿ÆÀb[/«û,OÖ¬ÌÞŽò ϱë¥ýÌ›Qþ‘òÿ¹”ŸTÛÛ•oIÿ™ëXÃúïfÞŒþÜÇžWÿÛÍÛѾÆ™i¿yZ>rÿ|^-ËkÎÛQtH-˱‡ÝμFE¹?¯¢ ºxCzŽÜ4Y,†ôÜμF=?ù}ÿaz¾æZæ¿%»¶Ç+ó!%·3¯QÉ#Wþ±`ÍŠ<{ºàäc?åµ}àD©üçþ‰Ì£]övâšew=!Ïtu©ÌPþ-OÑZ’tvÔÙm,ä»Áùª¢‹w3B:ZùL 5w†I]·’˾WïÀ+Eó½;B“7IΑ[JÀÄëNp' Ûµ¡Ki{8|Ù^Û\ýº-›|M/nꋳëúÒlÇ/MÀ¤ãXÐ%-³ H€þ-[Ó‘ÝnÛ.8CBÇ–´T@û0½Ý$¥zí±â  ŸVvbÔĦ`Á¯ýÉàBî¬êý¢èšURòj:ɲР\ÿ‘ð“þë=÷,As–¿m(ÍÔ0ù1vç§W|±Å­·ítÊð#£Ž±JjÕñ¬EŒkV{¸€€Mœ¨5Ñ8|Ð>4œ€ø!ëM±Š_’†ß èRœ«åàÛ±5^r°‰LJ|® 1Ø+š }wM[Üv+„7U¾Nª;ãvXO~;ÌSMÕ}Ó¼“ELeB:M j±dW“*"–³ä`oÉ¿¨—]l1ÁS~†%«D y¼Åx#ó£°˜^Æe•gƒ"„`ü—U2VíUsZMwÃjº;¦¦AØ-âùvŸ;°} öè~ùgMSVfõ÷§‘jZ2DÛ&î¶¹IŠ-­‡+ö§QÅþBt}Åeòþc¡†¯8ž: d Ú°¼lú7)ÊÍ:ã>ÜVÛËôhèÞB÷þ,èÞB÷þe ³Ï†îÃ(t΂îÃ(t^:çlè>ŽB÷ñ,è>ŽB÷ñe sφîÓ(tŸÎ‚îÓ(tŸ^:ïlè>B÷ù,è>B÷ùe óφîË(t_΂îËx1Ý.)Šaà‚}1¸(xM?¼ îÜ_wxµ%ËTùw[²ª' Æ+iC}P¨­2V4Éh%îÀÛuYësjP0z؃ƒÝyÔ×cíBc–’ht³–{Åv|ä€-·…®þ¼·µš;é?²&ÛÞKêF|9²/dÿøs_¹ÊÂòêRÔ­ӫ'(é.Í~·K³cðDg/¾­“GŸ~RU…yúå·|óèÓO*lp±g>ü¤Òóð_ÿô}øIÉóðßê&{ôé'åWÌÓ‹öé£O?)ÅM=½6$£íPZ ÌSBõ<&ÍL›Ÿ“õàXFfZ_æ);( ç7ÞKÔœ‘c¦´%ÐåŠcŸxî^,EŸ‘{¦t Ð¥ ¬˜„@YŠ>#'M¹Ì£Kú.á7^*EŸ‘¬¦Ü³ñ=Ë%>è?JòŒ6åŒ.“ï[Äö§è3rÛ”»)¾T$ýyA‘ï¦\Ÿðž.‰ÙÀ4A+üIS‹+€©Eàp‹c‹¸ZÀ4/?ŽHä“‘4/ }WŽ>’\¶Mê‹“HòÀá†z qC^q~‚:ÉÓéÀ mË4¹Òlhš‚Fâå$€¼$Ã˵­FP4¯ØÓ0T4¯€x€“ pø8\šŸ¤±ÎÖÎ~9‡ç@Š™žíPIÉ,C‹x ¢T4’¹1 ¹H‰W¨—ÿŠÆ2Íȃ¦!ñq,_—IÒX2Á{†§]3žÙÝ ¶HNàXĆ©h$%FqA=¤h¤Ò+ð ,½‰åÚ¼ÔÒ\[Ðh¥W¬•^H¡Ñöc¤±d (Sp;LwƒW$c \Ã8&i$^®O`.ŠF2^ª:ZwFÐH¼BG»™*­ôÒÎFÃÏ!Nñ4–L±–³GìÓá@3©á8úuUÑXEŠEÈKÒX­~·jT4// з‰–ß´Ò -„ðÛn¨½¤q´¾ÿÓvC- “ ^ÌÝ@€AŠ-vÚŠ¤±ò@ •“ŠFâ%\¶^%Äˉ´6¢ÑNËR‰câø$‰ånð5¥þ’ò™Ý øš ðx³€¤±î!‰x'4¯H-ÅhÑÙ¶]âÁÂKÒXÎæÐÙ<$à×j(J’XÎh¹-x¹ÜÌËT‚€øÐ$UrE$Òj.A#™‹K,n$‰•E­‹§h´¶‚–Ù$>¿@ø!I,g µ×êáéðú²Û‚JÄëAi失ÜÍÖªtEãð C-eK­è‚ï$ù¶_D¶VKËÙ`“ÄÉ&‰ ïX‘Ù?pÏdoPëuh½®ÐÖR¶$‘œMï3Åxm&ü.5I¾­…(íu=¬F«T7ƒ¥ZYç‘ò‘4^!>h#ÄXj µ<$‰v3„½IbeG»Û`õ"C@_ã–«ÁÏÒí›ôçv5øö«AùKËÕÀŒ&i$5ºZƒK’h7h1’DëÙÁÏÞÐ>Úâé?rµj BÂ.Ðh0–?Q ^¸±zƒ®£ÕËŠÆêØ…ïnB¼w71ñ´ï’9‰äl‰`AhmÍÙ°¾ë"íÇ$‰ål° ¼dÒÖXÙ&Ð:AŠÆkûÚýÓE» øþ {€ö{¨[L8j2ç8›ÖF‘‚bàk¯]%‰ål±–ÙFj*TgƒN€æñÁÏÜÄh?rc[!ш¤‘ Ó!>ì& ­èòb­èò²µ?ðÂGàhM%I"a§] Gn†÷]íp¤Ö‡®ºß7Òþ†Ëî×bê—avÔáÿ›òêÿPK“Ê¿ÉÏ ÜrPK|k|Rmeta.xmlSMÛ ½÷WXì^1’ØF¶Wê¡êa¥Vj*õ90ëÒˆ0^§ÿ¾þÌî¶9ì™yoÞ¼ЇËÉDÏà[íl‰’˜¢¬tJÛ¦Dß÷Ÿp†ª…{zÒ„r²; ø¡ŽªmEã•2%úÂYÒ÷}ÜóØù†0J9iˆªCŸ5ôwhaŒäuÞ W·º¶>A+‚î v•/X15ÇJ^¥Î7“’ Œ¤–$qBVìÅhûûÖdIžçdª®PçÜ8N1û]mlÈ_ÑSô^ Ëî&ËùÕÂ9ªÖíŽ^«br,=Ôa@àa{P1Ê(¦ ³Ýžq‘d‚e1Oyºáû¼Í¿-ÿ•ß²äi ­’4ÿ¿ftºóÕ£>zø2Y'iLãmÌîµí.‡Ùî°ÛD¯‡³w¿@BéýÇN…Wß/ýæþ×'Ú†aÈ6hMùP `é:JÄÑœ”`ÌšKódɺã(µæsDª‚¼¹"rë;TPKË]T¥¥LPK|k|RObjectReplacements/Object 9í\]L\E>» ¿ò³¥ÀÂBhm ARMlWÁ"¬e4H-´›ð£@ib@C«±¦jûP“úÖ#>˜&¦‰I㻵}°¦FM|0øb|YÏ™¹Ã½\8Ä–ºç’¹gî|sç;óÍ;3;7<é&þ¤ZA>¨çásX<0^ð¢à&¡dòMŒ9n[À\Çëíax cµó ¿H¥ƒrùVïÁÔ x™a­ÛsV ÀŒe Ò$ø–¬ qÌ`ŽágtÕH7éPV¹ï¬†ï=Ë ›I¾¼¼Li>8e0ëŽ÷ʼnŽEŸí‰>Õ7 ù‰;!Îmx΂7ÂMXë`†mÀ"ï>a[JWù,jÛ®?|k®Ö¸xÆ)ÆV`辜ȀŠu<ê ƒR„Д¬“":pXåürNo.ç ð¢OœKˆUxÚhÕ 1õrµmH’Þr\ª’ž’Õ&‡“$½åÈ/—ž’Õ&‡“$½åX,‘ž’Õ&‡“$½åø³@zJV›N’ô–c"WzJV›N’ô–ãûLé)Ymr8IÒWòðkÝ­›ÄÈá9Úò¤§dµÉá&1rxDŽX©ô”¬69Ü$FÈ1]%=%«M7‰‘Ã#rœ«—ž’Õ&‡›ÄÈá9®ì—ž’Õ&‡›ÄÈá9®·KOÉj“ÃMbäðˆ´ö&OÕ¯»Zäp“9<"‡Zsk•ÃM’¾r,À>‹›ÄÈá!9´o;¹IŒ’Cû.œ›ÄÈá!9¬ýÂ]›’n#‡‡äX±¶OWtíѺIŒ’Cû–µ›ÄÈá!9´ïà»IŒ‘C­ªÈÞÔ%‡›D—Šþl6ø¶YpfÂwÒ›ÛmK]˜Ò•«Ùûfø¼Y–pí€mÿÆÅ ¥+»5†d —÷Ùö§0ˆte·ÆpªZ–°TgÛjL£te·Æ0”%LTØöúNéÊná`,¡¯Ø¶Ÿ•‚HWvk ¡,YÂþ\Û~X"]Ùµ ôhcå #ñèH|4ÖÝ7êíŽEínJÇ»˜¿[xò8ž ¡?ql|vl>13›ž“™Æ|ô¹7u1å]N†ñA2 "ïXUòÁÛV-’ô…øY°¿&ä÷á?£·Ã{d<§!¯]ÑõU óHwâŸRWsøåË$ǪQt0¢*±äã!?X¨ŸçŠ9!|\¡t½æŽ÷Iº¶ =k/Ê–¡'Q÷vÁOWô){žÕ.…éo‘í/;uˆßƒv‡ i§` T‡PÂ7cY¿ˆ±šÃtí⣚µbø1c:D*dÇ)Üi”²Sö×QZŠQ\~™š"¹·¿¦ô’™zÔ~Éœ†9˜‡ãàuoa¸ ^2«9ÌKFk˼†v¢]¶LÆOáÚêe|•LáŒgƱæðL-•'pÕRj8 âo0B†¿0|s«É Ù?fÓ]l‰ÿiËF2x(“‡²xheóPåòÐC,´ýŸXòüæ»T+×Ö-âŠ]±–{{²¶¿`4èÆâòCrÑ‚)@Í#H´ZëÕP˜™G¤NöÅr[v{=³^v³žI©ì+%¶ì›ÈÞadO¥ìã…¶ì›ÈÞidOå`x3W†­°v0l5ƒáÆÏé‘,ùœ¶n2¶þÏé“}‹ $ð®G0í(.&a —x \‚Œ£Ïó¸DÃsÌb! MЋyOÀI¼žÅë"aèÁ«yü£´½@eSéO#[zuÃed ¡+KN¢£Øð·è`œ¯âÄü†2ì=ù8§þï¸Êy=ã‹ì.†ÛE×v|RŒSNl›÷K Ê>Æð[ ¶  ‡ø®ÝU|‹áp%À§ÎWä⬫ ®³àK -aùSùWzëÈcMZ˜åUtpø®¼†y(’ÇCù£3[ˆj[ˆž ½‚"8š}ge)ÍÞB­š¯F„Žà¬ g!A¥|p-g® JOâr… žr›Ìu”CGq–+HgÉ™q/Ä‘DÛEœyôU “åUø©·áÕ{Õ ÃcRŸ;wŽËz Õ4<ÐïŒ?Øÿ@ld0¶sd[l{OŸägî„øÍ4yt©‘èGXk±çÏ£ËÓ›=Ï媞Kíù…‡3ïj^ˆGSÅà ^W+hõuÈ p• ¤ˆõu©š× çøÕåìQôˆß•Ì*"½ìÞÁåÌ˵%-IvËq<,#eoLŽT’ì–££FFÊÞ˜©$Ù-Ç×2RöÆäH%Én9—ÈHÙ“#•$»åh)”‘²7&G*IvËñy®Œ”½19RI²WŽð™ˆáÖOb刻+e¤ìÉá'±rD޳u2RöÆäð“X9"?dr¤ìÉá'±rDŽúV){crøI¬‘ã‘›e¤ìÉá'±rD~ºâHÕßïŒÈá'±rDõTeT?IöÊñzÑð—t?‰•#@r±à'±rHãïYü$VŽÉaüµ“ŸÄÊ 9Œ¿…ó“X9$‡ñ—’~+G€ä0þŽÖObåˆê©Ê¨~Sr(úc1Jóc‡àÌ¥+·Èhò7yÞ©%Q®¼xš½n†wÚd_lðü<—+¿4†ñ&Ùɵžoƒ¨\®üÒ6†eýk<ÿ<—+¿4†?ªd嵞Î'Q®üÒÞ+‘=\,÷ü/õ$Ê•ŸÏÀ‰[EÃñØp|d ³§/>ØÝ9ó&/£~§ˆä.ü.¥ÞÄ®½“£Ó‰äxdpt|JVuøs[ž*ºõ…®C³9âäE÷–:îÞÅ,¡{Œ¼¯yü>7·5˲Q‚&ˆ¯øúØ4è>=âÒ¹!9Õ Ü;ŠõGÕM¤=ÒC9Z¨WÏ5 !¹¨j¡æ)³=[G†síLµnøtß*øùŠ?%.rÇ%GaæGdùËÎbGØ›,í’šJøôõ“˜s5ì„0>!ÂÍó'D’ô"i'D&do…?Ò.eçêÏBZ>ãsµ;„di†ä^þšò"sk‡·È¢)š¦=äßu¿…‹Ì\ »ÈŽ|r%Q<"Ÿ#6Š&Ž;&_/f–¿`ÿ ôï0ßÑmhþ——Ôjç< žGÑU5Z_€½”Ç5Ó´ÿ_S>ºBåê¡<=t“Ê¿éIø+(8[CÄ\Û´ 8• v-©Ä:¦ïÅ& -tãäZw¥ÊµtËÖâsÍ.^é!›kD¿–È\Ûœ‘u-m/6×l®‰\;U¨r-ëZÚ^l®Ù\¹vwžÌµ-Y×Òöbs-ˆ¹v>ƒ¹†®NC›]hµe]´—vS’Æh‚&q>…Ÿ$Î"§ýðIzšöál~¥íÔM Q²WÔJ ~„¶áj?\¶ž¸gîû~pmCLëÀÕ€ Þpó³ÃVŽþ„}ƒÁ¸€Ä~b%Ñw°Ÿ‹ˆ~(&šÀ¿¿åeDÃåDDÛ1"÷V}»£šè3XžqÂÔ%ê0-WýÃCôEØûõ˜O À0d£DßÃÆÀìä·o°ÉfŽÖˆ v®]u®E õÐJ=T¤…†ôP´X•è¡R=T¦‡ÊõP…ªÔCUz¨Z­ÒC5z õP´VÕé;ÔCÑÕz(¬‡êõPƒZ£‡õÁë¡hD5é¡f=—Š¶è¡µzhjÕCmz¨]­×ß—ÊÒÝöž´»í ¯µ‹Ým¿„ïB‹­HužYýbÿP»È.:(v Þ;&ÜdÊ݉ÆÅ;ŒÑê/Ø›ê7÷ÆèñìltÀÖ„ÕûSwý¶ëòyXáaû±âci}6ŒÅòŸ"fXdLËkW²Þ’7¼¡ ï…™[ƒü_@-⪦Êÿ PKwüü“R»PPK|k|RObjectReplacements/Object 6íœ]LUÇÏî²Ð-lY>–(vÛk“JÀ6+ Z ë¶%"в iL(iH(´”€!&âµÖÖćRûÐøfR_JâCc|ÐĘhbß|h´›øêCk|ÐàÿÜ;—Ù¸D`¯Ýr÷ÌÜsçüÏþî¹³3»¼œh{1uØG $USúrã¢éƒDWk‰òöÜà•I?ÑûhCóóob-’¶ÛhØc·ˆÑ[XÛ‰5¿ð‹Z^¸•oaÔ^Áfžåœµv- Ð Ö‚Btž|ïXbE‹¸xùth‡•/Ù–¬ô•åz»Ý%+ [IŸ››ã:}€(õt6§Žv¥Žw¼ìíJëmIim—ú¬=$^÷ã5Ÿnƈn'ªÈ³mBzÖ¶\¯ÚYÒ¶]¼ø2¶2RœI‡qo kUIäQÕ"µÈŽŠ˜²E(’íÏ«–ÿçÈò8;D­âµŒUE¦¬wð û¸ö/)’Û8þÙ&3ek GºHnã(ˆÊLÙÑ.’Û8"%2S¶Æp¤‹ä6ŽÊ°Ì”­1é"¹cwHfÊÖŽt‘ÜÆ±/(3ek GºHîâà /Öžh"—àh*”™²5†Ã)âáp Ž“¥2S¶Æp8E<.Á1S)3ek ‡SÄÃá| ‚3ek ‡SÄÃáwöÈLÙÃáñp¸ÇÃ:™)[c8œ"—ਊËLÙÃáñp¸ß™áLÕ½#8œ"—àPwdŒâpŠä.ŽéÀÿðÎ)âápã%"á0þŒÖ)âápã¬"á0þß)âápã_hpŠx8\„Ãø÷;œ"—àPŸªŒâpŠ˜Â¡äÏ'i‰ïG× Í ÝZf3ÑdÛÏKIÔ++>Í®ZáËzáØÛ~VN¢^Ùµ)\Û+#ì{Ò¶TA¢^Ùµ)Œï’ò·í$.¡¹^Ùµ)tTË¿>fÛYäÎõÊ®Mao¹ŒðÅVÛ~„È\¯ìÚòÂ2ÂÕˆmÅt¶íÚîe„³!ÛÞ. Q¯l¦<11 zRÉžTogsk{ªëhsgÒ>ðrí›E&8¨˜Ú†úÇúƇFGb]}#çd£>ÿÜ€b•]GèW>‘Ý|@Ô\°Þ’ÞµÞÅ<ÿBá<Ù¿f8‹|nºv?í&*Äúqê£Idš‚£~¼ã}ìý ådn`èÜC¹†pQÄÿå½|¢»(u¼‡&–_žæB‰dGB½ù%\>½Ë¯w´®n½+‘§wõ®|½«@ïÚ¤w…ô®ÍZW›žagº ‡3•/ƒigð\©°7­©–¨ë/a쾊£ò4Áè¤sø¤b/ûÕXFìZ(æ`ˆþ:Jöʧè¡Ĭ ñ^ÚˆtĺzX& µ®õ?b19S³òß;H4¬XÿäèôËÚ,°õÏ”Ow× ?ÆäUé0Máxí#Þâmþ‰Y×!Öï~®]hñHâÑ3ßâúó²Õ3§À}gRÕ3·QÆø”iÑ3 -¼ž1Þ3?ã³Çu«g¦0¯‹™2³gî¢\=³ÐÂë£=ß'fp­cMy6ú¼¥Îe|Ä”Ï|l ìDýö6LÁ°óápa[ü\cù<ìØÅ²Rìœy?Úª‘÷™êÅ.> ¹·W‚yýãqÚŒpg*å8­GÍS”>NwZ§í€òyã4{ا¢6öÌÓC&vïôUìo—ØØãË`{س‰ýRØÆÞ¸ öF{6'ÃÙœ (s2lð&Ã¥ÇéÇA9N–™ 6ødø]ö°ÿ ûa9Ñöâ']øÈ>EƒÈ•oïckëOà:y”&Q?†­!||ŒQ ¶ÆñÇuül»Ø?ŽÔ®[·^ão‘¢´nÉÞB©, *ÙDtÙº-ÝL.$ê)"êÀ)ê”W¶}òI1Ñ®|˜1:K‰¾G9‚^xùÞA9eÕUgœ«·}»Ý|G¸{U7‹Eú€zW"¬wmÑ»Šõ®ˆÞU¢ÏPïJ”ê]ezW¹^KïJDõ® ½k«ÞµMïªÔ»ªô®j­«[ïÊÞ9Øù8xÿ%Níªì¿PKýIµ`7ÈNPK|k|RObjectReplacements/Object 7í]]oE~·_p ¥ß-ý¢§”B%‚Mib ¶‡ƒ ª=@E„T¨ÐÚ …(QDBE¹0)â…&~Ä .ˆ‘è…?@‰øÄDã…7øq!“ú¼;;ÝíviOw9sš9ïî<³ó¼ûÌ»;³³Ãa[¢sSj½E+H|,ª!ïgi>ÑÒO DË‘ñÁZ±ñ|6ÑYÏBÞØØØ*òv¥ntKÄé$¶Pöjìï,§$—²ÆAî(vsð¼sxl¼¢lÁV®M:FÖ)gÇþ ¢D«}y´©Þñ—\KŽûÒr¾[îÇ —IV~ùòeγèvÊ©§«=µ¡;õÌ–§’»»“OïîH>±q³àgî~û{¾óhgÑ_`ØUàÚë¨òÙ5®å|YΡvíä5ao‚‹#^1⺧åDUOâ‘Ñ0¢¦t’ÉÍëdÉ{sàîrn±½Øh—2«íùµ\q·g^®U$á•£­LxÊV›~’ðÊQZ#œ+Wê%1rDDžÍeOµO{IŒ‘ƒ'·ÙSí3é^#GDäà¹~öTû‹/‰‘#"rð«öTû{/‰‘#"rð³7{ªýµ“—ÄÈ9ä3·V9ü$á•ãJþ}x)é'1rDHíïhý$FŽÉ¡ý•µŸÄÈ!9´¿Á÷“9"$‡ö ~#G„äо¾ÃObäˆÚ—»øIŒ’Cûê?‰‘#Brh_ å'1rDHíkÃü$FŽˆÈ!çë´Êá'Ñ%‡¤?¤€½ÜæÌ¥–6áÍ'¹öóYdçKkÏ“N›aÇ QÃw-®=³€ì|iÓcx}©¨áÖ2צP3çK›Ãg¢†’%®ÍAÍœ/mz ß׊VÇ]kÁr¾´é1ü^.jØYåÚjXΗ6=†²BQÃÉ×v"†8_Ú‰ Ú8Qª žT²'µ»«}ãæT÷†ö®¤{™òçm”o·=AxR!uö¿Ðw¨w¸p ÞÝ;pXêµø_ûó%&½‹É°h,ÛÞ8㜒Eo:g1Æ?pšÜx ŽÂ®á‰¸…D0´÷’=8׃4D‡¨ã¯ö£ŒßAúôPw˜Öse¨ýU¤Ö<¢?.B½èëb|”²Æ,q3Š9Š$·$¤¥†²ÔP¶ÊQC¹j(O ÍRC³ÕPL ÍQCs•P§ZÃ./„K™Ê&Ç¢ç>Öˆ] {½BD,ï¡쬽ý3‰è̹3a¾¦ü#!ü‚†5åüt ×k/ñïw#åƒn9êú%‹sÇKü¯Wqf´Lí"·eöA÷~ÜIeË|ƒ4 ºo‘NØ-3^´Œö–¹µÔm™cè׆ížrbËÜDµ[f¼„i­-ã´å+EËð|¯´ùyOÞËøŠÉ–˜þÉ Ù¯¶¹²w¢ n—/‡3 ëÈâ3²§/;{~Âýqñlý¨}ˆåHnñþTd~ðÛ{è‰:>9¢•hâçÇȽ…ËaÏÇàyU•ãèHoåqÉ€ãÍ£M&Ò…jH-iRÀñ&2)¾¨Ôšf o)“éJ™¤¶4)àxH™H/Â6ý±˜ˆç!WL ¥96v/¡Ä:Ï ®ÅÔ]§3'ÖîÌ“±4"Ÿz¬™qy0db¨(_ÆZР}ê±f†îÁ‰5¢æ˜Œµ qýÔcÍŒîƒ!kDçÉX úO=ÖÌ@0¹X»:s±¶ UY…D{pT3ò¾ÅƒØŠSŠöÃÒÚ‡­!Øaä6Óê·súìRý(§ì ãó"®•ë}Òy[ð5x.ÁµÎ“*4Ù?H¢}FЂº`.Q.úôQ¤wuݯÁÇ#EDð ³·Ï2H;Ñ"¿"­.'úé|ѹJ¢Óó¡sÑ{H#ýPMÔS¬XdCsG:ZO”‡Fy?Î^Îø™›kkºKb¶OkµL"_ ¨¡yj¨P ©¡b5T¢†JÕP™*Wk¨†j¨R]¡JÌWCUj¨Z Õ¨¡Z5T§v^ %¨¡z5Ws©¡DƒZ¨†ÕÐ"5Ô¤†«¡%êóRC!í'×ö“§ø®9Õ~ò<ì:ñ2B¯ýzžÓó}Èéºayq䀽p¤È.wƒÜ%=¼Ì²8[ôµìÈ%gœ¶ßé o"Àmv1î§W‘†b\Ë2E°ŸÙŽ.hûL/ؼŸ]ÐÌ]sþ…ÓSøïÌä¡Òþ PKýnD€tqmPK|k|RObjectReplacements/Object 5í] TÕ™>30Ã03ý˜WwœÈ#†Ñq†QcŠ×8 ÀdãfP [.@W“¨ÁðJXÙ*ˆ‰eÉú*+ÆlHÌ.ì–w¸D¨¸‰R›¨[º©X¦ÜwY-fÏÿÿô½Üî¾çôe~O·s‡ºóÏ9ç6ß¿ïôí{ÏwÎí½‹>»üê Ñ)ð§Bd„÷ç±O ±s– k…xVnN¡c²‡Ç ±Kî>JÖ m‘ÅÙò¥Kö ,ëûÜÀ¼¾k,F|À^çüî’¿ÇˆŽ‰BÌœ Ä{cÝø‚ü/»Ýõj?‚vcîOÅ9¥sRÜæ%ã/å, •D¥Hçà¨FRñ1’9Tô-¾Jíi@çú`:—8Y,p~'ÕÉüxA÷ðÓÕ•¤téxkf ‘?HéÒñZf ‘?HéÒñëf ‘?HéÒq"‰™Bd£ÃRºtü¬3…ÈF‡¤téøûf ‘?HéÒñƒ ˜)D6:ü ¥KÇ£`¦Ùèðƒ”.û&a¦Ùèðƒ”.»/ÄL!²Ñá)]:vMÃL!²Ñá)]:¶\„™Bd£ÃRºtÜÑ™Bd£ÃRºt¬ïÄL!²Ñá)]:Övc¦Ùèðƒ”6ݘÇ;ü ¥MÇÍ)Ì"^Ò¦ã{͘)D6:¼ ¥MÇ IÞL2Òá)m:D3…ÈF‡¤´éèªÇL!²Ñá)m:k1SˆltxAJ›Ž«1SˆltxAJ›e@d½ S ¥Kdx”û2ÌÑQ&t€1™²»p^ˆŽ2¡|BȔݔô‚Dt” `›B¦ì­$¢£Lè2e·¬½ eB˜ê)»ƒï‰è(:~šÂLÊ9¡ÁÑQ&tÀ” È”}~‡$¢£Lè€()ût/HDG™Ð±fºŸsö$¢£Lè€ùI)ûd(/HDG™ÐÓµ Sö¹a^ˆŽ2¡f¯A¦ìSå¼ eBLæƒLÙgzA":Ê„˜Û™²O¤ô‚Dt” 0×2eŸWê‰è(:ÔV:ü ¥KÇ걬c?HDGÑÁ> ÛÑQFt°ÏI÷ƒDt”ìSôý eDûŠ?HDGÑÁ¾€ÃÑQFt°¯gñƒDt”ìË{ü eDûj'?HDG™Ð¡FdXéðƒpÓ±½O ÿLÇ'‹Ÿ8¿¿.Dz—°ò&Ù€k,žkÀÙôÉΛþR gÈ>Ãg]ý!†³Þ&Æq~Ó¯âøXŸÛ8gátÝéÞ$ú»’è8N¢·0®‡Mëµô/ŸãÒ‹s9‡þ‡Kâ ”ÜýÃ,÷6ï±oõ;<*µÃÁ¬wÊ£ÿ‘bÿ!™ÝÅs£ª?À&1:‡@¦àvºHÞÅ}^fñZy÷va›U=DŒÑ92·+P»¼½>#³€øG™]f²Uý6ŒÑ92·+Pë…òÂ_fq•¼lILq£ª‡È#1:‡@¦àvªŸ&Äó2 ˆÿ(³«žîFUÿ<›@Æè™‚Û¨R2U³4d¬–7g=QÕCäÈC Sp»½?C¾eWÉìþÔáFU?À&1:‡@¦àvzç!î“Y@Ü!³û}§Uý}l£sd nW 7»„ø¶Ìâ.™Ý«ÝnTõßfÈC Spnû¯~Þoÿµ‘SÖCNÙcä”M$§l€œ²ýä”ýŽœ²éä”'§l)9eo’SÖINÙíä”!§¬’œ²ÄùØÎ!Ø6ŠÛþsŽ=Ðþk£I^{ÉûRå‡ÉkÓ¸KÙÊþ3Bû~Ì/¼kÿ™€sŸ0ƒÚ@ݺç3ä}©òOÈëÑŒ‡ÈC Sp» ÒùÎSÈûRåO’ö›@Æè™‚Ûèz:‘à ý!ò¾Tùy`5C¯á2FçÈÜ®@Ké>iO“÷¥Ê¯‘6À&1:‡@¦àvšO—>p 4¼/UžDØ~6ŒÑ92·+Ðlº&…kÓ›ÈûRå[Èû›@Æè™‚Ûh&Ý,ÀMÃAò¾TùIòÀ¦kÌ‹ð£sd nW ‹è.îæ÷¥ÊKÈ;Î&1:‡@¦àvj§Ûk¸Í>AÞ—*ÿ’<°¥ó"¼@Æè™‚Û¨•Æ=`üc-y_ªüä½É&1:‡@¦àvª§)˜zм/Uþ!y`š¡×ð£sd nW J)„Ã?&ÑûRå3 ‘mçÈC Sp»Çu„†r?MÞ—*Ï#ì›@Æè™‚Û<.[‡1ö-ä}©òòÀ Ì#1:‡@¦àv+AæÇ·ÈûRåÝä%Ø2FçÈœÛþk;u>öß;u8þ¾½‡y«È)ûlZ=Ñ€c#ÿ×€·àMä”õÇð†âg1¼n……spy´*ŽŸÂ{ãx²‡…tpNy'Ž]7@†Æë¯Ý ÛÎ!Ø6ŠÛþsŽ=çý8ÅÁ¬t<®Íuèy­'l3y_›5«þ²éîàŽ4à`­r~OvŽJ‹öý—_è^#Pîc°àeÕ =š¼®ò¸j4þkx!´¨Bè@í žÕN:ÜÛ€ž”U=ZT!t v…oêõx">Ú€Þ”U=ZT!t v…ªƒ> §‘GÕAÞT‡ÆØ/„•C¨]!Àkº‹.Eþ*†^”U=ZT!t v…OéAºü›zNPVõ^”U=ZT!t v…Of/ 'ž ”U=ZT!t v…ïå[q”ÿF½(«z!´¨Bè@¹í§ {WŠðöS7Ÿ¢aËåäÔÂ.‹Ë%lÅ—°Ë}„]–KØ¢ \®ðvEDع„Á„Å7Ú‘0ؽS\É:e›+ äÔC\oEÁ¦‘Áéé{9íÌËi‘7#žÓ“S½œ^š—Ó"ïF<§ÿú /§]y9-òÖbÄszd†—Óy÷1â9ý»K¼œÎÌËi‘7(#žÓ'º¼œöäå´È{˜Í)d>oŒ;/>/ç^|y õÑ' †ÑÛeëË)ÀÂ:œõÑN Hk—`¯T@ µ SgìU°iäÐ^×âÒÞ@ûð}VE´ËÖ9.í=´ß©7¢]¶nŠ»´_@{‘c*íÁ†ë#G¬¨~ú«Z짆чápÓ^[ãÒ^èð3ú0nÚgqi/ôaØ9Â? ío˸[^¬‘¯‚ÈsÅb“Ø n·‰ÍbPæÜ'n’ÿÖ‰5rëe¼SÖÞ.÷jŸ‘{ÂþðZxuL¬C¢ÆGcJ·Êí‹ûu¹½"…]9Vˆûªe›|om“Û[5øÅ|=ò×1^ˆ)u² •+·/6±0&ÄÅòcúq¹ÝŸÀo1½Kf»º CæÊßmzGnª,ÜTU¸iLᦱ…›ª 6­,ÜÔ[S¸i\á¦ÚÂMã 7Õnª/ÜÔP¸)V¸)^¸)Q¸)Y˜ÃÂM½…›š 75Æ*ÜÔÛR¸©µpSªpSº`Óðs§½mïsæ£UÈ·mà´·%ö[àü†F‰_ÊÏ‚ã²ÿýé Œ"ìcœr¦·uåá~¬“Kᙺ—ÉS¶Ìb|ýÛånTõkjó#ä^üL]Sô°ó ó ±°(pîÇŒÔÏ:þúlv%ˆ¡û«“B@õ‚p÷W'—Àþ Y J‘>^b©òçêD¶ÝˆðPýÕ=lÍ/„Û_MÀ¹ûkÛ©óé¯6`WzTÿ¨ðÂýÕI! ¿zA¸û«“Ká˜IQf7 H‰ŠeUoDtñ0Ó¡†íŸù‰ï5åî—ö®<~¹'Ž]fþ!-…û¥“B@¿ô‚p÷K'—À~)(DmF\dëˆÕ/QÃöËüÄ»ý2”»_f¿ô'T¿ÜšÄ.1t¿Ìÿ%?n¿ô‚p÷KÍ÷IQ2Içí$ж•Ê™¤!Ñ¡úe jØ~ø}cÐíÛ•BõËMØe6ꟙTì·¹ýÒ b÷Û‡@ÈD:M±¦Idë™DªC Û/Dªýлª_®nÁ.³Zÿ¨¨bƒèöK/ˆÝÇ‚(ÿÓŒâmA±  ʪi¥­):ËJ[Cð‘±NNT«>%ĬñÂÙý:ñg¬ó÷ërû3íÊ©¦qëõ4†‘Óâô“õØSn_–ýÞ•Ny™ÜÆ“Sñæ(¨ÍîaÕ9ʤc¨ÌÍ’÷uâÖ¬2j¢áórûš£LvHvû”©N ý ïˆ BÙ¯êó¼c¿RÛˆµ_h/‰´/’'ús'ì”`óFA µE´ßyè?›ð<ôe±Ilk…ÿ<Ó v;ç¡ìÑyèCQæd *³^žiÖ‹ÁeÜÏîì‘2¹ÊÌ>eà.ã­B¼-“o~±¸M^3Ý(™ß(óÝ n:g>H½³\Áî¤Ïò %Ú1¹]*ÿÓ­rû„DúªÜNÈmó!Rc…8 Ǫ†Wþï­+‡{rGÀ Ž•¥1¹£¬Þ-[¡/ûnyJÆ«ä+~ß,œ4›«ûÂÞ'žåRé°Ü>Ûù¿½+·ïJ˜·äö,LØ—]g@n{å}åkr›";Æ1¹Ý*¥nÿ£!öGë]Í‹b5|ï`ÿW›¸WþÕ/wæŽÊŽ=͘¬ÆƒvÌrFKÔKUüPKj…ÌÔê[PK|k|RObjectReplacements/Object 4í] pÕy>²eÙzËÒ•®°cdÙ×3# Çø!üPí”æe^l`JhæÑ4ÐL<ÀL(%mBiL2v˧CŸC‚NÝÐfÚ’BÒ$ÐNÒ& žoÿÿܽ¾Ò½ÿ¹‹~νh5³úuÎÙ«ïßï;»w÷|gwm`íºÍçÖ˜^C?5¦Ûäÿl;ј•'³f†1íbî^n̹ÆüÞt»ØÕ'Ùº‘‘Ûì_Íy»Ë®µ`n¼ÆlóIû×ûפ`RT‹¬U“ûŒ­½Ïk¹q/¼.÷&›;ì_S"ÐS³‡ ÑÏN»F´ S£œ‘&~\4œ¾‹¨×û4§#¹~àÀÔÕ˜{m¡Ýl=ÅælÚ¼qhÍà¶Mƒ¶3¸jõzÂöUÑï>û{ª9ûÆ,™eÌ‘©q|ÞþË?ë‹#êÝz ÇÑ?5Ç•ŽKñŽ|2®µ°)Qµ&; Çý0– »®iEÅàú•nM:w”¦s(Êbuô» ¨QæÏM¡-X<þtõ R¹t®£LÕè(©\:^¨§LÕè(©\:žk¢LÕè(©\:ži¥LÕè(©\:¾ÑN™"ªÑQR¹tü}'eЍFG!HåÒñ7Ý”)¢… •KÇS'R¦ˆjt‚T._ŸC™"ªÑQR¹t¡!$¥£JèÀ” dª>¿#$¥£JèÀ dª>Ý%$¥£JèÀ„dª>û'$¥£JèÀü$dª>*$¥£JèÀt-dª>7,$¥£JèÀì5dª>U.$¥£JèÀd>dª>s0$¥£JèÀ\dª>‘2$¥£Jèps Té(©\:ΚúL³-Ié¨":Ôg‚¤tTê“° AR:ªˆõ9é… )UD‡úýB”Ž*¢CýŽ…B”Ž*¢CýŽB”Ž*¢Cý~–B”Ž*¡Ã]s«ÒQ¢Mǃ†èø^i:þ*ÊâkÑïÛíïæ(ótoE_Í¢ÿÏ Í—~´ƒfÆÞÒNO1:o&Ívëi£yM-­4ƒåÓ-4W¡µ…\雚Éüï&rš¶4‘§ðJ#/i¤±u "ý›ÏŽiÃßæøMEÿ*b£œÜç³ÜwäoûžümÇÓF˜SÌV»õ° fìmœGWèÕ¡î,Ý¡®ˆ0/Ž~Ï·¿Ë@Oºß-üš²ÀµöG?†m·|Âv9įÚî94=Ž®QG ot |Áà ´Ñ/vÛãâµ6›µ qtõˆ:y£kä V !{ðo䈧ØlV7ÇÑÕ#êä®!/xXÖÚoÑwì7+âÏí·ì¹mqtõˆ:y£kä V ólFÏÛŒ¿a³YÙGW¨#7º†@¾àaZeºlWAÜg³YÑGW¨#7º†@¾àaZiÏ+?g/ïµÙ,ŸGW¨#7º†@¾àaZa¯âšlˆu6»e'ÅÑÕ7© ä®!/xX–ÛËëûìi âïÛlÎü`]ý}ÍZy£kä V e'ó¿ö|ñl6KN‰£«GÔÈ]C _ð°-=Õ˜mˆ;mv}¿GW£š@Þèù‚‡¨ž1ßm4Q|ÙfÓ3?Ž®QG ot |Áà ԷÀ˜ mˆ[mv ÆÑÕ_¨&7º†@¾àaêé1æÉÅý6›y½qtõˆ:y£kä V }ƼXo¢xÈfsêâ8ºzD¼Ñ5ò×¶ÿÏ1Éí¿ûÙ)[ÆNÙëì”íc§ìzvÊØ)›ÏNY7;e·²SöÃfrÊng§ì(;eg°S¶²yì” ¿û/Úäö_ÈÒ¶ÿ¢m/iÿaëÁB;{_®ÜÍØý‚ý—ëP‰ì?/ô¤ûãØÂÇöŸ¸ö³´@ÃÜ-Ñ=ŸdïË•Ÿb e¼Ñ5ò+ÐF>^à¸q{_®|5{`¯ ö_r¼Ñ5ò+ÐÈq@ŸÇÞ—+÷°¶O°ÿ’ ä®!/xXÖò7,¾iß`ïË•ßb e¼Ñ5ò+Ðy|êƒS ¿fïË•Ÿfl@ðg“ ä®!/xXVñ9)ÎMeïË•gl¾àÏ&È]C _ð°­ä‹\4ÜÏÞ—+?ÄX·àÏ&È]C _ð°­à«8\ͽÝBÞ—+OfìVÁþK.7º†@¾àaZΗ׸̾œ½/W¾†=° ö_r¼Ñ5ò+Ð2÷Àø‡i!ïË•§6›\»Ž@Þèù‚‡h)Ha`j˜½/W¾€=°£‚ý—\ ot |Áà ÔÏ#…1üsö¾\ù {`g¨ ä®!/xXúxC¹óØûråöÀö ö_r¼Ñ5ò+P­cŒýKì}¹ò>öÀæ© ä®!/xX°éóã0{_®|”=°aÁŸM.7º†@¾àÚößìo¿ûïØ)kc§ìqvÊÖ³S6²Ã| ~_é óÅ.>oýÝ&:=º·‘¾…o ƒýKõtLyfuÝW§CÔ‰²·ÿ¢M.aÿ…Ü(mû/ÚöQûã‡"ÌÚÈãjgÏ«‰=°vö¾ÚÛ/ב~§tGÚa}4ú=7Ú*5éþ7¶Ð^ ÚÆÒBÀËÚÛIÝð3ìuíek¯pw_r!DT !$аBÀ³êek>{Z½ìeõ 6^r!DT !$аBÀ›z©ÄÏvw…²«×BDÕB +<¨{fÒ7á]ìQÝÃÞÔ=‚ë“\UC 4¬ðš†ÛèTdˆ½¨aö †w'¹"ª†hX!à)Õ¶Ò¹à;­ä9Õ²×T+¸8É…Q5„@à ïèh3Œn!o eW¯#„ˆª!„VxDÇšèjèÍfòPvõ:Bˆ¨BH a…€TßD—£°à¡ìêu„Q5„@à ϧ¥‘Æf4‘'„²«×BDÕB +¼Þ™ßHÞÊ®^GUC 4¬ðp6×ÓˆØúòxPvõ:Bˆ¨BH a…€W³v Iž[O^Ê®^GUC 4¬ðd.ŸNcÂÍ ÏeW¯#„ˆª!„Vx/§ÔÑ üIÓÉ›AÙÕë!¢j!jÛO³>¿Å$·ŸÜsOa§æ;5¿ÉNÍvjÞa§æevj>ÇNÍQvjæò윫xÈ_4ÒÁúG t¬ÀP(ºê-õÄÔùUÜ~Š6Ùãá“!6JÛ~ж½¤ý”ÿäÅ#OdôêH‰ì§’¨I÷»±…Ží§R ÚÀÒBÀ{y„½˜Gø«GÙ“yD¸»,¹"ª†hX!ཬb/ǃuìɬ|ÀäBˆ¨BH a…€÷rŒ½˜c|ËÔ/Ø“9&Ü-–\UC 4¬ð^ö³³Ÿoz’=™ý‚˜\UC 4¬ð^na/æ¾ê6ödn|ÀäBˆ¨BH a…ÀÁqßê´Ï ·°'³Að“ !¢j!†b%?^L Ÿ”gø–¦ÁL.„ˆª!„Vx/Ã|ë’›”wA³ÉÕë!¢j!†ÞËÃìÅ<Ì—¥_dOæaÁM.„ˆª!„Vx/¯±óOKý1{2¯ >`r!DT !$аBÀ{YÔD^Ì"˜9³Ñäêu„Q5„@à ïåÊFòb®ä‘± &W¯#„ˆª!„Vx/Ï4ó MâÚßÕë!¢j!†ÞËÏëÉ‹AÄØð;3L®^GUC 4¬ð^^žA^ "ç¿;Ýäêu„Q5„@µí§9–§äöÓ ;5/ðpåìÔÌÍÐ`Mm†Æ fgèRµ-CWJ›3t¢¾'Cç‰øNS^ÉзdG'¤Oï¤cÄöNê¢WwC‡ä1ØâöS´É%ì§¥m?EÛ^Ò~úfy0›%of=™o ®G®#%²ŸJ¢&ÝïÆ:¶ŸJjK ïe#{0kÙ›yË×#¹"ª†hX!à½,e¦½™'¸¼T¸û)¹"ª†hX!à½|‘ŸÐ÷0?¹dW¯#„ˆª!„Vx/wus[†¼|#ºz!DT !$аBÀ{ùBy0ŸÏ7ƒSW¯#„ˆª!„Vx/{;ȃùL†¼œºz!DT !$аBÀ{ù‡ò`^Ê7ƒ“rW¯#„ˆª!„Vx/Ó2äÁÔt’7³‡ËÓÔΚDT !$аBÀ{Á¹"ÎéàÍ<Áe½ÓWUC 4¬ð^.̳µ“¼™W¸|¡š"ª†hX!à½Å7 ]ʯ©Úį©êç×Tá×T]ÑBNÍ~MÕP395oòkª®l"§æ[äÔtËŠÛOÑ&{Üýb£´í§hÛKÚOfã[‚Ý-A ßËu¤DöSIÔ¤ûÝØBÇöS)Pí`i!†¹ûÁ‹ÙÏñIöd–>`r!DT !$аBläý^Ìvöd>ÆžŒtn–\UC 4¬C|†s{2‹Ø£ùŠ0´˜\UC 4¬kù^Ì´Š í&W¯#„ˆª!„Vˆóø^Ì¿p|o ºT¸û)¹"ª†hX!Vñ9 ¼˜9~‡o Ú$Üý”\UC 4¬+ù$^ÌŽ_ã[‚ú…»Ÿ’ !¢j!†b_Á‹ÙØJq+¿¸éˆp÷Sr!DT !$аB,çËPx1ßçøü‚¦+Ô„Q5„@à ±ŒÇàÅlä2må1nCK.„ˆª!„Vˆ¥</æ9އù…KCjBˆ¨BH a…èç‘0x1xgâ®&“«×BDÕB +DE‹ùwŽ?j4¹z!DT !$аBôðX0¼˜5M74š\½Ž"ª†hX!ð`<¼˜û9>Ô`rõ:Bˆ¨BH ÚöSíÙ½&¹ýôK~ZÖìÔœš%§æ´,95ë²äÔ\%§ææ,95÷dÉ©ùl–œš?Ì’Sóå,95™%§æï²äÔ<—%§æh–œš•]‰âöS´É%ì§¥m?EÛ>j¿[aN‰¼—Oð-A7³7ƒ2âÍìÍ|Bp?rªìw±y£'ÝÇ~MYàÚÆÒÁ“¹¾‹¼™eÉ«¹ž=”]»Ž@Þèù‚‡^Íc]äÙüq–<”Qví:y£kä V ÈÃé"/çO³äí Œˆ²k×È]C _ð°ÁÛy±‹<žCYò|PFDÙµëä®!/xXàù|¯‹¼œºÀ Be×®#7º†@¾àa‚„sXÄŸfÉ#Be×®#7º†@¾àa‚GÔœ%¯hz7yG(#¢ìÚuòF×È<¬@ðŽ:²ä!5w“§„2"Ê®]G ot |Áà OéƒYò–fu“ׄ2"Ê®]G ot |Áà ¯éô,yNî& eD”]»Ž@Þèù‚‡ÔYYò¢–v“7…2"Ê®]G ot |Áà oêÜ,yTgw“g…2"Ê®]G ot |Áà Ïj(KÞÕênò²PFDÙµëä®!/xXàeaŒq¸›<.”Qví:y£kä V x\—eÉ뺰›¼/”Qví:y£kä >¾aåÙ6t˜­›·nÞvþŠÕë7oúÈŠóc¢ðó)»þŠˆæeöw“Y{Õ%Ûw]|ÃU;wÌÞtñŽÝ´ÒÅöŽŒØÿ™Û¾ºÚ#“£?îfÒjÌ]ÌÈý±ZÕ·îõv{Manžj·øDÚÖÞè#øë`ôWo”9æÔÖqæƒC9F5­-Þt~~`gަ"¸J&즺˜°E„-J MØîú˜°¾ÂúRÂFv]SLØâ§„&ìÚÖ˜°%„-I MØÕí1aý„õ§„&ìÊΘ°3 ;#%l4aÛ»cÂÎ, ìÌ”°Ñ„]rbLØÒ–¦„O&,NšC„aõ^ÓƒšèoPvC JQm9Ä ÔmšœŽÌÍç´wLN˼(˜ðœ¾ý¡|NÉi™× žÓÿûp>§}crZæ¥Å„çôg§åsºxLN˼ú˜ðœ¾uz>§KÆä´Ì ” Ïé-Êç´LN˼†™Ðœ"ó9¶õK³èä³ÇòYæ9ÔÄ!lwW:Xagw¤cZeVß–Ñ”EØ?6¥#Þ_¤WØŠ?©O/ Ç›Ó]uéÅÞxszÖÔ ~aòìøqúÿy¦1—ÚOáé®+Ì%f·Ùi®17šÌv›ó&sÛÍe¶æ:[¾É¶Ï6çÙu°&>…ÏõÛ”Z¼–É”Ìuv¹Ò¢¾j—W¬^[¦sÏ ÆÜa÷‡ßžnµšaÌ5ðŒùFcŽÚåëö€ýP³1Ùc޶˱Vc~‚Û/f¥ìÌÆ© L*Þ4¹xSmñ¦)Å›¦ošV¼é„¢M[Š7 Ôoš^¼iFñ¦úâM Å›‹75oj.¾ÉÅ›ZŠ7µoj+ŽU¼i`fñ¦öâMÅ›2E›Æï05znσѤ›³Ã”œÛ3d°Þêè7R˜dŽØ3Óçlw9¶”¢Iz¯x”¨9<}c‚hß»åRt2Õ/ΰGR›b¿Ý¾uf]ý¥Â›cs„—=™Ê=édª±…XS¸ö³Ïyýõ`u¥ƒò«§‹÷×(…ý5D»¿F¹”ì¯Èb»éäFË•74˜\»á‰ú«zÒþ:¶qõ×¿ýnúë£MÔ••T¼¿F)”è¯ù Úý5Ê¥è3i ÊYM$Rk3‰†²«÷"ºìgÒˆ¨IûçØÄxj÷ËÜ Ìõ˽-ÔeöÊ:,Þ/Ç~ayÜ/óA´ûeé7ÅCÓJ"!B´-&W¯ó¦x5i¿,õ¦x ô={S|¢~¹§º bâ~9ö›Lâ~™öMꥻÄy£DÛÃåî6O¢õË’¨Iûe©WÈH ïÙ+dõË]íÔevÉOq/÷•.q¿Ì ûŠˆ‚, Ò?q¬k7¹zgˉ¨Iûe©gËI ïÙ³åõË‹2Ôe.’ß·Qî³Þâ~™öÙkåg$Ρ ‰…2"Ê®]çf(ot›¡|Á'ÆÍP8P}ôWYΖØVóëÆY ¯Úed5Dµïo«a9ígNOk¤žrù¸íØj£ò&»Ô³ðú$ÔæÖ:”?q”É6“2WXÞ¯2×å”qæÚÓv¹5R&·FªŒª2 Y™ZIûÌÎ%·Ç@—É®M_‘‰CûOÛˆöµö@¿8G;v‡»-Ø9“PÃm)íãwú~;‡>nv›Ìe¦ð8ôŠ]C¹5ÒãÐ{¢Ìá )³Ãiv˜í£”‰¿»sk¤ÊŒVfùø)ƒ«Œ7;yÃ&+}½¹Ñž3]b™ßeóÝi.?n²Fc´Î`ïæïòS,Ú³vYdÿé»Ì³H¿e—ìrÃTcº¦ó”].;Ÿ.ùßß_.¶Œ÷\Œ.¶TÆ\ŒªÚ[ö /”»·ìµq¥ýľ30|3Ì0Ìǽ}%ƒ¬‚ºA`ø2R0Î&€Q0²ºÆ`4DWS~U"ÆÄÏ(£!©DS•èÖ® «k’ ©MRšÄì–µ¥Ù­5»µl´0?t7ÖZ:{ž~ßÓÝÜ>gšy9÷†žª;ïœszîóöóœîÛ}žsú~¤ÿ‚¯ÿ‹&5OÑO“ Tòç[K•ºã<¥VOTê9ý ³g*õÚ¥îÑ›7뺡¡ú¯¶Ä¿íÐ[ÍšoÑ«vé¿f迚C€æ°?ت)ú]»WGsã>þ÷ñÑR»õ_-!èjº“ áϽŢpƨeï£4ñc¢âôMD}¼Ý½œFŒdÞüÀ¨kR÷ëB§Ú0¸|ý‡Ö­¿híê+Ö \xÅŠ®\CøÀ¾&üݧQ!27%“ 9 BlSd*îÑ&Ar:„¸ÈÈTܲN‚ät40Õ‘©¸ƒŸÉéh:0Ç™ŠOhH‚ät4˜rLÅçw$Ar:„Ì@A¦âÓ]’ 9 B&ä SñÙ?IœŽ¡ó“©ød¨$HNGƒÐéZÈT|nX$§£AèÀì5d*>U. ’ÓÑ t`22Ÿ9˜Ééh:0·™ŠO¤L‚ät4˜‰‚LÅç•&Ar:„3E”Žrú¥ãÀØ0ë¸$§£èŸ„]’ÓÑ@tˆÏI/Ééh :ħ藃ät4â+ÊAr:ˆñå 9 D‡øz–rœŽ¡ÃÜU‰ÒQ"MÇ]Šèøm:/„Yücøûý»=Ìp×XZ[qùXšE?«æK_7fÆ.ê¢9Ë»i¶ÛŽ˜×´«‡f°¼ÖCs: äJ/,ÿøWršn+§ðXFhœðŸ 4ò?« ë—Åôáouì®WÈðT]휑p„åßäàÎ$x䜳EýBïy‹~!é×óãâhê:Ø]élSˆ¹1ü­IzÖã°zX=,p©ãÓM º{þÇÆÕÙÜ61ަQF gt \Áý ´XŸ7žÔç Ä'ôùc^kMý“mR9£Kä îW £S”úƒ>™#Ñ'ößµÅÑÔ#ÊäŒ.!+¸_¾>U©ïêOYÄú÷Ëq4õˆ29£Kä îW  X뢳B<¨/.ꊣ©G”È]B Wp¿µêëÐ]:+ÄÛõk\!ަQF gt \Áý ôc Þ¢3AÒñAM=¢Œ@Î蹂ûèýÎ×ë,7鮳mzMýõb9£Kä îW ³{•Ú§³@|PgwÆŒ8šú}b9£Kä îW Ã§*õœÎñ;:»WgÆÑÔ?'&3º„@®à~zô4¥~£³@üµÎnÏéq4õ¿È]B Wp¿ ÎVê=âÛ:»UgÄÑÔ¿'&3º„@®à~jÑLM×Y!öàr&M=¢Œ@Î蹂ûèûïWj‘Îq®Nä™9q4õ‹ÄrF—Èܯ@7ÌUjÎñÃ:‘¿žGS¿NL gt \Áý 4«O©›uˆ7êDf,ˆ£©¿YL gt \Á¥ýÚÖC*»_»é¤?T¤sËÔ€ºpo@LõVyjûµá®§øµõ°sÒ~mÈAÅñyzˆ9:4%/䧪¬n'ÓeSïÔ±>Þ±®±þ2ü=3Ü++jÖ㱺àýN Ò'Êt!`>îæî¸£ÌI”M½ŒVT !l ~…€Éøj'^é"eS/#„UB¨_!`&>ÙM'ä'zÈlDÙÔËaE•ÂêW˜†ò'âž™Š(›z!¬¨BØ@ý sðt¾$Á°ÌC”M½ŒVT !l ~…€ ¸›¯ wÉ$DÙÔËaE•ÂêW˜}xX .Ê^$3eS/#„UB¨_!`êæ»¢÷Šdú¡lêe„°¢Jaõ+Ì»?ãÛÒé™{(›z!¬¨BØ@ý “n9 œ‰‡²©—Š*!„ Ô¯0ã.㙋2ëP6õ2BXQ%„°ú¦ÛV»6 SeS/#„UB¨_!`®íâ¡ÉÛ2ßP6õ2BXQ%„°ú&Ú^~ “ eS/#„UB¨_!`–=^¤Áùý™i(›z!¬¨BØ@ý SìY6ÉžÈ4CÙÔËaE•Â*í –ö_¢²û‚§¶Ò~o+¿4†/;»iôly Þü‘çNßÀ·®/èΩ/Üûøºq/[®ãOÍÏðIû1>g<Å]ö‡ÌØ+vYjû‚á®§ø‚õ°sÒ¾`ÈAª/øÕV2ÉÁÊ7&«¨Þ©ceòSQ³‡Õ}Á4Pébº0Åö·’I¶Ÿ»åã“UT/#„UB¨_!`Š v’I6ȦÙG¦©¨^F+ª„6P¿BÀ{¨›L²‡Ø4{¤KEõ2BXQ%„°ú¦ØÁ2ɲiö“nÕËaE•ÂêW˜bÛ d’mgÓìÖÕËaE•ÂêW˜boÈ${ƒM³·zTT/#„UB¨_!`ŠÏ&".ÎW±Yv¾å62»VT !l ~…€)v›d÷±i†Ùɦ^F+ª„6P¿BÀ{ºH&ÙÓ|{úlAEõ2BXQ%„°ú¦Ø¡"™d‡x|ूŠêe„°¢Jaõ+L±#E2ÉŽðÍ *ª—Š*!„ Ô¯0Å›dˆ!ÃfÊ2BXQ%„°ú"|x*›dˆ¢œÎfY˜VT !l ~…€)6›M2DŒÏa³l¶˜VT !l ~…€)¶”M²¥¼xg›eKÅ„°¢Jaõ+L±‹Ù$»˜WO]ÆfÙÅbBXQ%„°Jû‚34OÙ}Á£ Ü¿иñ” [v•hÔ¬P¢ADŒ â–wL=%º`ï.Ñõ"þ—+ˆø´œV¢“5b¸¤®D]LÏ"Km_0Üõ_°vNÚ 9Hõ·dŽ]["Óì(—·ZŽÃ¨ceòSQ³‡Õ}Á4Pébº0Ån È»©D¦ÙÛ\¾ML+ª„6P¿BÀ{( sìÞ™f8NM½ŒVT !l ~…€)ö¥€Ì±‡KdšáÄjêe„°¢Jaõ+L±/dŽí+‘i†O@S/#„UB¨_!`Š! DdÓ Ù™z!¬¨BØ@ý S Y ";˜fÈÎÔËaE•ÂêW˜bÈÙý€/ÎM½ŒVT !l ~…€)öX@æØÞ™f¸;2õ2BXQ%„°ú¦Ø£™c{JdšáöÔÔËaE•ÂêW˜b¸vCÄ5Ý«<>`êe„°¢Jaõ+L1dˆìöð©—Š*!„ Ô¯0ž9ö…™f3õ2BXQ%„°ú¦²@DvCžÌý,Ï%î੬ëy&åÝ<‘ïžGöMžÆô}^R÷ ^R÷2/©ûo^R÷&/©e—¥¶/îzŠ/X;'í †¤ú‚Gø¡šŸ˜JñºvÕ;u¬L¾`*jÖã°ºà±/˜*}BLb'wC˜d›¦R¼¾]Eõ2BXQ%„°úb1Ÿ`’Í馸 KEõ2BXQ%„°ú¦Ø¥üPÍ—9þ;?\óRË2µìBXQ%„°ú¦X?TóiŽÏòÃ5û,«£² aE•ÂêWˆ |)“ (@<§ ¢z!¬¨BØ@ý ÑÊׂ0É丮ÙaY‚]+ª„6P¿BÀÃÅ8L²æø2/¢[/&„UB¨_!ná»!˜d-ʼnEÕËaE•ÂêW˜b¸…IÖP Š*ª—Š*!„ Ô¯0Å0“ì´€âYEÕËaE•ÂêW˜bI¶$ x^QEõ2BXQ%„°úbGÄ`’­ (®-ª¨^F+ª„6P¿B´ð$L²õÅK‹*ª—Š*!„ Ô¯0Å0& “ìÊ€â'‹*ª—Š*!„ Ô¯7ð °¶?â¡¢é‚ÚMƒÉ&ÀN«¤"A\=öõ 1aóÊ›—VI؆É1aóË›ŸVIXk[LX_a}9a•„ýxjLØ‚2Âä„UvKgLØÂ2Âæ„UvvOLØ¢2Âå„Uv¸¶¸Œ°Å9a•„=zJLØ’2–ä„U6ØvNaçä„K¦ä~óT" ›Ï ¯õ›Ã¿AÙö&”æ ÷Š¿¿©fÓÉÁéGOKr:¯*§Ã¼)8é96;Ééüªœó¾á¤çôgg&9í«Êé0o-NzNw¼?É邪œóîã¤çtáÜ$§ «r:Ì”“žÓ×ç'9]T•ÓaÞÜԜ"ó½c”úZ)5u" ¾Ã ÝúÛ`bpnHÚ¨°dºáœfÔpÛuÆþæšM'íguí Rh_Ó>Ò´om'ڥо(§}¤if2Ѿ$…ö%9í#E;.'>©+þo<£óBjAüè°œ$þwͨ¶§¿T®ŒZ6Æ(3ßªÌ ûôm­òóKâhê?6±:BáÞw匞uÞUu!V \ú9­+†Ž£¿þÃdêJˆ™ûk˜BJM‚H÷×0—ÔþŠ,.×"Íh%±LyÍdµ;ž©¿:¡gí¯Õ…ˆû« ¸tí}éxúë7¦PWBÌÜ_ÃRúkDº¿†¹Ô|ÞDY:…DšÒF¢¡lêˆö󆬨Yûguâû@¥ûeiÿ%ÇÑ/n§.ó°ý[ j÷Ë0…”~™‘î—a.©ýòÝv ¢Ù®¢z'¢3õËTÔ¬ý²:ñq¿L•î—Ñ× eê—;;¨Ë fî—Õ¿>(î—Ié~™þ½M¥§ƒÄy½ƒDÛÉeÔË|o“5k¿LûÞ&è ûÞ¦LýrK'uÄÌý²úãëâ~™ñû=FY@¤_sÓ©¢z™çZQ³öË´çÚ@OØs3õËË»©Ë fî—Õ—ëÄý2 â÷9zå­.ç…n eD”M»Ìº)gt‰uS®à'Ǻ)œ¨.ü€RçN¢YÔ¥Ê̪ø/ýúN8«"¬ÍgU î§g´RO¹Nݪû²+ÖéפcìŠh‹Ü®8!Êt·‘2›4ïר­‘2ÆHz^¿î•‰¶È•÷¼¡LËTò¼qDlQÆó6G teÚrÏ{äh³ƒh¿@ŸèD´ãp¸›½ºQ¦-§}äÎCÿÙIç¡[Õ6µ]]¥ÊÏCÿ¦_{ÃóP´E~:!Êüª›”Ù¬Ï4›ÕÕÊÄŸÝѹ2•Êœ7rÊà.ãõ¥~¯“ƒC¼Fݤ¯™®ÔÌߨóÝ¢>qÌDƒÖp\ÁÞÍŸå§i´õk¾~Ó;õëLt»~ýJ¿¶Qª0V©ƒúuÕ8üwê»ÿiM1¸$ÓìƒlS .É4û`ħ4ÔÑr'úÂp–¯èx¾þW»ðå•sfë~|µúxxîºJÿ}†ÂvØÒ38Ãá¸9ØL“t.Óoÿ·úÕ©1¿§_Wðd'õk­î oé×—´¼MðNxZGщœ¨S'GQÊDþ)µ›ÚjïWí¦‘;bË´zÕ.ý× ü» ”ûÀϬ™fügÇyáèˆùWÿPKV(½•ç4aPK|k|RObjectReplacements/Object 3í] TÕ™>Ãcý˜÷LÏCQH€ám$(Œƒ $ƒH!¢ P1ƒ >ˆn”еÖbSPI¹˜5UJ&ÆGB¶tKËUCv£ë7U*eŒîìùïÿŸ¾—žîþOß™ŸÓí\¨î¿Ï9·çûï÷{ûÞó{ï×;—^¾rQ™êPø¯Lµªà¿Çç)u÷|¥ŽŒUêÜq ðáÖJ¥îÕ‹Ñu½½;ô§dàkwé¥&Žó—hWßÕŸôßÐ¥Vï}- K•¥¿£k÷êb95þ3}½"ý‡†ª]úÓ0´W•í¤‚÷o‹^b¶·ÃÕEc(_åGEé›õþrÿHiøHæ?õÔSPW¦îÓ…µzÅ‚•—v¯üÚò%]뺻¾ºna×%—-C|ÀÞì½ÏÐïÃÕ¤ÑJMmSêô?þ»þ“WÍô#Ô›åÚ}ÿ•Q:#Å]A2¾¥W ;Tå*ÕÇüCa4£t¨ëCEײ‹Í’töä§s¹—ÅeÞ{ z™†k0eàéš‘¤xé8U‰™B£#¤xé8ÃL!ŠÑ‘ R¼t¼•ÄL!ŠÑ‘ R¼t©ÃL!ŠÑ‘ R¼t¼Øˆ™B£#¤xéx¶3…(FG&HñÒq° 3…(FG&HñÒñÓ1˜)D1:2AŠ—Žý_ÀL!ŠÑ‘ R¼t<03…(FG&HñÒ±û\Ì¢™ ÅKÇ“1Sˆbtd‚/7OÁL!ŠÑ‘ R¼tlêÀL!ŠÑ‘ R¼tÀPÄ’£#¤¸é˜Ò&<Þ‘ RÜtlmÆL!ŠÑ)n:ž©ÇL!ŠÑ)n:>Kb¦Åè‚7 c˜)D1:‚ ÅMÇ=˜)D1:‚ ÅMÇïi¢Aâ¥2ü7éÚLˆŽ¡¬ÈTÜg ‚Dt”àA¦â¶S$¢£Dèc 2wá‚ %Bø„©¸)‰è(:À6…LÅ=Ú HDG‰Ð.2d*nYA":J„0Õ!Sq?ÑQ"tÀÈT|BC$¢£Dè€)©øüŽ HDG‰Ð3P Sñé.AˆŽ¡&ä@¦â³‚ %BÌO‚LÅ'CA":J„˜®™ŠÏ ‚Dt”0{ 2Ÿ*‰è(:`®d*>s0ÑQ"t˜9¢td‚/W8 óJ3A":Jˆñi¶™ %D‡ø¬ãLˆŽ¢C|v&HDG Ñ!>'=$¢£„蟢Ÿ ÑQBtˆ_± ÑQ"t˜³*só&:2A¤éØÝ¥Žùéø­—ųÞû÷ô{2½9ÃìùC•8OúGU8#vd5Î}ÜY³Ü^«ÆùL_ŒáÌ•oÆpŽÂµqt£g%ÐwüM¦EIô~–ÄQãÚ\VƒC+kXúW^äÓŸÕ™«Ü‡þŸÅJ¹W °Ü»‚ë¾3¸îpC»)æ0µ]¯}¥^{ˆåzí·Žô£©‡hÕ¡vçïP×{˜ë½wMJ!èa·»ìÂ/)\j{´¨Gg4K¿ NÕÝrS•M=D¬Ñ%²w+н£3‚Ø¢÷WÅýhê!Êd.!-¸[®Ô;ß{uwUãN×DS¯˜@ÖèÙ‚»h…þ…¡³‚X¦»Ìâz?šzˆ2Y£Kd îV EúЧ[gñrÝEM~4õÝbY£Kd îV yú˜ô Îâã:»Ù)?šúƒbY£Kd îV éúdá}Ä÷tv_åGSÿ¾˜@ÖèÙ‚»h¢>‹{WgñOúðel»M=D¬Ñ%²w+Ð(}zýŒÎ â!}zÝ4Φ¢Œ@ÖèÙ‚»¨n‚þUÔYAü²>®ŒãGSQF kt lÁÝ Tq®R¯è, ¾¨³zžMý+bY£Kd îV ÞIJ§³‚8NŸ¡ým²MýyI)¬Ñ%²w+ÐG_Rj¯Îâý:»ÿâGS¿WL kt lÁÝ tjšR¯ë, ¾ª³{§Ã¦þu1¬Ñ%²w+ÐÉh¬@|]gw|¦Mý[bY£Kd .mÿÅöªþÛ“Sö9e“È)ÛANÙÈ)›CNÙÈ)ÛHNY#9eÇÉ)û 9eûÈ)û$‰NÙä”mêýç­²…ýçb¥¤í?oÝóÚf¢†ñ¾LÙx`÷0ö_ºC…²ÿ¬ÐÃnÙ…÷í?péf~z¨[B÷¼…¼/S¾ƒ<°û/¼@ÖèÙ‚»hí/`¿#ïË”ëÈ{€q— d.!-¸[®¤9ìÐ%ïË”Ÿ l3ô^ kt lÁÝ ´‚~aá—öÿªÑû2åròÀvˆ d.!-¸[Ñ¡u“÷eÊß ìbY£Kd îV ytL Ǧ¿ ïË”Ÿ#lc^„È]B [p·M§“8i¨Œ£÷eÊɘJ·Ëd.!-¸[&ÒYœÍýy_¦üy`w)¼@ÖèÙ‚»h^Ãiöƒä}™ò>òÀó"¼@ÖèÙ‚»¨ŽÆ=`üc5y_¦|y`ÇŲF—ÈÜ­@4 SÏ‘÷eÊÿJØW˜¡×ðY£Kd îV ð¸öшa#y_¦ÜFØ>1¬Ñ%²w+x\ŸÐPîmä}™ò?ö‰˜@ÖèÙ‚»<®+è›7Èû2åcäAYF kt lÁÝ ×&2?Þ!ïË”O‘¶IL kt lÁ¥í¿ö7Âþ[LNÙ_+q4qyZý® ÇF:È)[Ygzªñ„â‚·>Ãã¿ÇðWøŠ8îìŽã>å…8vÝãÈлüÙOnûÏ[e ûÏÅJIÛÞº÷ÙÏñ0ËÓ—¸=0Û«þÒé;ù;Ò:ëÞû8o­XÔ°Û_v¡;­@¥wŒù…/ëu×+Ñ낲©—‚E•‚u+xV[i?ð­*ô´ lêe„`Q%„à@Ý ÞÔ´#þ}zWP6õ2B°¨Bp n…êrú%¼„<*(›z!XT !8P·B€×ô :y²½((›z!XT !8P·B€§ôV ¾^ž”M½Œ,ª„¨[!À;Oã£é’²ñtIÙxÆ/‹*!êVðˆÓÙÐ31ô lêe„`Q%„à@Ý ^PŽNŽ£WeS/#‹*!êVð|¾Cã·ÇÑ‚²©—‚E•‚u+x;Çh@æ8z?P6õ2B°¨Bp n…gˆI ÇeS/#‹*!êVðjºhHòÂz9P6õ2B°¨Bp n…Oæ¾%ž ”M½Œ,ª„¨[!À{ÙLƒò×&Л²©—‚E•‚•¶ŸÚ~¼Jõß~ZCÖ#èB­í4hs‚Æ Î'§æŸèŒé°ßNÇ‹£âx¸òP-k¸³†Ù:Þ£‹ØUO'©?óS‡rÛOÞ*[ØO.VJÚ~òÖ=¯ý¼óbðÒ«rfŒ7Ý‘BÙOyQÃnwÙ…öí§| Ò;ÀüB€÷rŒ¼˜ct‰Õ òdމ Á¢JÁº¼—äÅì¤K©¾OžÌNfh1¼,ª„¨[!À{9M^ÌiÚ!JžÌi1!XT !8P·B€÷²†¼˜5tiÔzòdÖ0C‹á…`Q%„à@Ý ÞËÓäņ£e&p°fR-Ž ©ÇSÕ¶•Téz!XT !8P·B€÷²‡ 4¬cUJ×ËÁ¢JÁº¼——éAI×b\\£Òõ2B°¨Bp n…ïå”y R-Æž•®—‚E•‚•¶ŸÊ/êPý·ŸÔ¢Ssc#:5ÿÝ„NͧÍèÔü°šT š›RèÔ<–B§æ)tj†´¢SsN+:5¶¢SóõVtj¾ÙŠNÍ­¬¹í'o•-ì'+%m?yëÞg»›âaËzIPeÀ£±¹ *Ý¡ ~›5zØí0»ðK —Þ1æ<™ñµèÍŒ®C¯Ê¡lÚe²F—ÈÜ­@àÕ|ЀžÍ_ÑÃ2D(›v¬Ñ%²w+x874¡—s]3z;P†eÓ.#5º„@¶ànoç®fôx¶· çeˆP6í2Y£Kd îV ð|R-èýÔ§Ð ‚2D(›v¬Ñ%²w+xA?nAOè‡)ôˆ  ʦ]F kt lÁÝ ч-轟BïÊ¡lÚe²F—ÈÜ­@àMN¡‡4¡=%(C„²i—È]B [p·§´6…ÞÒªVôš  ʦ]F kt lÁÝ ^Ó®zNwµ¢eˆP6í2Y£Kd îV ð O‘ÕŠÞ”!BÙ´Ëd.!-¸[À›z!…Õ¯[ѳ‚2D(›v¬Ñ%²w+xVo§Ð»z³½¬·é’*(›v¬Ñ%²w+xYpL ñ½Vô¸  ʦ]F kt lÁÝ Wo ½®¿µ¢÷eˆP6í2Y£Kd >°ÁÂí:4ªÕ+»V¯\·bÁeËVv_º`E—Oüû¾^~Góú=¡–n¾fãÍë·mÞÒÓÞ½¾g+.´^ÿÍÞ^ý7ÓëWQžæ£w¨÷a‘V¦î!zõ?­U™2ËÞ¤­ãþJ]5×µÃû |zÎûÔáe÷Ö© Ì»–w¦yèÓ´4wÓŠ`ÀÖ÷¥"@\1v¥OØô ¦G„õ%lWÌ'lFa3"ÂúvGÒ'lfa3#ÂúvSOج ÂfE„õ%ìúFŸ°Ù„ÍŽëKØÚŸ°9„͉ëKXw›OØÜ ÂæF„õ%ìÒ1>açgv~DØ™„Á„Å“c‘0X¼CMƒd½Ï@Ù¶2(yµ…×Y–³ippúÖø §Y9-ð¤`ÐszdbÓéY9-ð¼aÐsú⤠§3²rZà©Å çôÙ/9™•ÓÏ>=§§9••ÓOP=§Ìr:;+§žÃ jN!ó…Õ:ІŸÓÔ™ŸC}þ ƒaô±ºõÝfÀ¦x?ëC½6Vƒ/ƒj ÎØ9$gÓà¡}rƒOûÌ<´ÜoUD»n½±Æ§}vÚn×Ñ®[óiŸ›‡öÇT"Úóÿ~R9bõÓ ‡c?íÈócØ1È _8Úß×ñh½R×êoÁ5™_SÕ j½Ú¦6«[õçvÕ¥®Óÿ7«kõk£êÑq»®½UmÕï‹Õu‚¬Z½o/Ö‰-Ô¨ë×ýGi¨úµIã¯ÓÂׯš‘Jý@¿*ô–q£~uÓm U)õójÝWlÆ•úý:šÀëü§êߌÚZý­A™ÎöÏõ€2WùnÓ94wSyî¦a¹›†çn‘»idΦU¹›:+r7UænªÊÝT»)–»)ž»)‘»)™»©&wSmÜænê¬ÏÝÔ»©17Vî¦Î¦ÜM͹›Zr7¥r6 Üþ°ïD Ý]Þ 2½Ùæ´\Ár—yïÂuD*¾¢ûß_ÎǨÂ^Xî¥ÐgÂÏŒ¬ Òz{¹äœyõÁ¥®ÖY@œ¡·¯—æúÑÔ_ÍÜ]5MxÁ3¯¬ÑÃμÊ.Ä’‚À¥o¼_ØÛþú«v¥_ñ÷·ËÝ_½òô× ˆtõrÉÛ_!‹µZ¤±qË”—ÅTºÝŠðPýÕ =lÍ.„ß_mÀ¥ûkû›ýé¯$°+=Âß}.wõRÈÓ_ƒ ÒýÕË%ç l@”y )‘DÑ lê­ˆ.ø6,jØþ™øN+Pé~™~Úy¨~ù` v™ûó<ƒìO7÷ûeDº_æ¬<ˆòY ŠD›T£Òõ2•gQÃöË|•ç@ÏÚcåCõËuØe †î—Ù{â÷Ë ˆÛÇ®ƒ(Íu(Ω:m•›ë,‰Õ/ó¢†í—ùž7ÞµçÍ„ê—[°Ëláï"Sèó_ü~qû<²‘Þ¦8¼A¥ëenDÇ¢†í—ùnDÇžµÑ…ê—k›°Ë¬åožSèáü~q{£6åÃFçå& Ê¡lÚe®œ²F—¸rÊ|p\9;ª¯~Y© ªq"Êju¥2QþS¿yQ¼Úh"Š%§³‰ÓóâØSnPßÖý6€r¯Ü­_ÕnŠþ[ÿ5jÓK8õ2MITæzÍûfucZ3õê%ýºÛS&½D¤Œ¸ý ÊÀMá‹°ElQÆ~5[ÌKžýJmƒÖ~ ýƒ:¤}©ÞÑŸ9iYªCM[DûÀí‡N6à~èÛj«Ú¦6¨ÌýL7Øëí‡ÒKDû¡³¢ÌÑ&T¦GïizÔÆ>Êø¿Ýé%"eú*3à”³ŒSÍJ½§“o~™ºE3]£™¿Yç»E]wÆ|¸· Áî¡ßò íUýšNsB&i¤;õë¨~m®TË¥ë׆‘ðí¼ýó5ƒcÕ@OîÈ3ƒcUqLî(©­e'ô…B·–'u¼Xã·Ê[A»¹J°,,m¶8ÞºA£Ö¯OiNÕ" ³æT=§_1Ý}–è×_õkŠî/oê×íºsŒÒR¿XÑûóµUEó¢DçE Üœ9ÀÕ®¾«?…¯{s ‡¤Çž&Ž3ãAwÍ÷FKÌWMüPK6>ydH¢RPK|k|RObjectReplacements/Object 2í] TÕ™>Ãc`Þ/fº§gˆƒhˆ È0£( ÃÛœ ¨A\ˆ¢.`J- ‚V·\-‰U©˜ì®TY Z‰Éf!™=ÿýÿÓ÷ÒÝ·ÿÓ—ù9ÝΪî¿Ï9·ç;÷ûÎ=}ïùιwmïÊ«—©N…eªMÿ½B©Ï^©ÔŠ*¥Žè——øØd¥n®Tê~½ù(74´Wª|mÞjÊd‹u¯þ4IåŒòrá¶*KGç>¨“c¨ð}½"ýF«úÓXtH•í§„÷·]o1ÇÛ‡r5ÿ<¬&ü™¨¨ú&B¾¿Ý¨>’ùçO>ù$ä•©/êD³Z¿zÁàÒÁkV­èÛ0зfþ%Ëú°·xï]ú½\Í›¨Ô%íJ½^îÇ—õ¿üÇ.?B¾ÙŽ ý˜ýWvFêŒ*’±UïÀ@¤JŒQ©,ó‡Âh*ô¶ª)‹Š¾þEfK :·å§s•W‹eÞ{ z5i,îÁŒá§«+'HñÒñXSˆbtd‚/ÿ^5…(FG&HñÒñbÖ¢™ ÅKÇóXSˆbtd‚/?nÆšB£#¤xéøak QŒŽLâ¥ãÙ6¬)D1:2AЗާ?‚5…(FG&HñÒqxÖ¢™ ÅKÇS`M!ŠÑ‘ R¼t<>k QŒŽLâ¥ãÛS±¦ÅèÈ)^:þy:Ö¢™ ÅKÇ?ÍÄšB£#¤x逡ˆ—hHBŒŽLâ¦cF»ðxG&HqÓ±3‰5…(FG¤¸é82k QŒŽ HqÓñ§z¬)D1:‚ ÅMǬ)D1:‚ ÅMÇç*°¦Åè‚7f¢è­)^: †?–þ¡Í‰é(:Àú€šŠû,A˜Ž¡œ ¨©¸í‰é(:ÀƒšŠ»pA˜Ž¡|B¨©¸)‰é(:À6…šŠ{´A˜Ž¡\d¨©¸e‰é(:ÀT‡šŠ;øA˜Ž¡æ@MÅ'4Ab:J„˜r5Ÿß‰é(:` ÔT|ºK$¦£Dè€ 9PSñÙ?A˜Ž¡æ'AMÅ'CAb:J„˜®5Ÿ‰é(:`öÔT|ª\$¦£D耹PSñ™ƒA˜Ž¡ÃÌ1¥#¤x鸪üÌ+͉é(!:ħÙf‚Ät”Ⳏ3Ab:JˆñIØ™ 1%D‡øœôL˜Ž¢C|Š~&HLG Ñ!¾b!$¦£Dè0WU¢td‚HÓq°O!?ÏOǼZ|Ï{ß§ßë½®NáìùÇ’8OzvgÄiƹë'à,·q>Ó¿4àÌ•OÕã…çêÐî®Cßqw-:LÿUƒ^ÂÌ5~¤Ç^…C¯V²ôÎ÷iƒÏêÌ]΢ÿŠb§ŒÜ«‡YîÁ}ßÜw¸¡Ý s¬Z¯÷þµVåÅ—uE®çG“ѪAÌß nò07zïÓô{èQ»Ü¯(\êx´hP7ËNÝä NÓµé¯ô£É‡(#5º„@¶ànZ£é#-Ê‹Oè~cEM>D¬Ñ%²w+P¿î”/×7ÄÙº6Këýhò!Êd.!-¸[–ë_Õ·ô Ä×õ/í¢&?š|ˆ2Y£Kd îV %º‰Ü©kq‡®Í‚?š|ˆ2Y£Kd îV EúWð}N ñ"]›+[ýhò!Êd.!-¸[è‹…·ug ñ-è|Ûýhò!Êd.!-¸[æé«¸ÅºçëÚõœçG“¿XL kt lÁÝ 4W_^Ö—Õ×µé>ß&¢Œ@ÖèÙ‚»¨çu- žÒ±óB?šü21¬Ñ%²w+P÷Mœ¾2ƒ¸Q×fÆÇühò!Êd.!-¸[fNUêI}É ñÛº6S§ùÑäC”È]B [p·MŸ®Ôd] ˆuí¦Ìð£ÉŸ,&5º„@¶ànš:S×UÊ‹×êÚ\ÐéG“QF kt lÁÝ 4¥K©JåÅ«um&uûÑäC”È]B [piû¯vá:{ûïûä”u’SvŒœ²uä”ý‘œ²§È)»‹œ²ŸSÖBNÙ×È);NNÙyä”í!§ì9eïýçí²…ýçb§¤í?oßóÚ°÷¯<0“6¤­T$ûÏ =êñ˜[xßþ³—î0ó 4HÍšçeä}™ôUä}Ÿ±ÿ¢ d.!-¸[ÖPýÆcä}™ôSäAZF kt lÁÝ ÔO9tè½ä}™ô2òÀŽ1ö_t¬Ñ%²w+Ðrú……_Úÿ$ïˤF¤e²F—ÈÜ­@KèÔN>CÞ—IßCØû/º@ÖèÙ‚»h“¹i'y_&ÝCØSŒý] kt lÁÝ ´€.à¢á÷ä}™ôiòÀÈ]B [p·Í£«8¸š[HÞ—I/%ì'Œý] kt lÁÝ 4—.¯á2ûò¾Lú!òÀZIJF—ÈÜ­@=4îãíuè}™ôùä}±ÿ¢ d.!-¸[ºi@ ¦Ö’÷eÒבvœ±ÿ¢ d.!-¸[fÒH!Œ~…¼/“þy`ç‰ d.!-¸[¦Ó. åž®FïˤG‘¶‡±ÿ¢ d.!-¸[¦ÒØ:Œ±o&ïˤo%ìcÿEÈ]B [p·M!ÓÌ­ä}™ôNòÀÞcì¿èY£Kd .mÿu¼6ößC䔕‘S¶œ²SM86rˆœ²frÊž¡ ŠïÕâyëc5xzô¥jüþRvöS‰}ʦ lº×G†~]ÎÊnÿy»laÿ¹Ø)iûÏÛ÷¬ãñ£æ˜ô· f»ê/ÝvçoH<¬Ox½bQ£¹…îó ^V;y[-äuµ“ÇÕÎØ{Ñ…`Q%„à@Ý žÕþìv“§µŸ¼¬ýÌ*¾èB°¨Bp n…oêý ؿیޤM¾Œ,ª„¨[!ÀƒÚÝ„¿„w’Gµ›¼©Ý̪¼èB°¨Bp n…¯©•¼§&ò¢ZɃjeì·èB°¨Bp n…Oéïêñ\ðË è9AÚäËÁ¢JÁº¼£ùux2>—¼¥ùä)ÍgÌ€èB°¨Bp n…èŠZ¼šS‡¤M¾Œ,ª„¨[!À ê¬ÁËÑiµèAÚäËÁ¢JÁº<ŸÆj¨®AOÒ&_FUBÔ­àí4Vá€Lu5z?6ù2B°¨Bp n…gB%ŽˆÕV¡Çi“/#‹*!êVðjÞC’oW —i“/#‹*!êVðd~1Ç„6=H›|!XT !8P·B€÷r{9Êß2½H›|!XT !8Piû©ý«ëÔÙÛO_%§f<95{È©)§…Z“S3ŸœšÉ©yœš‰4;§—&ì¨ÆÎú`ö/UbS=^Líç;¨pûÉÛe ûÉÅNIÛO޾絟‚w^ .½z™Yu–nH‘ì§¼¨Q»ÜBûöS>Pé0¿ཤȋͱƒ<™ãF‚E•‚u+x/È‹9@K©î'O泊,º,ª„¨[!À{9M^ÌiZ25Š–LfV‹E‚E•‚u+x/ûÈ‹ÙGK£>OžÌ>ÆŒ.‹*!êVð^äÅ$hFÌDòdŒ]UBÔ­à½`t!XT !8P·B€÷ò0y1Óeé#äÉ<Ìø€Ñ…`Q%„à@Ý ÞËQòbŽÒ´ÔcäÉe|ÀèB°¨Bp n…ïå$y1'i^ðäÉœd|ÀèB°¨Bp n…ïet5z1£id¬¢J¥óe„`Q%„à@Ý Þ˲*ôb–ÑÐäªJ•Η‚E•‚u+x/k+Ñ‹YKcÃ×U¨t¾Œ,ª„¨[!À{)«@/¦ŒçÇWé|!XT !8Piûi’æéìí§wÈ©y“œše ¬™–À±‚Å ¼T½H¡·iˆ6å2Y£Kd îV ðv’)ôxÛÐó4DH›r¬Ñ%²w+x>“SèýLlC/Ò!mÊe²F—ÈÜ­@àÍK¡'ÔÓ†¤!BÚ”Ëd.!-¸[À#º:…^Ñ’6ôŽ Ò¦\F kt lÁÝ ÞÑ5)ôV¶¡§iˆ6å2Y£Kd îV ð”®K¡·´¶ ½&HC„´)—È]B [p·×ts =§ÛЃ‚4DH›r¬Ñ%²w+xP0°q[zS†iS.#5º„@¶ànoêîzT»Úг‚4DH›r¬Ñ%²w+xV÷¦Ð»º§ ½,HC„´)—È]B [p·—õ…zZÛÐã‚´·ÔªM¥Ëe²F—ÈÜ­@àqým ½®/¶¡÷iˆ6å2Y£Kd >¼ÁÆ:´¨õƒ}ë7¬^°¬p`é‚Õ}>Qð÷WzûÍ—ë÷:µrË›wlܵeû¶ŽÛvâFõÿÒÿ3½cÒ| ö>ÜG¤•©ÏCúOkU¦Ì¶©7Ð×ê®r¥>õÜ×Nï+ðéiïS§Ws˜Ü\A5ï[Õ›æ!«hexÑê`ÀNȦ"@\1vG…Oج ÂfÅ„e?3„ueÖ–MØö:Ÿ°î ºc² »µÑ'lva³c² »¹Ù'lNasb² ƒ醰ž Âzb² ƒCØ¥„]–Me»,ƒ°ËbÂÎ$ &,þ¹ ƒÍ;ÕL¨¬÷(ÛU)/·âzËB‹F§§&9íÌÉi#žÓÿýhÓY99-ðºaÄsú»‹‚œvåä´ÀK‹Ïéû9íÎÉiW#žÓß\ätvNN ¼@ñœ¾;+È霜œx 3¢9…šOÒ¥_oǓϙêÌ“ÏÏ¡Faÿ“Œ "lZs<¦Ua·7ÄC4öÝšxÄÁú‡ô&ññôpszUù¿Ø{qø8}CÇ„þÙø ý-¸ß5j³ºMmT»Ôõý¹C ¨Ûuܬ6©;ô§»S¿/WÛÕ ê“ô¾·\Wi¡Æ{T¿¾®ÿ×D ²_¿nÖÈ´^oéWÃx]Q}<”Uê‹výz¯J©ÕJýBw*×Õj]ë”:¿^©¿×¯½ºg¾«Q×Q³>8P ®Ù0µÞQáE£Ã‹Æ„ /*/^4>´h]xQoExQexQUxQuxQMxQmxQ]xQ}ø.‡õ6„5†5…c…õN/j/j /J„ _7•=å`Ÿ7A¤LmSy硬R°Ý2ïª0J½¬Ï¢~ª›Ë¯/袮köª5ߤ+'ˆô:c¯.¡~Û£Ô º»ôápìR?šü˜uþi žøcuâOn!V.½î¿váÐY´×Ã5Ø”ó7 o¯^ò´× ˆt{õê’·½B-®×"MªE±Lº¿F¥Ë­Ô^­Ð£¶×ÜBøíÕ\º½v¼v6íõ›uØ”¾Éßü,¼½zUÈÓ^ƒ ÒíÕ«KèýS@”+êP¤ºz Ò&ߊè‚ïŸÂ¢FmŸ¹‰ïµ•n—é‡mGj—4`“y€¿_hx»Ìýpm¿]A¤Ûeþ§šƒ(j@‘ ‚hST:_æ©æ,jÔv™ï©æè9{ªy¤v¹· › ÄÈí2÷S7üvqûÔo%Ù„â¼Û„¢í¥t²É’èHí2/jÔv™ïq'è9{ÜI¤v¹½›ÌvþÖï…>~Äo—A·Q  ÒqŠåÍ*/s45j»Ìw4ôœÝ-R»¼>MæzþÙ…Þ—Ìo—A·÷ Q~ׂâ<Ÿ@± Ò¦\fáŽ5ºÄÂ[ð‘±p:ª5s•º¼­†õê“ÊX ¿Ò¯'<«ÁËýp[ ÃÈéâô¢Zl)·©»uûƒ`Œ—Яj 7Cÿ¯wFAnz §Cù#G™D=*s“æ}‹º=­Œ1׎é×g=eÒ[Äʈ*3ƒ”Û˜xÌl÷ð!eŽÐe´)“WdäÐþÛ&¤}¥îè»Ó´Ãápù£MYLûðõCo7c?t·Ú©v©M*³zK¿ôú¡ôq?tN”y%ÊlÓ=Í6µ9Kÿ·;½E¬L¶2WŸ2p•ñnR©ºr`¥÷«;ô9Óšùº¾ÛէϘ¬Qëmg°÷Ñoù…íEýšE6¦j¤{ôëýÚU®Të8¥žÕ¯MãáÛyÿû‡kÂźឋ‘gÂź☋QRGË~h …-‡t\¤¿±·nŠÎOw‚­`;sÄÀ™ÖmçYý:MSk€‡hªSn4Oè×:ÝBNë×7tsXRÿ…Eúp=ñt%™+sÜ©CÝ«?M‚¯«6e†(àoÊd3L³çJoÃ|ÕÄÿPKî†/¸PPK|k|R manifest.rdfÍ“Ínƒ0„ï<…eÎØ@/r(ʹjŸÀ5†X/òšÞ¾Ž“VQ¤ªêŸÔã®F3ߎ´›íaÈ‹²¨ÁT4c)%ÊHhµé+:».¹¥Û:ÚØ¶+šñjƒ¥Ÿ*ºwn*9_–…-7 lϳ¢(xšó ¸ ÛÐúL(Ÿ•ÌyÑÔ}^}rqÝÈ®nÃ]v˜á"ËêŽwD€½#‚iwÄý¦)$åB{fº•,Žˆà%-ÀëÖ/ñ¶@E¿@F¿ÀF¿˜=²›v‚ ;Á†L ·[!*ë™uˆÍ:œ–õPu‰ e‰ e‰ eù&PbT(12”Jü&P"T(2”J4ñQÆ´¿ðhýòQiûÈ´}lÚþ´´Yü„JP2í_ýÌmÎÔóuŒSd¼“ؼyâ­Õ'ý;WuIMw…“~øÛ(sóõË·ïmÐ]ôüàµä&󒻘—Üx^r£yÉ ç%ן—ÜSîtß‘Üë#rÏ<,†gnå'øo£>gÇÊÛGƒõ?PK¡ñ°"APK|k|R content.xmlí}ksG’í÷û+¼w?°Yï‡v䱞cI~OÄ„7æD4I쀭ǯ¿™ €dHuV1» km9h ¬Ó8U•uê•ù—ÿúx1=ø½^,'óÙÓCY‰Ãƒzv2OfgOÿþëË£pø_ßüŸ¿ÌOO''õ“ñüäꢞ­ŽNæ³ü÷гå“ËE½„¿VÍC®³'óÑr²|2]ÔË'«“'óËz¶Å>ÙÅ|¨>èj¾8;þõçcüÝѪþ¸:Þ–>[ŒÇÓ»J+!ôñÙñx´ý>©?üß-âãùêâN„Œ17¿Ý=/.>nêØ´ÁºRõÇËz1ÁúŒ¦OæsÍǧGh–ÅüòÉÍn×íãrrWyüwo99¯/FðŒåj4;©·¨ÅåM{ÜðØ"íñ¢¾œ/VÛÒã“ë—W‹iSl|r\Ok$»<–•¼n½“óÑbE}aMáÛµYþ~FÅBQ0™‹KxÛï§õ퇌£Ô§`Y°ËÛp4*ËÞÆÎçó“/4¬9>MO®+»ú4­ÉÕÅÂÉW}øâW}XLÀp¶Å/êÕˆú]X¶ecÓÉì_÷Û7þöƾ©_òq9=:ßó ¡r_¬Ûúï×¥›¿Q¿xÓçZ£ÇtƒÑã–µŒÞÓ_bSø6zvuñ¾^¿<Ï®œÒ+8uÝQÁo,}qvíˆOçW³ñÚ‡Þá—ö¤õ„ÖøòË‹ÇM¡Ûõ‹)Ž±Ì¶üt^@zóÊo=¡õúç÷ºPtÂõïèå®-|ROÇ4>?ºX¦îÑmÿ=îöß×x´:¿§†ãwðËæÇ»··Gªu¤Ëòd1¹$;ÂuéVÅðË{ꦎ׿><Ø´ß-¹ ¿ÙjƒõS—Çל‚F8:ÔGãúdºüæ/ëîpýñÁúïÈôéáÛ ô­Æ&~Íà»`ÈØ½˜L?==ü÷Ñå|ùŸI¹õ‡‡­Gcù£³z¯üòÃd¹l•¸œ¬NàÕü>ZLë>î 6?Ÿ¬ž×¿f£3ÀÜÏ-)H!÷i¹ª/ÂîûùjÞ4ÅÁ³ïÞüòì^viA6vÇ÷½ïÍ磫Õ:Ãää¨yε!4?[u9™Ëë/Ûpoü <Ó«‹ÙáyûãKè°õb5©—§ó'ïõè_Gïk°Xx ~õö‰›â&cì—à\¥q“YS[|¾DN ENT!Ä›§áDæÐGó1÷ ›™õ¹íšUS×Ñtr6;Zί8õ=|Ü>|Q_Ö£ëå4øÕhº¼%ùà»Gg‹ÑåyÒZ7}zò¾±Ó‹Ñâl2;šÖ§hÀYþ¨¾o ä­ÛÏ>lúÊ f£)ýu¯qÛ_߇nÛÃøë›'Ûêl±Óš49¹n†Íßo·NŠáa‹ù¿jüÊtÇæ£ÓÉtÚú›ztt>_L>Ïqnº}Ï'õ¬Y"j—û¿áä¦ÔÅd<žnŸ6‡W…ÿÍuYŸ@kKzEÜ©çµ1BM×óòûjz«ZY¦¼©âÇã{g›_¼Ÿ?ÝLD/¡aÆËóº^}ó—µ+ÄÕ«i3m}zXÿ°v(jûùêÓ%8®åïígËsÜ[©/Þ£]4:Y]Ëyz8Ÿ½Æ×ãÕ%Ž,·<ÕÆm€?«ïüæŸkPÐ'›3&2×þw.‡pçXLë¶O×Bsûôïºßÿwݶø¦ÛßtÛâ›n.oº¹¼íæò¶›ËÛn.o»¹¼ëæò®›Ë»n.ﺹ|ßÍåûn.ßwsù¾›ËÝ\~èæòC7—º¹üØÍåÇn.?vsù±›ËOÝ\~êæòS7—Ÿî÷ézŸ|úCÉ}ºZ¢û*(aÙ%zì²ñ =ê.iuç3"¿SžwyÖIäY'‘gÝDžwyÞIäy'‘çÝD^tyÑIäE'‘ÝD^vyÙIäe'‘—ÝD^uyÕIäU'‘WÝD^wyÝIäu'Ðë÷9n¹OŽû¡dÈŽ[?Ðqk¡îòÛVßã·M¥´a—ᕺ{iÅv/­èn¯«»—V:¹|ÛÍåY7—gÝ\žusyÖÍåy7—çÝ\žwsyÞÍåE7—Ý\^tsyÑÍåe7——Ý\^vsyÙÍåU7—WÝ\^usyÕÍåu7—×Ý\^wsy \îñæfŸ¼ùCɽ¹À›C)­cË¥K/ÙWË¿ë~ýßu›â›nS|ÓmŠoº¹¼éæò¶›ËÛn.o»¹¼íæò®›Ë»n.ﺹ¼ëæò}7—ﻹ|ßÍåûn.?tsù¡›ËÝ\~èæòc7—»¹üØÍåÇn.?usù©›ËOÝ\~ºß¥Û}ré%CvéöÁ.=ø»|ºi¯–[-m{y%Z~ŸÞ9?ûNŠN×Õùwð{lÈï“ =”LjCÇíýô[ô›ÃvwlÄ7G“7çªÖÇ^Öç}î:sL~¦Ú>s}·dSty´>TŸ†¾U÷YÆgšžiå-×d1ÿpÇWâÍ„<ï.nx`o{kc4½ª6]hµ˜Ìζ·‚îø<¿éÀß\{ÙÙÁóúýd4;ø[Ó}?LVçÞ¿lNÕ@çZö½ì†ß½Œïm6éš¾›4­…Ò—åZý*Þß‚÷óñásÇ÷=ðÕü|5[M.jÖ6;†Ï@tm0¿L>gr}hûà‰¯A¿ñÝèãÁ¯ç‹ùÕÙùåÕŠõÅfÛûƒÙúwó÷™Îå_øßýºéç‹Ñ _xöyr9p ‡í‹o3ƒ~ßçåj<èâÛ¬/ìÁ³ÿ<úpðëhñ~4>¸î§ÓùhÕÖ+0Ã"H”WwZƒ®ùÜ/q¾xÏõj:‚™Ãé“§ÿ]½‰úŸÇøóÏ;eÔ„e%<0p:(„'±N0¬ã†íßßÙ‚TsÑÕH)B¥#­ñ×eû!£­Œ&ñX퇆±ò´æXí©3[]ICëÈMÑž¬CW6ÐlKöCÂZQYÚ+Yí‰F|ìÑæï³“Íl´~øP{wG±Fé•¡ÙÇMq–áÆ 74;æ ßýíûÿ÷ßÍ’æŒÈ?ÿ#ƒ©‹.µÇ#ÆŠ82& ½žR=ê;£ÒU‚Öç7eûñ~0qhÄ¢=9Àª`i.°)ÚßÐhi­±.Ú—uTŠÖMÉþ†FMóë¢ýÐÀ3X$Xð±‡F†åŠ;A‰ŒsAÒ<ÆMq–Çæˆ`Jxµp>¨(iÖ¼ƒ*¡ß^z¿Þf¹ÅãËÍ åœqÔ†à€«¬€§V‘&TRÐW1ŽÒ–2WèãÆ"ŽX´'ÚUŽH£)Ú -uf·.Úh¨/e]´§YDeh$°`O~ª'‰¤)Ú×*©c¨|ô1”e þî·!…©Œ ­ŠÝgE]Ù(*µ´ ÆD£$q”ÙAñN;ñ’Ms&g¸„)º0Þ€d¶Ö;E\K@_ÅpIëˆ=M,Œ´­í›’= ^TÄ•uѾ¼Ôè››¢=ÑðäÕØuѾ†ˆ@#ûšäXrK4Eûj â²´É]•îa°dÙ?¾ûm/•L|#×ÅYF_4XzïµÚX¯”#O9Û(ÞÁ/‚6w=³Öh5ücm€ÏIG|)è«,‰.©§qJ‰JЈuÑž:b•¦óºhOk’ÎVÄ5I,Ù›‚qKUïP´·5I¢ŠªrU+ùuØXyâFS´¯–p´×{°d9t·]ªh…ô†x>æ¦8ËhŠK+|”Ñã]p´YÉŠw°Ä«ùÍíû¬ÁR*PªRÎOÝ6iƒ¾ŠÁ’¸}ÖSpºŠD©Ø퉆¶•£ùæuѾζ¸JQ·`ÑžhxEÝÜ]ímù‘:\ÊÞ^tuâa‡¦h_-Aì#&·‡ô²ûð£Â÷-y(!qšB]öØgnbÉhi¤·ÁmŒž:X¶A¼c%öib÷䌕15žcsÎJ¢ƒJA_ÅXI³¼žÎÜ(E>Ô°.Ú×z›¨ˆÂo]´¯Ã.®¢®q4E{¢a\E±±doKêÚcošA*ê!åuÑžvÔ+A¿d8$ê"®ïir–z:®)Ù—C"Î\öìa_Ñôæ "=zhì-x¨¾¢ºæ¦hOÝPU–¸»‹%{›ªšXlJö¶vBóÌý9f§¨; MÉž^u+wk`wÔñuû–æçz:ZM~¯× î®2#ÓtT6ô‘éšñ |Τž|b%Íý‚þ`ñg¾ æ/ü3_ó÷ý¯Ìôg·ü³[þÙ-óºåcL2i3«Á’Ü•üvôäÛ=Mzòl‚ÿ³ôž¯é=ßSz/Öô^ì)½—kz/÷”Þ«5½W{JïõšÞëG§G”)íè¤lÚë;üï>¾ÏgûNðù¾|±ï_î;ÁWûNðõã|ØK‚•/Ë@—•j5xãƒRÊ© ‰Á÷R§HÌ$ol“ÓNë@ êš`8dõ(ÐÆyñ aâ”—¹ä¥‰:xhBMÜÉjc8¥guo­ðB 0Í1‘{Ä)Ls;«TF -&Õ wÖÛ NÙšG^(£¤Þ 'ˆyúRÐ`¢v@·-¥Û ‡9Í)ª¨½†>%µ4"Ã'ì xDoaæÚ¹( qÛqÅ#Š +`£rà´vÆì xDsi¬7.jhJéÉ Š¨.«@ðÞj+aDFì xDwi'¶NI+-r¸µ6ˆG’—Ñ·RIá¬ö¨¨ˆ¡­vPû&Ù{IòôeÉî3Z]U܇ލc &TH@œ’=“¼×"Fï”ñBÓôW âíyä½×&PáÁâUøÄ)Ú3ÉÛà]ÐÑKˆA=R§lÏ#¯AÂ!U!c¶¥ NÙžióDýÎGâ)øÄ)ÛsÈ£àÒ6øjEŒÍ³ƒL¸è¼Aýlͼ !hãp.æœ$ÝAñ÷ D)´VÖHëJøIS)ˆSºç‘WB­¢t`kĤa)ˆSºç‘×0k%.u”Šxl qJ÷Lò0ìÀ\?hi 1 S â”î9äqÚDg`²o¼'ØKAœÒ=³Ã:³NÜb–8×NAœÒ=‡«4Xh@%N>ªÑBñH÷Ò  [Ò9-1°ÝŠGº—UÀj£“gˆ)ñvP<Ò½´+esôFGrh£x¤{Y¤5‚'½B» V Aí›tï%ï¥;æ¶Ì‘Ñh48¥*†Q¹gs×0]“*U‘šžª b”îyìm%qÃK‰051ìXbÔîÙì5h© —8k¦²oÅ{{\CVàM‰ñÊ £vϵúà#Ì€ÀëÁä`91Š÷<ö͈㼗Îj˜D vPƒ©÷ý·Tb=nªÌUf•Bá<ù¼CĢ݋é+à­º ¼!ÆÆÝA±h÷Ò ØÆ.MN–|b&A±h÷â  €ÒÖÃ9ã ´Q,Ú½°xŽ1)•4Ñ×bvP,Ú½ô HpoÎÿJòI÷Å¢Ý +ù‡F/‰‚ì„Ô¾i÷^’ƒwhw™¥Áˆ¢…D Ö±ª÷LöÒzáaî,¬#‡¿nƒXÕ{ûhð¯0Áù@”)ˆU½ç±Ðã4ÞPAE¼`›‚XÕ{{Pâ௸<ç£"®¤ Výž×öÖ¯A6Gã1o[ bÕï9ìñ iÄ»² lXbÔ`ú}@Bb3væ5h€9¥SJÓb²~l£x|i”2:âÊÌíÉ36ŠGÁVTx^I¥cäÕ÷6ŠGÁV@ƒS0u`$†zHA<ú½Œ¾ç`´1Œ#†LØAñè÷² Àˆ"¡)QOáñ{ªOl£xô{Y$LƒÞ´V:rÔ¾éwÚÐÊ1–Þèw•Ñì®Ò\*µ;3DWÖ{úØ™ xô{a‚ÑAYTㆼøØñ¨÷2úAƒ;Úy§ñ5Yý¶P<ê½´0­Êé`ñV;¹-~/«€–M¬’(€ q+8ñ¨÷Bû÷ØŽBhé5ýìR‚âQïeQ‹Š0x sQCí lßä;­#pþ|×y"Ì:½ÁX=‘.ÂZ VùžË^çA;èdܱÊ÷ö˜.ÝHPRNBÿ#¦hKA¬ò=½ˆQG—u 8±Ê÷öª2ÊX0N‚ôŠè§ b•ïyvo¼WÊ+뜧zûÄ*ßsØÃ°£&NW·…ëzl©ôf¨Ìm¾‚Û€fuà èíØBñˆõâ ˆèšP+q•^Û(¹^V ¤õЋŒ‚¾” xäzá!(pçB)ò¶Å#×Ë*¢[oý°ÔTJP<‚½ð Èè"[7` Ę;(Á^V¤0 lÿ¡ ö6lß»´°Å‚U²çäõqžÕE+$N‰gõR«dÏa LXe…ð‚¸R“‚X%{{óLã‚VA¯{¦ VÉžÇÞ‚×ÓZ8S7b¼Ä*ÙsØï[„ÈÜ^ëÆ JД†x¾ Á° öî8ì¨ÛȃU 5˜„Ô‡Ke6Ãg^“º/¾ƒâñeðŽ|Å#â +PàËwP<"¾°î|Å#âË*  <úŠGÄvâ|§ž‚x$|}Qæ×ØÞIxbÎÎÌœRädºò¦†0F955/ïžÃvׇÕÉQ˜Û¨áDü^\ªM*C•Ù¤˜—8€S0ôÒ@Å#â +`„Ä«Óѯè"¾âñeðFY)…¶Â¥È· Ú(_X¡¢qBàEöè¨g”ˆ/«€ô0¦Üøgr|ŠGÄö`Ní¤w˜ÝZÅ#ãË* ðž©Ñ2*ðæUÆ·a{'ã‰Ù¾9sªJ‘“+TB{aµlòŒ3¤ VŸÉÞk©´ ̘& «ˆÏá+‚J:<µŽÇil«ˆÏkù¨"̃¬Q.=Íç§ VŸ—™Txøñ†[TŽ˜Í#±Šøö0uJÊ"L%|¤î4µA¬">/Ÿ3Ñ»ªQYmÈÉËÔp"~H.Õ&­¡ÊlRÜ…3Áz¼°Fœ”î xD|aTÉÁ˵–xmfÅ#âË*à•Sx…ǃ äü@ ŠGľà¨YǨœ§ °Å#âË* mŒNHLÛíÈ™Ú  _F߆¨£öNDùÜU‚â‘ðes¼*oñÚ©"J‚ØÞIxZ=$gvU)r2c…JãÁá0E­&æ¥NA¬>½¬Ø{0ßH¼/‘‚XE|{)šÙ„ßþ ž°mƒXE|ûXЂ1 m 1¼âñ…oŽ:/µÎ×”vP<"¾¬vkŠQŠHŽ÷ž xd|a'ö"Äh1  sä‰l‚â‘ñeÆ*-¢sc·*òJ|¶w2ž¶±ÉrñàFÆçdÉ ˆ £¶¢ºòD÷Ÿ‚Xe|&{…éß½“N8bÔ–Ä*ãsØK\JÒÖ§1}'qÛ>±Êøö± ÊE£Á â,ûÄ*ã³Ú¾ÂìEZh~B @HA¬2>ÏîQŒ«€ì .f§ VŸ—™Ïf±x#v—ºwPÃÉø!½¸T›d‡*3ÝdPxÕAù€ é y-Œ/­€tà`2WÚ¨çQŒ/«ˆ)Lûjø‡@œúí xd|Y0 ô.£øúqŽ6ŠGÆ—U;b8"…d ™ xd|a°3ðbCjMÌ`³ƒâ‘ñeF)ŒŒú¥"ODÚ°ý»ÄJ`YŽu\Ëx™“0+V7Ã|w•hÎ'qÊøLöàÿ Lá´…‰Ñó¤ NŸÅ^ÊJGø-îçÅBâ”ñyìÁL¸ ¢1†;=qÊø<ö•wND)¬ Âjâzkâ”ñYìC`ÔÃohO ˜‚8e|ûfØ{ãÌ:â/Uñ$¨ádü^DÅzÕ™Mdo¦ƒŒ‘~*;A±ÈøÒ x﬎!zmùPs‚b‘ñ…ÀxB<¡„>.#¼Ò-‹Œ/¬€³c àÑɽ2A±ÈøÂ àý‚ \^û@ôé;(_Ú¬ÍøÈ—C‹Œ/¬ ,Jc^zkªŒoÁ¾Ü‹eüúƒÙÕÅûzqt2Ÿ^]Ì–G‹ú²­ê1ø}xÜ÷´a¹ZLfgw7Óæw×íô×÷Kส~¹¬ëñÕeÉ‹¾·²á8D1~ϙԳ“O¬¤¹_ÐÌ ¾›¿_æµç¿ð¿ úuÓÏ£A¿ðìó$³W=¸†óA¿ïío™ãÛ¿ïór5ô góYÝÇÄàÏnùg·ü³[ö×-cq–6«g\š9‡£÷lM—`sЄ°ÒDSâqÐ6„eÙµ!þ<—¸ ÊÁLÏãéâÚNbYumÈ¿È"/ª×—úœR)êNO‚bYvmè¿Ìlû&Ãõ?ĶoƒXV]ò¯²-^è½–xú6;OA,‹® ù×™äu°Ú(¢ÜòmÐp+®Ã¹f©7¼tVsçœ îƒ{.¦¾ºŒþ¹èÒöß']nù{á¦Ké÷í¨AjÃŽqJíœx.² Þø ”r*HG5™6ˆSˆg’·Vè&²TÄk,)ˆSŒç‘x!àÉLøùR\ Ä)Æ3ÉKa…±Ðõ¤ ÞáNAœR<30  ֌ˈ¹Â §Ïí®2B3*cN ºÈ6ˆSˆç‘72„à$¨,㉈SÐ`B|@Ç-õ&—Πŵ×^ ĈP;(¡^Xå­Ä‘Ê:r¾6ˆG¬ÒÇÀNQ9­!ÕúŠG®Vf;v}ÇÚjAÝtOP>ÁpŠö¼€‹x †ÍOêÞR4˜hÐmƒ„Û —y¡¶^õõ-”wÁ’5oÅ#ÚK+ †¯.xM¾^• xD{Y`,1¥°üŽšü(AñˆöÂ7­Ò˜ ÂàU/ºdl¡xD{Y|”ˆxû“ºÖÛFñˆöÒ>€‘71r%=æu Ã#ØËÈcâsèÍO"ýµo‚Ö9#ýÅœØ,¦Â¹ŽÓm2ÒL>q ö<òJì–V4g‰Ü[N¹žGD”ÕÞKg1.W‚áëyԵƚ UðÚìm§\Ï!¯+eð„Ž”Qkjª Ä)×3;+f –QD¯¹¯ÞÆpÊõ¼ÀŠ28Ìï"}Ô¼)h0¹> Ó–zRKç…Ô LÕmƒŠœa$AñÈõ ƒ!?… ¼1Y®·QâÞFñh÷² `2auÀ»G‘|è$Añh÷Ò>€½ÒÁ+I¼ç¶ƒâQïe¸”(•óÐ¢ŽžÇ¨Ú7õN3$ùQï:OƒY'cs~ÚFâ9÷ĪÞ3ÙxùZà1E ¯‘‚XÕ{{_iƒq2@Tyë‰gUS«zÏc/¢õJá®]ÄÌo)ˆU½ç°WUŸ­ˆùÃ$‘} bUïyvo|lRy[ãD½‚Xõ{{ŒZíµË µÜ ¦ßôàÒèÍșӜ0ˆë&Gh4Á)i¨g~/¬€Q`‘0¦hÜ"/<¶Q<ú½¬˜‡ox¼LB>5 xô{áÀí í 5qQŽºy“ xô{Y4Œä0!õ¦Š|Ç6Añè÷Â7 côÑ áŒÖÄ”ä;(ý^V˜‰ #@X M•ï-о©w)hc“`Õï9ù’†duÑ )@Jê pĪßs؃´Ö7Ý'Ž\)ˆU¿ç±Ç0‚vÑyb,«ĪßóØÃì Nei3±ê÷öû2·×zãlŒ.H˜Ç/ù¥ VýžÃ^T18«åö'9úx5˜‚Ô‹Kc6£g^“º?¾ƒâÑðeð®|Å£á +PàÍwP<¾°}Å£áË*  |úŠGÃvâ·¾ƒâÑðeež½ Û?O\bâÌz*ENþ+_áìÍ\ ÀhIÔ¥àˆUÆg²Ç¨¦ F`·!¬>‡y¨0®,t?Wlj—åS«„Ïk÷•õRã9ðH¼óœ‚X%|{Uyâ Õ”dëy­,…6 Tð‚5úY b뙉- í^ckGÚ †ëCúji6É M^“:Ñøá%ï xÄziŒðx&XeÉb=Añˆõ² xoaÒª´PŠzë=ñHõBú ŸoâkKÜ}ÜAñHõ² `¤qý‡ÚT/ìÆz/„6uŽ|â*AñHõ² ˆà¼Ó&nàVñîX Û»(3Ä•–¥Ž©ž“ùÊW΂Ýg5¸ªToƒX¥z{©80Û/Þ ¤Í±S«\Ïa+áUÄÀæÞG …š‚XåzûPa°!ŒÜ'(ošÓLA¬r=/uªÅ€1›?ÔX- «„ϳzhDq†š[T«oƒX%|f²@éׇv`ô§^0ßA 'á‡ôá0ß ŸyMê4ŽiÒ·+K̹ƒâ‘ð…0ÚDi&Ù’ä s ŠG—UÀ[‡žJ ºXš xD|aØ Œ£‘W{ˆ/«€’‡*lÿPE|Å#â û€ŒfCx3Jr.¦Å#âË*€Ñý¡=1?—1†|õ¦û²z OsF’3ª9I°B%4f0–¯oã,¥ V ŸÇ^‚E6,a@¢])ˆUÃç°Ðÿ´ ÑŒ”OÜbJA¬>¯í#xmé1ƒwærR«†ÏKG “Oé4¥…¹mÈJA¬*>‡½ÇDÆ2HÌ¡þ<ónXU|fêC!@r9˜ÍÁУÈ)×Û¨áTü^\šM.C“פNŒƒo½× ɧfÚ(_Xe`6ï:}¾âÑðeôñpQ8ã”#g´MP<¾°ýƒ³˜ªÅƒòäì^ ŠG×U@FÐâÊ7€$_ûLP<¾¬ÃPDmcTB“—±†/«€ÀP`¸ 1˜Ÿ§v‚6lïÂÎHÚ"™äL©*ENb¬Pi<õ" 3 E ;ž‚XE|{cE^z%@Wц®Ä*â³²LŠ Ïà&žóš¸›‚XE|ûXi©ÀûmöÄ„¶)ˆUÄç°W•öAažÓ!âºS bñ9ìñø‘õ!X ¶,‰™S«ˆÏÌ$è=Ìå Æ#9ÐE5œˆÒ‹K³Éohòšf•86yç9jw‚âñepðJ5.H ᤦ.â%(_V¯•k‚Ÿ ŒèC¿@ÜBñÈøÂ7àš`ƼNK=9ž xd|Y0ü£÷Fa0Ô‰l‚â‘ñ…·Ä¼‡¦¤ŸŠØAñÈø² ˜‹B—ŒÚ@›Jòî6lïÒ¬JZg–œ‰V¥ÈI—*SB#½žèþS«ŒÏdï¢ÕÖ7Ù»ˆ‡ßS«ŒÏÊ>‰1ð8˜‰AŸI/±Êøö±jöQ=Læ0ƒ*UÆ·A¬2>‡=(r‡³ÿͲŒoXe|žÝkY0’¥µÄ )ˆUÆçfÙÄÀãZàÁ$AÏSÒF 'ã‡ôâÒl²š¼&µAE­Ì/­–ÄlX;(_Zƒi€p_súJd Å#ãË*à³SY¢Íì xd|Yœ…ù0¸QªèçÜŒ/«€´Kñú9y Å#ã û€ BJ˜Y;Ú„º xd|Y`&4ŒÂÍ%ÌGF•ñmØÞÉxZ_`¹%w-ãeNæ¬XÁ°h…RÊà‰GlS§ŒÏd¯¢˜Œ#“Á¤ NŸÅ^ÊÊ® Áh¥¨±—R§ŒÏcþC¼\€ñìpÃ)⳸«Jj\Øþ$Šø6ˆSÄg±˜¼ÆÁt(hHâÐÄ)â³Øcøh˜DƒzÙDÿ%n¡†ñCúpi7émfÊ ƒ1 Ó¨GENš XD|q`Ðĸԑ|"%A±ˆøÂ xea4ôÏizâþØŠEÄVÀÁŒHK¥²ÔP;(_X©µ2xYlý“*âÛ(_Úl“ÌËyëùHPÄ"á é ˜…69:„Qè«Éþ6,7üíOZ5ÜTn-ãŸÍ/.õrY~™|®ïPú«‘¼VúËóÑe½üæ/ãÅèÓÓüþ ùßÏG“Ù¸þ•=\pûg ¹ù+ptûW?Âo–¿Ÿ=ù0¯Î›$.1„Élýáy=9;_abš¨eÜ~ú±iQäöƒOë<Ã^Áfó÷ÿSŸ¬Öß9›¯&§ŸŽæ³£«ËñhUÍO£ÙY½|zøï£Ëùò?“6XXýÕÆ'_ü½s_úý³ü3L*ñq:™ýëÉù¢>}zXÿ°¦}ýùÚ.–“‹Ëi½ýly>ÿðô°¾x_·NVWP10ÓÙÛùh 07–ƒûñ­öØ4ÎäbtVßùÍ?×—ÓÑI}QÏVK.2×k¹6Ë­!µ,y>½º˜Ýa's·5Ìq}:ºš®«oYÒóõ/SAsÿ3Õö™³+¨ÀbSty´¨/k¨ÁÄpßêû¬Iî3ï›ÚË»¦öw|smïtƒËÕb2;»ÛËm~wíæ¶>Ú‡r-à ÷7à½tìCèäÞ1°û^ò%{k:÷øMGœå˜ùÚ¦Ñýç™ð¿öçÑj2ïcB˜Câ×Ñ¢»æÃ>w?{ð+úp-ô~4>\£þ[[qL泃_.ëz¼ÑobVB!‚”A—ýoŠsÌ÷m³Zþ­ËZx«ð{À;í0ÿ%§Ñ»a`Ô~YÚßg'×JtSËŠmq±%*bÀ›â¦æD©ÑŒëÁ®÷ÚÉ÷ÇèÿñÛ¦ž%RÄ`œ ÔP‘×ÅYL]æ›:&ÞD-œ*’·áÔ×ìY§Ÿ/F˜YVt#)”ÄS=´Xñ7ÅYÌL•™™Ô²IZŽYL©Ë| êk6³³Ï“ËÌ,ë>žRñ0V\g13]dfÞã‰=mš¼}doÖF}Íföy¹Bf]S˜Â¢J¹)Îbf¦È̬ðQF ì„©fÖF}Íf6ýœ·2QfeY'˜=hiUš]g±2[beÍÖœ Ú<ÕÈÚ ¯ÙÆÞþÆ0ÐtÙ˜9ïLkŒÛ)©§Snг™+12+]ˆÁFƒ{Ú¼ÿÚF af´=ÏçõIú*yßÓVJ¨;ö=ñüi{ßS+ßÚ÷ôëŠö=ïh‡íÞ¦öO:ËuÐUæá9/ðràû aŸöAJ&ݽגd/;èÎyË’,L›´k[’ö±,IwZÒKÂs^âá¢;-)î“%=” ÏŽzx”uÝ÷Æ?wÔ[šçV~OýA›k_ÜÖº`¯ÏíýÆðÏW³ÕäbØ]aÒ†ìuÿõ|1¿:»¼ZíÙžøp{ÑÌö9ü#}×£²ÄûX’²@› =[§üVç¬j#påÜÛʘèˆ)(RÇgÝÄ{{‘™æjŒcå…ÓŽ|M´ {̉y‹N™-M¿8C¾ñ^vËÕ¼³Ö^½°gËã™vGŒ”‚XÌ;”˜·5ƒùlR/Ú¨?ŒuçîCÒoä…Ša]ôÑMydH¢R”ObjectReplacements/Object 3PK|k|Rî†/¸P%4ObjectReplacements/Object 2PK|k|R´÷hÒƒ Lmanifest.rdfPK|k|R¡ñ°"AÜMMETA-INF/manifest.xmlPK|k|Rzjƒ9Ý6Âj APcontent.xmlPK55È W‡squashfs-tools-ng-1.3.1/doc/benchmark.txt000066400000000000000000000321471461472204600203660ustar00rootroot00000000000000 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.3.1/doc/format.adoc000066400000000000000000001217751461472204600200210ustar00rootroot00000000000000= 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 ! LZMA ! LZMA version 1 ! 3 ! LZO ! ! 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. | u32 | 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 (`8192 / 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.3.1/doc/mainpage.dox000066400000000000000000000026041461472204600201630ustar00rootroot00000000000000/** * @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.3.1/doc/parallelism.txt000066400000000000000000000112721461472204600207350ustar00rootroot00000000000000 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.3.1/extras/000077500000000000000000000000001461472204600164255ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/extras/Makemodule.am000066400000000000000000000010521461472204600210250ustar00rootroot00000000000000mknastyfs_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.3.1/extras/browse.c000066400000000000000000000407031461472204600200760ustar00rootroot00000000000000/* 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 "compat.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: " PRI_U64 "\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: " PRI_U64 "\n", inode->data.file_ext.file_size); printf("Sparse: " PRI_U64 "\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, SQFS_DIR_READER_DOT_ENTRIES); 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.3.1/extras/extract_one.c000066400000000000000000000102331461472204600211030ustar00rootroot00000000000000/* 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; } 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; } 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; } 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; } } idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fprintf(stderr, "%s: error creating ID table: %d.\n", argv[1], ret); goto out; } 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; } 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; } 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; } 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; } 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; } if (!S_ISREG(n->inode->base.mode)) { fprintf(stderr, "/%s is not a file\n", argv[2]); goto out; } 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; } 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; } 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; } 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: sqfs_dir_tree_destroy(n); sqfs_destroy(data); sqfs_destroy(dirrd); sqfs_destroy(idtbl); sqfs_destroy(xattr); sqfs_destroy(cmp); sqfs_destroy(file); free(output); return status; } squashfs-tools-ng-1.3.1/extras/list_files.c000066400000000000000000000062661461472204600207400ustar00rootroot00000000000000/* 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.3.1/extras/mk42sqfs.c000066400000000000000000000130431461472204600202440ustar00rootroot00000000000000/* 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(int argc, char **argv) { 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]; (void)argc; (void)argv; /* 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.3.1/extras/mknastyfs.c000066400000000000000000000116201461472204600206100ustar00rootroot00000000000000/* 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(int argc, char **argv) { 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; (void)argc; (void)argv; /* 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.3.1/include/000077500000000000000000000000001461472204600165425ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/include/common.h000066400000000000000000000036621461472204600202120ustar00rootroot00000000000000/* 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 "io/ostream.h" #include "io/file.h" #include "io/std.h" #include "compat.h" #include "fstree.h" #include typedef struct sqfs_hard_link_t { struct sqfs_hard_link_t *next; sqfs_u32 inode_number; char *target; } sqfs_hard_link_t; 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 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.3.1/include/compat.h000066400000000000000000000142161461472204600202020ustar00rootroot00000000000000/* 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 "io/ostream.h" #include "config.h" #include #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 #if defined(_WIN32) || defined(__WINDOWS__) extern int sqfs_tools_main(int argc, char **argv); int sqfs_tools_fputc(int c, FILE *strm); int sqfs_tools_fputs(const char *str, FILE *strm); int sqfs_tools_printf(const char *fmt, ...) PRINTF_ATTRIB(1, 2); int sqfs_tools_fprintf(FILE *strm, const char *fmt, ...) PRINTF_ATTRIB(2, 3); #define main sqfs_tools_main #define printf sqfs_tools_printf #define fprintf sqfs_tools_fprintf #define fputs sqfs_tools_fputs #define fputc sqfs_tools_fputc #define putc sqfs_tools_fputc #endif #ifndef HAVE_STRCHRNUL char *strchrnul(const char *s, int c); #endif #endif /* COMPAT_H */ squashfs-tools-ng-1.3.1/include/compress_cli.h000066400000000000000000000015731461472204600214030ustar00rootroot00000000000000/* 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.3.1/include/fstree.h000066400000000000000000000152271461472204600202120ustar00rootroot00000000000000/* 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 "io/istream.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; #define container_of(ptr, type, member) \ ((type *)((char *)ptr - offsetof(type, member))) /* 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; /* used by sort file processing */ sqfs_s64 priority; int flags; bool already_matched; }; /* 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_u32 link_count; sqfs_u16 mode; /* 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); /* 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); /* 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); void fstree_insert_sorted(tree_node_t *root, tree_node_t *n); #endif /* FSTREE_H */ squashfs-tools-ng-1.3.1/include/io/000077500000000000000000000000001461472204600171515ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/include/io/file.h000066400000000000000000000025251461472204600202450ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * file.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef IO_FILE_H #define IO_FILE_H #include "io/istream.h" #include "io/ostream.h" #ifdef __cplusplus extern "C" { #endif /** * @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 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); #ifdef __cplusplus } #endif #endif /* IO_FILE_H */ squashfs-tools-ng-1.3.1/include/io/istream.h000066400000000000000000000065241461472204600207750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * istream.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef IO_ISTREAM_H #define IO_ISTREAM_H #include "sqfs/predef.h" /** * @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 { ISTREAM_LINE_LTRIM = 0x01, ISTREAM_LINE_RTRIM = 0x02, ISTREAM_LINE_SKIP_EMPTY = 0x04, }; #ifdef __cplusplus extern "C" { #endif /** * @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); #ifdef __cplusplus } #endif #endif /* IO_ISTREAM_H */ squashfs-tools-ng-1.3.1/include/io/ostream.h000066400000000000000000000067021461472204600210010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * ostream.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef IO_OSTREAM_H #define IO_OSTREAM_H #include "sqfs/predef.h" #include "io/istream.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; enum { OSTREAM_OPEN_OVERWRITE = 0x01, OSTREAM_OPEN_SPARSE = 0x02, }; #ifdef __cplusplus extern "C" { #endif /** * @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 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); #ifdef __cplusplus } #endif #endif /* IO_OSTREAM_H */ squashfs-tools-ng-1.3.1/include/io/std.h000066400000000000000000000013641461472204600201200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * std.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef IO_STD_H #define IO_STD_H #include "io/istream.h" #include "io/ostream.h" #ifdef __cplusplus extern "C" { #endif /** * @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 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); #ifdef __cplusplus } #endif #endif /* IO_STD_H */ squashfs-tools-ng-1.3.1/include/io/xfrm.h000066400000000000000000000066251461472204600203070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xfrm.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef IO_XFRM_H #define IO_XFRM_H #include "io/istream.h" #include "io/ostream.h" enum { /** * @brief Deflate compressor with gzip headers. * * This actually creates a gzip compatible file, including a * gzip header and trailer. */ IO_COMPRESSOR_GZIP = 1, IO_COMPRESSOR_XZ = 2, IO_COMPRESSOR_ZSTD = 3, IO_COMPRESSOR_BZIP2 = 4, IO_COMPRESSOR_MIN = 1, IO_COMPRESSOR_MAX = 4, }; #ifdef __cplusplus extern "C" { #endif /** * @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 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 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 Resolve a compressor name to an ID. * * @param name A compressor name. * * @return A compressor ID on success, -1 on failure. */ SQFS_INTERNAL int io_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 *io_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 io_compressor_exists(int id); #ifdef __cplusplus } #endif #endif /* IO_XFRM_H */ squashfs-tools-ng-1.3.1/include/simple_writer.h000066400000000000000000000037511461472204600216060ustar00rootroot00000000000000/* 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.3.1/include/sqfs/000077500000000000000000000000001461472204600175165ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/include/sqfs/block.h000066400000000000000000000100421461472204600207560ustar00rootroot00000000000000/* 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.3.1/include/sqfs/block_processor.h000066400000000000000000000313621461472204600230650ustar00rootroot00000000000000/* 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.3.1/include/sqfs/block_writer.h000066400000000000000000000114701461472204600223600ustar00rootroot00000000000000/* 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.3.1/include/sqfs/compressor.h000066400000000000000000000270151461472204600220700ustar00rootroot00000000000000/* 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.3.1/include/sqfs/data_reader.h000066400000000000000000000121761461472204600221310ustar00rootroot00000000000000/* 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.3.1/include/sqfs/dir.h000066400000000000000000000065751461472204600204620ustar00rootroot00000000000000/* 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.3.1/include/sqfs/dir_reader.h000066400000000000000000000326611461472204600217770ustar00rootroot00000000000000/* 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[]; }; /** * @enum SQFS_DIR_READER_FLAGS * * @brief Flags for @ref sqfs_dir_reader_create */ typedef enum { /** * @brief Support "." and ".." directory and path entries. * * If this flag is set, the directory reader returns "." and ".." * entries when iterating over a directory, can fetch the associated * inodes if requested and supports resolving "." and ".." path * components when looking up a full path. * * In order for this to work, it internally caches the locations of * directory inodes it encounteres. This means, it only works as long * as you only use inodes fetched through the directory reader. If * given a foreign inode it hasn't seen before, it might not be able * to resolve the parent link. */ SQFS_DIR_READER_DOT_ENTRIES = 0x00000001, SQFS_DIR_READER_ALL_FLAGS = 0x00000001, } SQFS_DIR_READER_FLAGS; /** * @enum SQFS_DIR_OPEN_FLAGS * * @brief Flags for @ref sqfs_dir_reader_open_dir */ typedef enum { /** * @brief Do not generate "." and ".." entries * * If the @ref sqfs_dir_reader_t was created with * the @ref SQFS_DIR_READER_DOT_ENTRIES flag set, "." and ".." entries * are generated when iterating over a directory. If that is not desired * in some instances, this flag can be set to suppress this behaviour * when opening a directory. */ SQFS_DIR_OPEN_NO_DOT_ENTRIES = 0x00000001, SQFS_DIR_OPEN_ALL_FLAGS = 0x00000001, } SQFS_DIR_OPEN_FLAGS; #ifdef __cplusplus extern "C" { #endif /** * @brief Create a directory reader. * * @memberof sqfs_dir_reader_t * * The function fails if any unknown flag is set. In squashfs-tools-ng * version 1.2 introduced the @ref SQFS_DIR_READER_DOT_ENTRIES flag, * earlier versions require the flags field to be set to zero. * * @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 A combination of @ref SQFS_DIR_READER_FLAGS * * @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. * * If the reader was created with the @ref SQFS_DIR_READER_DOT_ENTRIES flag * set, the first two entries will be ".", referring to the directory inode * itself and "..", referring to the parent directory inode. Those entries * are generated artificially, as SquashFS does not store them on disk, hence * extra work is required and a flag is used to enable this behaviour. By * default, no such entries are generated. * * If this flag is set, but you wish to override that behaviour on a * per-instance basis, simply set the @ref SQFS_DIR_OPEN_NO_DOT_ENTRIES flag * when calling this function. * * @param rd A pointer to a directory reader. * @param inode An directory or extended directory inode. * @param flags A combination of @ref SQFS_DIR_OPEN_FLAGS. * * @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 or NULL. */ SQFS_API void sqfs_dir_tree_destroy(sqfs_tree_node_t *root); /** * @brief Recursively destroy a tree of @ref sqfs_tree_node_t nodes * * @memberof sqfs_tree_node_t * * This function can be used to assemble an absolute path from a tree * node returned by @ref sqfs_dir_reader_get_full_hierarchy. * * The function recursively walks up the tree to assemble a path string. It * returns "/" for the root node and assembles paths beginning with "/" for * non-root nodes. The resulting path is slash separated, but (except for * the root) never ends with a slash. * * While walking the node list, the function enforces various invariantes. It * returns @ref SQFS_ERROR_LINK_LOOP if the list of parent pointers is cyclical, * @ref SQFS_ERROR_CORRUPTED if any node has an empty name, or a name that * contains '/' or equals ".." or ".". The function * returns @ref SQFS_ERROR_ARG_INVALID if given NULL node or the root has a name * set. Additionally, the function can return overflow or allocation failures * while constructing the path. * * The returned string needs to be free'd with @ref sqfs_free. * * @param node A pointer to a tree node. * @param out Returns a pointer to a string on success, set to NULL on failure. * * @return Zero on success, an @ref SQFS_ERROR value on failure. */ SQFS_API int sqfs_tree_node_get_path(const sqfs_tree_node_t *node, char **out); #ifdef __cplusplus } #endif #endif /* SQFS_DIR_READER_H */ squashfs-tools-ng-1.3.1/include/sqfs/dir_writer.h000066400000000000000000000245471461472204600220550ustar00rootroot00000000000000/* 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.3.1/include/sqfs/error.h000066400000000000000000000104471461472204600210260ustar00rootroot00000000000000/* 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.3.1/include/sqfs/frag_table.h000066400000000000000000000113321461472204600217550ustar00rootroot00000000000000/* 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.3.1/include/sqfs/id_table.h000066400000000000000000000074111461472204600214350ustar00rootroot00000000000000/* 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.3.1/include/sqfs/inode.h000066400000000000000000000466451461472204600210040ustar00rootroot00000000000000/* 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 promotes the inodes to extended inodes if * the index is not 0xFFFFFFFF. The function does not try to demote extended * inodes if the index is 0xFFFFFFFF, because that would cause additional * information like a directory index to be lost. * * @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.3.1/include/sqfs/io.h000066400000000000000000000131561461472204600203040ustar00rootroot00000000000000/* 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, /** * @brief If set, do not try to apply any character set transformations * to the file path. * * This flag instructs the @ref sqfs_open_file API to pass the file * path to the OS dependent API as-is and not to attempt any character * set transformation. By default, if the underlying OS has a concept * of locale dependent file paths, the input path is UTF-8 and it is * transformed as needed, in order to retain a level of portabillity * and sanity. * * This flag currently only affects the Windows implementation. On * Unix-like systems, the path is always passed to the OS API as-is * and this flag has no effect. * * On Windows, the input path is converted from UTF-8 to UTF-16 and * then passed on to the wide-char based file API. If this flag is set, * the path is used as-is and passed on to the 8-bit codepage-whatever * API instead. */ SQFS_FILE_OPEN_NO_CHARSET_XFRM = 0x04, SQFS_FILE_OPEN_ALL_FLAGS = 0x07, } 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. Similarly, * on Windows, the implementation tries to preserve the GetLastError value. * * @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.3.1/include/sqfs/meta_reader.h000066400000000000000000000210321461472204600221350ustar00rootroot00000000000000/* 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" #include "sqfs/super.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. */ /** * @struct sqfs_readdir_state_t * * @brief Encapsulates state for simple directory reading */ struct sqfs_readdir_state_t { struct { sqfs_u64 block; size_t offset; size_t size; } init, current; size_t entries; sqfs_u32 inum_base; sqfs_u64 inode_block; }; /** * @brief Rewind a directory state object back to its starting location * * @memberof sqfs_readdir_state_t * * @param it A pointer to the directory state. */ static SQFS_INLINE void sqfs_readdir_state_reset(sqfs_readdir_state_t *s) { s->current = s->init; s->entries = 0; } #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 Initialize a state object for reading a directory * * @memberof sqfs_readdir_state_t * * This function initializes a simple state object to point to the * location of a directory header and store the total, uncompressed * size of the directory. * * The state object can be passed to @ref sqfs_meta_reader_readdir * to read entries one-by-one. * * @param s A pointer to the state object to initialize. * @param super A pointer to the super block, telling us where * the directory table starts. * @param inode A pointer to a directory inode from which to get the * directory location. * * @return Zero on success, an @ref SQFS_ERROR value on * failure (e.g. the inode is not a directory inode). */ SQFS_API int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, const sqfs_inode_generic_t *inode); /** * @brief Simple directory reading interface * * @memberof sqfs_meta_reader_t * * This function successively reads directory entries, transparently * parsing and skipping across headers. The state is encapsulated in * an external object that is passed in and the function seeks to the * location, so one can swap between multiple states and read several * directories interchangeably. * * @param m A pointer to a meta data reader. * @param s A pointer to a directory state that is used and updated. * @param ent Returns a pointer to a directory entry. Can be * released with a single @ref sqfs_free call. * @param inum If not NULL, returns the decoded inode number. * @param iref If not NULL, returns a reference to the inode. * * @return Zero on success, a negative @ref SQFS_ERROR number on failure, * a positive number if the end of the directory is reached. */ SQFS_API int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *s, sqfs_dir_entry_t **ent, sqfs_u32 *inum, sqfs_u64 *iref); /** * @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.3.1/include/sqfs/meta_writer.h000066400000000000000000000144451461472204600222210ustar00rootroot00000000000000/* 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.3.1/include/sqfs/predef.h000066400000000000000000000160041461472204600211350ustar00rootroot00000000000000/* 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_readdir_state_t sqfs_readdir_state_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 or NULL */ static SQFS_INLINE void sqfs_destroy(void *obj) { if (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.3.1/include/sqfs/super.h000066400000000000000000000206441461472204600210330ustar00rootroot00000000000000/* 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.3.1/include/sqfs/table.h000066400000000000000000000065011461472204600207600ustar00rootroot00000000000000/* 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.3.1/include/sqfs/xattr.h000066400000000000000000000116461461472204600210410ustar00rootroot00000000000000/* 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.3.1/include/sqfs/xattr_reader.h000066400000000000000000000164251461472204600223630ustar00rootroot00000000000000/* 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.3.1/include/sqfs/xattr_writer.h000066400000000000000000000113351461472204600224300ustar00rootroot00000000000000/* 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.3.1/include/tar/000077500000000000000000000000001461472204600173305ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/include/tar/format.h000066400000000000000000000036261461472204600210000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * format.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef TAR_FORMAT_H #define TAR_FORMAT_H #include "sqfs/predef.h" 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; #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) /* artificially imposed implementation limits */ #define TAR_MAX_SYMLINK_LEN (65536) #define TAR_MAX_PATH_LEN (65536) #define TAR_MAX_PAX_LEN (65536) #define TAR_MAX_SPARSE_ENT (65536) #ifdef __cplusplus extern "C" { #endif int read_octal(const char *str, int digits, sqfs_u64 *out); int read_number(const char *str, int digits, sqfs_u64 *out); void update_checksum(tar_header_t *hdr); bool is_checksum_valid(const tar_header_t *hdr); #ifdef __cplusplus } #endif #endif /* TAR_FORMAT_H */ squashfs-tools-ng-1.3.1/include/tar/tar.h000066400000000000000000000037351461472204600202770ustar00rootroot00000000000000/* 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 "io/istream.h" #include "io/ostream.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 tar_xattr_t { struct tar_xattr_t *next; char *key; sqfs_u8 *value; size_t value_len; char data[]; } tar_xattr_t; typedef struct { 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; sqfs_u16 mode; sqfs_u64 uid; sqfs_u64 gid; sqfs_u64 devno; sqfs_s64 mtime; } tar_header_decoded_t; #ifdef __cplusplus extern "C" { #endif /* 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 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); void free_sparse_list(sparse_map_t *sparse); void free_xattr_list(tar_xattr_t *list); #ifdef __cplusplus } #endif #endif /* TAR_H */ squashfs-tools-ng-1.3.1/include/util/000077500000000000000000000000001461472204600175175ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/include/util/array.h000066400000000000000000000032071461472204600210100ustar00rootroot00000000000000/* 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.3.1/include/util/hash_table.h000066400000000000000000000062511461472204600217660ustar00rootroot00000000000000/* * 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.3.1/include/util/mempool.h000066400000000000000000000011131461472204600213340ustar00rootroot00000000000000/* 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.3.1/include/util/rbtree.h000066400000000000000000000027341461472204600211610ustar00rootroot00000000000000/* 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; } #ifdef __cplusplus extern "C" { #endif 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.3.1/include/util/str_table.h000066400000000000000000000034401461472204600216500ustar00rootroot00000000000000/* 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.3.1/include/util/test.h000066400000000000000000000053631461472204600206560ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-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.3.1/include/util/threadpool.h000066400000000000000000000071341461472204600220360ustar00rootroot00000000000000/* 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.3.1/include/util/util.h000066400000000000000000000047031461472204600206510ustar00rootroot00000000000000/* 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); /* 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. */ SQFS_INTERNAL int mkdir_p(const char *path); /* 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. */ SQFS_INTERNAL 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 '..'). */ SQFS_INTERNAL bool is_filename_sane(const char *name); /* 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_INTERNAL sqfs_u32 get_source_date_epoch(void); /* Check if two regions in a file are equal using a scratch buffer to load the data into for comparision. Returns 0 if equal, > 0 if not, < 0 if error. */ SQFS_INTERNAL int check_file_range_equal(sqfs_file_t *file, void *scratch, size_t scratch_size, sqfs_u64 loc_a, sqfs_u64 loc_b, sqfs_u64 size); SQFS_INTERNAL int hex_decode(const char *in, size_t in_sz, sqfs_u8 *out, size_t out_sz); SQFS_INTERNAL int base64_decode(const char *in, size_t in_len, sqfs_u8 *out, size_t *out_len); SQFS_INTERNAL char *fix_win32_filename(const char *path); #endif /* SQFS_UTIL_H */ squashfs-tools-ng-1.3.1/include/util/w32threadwrap.h000066400000000000000000000040231461472204600223640ustar00rootroot00000000000000/* 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.3.1/lib/000077500000000000000000000000001461472204600156655ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/common/000077500000000000000000000000001461472204600171555ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/common/Makemodule.am000066400000000000000000000014731461472204600215640ustar00rootroot00000000000000libcommon_a_SOURCES = 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/data_writer_ostream.c libcommon_a_SOURCES += lib/common/perror.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.3.1/lib/common/comp_lzo.c000066400000000000000000000151031461472204600211430ustar00rootroot00000000000000/* 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.3.1/lib/common/comp_opt.c000066400000000000000000000247271461472204600211550ustar00rootroot00000000000000/* 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.3.1/lib/common/compress.c000066400000000000000000000031311461472204600211520ustar00rootroot00000000000000/* 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.3.1/lib/common/data_reader_dump.c000066400000000000000000000024551461472204600226070ustar00rootroot00000000000000/* 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.3.1/lib/common/data_writer.c000066400000000000000000000022101461472204600216210ustar00rootroot00000000000000/* 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.3.1/lib/common/data_writer_ostream.c000066400000000000000000000034341461472204600233640ustar00rootroot00000000000000/* 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.3.1/lib/common/hardlink.c000066400000000000000000000040021461472204600211110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * hardlink.c * * Copyright (C) 2019 David Oberhollenzer */ #include "common.h" #include "util/rbtree.h" #include "util/util.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) { ret = map_nodes(inumtree, out, n); if (ret != 0) return ret; } return 0; } if (!is_filename_sane((const char *)n->name)) return SQFS_ERROR_CORRUPTED; idx = n->inode->base.inode_number; tn = rbtree_lookup(inumtree, &idx); if (tn == NULL) return rbtree_insert(inumtree, &idx, &n); target = *((const sqfs_tree_node_t **)rbtree_node_value(tn)); lnk = calloc(1, sizeof(*lnk)); if (lnk == NULL) return SQFS_ERROR_ALLOC; lnk->inode_number = idx; ret = sqfs_tree_node_get_path(target, &lnk->target); if (ret != 0) { free(lnk); return ret; } if (canonicalize_name(lnk->target) != 0) { sqfs_free(lnk->target); free(lnk); return SQFS_ERROR_CORRUPTED; } lnk->next = (*out); (*out) = lnk; return 0; } 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) return ret; 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 ret; } squashfs-tools-ng-1.3.1/lib/common/parse_size.c000066400000000000000000000024621461472204600214710ustar00rootroot00000000000000/* 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.3.1/lib/common/perror.c000066400000000000000000000034101461472204600206300ustar00rootroot00000000000000/* 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.3.1/lib/common/print_size.c000066400000000000000000000014531461472204600215120ustar00rootroot00000000000000/* 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.3.1/lib/common/print_version.c000066400000000000000000000013321461472204600222210ustar00rootroot00000000000000/* 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.3.1/lib/common/writer/000077500000000000000000000000001461472204600204715ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/common/writer/cleanup.c000066400000000000000000000014741461472204600222720ustar00rootroot00000000000000/* 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.3.1/lib/common/writer/finish.c000066400000000000000000000104561461472204600221230ustar00rootroot00000000000000/* 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 were 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.3.1/lib/common/writer/init.c000066400000000000000000000115071461472204600216040ustar00rootroot00000000000000/* 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 #include #ifdef HAVE_SCHED_GETAFFINITY #include static size_t os_get_num_jobs(void) { cpu_set_t cpu_set; CPU_ZERO(&cpu_set); if (sched_getaffinity(0, sizeof cpu_set, &cpu_set) == -1) return 1; else return CPU_COUNT(&cpu_set); } #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.3.1/lib/common/writer/serialize_fstree.c000066400000000000000000000107031461472204600241750ustar00rootroot00000000000000/* 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); if (n->xattr_idx == 0xFFFFFFFF && !S_ISDIR(n->mode)) sqfs_inode_make_basic(inode); 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.3.1/lib/compat/000077500000000000000000000000001461472204600171505ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/compat/Makemodule.am000066400000000000000000000010761461472204600215560ustar00rootroot00000000000000libcompat_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/w32_wmain.c libcompat_a_SOURCES += lib/compat/w32_stdio.c libcompat_a_SOURCES += lib/compat/fnmatch.c libcompat_a_SOURCES += lib/compat/getopt.c libcompat_a_SOURCES += lib/compat/getopt_long.c libcompat_a_SOURCES += lib/compat/strchrnul.c noinst_LIBRARIES += libcompat.a squashfs-tools-ng-1.3.1/lib/compat/chdir.c000066400000000000000000000010371461472204600204060ustar00rootroot00000000000000/* 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.3.1/lib/compat/fnmatch.c000066400000000000000000000151621461472204600207410ustar00rootroot00000000000000/* * 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.3.1/lib/compat/getopt.c000066400000000000000000000033671461472204600206270ustar00rootroot00000000000000#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.3.1/lib/compat/getopt_long.c000066400000000000000000000041741461472204600216430ustar00rootroot00000000000000#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.3.1/lib/compat/getsubopt.c000066400000000000000000000007171461472204600213350ustar00rootroot00000000000000#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.3.1/lib/compat/mockups.c000066400000000000000000000017221461472204600207770ustar00rootroot00000000000000/* 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.3.1/lib/compat/path_to_windows.c000066400000000000000000000015671461472204600225350ustar00rootroot00000000000000/* 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.3.1/lib/compat/strchrnul.c000066400000000000000000000005011461472204600213340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * strchrnul.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "compat.h" #ifndef HAVE_STRCHRNUL char *strchrnul(const char *s, int c) { while (*s && *((unsigned char *)s) != c) ++s; return (char *)s; } #endif squashfs-tools-ng-1.3.1/lib/compat/strndup.c000066400000000000000000000007471461472204600210230ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-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.3.1/lib/compat/w32_perror.c000066400000000000000000000012621461472204600213210ustar00rootroot00000000000000/* 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.3.1/lib/compat/w32_stdio.c000066400000000000000000000040071461472204600211320ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * w32_stdio.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #undef fputc #undef putc #undef fputs #undef fprintf #undef printf static HANDLE get_handle(FILE *strm) { if (strm != stdout && strm != stderr) return INVALID_HANDLE_VALUE; return GetStdHandle(strm == stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); } static BOOL isatty(HANDLE hnd) { if (hnd == INVALID_HANDLE_VALUE) return FALSE; return (GetFileType(hnd) == FILE_TYPE_CHAR); } int sqfs_tools_fputs(const char *str, FILE *strm) { DWORD length; WCHAR *wstr; HANDLE hnd; int ret; hnd = get_handle(strm); if (!isatty(hnd)) return fputs(str, strm); length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); if (length <= 0) return EOF; wstr = calloc(sizeof(wstr[0]), length); if (wstr == NULL) return EOF; MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, length); ret = WriteConsoleW(hnd, wstr, length, NULL, NULL) ? length : EOF; free(wstr); return ret; } int sqfs_tools_fputc(int c, FILE *strm) { char str[2]; str[0] = c; str[1] = '\0'; return sqfs_tools_fputs(str, strm); } static int sqfs_printf_common(FILE *out, const char *fmt, va_list ap) { int ret, len; char *str; len = _vscprintf(fmt, ap); if (len == -1) return -1; str = malloc((size_t)len + 1); if (str == NULL) return -1; ret = vsprintf(str, fmt, ap); if (ret == -1) { free(str); return -1; } if (sqfs_tools_fputs(str, out) == EOF) ret = -1; free(str); return ret; } int sqfs_tools_printf(const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = sqfs_printf_common(stdout, fmt, ap); va_end(ap); return ret; } int sqfs_tools_fprintf(FILE *strm, const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = sqfs_printf_common(strm, fmt, ap); va_end(ap); return ret; } #endif squashfs-tools-ng-1.3.1/lib/compat/w32_wmain.c000066400000000000000000000032221461472204600211210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * w32_wmain.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #undef main int main(int argc, char **argv) { WCHAR *cmdline, **argList; int i, ret, utf8_argc; char **utf8_argv; (void)argc; (void)argv; /* get the UTF-16 encoded command line arguments */ cmdline = GetCommandLineW(); argList = CommandLineToArgvW(cmdline, &utf8_argc); if (argList == NULL) goto fail_oom; /* convert to UTF-8 */ utf8_argv = calloc(sizeof(utf8_argv[0]), utf8_argc); if (utf8_argv == NULL) goto fail_oom; for (i = 0; i < utf8_argc; ++i) { DWORD length = WideCharToMultiByte(CP_UTF8, 0, argList[i], -1, NULL, 0, NULL, NULL); if (length <= 0) goto fail_conv; utf8_argv[i] = calloc(1, length + 1); if (utf8_argv[i] == NULL) goto fail_oom; WideCharToMultiByte(CP_UTF8, 0, argList[i], -1, utf8_argv[i], length + 1, NULL, NULL); utf8_argv[i][length] = '\0'; } LocalFree(argList); argList = NULL; /* call the actual main function */ ret = sqfs_tools_main(utf8_argc, utf8_argv); /* cleanup */ for (i = 0; i < utf8_argc; ++i) free(utf8_argv[i]); free(utf8_argv); return ret; fail_conv: w32_perror("Converting UTF-16 argument to UTF-8"); goto fail; fail_oom: fputs("out of memory\n", stderr); goto fail; fail: if (utf8_argv != NULL) { for (i = 0; i < utf8_argc; ++i) free(utf8_argv[i]); free(utf8_argv); } if (argList != NULL) { LocalFree(argList); } return EXIT_FAILURE; } #endif squashfs-tools-ng-1.3.1/lib/fstree/000077500000000000000000000000001461472204600171555ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/fstree/Makemodule.am000066400000000000000000000005651461472204600215650ustar00rootroot00000000000000libfstree_a_SOURCES = include/fstree.h lib/fstree/fstree.c libfstree_a_SOURCES += lib/fstree/post_process.c lib/fstree/get_path.c libfstree_a_SOURCES += lib/fstree/mknode.c lib/fstree/hardlink.c libfstree_a_SOURCES += lib/fstree/add_by_path.c lib/fstree/get_by_path.c libfstree_a_CFLAGS = $(AM_CFLAGS) libfstree_a_CPPFLAGS = $(AM_CPPFLAGS) noinst_LIBRARIES += libfstree.a squashfs-tools-ng-1.3.1/lib/fstree/add_by_path.c000066400000000000000000000022531461472204600215610ustar00rootroot00000000000000/* 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.3.1/lib/fstree/fstree.c000066400000000000000000000047651461472204600206250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/util.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.3.1/lib/fstree/get_by_path.c000066400000000000000000000024101461472204600216030ustar00rootroot00000000000000/* 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.3.1/lib/fstree/get_path.c000066400000000000000000000013451461472204600211170ustar00rootroot00000000000000/* 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.3.1/lib/fstree/hardlink.c000066400000000000000000000026201461472204600211150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * hardlink.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.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 == 0xFFFFFFFF) { 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.3.1/lib/fstree/mknode.c000066400000000000000000000034021461472204600205750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include #include #include void fstree_insert_sorted(tree_node_t *root, tree_node_t *n) { tree_node_t *it = root->data.dir.children, *prev = NULL; while (it != NULL && strcmp(it->name, n->name) < 0) { prev = it; it = it->next; } n->parent = root; n->next = it; if (prev == NULL) { root->data.dir.children = n; } else { prev->next = n; } } 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; 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 == 0xFFFFFFFF) { free(n); errno = EMLINK; return NULL; } fstree_insert_sorted(parent, n); parent->link_count++; } return n; } squashfs-tools-ng-1.3.1/lib/fstree/post_process.c000066400000000000000000000106541461472204600220520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * post_process.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.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 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; 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.3.1/lib/io/000077500000000000000000000000001461472204600162745ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/io/Makemodule.am000066400000000000000000000025651461472204600207060ustar00rootroot00000000000000libio_a_SOURCES = lib/io/internal.h libio_a_SOURCES += include/io/istream.h lib/io/ostream.c lib/io/printf.c libio_a_SOURCES += include/io/ostream.h lib/io/istream.c lib/io/get_line.c libio_a_SOURCES += include/io/xfrm.h lib/io/xfrm.c libio_a_SOURCES += include/io/file.h include/io/std.h libio_a_SOURCES += lib/io/compress/ostream_compressor.c libio_a_SOURCES += lib/io/uncompress/istream_compressor.c libio_a_SOURCES += lib/io/uncompress/autodetect.c libio_a_CFLAGS = $(AM_CFLAGS) $(ZLIB_CFLAGS) $(XZ_CFLAGS) libio_a_CFLAGS += $(ZSTD_CFLAGS) $(BZIP2_CFLAGS) libio_a_CPPFLAGS = $(AM_CPPFLAGS) if WINDOWS libio_a_SOURCES += lib/io/win32/ostream.c libio_a_SOURCES += lib/io/win32/istream.c libio_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 else libio_a_SOURCES += lib/io/unix/ostream.c libio_a_SOURCES += lib/io/unix/istream.c endif if WITH_XZ libio_a_SOURCES += lib/io/compress/xz.c lib/io/uncompress/xz.c libio_a_CPPFLAGS += -DWITH_XZ endif if WITH_GZIP libio_a_SOURCES += lib/io/compress/gzip.c libio_a_SOURCES += lib/io/uncompress/gzip.c libio_a_CPPFLAGS += -DWITH_GZIP endif if WITH_ZSTD libio_a_SOURCES += lib/io/compress/zstd.c libio_a_SOURCES += lib/io/uncompress/zstd.c libio_a_CPPFLAGS += -DWITH_ZSTD endif if WITH_BZIP2 libio_a_SOURCES += lib/io/compress/bzip2.c libio_a_SOURCES += lib/io/uncompress/bzip2.c libio_a_CPPFLAGS += -DWITH_BZIP2 endif noinst_LIBRARIES += libio.a squashfs-tools-ng-1.3.1/lib/io/compress/000077500000000000000000000000001461472204600201275ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/io/compress/bzip2.c000066400000000000000000000041661461472204600213300ustar00rootroot00000000000000/* 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.3.1/lib/io/compress/gzip.c000066400000000000000000000036571461472204600212570ustar00rootroot00000000000000/* 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.3.1/lib/io/compress/ostream_compressor.c000066400000000000000000000041461461472204600242260ustar00rootroot00000000000000/* 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 IO_COMPRESSOR_GZIP: #ifdef WITH_GZIP comp = ostream_gzip_create(strm->get_filename(strm)); #endif break; case IO_COMPRESSOR_XZ: #ifdef WITH_XZ comp = ostream_xz_create(strm->get_filename(strm)); #endif break; case IO_COMPRESSOR_ZSTD: #if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) comp = ostream_zstd_create(strm->get_filename(strm)); #endif break; case IO_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.3.1/lib/io/compress/xz.c000066400000000000000000000032141461472204600207340ustar00rootroot00000000000000/* 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.3.1/lib/io/compress/zstd.c000066400000000000000000000035431461472204600212640ustar00rootroot00000000000000/* 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.3.1/lib/io/get_line.c000066400000000000000000000037061461472204600202340ustar00rootroot00000000000000/* 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.3.1/lib/io/internal.h000066400000000000000000000032011461472204600202550ustar00rootroot00000000000000/* 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 "io/istream.h" #include "io/ostream.h" #include "io/file.h" #include "io/xfrm.h" #include "io/std.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.3.1/lib/io/istream.c000066400000000000000000000033041461472204600201040ustar00rootroot00000000000000/* 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.3.1/lib/io/ostream.c000066400000000000000000000027201461472204600201130ustar00rootroot00000000000000/* 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.3.1/lib/io/printf.c000066400000000000000000000007461461472204600177510ustar00rootroot00000000000000/* 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.3.1/lib/io/uncompress/000077500000000000000000000000001461472204600204725ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/io/uncompress/autodetect.c000066400000000000000000000024571461472204600230070ustar00rootroot00000000000000/* 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[] = { { IO_COMPRESSOR_GZIP, (const sqfs_u8 *)"\x1F\x8B\x08", 3 }, { IO_COMPRESSOR_XZ, (const sqfs_u8 *)("\xFD" "7zXZ"), 6 }, { IO_COMPRESSOR_ZSTD, (const sqfs_u8 *)"\x28\xB5\x2F\xFD", 4 }, { IO_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.3.1/lib/io/uncompress/bzip2.c000066400000000000000000000047071461472204600216740ustar00rootroot00000000000000/* 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.3.1/lib/io/uncompress/gzip.c000066400000000000000000000034231461472204600216110ustar00rootroot00000000000000/* 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; int ret; for (;;) { ret = istream_precache(wrapped); if (ret != 0) return ret; gzip->strm.avail_in = (uInt)wrapped->buffer_used; gzip->strm.avail_out = (uInt)(BUFSZ - base->buffer_used); 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.3.1/lib/io/uncompress/istream_compressor.c000066400000000000000000000026261461472204600245640ustar00rootroot00000000000000/* 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 IO_COMPRESSOR_GZIP: #ifdef WITH_GZIP comp = istream_gzip_create(strm->get_filename(strm)); #endif break; case IO_COMPRESSOR_XZ: #ifdef WITH_XZ comp = istream_xz_create(strm->get_filename(strm)); #endif break; case IO_COMPRESSOR_ZSTD: #if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) comp = istream_zstd_create(strm->get_filename(strm)); #endif break; case IO_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.3.1/lib/io/uncompress/xz.c000066400000000000000000000036141461472204600213030ustar00rootroot00000000000000/* 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.3.1/lib/io/uncompress/zstd.c000066400000000000000000000032611461472204600216240ustar00rootroot00000000000000/* 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.3.1/lib/io/unix/000077500000000000000000000000001461472204600172575ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/io/unix/istream.c000066400000000000000000000042661461472204600210770ustar00rootroot00000000000000/* 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; 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) { strm->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.3.1/lib/io/unix/ostream.c000066400000000000000000000061231461472204600210770ustar00rootroot00000000000000/* 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.3.1/lib/io/win32/000077500000000000000000000000001461472204600172365ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/io/win32/istream.c000066400000000000000000000051541461472204600210530ustar00rootroot00000000000000/* 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)) { DWORD error = GetLastError(); if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) { strm->eof = true; break; } SetLastError(error); 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; WCHAR *wpath = NULL; if (file == NULL) { perror(path); return NULL; } wpath = path_to_windows(path); if (wpath == NULL) goto fail_free; file->path = strdup(path); if (file->path == NULL) { perror(path); goto fail_free; } file->hnd = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file->hnd == INVALID_HANDLE_VALUE) { perror(path); goto fail_path; } free(wpath); 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(wpath); 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.3.1/lib/io/win32/ostream.c000066400000000000000000000073761461472204600210710ustar00rootroot00000000000000/* 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)) { 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; WCHAR *wpath = NULL; if (file == NULL) { perror(path); return NULL; } wpath = path_to_windows(path); if (wpath == NULL) goto fail_free; 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 = CreateFileW(wpath, access_flags, 0, NULL, creation_mode, FILE_ATTRIBUTE_NORMAL, NULL); if (file->hnd == INVALID_HANDLE_VALUE) { w32_perror(path); goto fail_path; } free(wpath); 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); free(wpath); 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.3.1/lib/io/xfrm.c000066400000000000000000000021361461472204600174160ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * compressor.c * * Copyright (C) 2019 David Oberhollenzer */ #include "internal.h" int io_compressor_id_from_name(const char *name) { if (strcmp(name, "gzip") == 0) return IO_COMPRESSOR_GZIP; if (strcmp(name, "xz") == 0) return IO_COMPRESSOR_XZ; if (strcmp(name, "zstd") == 0) return IO_COMPRESSOR_ZSTD; if (strcmp(name, "bzip2") == 0) return IO_COMPRESSOR_BZIP2; return -1; } const char *io_compressor_name_from_id(int id) { if (id == IO_COMPRESSOR_GZIP) return "gzip"; if (id == IO_COMPRESSOR_XZ) return "xz"; if (id == IO_COMPRESSOR_ZSTD) return "zstd"; if (id == IO_COMPRESSOR_BZIP2) return "bzip2"; return NULL; } bool io_compressor_exists(int id) { switch (id) { #ifdef WITH_GZIP case IO_COMPRESSOR_GZIP: return true; #endif #ifdef WITH_XZ case IO_COMPRESSOR_XZ: return true; #endif #if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) case IO_COMPRESSOR_ZSTD: return true; #endif #ifdef WITH_BZIP2 case IO_COMPRESSOR_BZIP2: return true; #endif default: break; } return false; } squashfs-tools-ng-1.3.1/lib/sqfs/000077500000000000000000000000001461472204600166415ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/Makemodule.am000066400000000000000000000104631461472204600212470ustar00rootroot00000000000000LIBSQFS_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/dir_reader.c libsquashfs_la_SOURCES += lib/sqfs/dir_reader/read_tree.c libsquashfs_la_SOURCES += lib/sqfs/dir_reader/get_path.c libsquashfs_la_SOURCES += lib/sqfs/dir_reader/internal.h 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 lib/util/file_cmp.c libsquashfs_la_SOURCES += lib/util/hash_table.c include/util/hash_table.h libsquashfs_la_SOURCES += lib/util/rbtree.c include/util/rbtree.h libsquashfs_la_SOURCES += lib/util/array.c include/util/array.h libsquashfs_la_SOURCES += lib/util/is_memory_zero.c libsquashfs_la_SOURCES += include/util/threadpool.h # and from libcompat libsquashfs_la_SOURCES += lib/compat/strchrnul.c lib/compat/strndup.c libsquashfs_la_SOURCES += include/compat.h if CUSTOM_ALLOC libsquashfs_la_SOURCES += lib/util/mempool.c include/util/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 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 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.3.1/lib/sqfs/block_processor/000077500000000000000000000000001461472204600220325ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/block_processor/backend.c000066400000000000000000000165201461472204600235710ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/block_processor/block_processor.c000066400000000000000000000174161461472204600254000ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/block_processor/frontend.c000066400000000000000000000120021461472204600240100ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/block_processor/internal.h000066400000000000000000000041741461472204600240250ustar00rootroot00000000000000/* 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 "util/hash_table.h" #include "util/threadpool.h" #include "util/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.3.1/lib/sqfs/block_writer.c000066400000000000000000000115271461472204600215010ustar00rootroot00000000000000/* 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/array.h" #include "util/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; array_t blocks; size_t devblksz; 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 info = { offset, MK_BLK_HASH(chksum, size) }; return array_append(&(wr->blocks), &info); } static int deduplicate_blocks(block_writer_default_t *wr, sqfs_u32 flags, sqfs_u64 *out) { const blk_info_t *blocks = wr->blocks.data; sqfs_u64 loc_a, loc_b, sz; size_t i, j, count; int ret; count = wr->blocks.used - wr->file_start; if (count == 0) { *out = 0; return 0; } if (flags & SQFS_BLK_DONT_DEDUPLICATE) { *out = blocks[wr->file_start].offset; return 0; } sz = 0; loc_a = blocks[wr->file_start].offset; for (i = 0; i < count; ++i) sz += SIZE_FROM_HASH(blocks[wr->file_start + i].hash); for (i = 0; i < wr->file_start; ++i) { for (j = 0; j < count; ++j) { if (blocks[i + j].hash == 0) break; if (blocks[i + j].hash != blocks[wr->file_start + j].hash) break; } if (j != count) continue; if (wr->flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) break; loc_b = blocks[i].offset; ret = check_file_range_equal(wr->file, wr->scratch, SCRATCH_SIZE, loc_a, loc_b, sz); if (ret == 0) break; if (ret < 0) return ret; } *out = blocks[i].offset; if (i >= wr->file_start) return 0; if (count >= (wr->file_start - i)) { wr->blocks.used = i + count; } else { wr->blocks.used = wr->file_start; } sz = blocks[wr->blocks.used - 1].offset + SIZE_FROM_HASH(blocks[wr->blocks.used - 1].hash); return wr->file->truncate(wr->file, sz); } static int align_file(block_writer_default_t *wr, sqfs_u32 flags) { void *padding; sqfs_u64 size; size_t diff; int ret; if (!(flags & SQFS_BLK_ALIGN)) return 0; 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) { array_cleanup(&(((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; int err; (void)user; if (flags & (SQFS_BLK_FIRST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { err = align_file(wr, flags); if (err) return err; if (flags & SQFS_BLK_FIRST_BLOCK) wr->file_start = wr->blocks.used; } *location = wr->file->get_size(wr->file); if (size != 0 && !(flags & SQFS_BLK_IS_SPARSE)) { sqfs_u32 out = size; if (!(flags & SQFS_BLK_IS_COMPRESSED)) out |= 1 << 24; err = store_block_location(wr, *location, out, checksum); if (err) return err; err = wr->file->write_at(wr->file, *location, data, size); if (err) return err; } if (flags & (SQFS_BLK_LAST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { err = align_file(wr, flags); if (err) return err; if (flags & SQFS_BLK_LAST_BLOCK) return deduplicate_blocks(wr, flags, location); } return 0; } static sqfs_u64 get_block_count(const sqfs_block_writer_t *wr) { return ((const block_writer_default_t *)wr)->blocks.used; } 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; if (array_init(&(wr->blocks), sizeof(blk_info_t), INIT_BLOCK_COUNT)) { free(wr); return NULL; } return (sqfs_block_writer_t *)wr; } squashfs-tools-ng-1.3.1/lib/sqfs/comp/000077500000000000000000000000001461472204600175775ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/comp/compressor.c000066400000000000000000000121521461472204600221400ustar00rootroot00000000000000/* 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; *out = NULL; /* 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.3.1/lib/sqfs/comp/gzip.c000066400000000000000000000154661461472204600207300ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/comp/internal.h000066400000000000000000000021331461472204600215630ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/comp/lz4.c000066400000000000000000000072701461472204600204620ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/comp/lzma.c000066400000000000000000000151141461472204600207100ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/comp/xz.c000066400000000000000000000160471461472204600204140ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/comp/zstd.c000066400000000000000000000072111461472204600207300ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/data_reader.c000066400000000000000000000201641461472204600212430ustar00rootroot00000000000000/* 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/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 > (UINT64_MAX / data->block_size)) return SQFS_ERROR_OVERFLOW; if ((sqfs_u64)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.3.1/lib/sqfs/dir_reader/000077500000000000000000000000001461472204600207415ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/dir_reader/dir_reader.c000066400000000000000000000174451461472204600232200ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * fs_reader.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "internal.h" static int inode_copy(const sqfs_inode_generic_t *inode, sqfs_inode_generic_t **out) { *out = alloc_flex(sizeof(*inode), 1, inode->payload_bytes_used); if (*out == NULL) return SQFS_ERROR_ALLOC; memcpy(*out, inode, sizeof(*inode) + inode->payload_bytes_used); return 0; } static int dcache_key_compare(const void *ctx, const void *l, const void *r) { sqfs_u32 lhs = *((const sqfs_u32 *)l), rhs = *((const sqfs_u32 *)r); (void)ctx; return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); } static int dcache_add(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *inode, sqfs_u64 ref) { sqfs_u32 inum = inode->base.inode_number; if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) return 0; if (inode->base.type != SQFS_INODE_DIR && inode->base.type != SQFS_INODE_EXT_DIR) { return 0; } if (rbtree_lookup(&rd->dcache, &inum) != NULL) return 0; return rbtree_insert(&rd->dcache, &inum, &ref); } static int dcache_find(sqfs_dir_reader_t *rd, sqfs_u32 inode, sqfs_u64 *ref) { rbtree_node_t *node; if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) return SQFS_ERROR_NO_ENTRY; node = rbtree_lookup(&rd->dcache, &inode); if (node == NULL) return SQFS_ERROR_NO_ENTRY; *ref = *((sqfs_u64 *)rbtree_node_value(node)); return 0; } static void dir_reader_destroy(sqfs_object_t *obj) { sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) rbtree_cleanup(&rd->dcache); 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)); if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) { if (rbtree_copy(&rd->dcache, ©->dcache)) goto fail_cache; } 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: if (copy->flags & SQFS_DIR_READER_DOT_ENTRIES) rbtree_cleanup(©->dcache); fail_cache: 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; int ret; if (flags & ~SQFS_DIR_READER_ALL_FLAGS) return NULL; rd = calloc(1, sizeof(*rd)); if (rd == NULL) return NULL; if (flags & SQFS_DIR_READER_DOT_ENTRIES) { ret = rbtree_init(&rd->dcache, sizeof(sqfs_u32), sizeof(sqfs_u64), dcache_key_compare); if (ret != 0) goto fail_dcache; } 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) goto fail_mino; 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) goto fail_mdir; ((sqfs_object_t *)rd)->destroy = dir_reader_destroy; ((sqfs_object_t *)rd)->copy = dir_reader_copy; rd->super = super; rd->flags = flags; rd->state = DIR_STATE_NONE; return rd; fail_mdir: sqfs_destroy(rd->meta_inode); fail_mino: if (flags & SQFS_DIR_READER_DOT_ENTRIES) rbtree_cleanup(&rd->dcache); fail_dcache: free(rd); return NULL; } int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *inode, sqfs_u32 flags) { sqfs_u32 parent; int ret; if (flags & (~SQFS_DIR_OPEN_ALL_FLAGS)) return SQFS_ERROR_UNSUPPORTED; ret = sqfs_readdir_state_init(&rd->it, rd->super, inode); if (ret) return ret; if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { if (inode->base.type == SQFS_INODE_EXT_DIR) { parent = inode->data.dir_ext.parent_inode; } else { parent = inode->data.dir.parent_inode; } if (dcache_find(rd, inode->base.inode_number, &rd->cur_ref)) return SQFS_ERROR_NO_ENTRY; if (rd->cur_ref == rd->super->root_inode_ref) { rd->parent_ref = rd->cur_ref; } else if (dcache_find(rd, parent, &rd->parent_ref)) { return SQFS_ERROR_NO_ENTRY; } rd->state = DIR_STATE_OPENED; } else { rd->state = DIR_STATE_ENTRIES; } rd->start_state = rd->state; return 0; } static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) { size_t len = strlen(str); sqfs_dir_entry_t *ent; ent = calloc(1, sizeof(sqfs_dir_entry_t) + len + 1); if (ent == NULL) return SQFS_ERROR_ALLOC; ent->type = SQFS_INODE_DIR; ent->size = len - 1; strcpy((char *)ent->name, str); *out = ent; return 0; } int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) { int err; switch (rd->state) { case DIR_STATE_OPENED: err = mk_dummy_entry(".", out); if (err == 0) { rd->state = DIR_STATE_DOT; rd->ent_ref = rd->cur_ref; } return err; case DIR_STATE_DOT: err = mk_dummy_entry("..", out); if (err == 0) { rd->state = DIR_STATE_ENTRIES; rd->ent_ref = rd->parent_ref; } return err; case DIR_STATE_ENTRIES: break; default: return SQFS_ERROR_SEQUENCE; } return sqfs_meta_reader_readdir(rd->meta_dir, &rd->it, out, NULL, &rd->ent_ref); } int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) { if (rd->state == DIR_STATE_NONE) return SQFS_ERROR_SEQUENCE; sqfs_readdir_state_reset(&rd->it); rd->state = rd->start_state; return 0; } int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) { sqfs_dir_entry_t *ent; int ret; ret = sqfs_dir_reader_rewind(rd); if (ret != 0) 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) { int ret; ret = sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, rd->ent_ref >> 16, rd->ent_ref & 0x0FFFF, inode); if (ret != 0) return ret; return dcache_add(rd, *inode, rd->ent_ref); } 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; int ret; ret = sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, block_start, offset, inode); if (ret != 0) return ret; return dcache_add(rd, *inode, rd->super->root_inode_ref); } 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; const char *ptr; int ret = 0; char *name; if (start == NULL) { ret = sqfs_dir_reader_get_root_inode(rd, &inode); } else { ret = inode_copy(start, &inode); } if (ret) return ret; for (; *path != '\0'; path = ptr) { if (*path == '/') { for (ptr = path; *ptr == '/'; ++ptr) ; continue; } ret = sqfs_dir_reader_open_dir(rd, inode, 0); free(inode); if (ret) return ret; ptr = strchrnul(path, '/'); name = strndup(path, ptr - path); if (name == NULL) return SQFS_ERROR_ALLOC; ret = sqfs_dir_reader_find(rd, name); free(name); if (ret) return ret; ret = sqfs_dir_reader_get_inode(rd, &inode); if (ret) return ret; } *out = inode; return 0; } squashfs-tools-ng-1.3.1/lib/sqfs/dir_reader/get_path.c000066400000000000000000000032161461472204600227020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * get_path.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "internal.h" #include #include int sqfs_tree_node_get_path(const sqfs_tree_node_t *node, char **out) { const sqfs_tree_node_t *it; size_t clen, len = 0; char *str, *ptr; *out = NULL; if (node == NULL) return SQFS_ERROR_ARG_INVALID; for (it = node; it->parent != NULL; it = it->parent) { if (it->parent == node) return SQFS_ERROR_LINK_LOOP; /* non-root nodes must have a valid name */ clen = strlen((const char *)it->name); if (clen == 0) return SQFS_ERROR_CORRUPTED; if (strchr((const char *)it->name, '/') != NULL) return SQFS_ERROR_CORRUPTED; if (it->name[0] == '.') { if (clen == 1 || (clen == 2 && it->name[1] == '.')) return SQFS_ERROR_CORRUPTED; } /* compute total path length */ if (SZ_ADD_OV(clen, 1, &clen)) return SQFS_ERROR_OVERFLOW; if (SZ_ADD_OV(len, clen, &len)) return SQFS_ERROR_OVERFLOW; } /* root node must not have a name */ if (it->name[0] != '\0') return SQFS_ERROR_ARG_INVALID; /* generate the path */ if (node->parent == NULL) { str = strdup("/"); if (str == NULL) return SQFS_ERROR_ALLOC; } else { if (SZ_ADD_OV(len, 1, &len)) return SQFS_ERROR_OVERFLOW; str = malloc(len); if (str == NULL) return SQFS_ERROR_ALLOC; ptr = str + len - 1; *ptr = '\0'; for (it = node; it->parent != NULL; it = it->parent) { len = strlen((const char *)it->name); ptr -= len; memcpy(ptr, (const char *)it->name, len); *(--ptr) = '/'; } } *out = str; return 0; } squashfs-tools-ng-1.3.1/lib/sqfs/dir_reader/internal.h000066400000000000000000000016701461472204600227320ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * internal.h * * Copyright (C) 2019 David Oberhollenzer */ #ifndef DIR_READER_INTERNAL_H #define DIR_READER_INTERNAL_H #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/rbtree.h" #include "util/util.h" #include #include enum { DIR_STATE_NONE = 0, DIR_STATE_OPENED = 1, DIR_STATE_DOT = 2, DIR_STATE_ENTRIES = 3, }; 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_readdir_state_t it; sqfs_u32 flags; int start_state; int state; sqfs_u64 parent_ref; sqfs_u64 cur_ref; sqfs_u64 ent_ref; rbtree_t dcache; }; #endif /* DIR_READER_INTERNAL_H */ squashfs-tools-ng-1.3.1/lib/sqfs/dir_reader/read_tree.c000066400000000000000000000124751461472204600230500ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * read_tree.c * * Copyright (C) 2019 David Oberhollenzer */ #define SQFS_BUILDING_DLL #include "internal.h" 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, SQFS_DIR_OPEN_NO_DOT_ENTRIES); 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; if (!root) return; 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, SQFS_DIR_OPEN_NO_DOT_ENTRIES); if (ret) goto fail; ptr = strchrnul(path, '/'); 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, SQFS_DIR_OPEN_NO_DOT_ENTRIES); 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.3.1/lib/sqfs/dir_writer.c000066400000000000000000000235421461472204600211650ustar00rootroot00000000000000/* 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 "util/array.h" #include "util/util.h" #include #include #define DIR_INDEX_THRESHOLD (256) 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 (writer->ent_count >= DIR_INDEX_THRESHOLD) inode->base.type = SQFS_INODE_EXT_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.3.1/lib/sqfs/frag_table.c000066400000000000000000000111501461472204600210710ustar00rootroot00000000000000/* 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 "util/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.3.1/lib/sqfs/id_table.c000066400000000000000000000067061461472204600205610ustar00rootroot00000000000000/* 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 "util/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.3.1/lib/sqfs/inode.c000066400000000000000000000216511461472204600201100ustar00rootroot00000000000000/* 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/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; } 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.3.1/lib/sqfs/libsquashfs1.pc.in000066400000000000000000000004511461472204600221770ustar00rootroot00000000000000prefix=@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.3.1/lib/sqfs/meta_reader.c000066400000000000000000000074571461472204600212720ustar00rootroot00000000000000/* 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/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) { if (m->offset == m->data_used) { *block_start = m->next_block; *offset = 0; } else { *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.3.1/lib/sqfs/meta_writer.c000066400000000000000000000077111461472204600213350ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/misc.c000066400000000000000000000004161461472204600177410ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/read_inode.c000066400000000000000000000220201461472204600210720ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/read_super.c000066400000000000000000000045351461472204600211450ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/read_table.c000066400000000000000000000034111461472204600210660ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/readdir.c000066400000000000000000000066631461472204600204320ustar00rootroot00000000000000/* 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/super.h" #include "sqfs/inode.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; } int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, const sqfs_inode_generic_t *inode) { memset(s, 0, sizeof(*s)); if (inode->base.type == SQFS_INODE_DIR) { s->init.block = inode->data.dir.start_block; s->init.offset = inode->data.dir.offset; s->init.size = inode->data.dir.size; } else if (inode->base.type == SQFS_INODE_EXT_DIR) { s->init.block = inode->data.dir_ext.start_block; s->init.offset = inode->data.dir_ext.offset; s->init.size = inode->data.dir_ext.size; } else { return SQFS_ERROR_NOT_DIR; } s->init.block += super->directory_table_start; s->current = s->init; return 0; } int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *it, sqfs_dir_entry_t **ent, sqfs_u32 *inum, sqfs_u64 *iref) { size_t count; int ret; if (it->entries == 0) { sqfs_dir_header_t hdr; if (it->current.size <= sizeof(hdr)) goto out_eof; ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); if (ret != 0) return ret; ret = sqfs_meta_reader_read_dir_header(m, &hdr); if (ret != 0) return ret; sqfs_meta_reader_get_position(m, &it->current.block, &it->current.offset); it->current.size -= sizeof(hdr); it->entries = hdr.count + 1; it->inum_base = hdr.inode_number; it->inode_block = hdr.start_block; } if (it->current.size <= sizeof(**ent)) goto out_eof; ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); if (ret != 0) return ret; ret = sqfs_meta_reader_read_dir_ent(m, ent); if (ret) return ret; sqfs_meta_reader_get_position(m, &it->current.block, &it->current.offset); it->current.size -= sizeof(**ent); it->entries -= 1; count = (*ent)->size + 1; if (count >= it->current.size) { it->current.size = 0; } else { it->current.size -= count; } if (inum != NULL) *inum = it->inum_base + (*ent)->inode_diff; if (iref != NULL) { *iref = (sqfs_u64)it->inode_block << 16UL; *iref |= (*ent)->offset; } return 0; out_eof: it->current.size = 0; it->entries = 0; return 1; } squashfs-tools-ng-1.3.1/lib/sqfs/super.c000066400000000000000000000025701461472204600201470ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/unix/000077500000000000000000000000001461472204600176245ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/unix/io_file.c000066400000000000000000000066671461472204600214150ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/win32/000077500000000000000000000000001461472204600176035ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/win32/io_file.c000066400000000000000000000114401461472204600213550ustar00rootroot00000000000000/* 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; WCHAR *wpath = NULL; DWORD length; if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } if (!(flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM)) { length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); if (length <= 0) return NULL; wpath = calloc(sizeof(wpath[0]), length + 1); if (wpath == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } MultiByteToWideChar(CP_UTF8, 0, filename, -1, wpath, length + 1); wpath[length] = '\0'; } file = calloc(1, sizeof(*file)); base = (sqfs_file_t *)file; if (file == NULL) { free(wpath); SetLastError(ERROR_NOT_ENOUGH_MEMORY); 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; } } if (flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM) { file->fd = CreateFileA(filename, access_flags, share_mode, NULL, creation_mode, FILE_ATTRIBUTE_NORMAL, NULL); } else { file->fd = CreateFileW(wpath, access_flags, share_mode, NULL, creation_mode, FILE_ATTRIBUTE_NORMAL, NULL); } free(wpath); 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.3.1/lib/sqfs/write_inode.c000066400000000000000000000134321461472204600213200ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/write_super.c000066400000000000000000000026301461472204600213560ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/write_table.c000066400000000000000000000031431461472204600213070ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/xattr/000077500000000000000000000000001461472204600200035ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/sqfs/xattr/xattr.c000066400000000000000000000017611461472204600213160ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/xattr/xattr_reader.c000066400000000000000000000171121461472204600226350ustar00rootroot00000000000000/* 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/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.3.1/lib/sqfs/xattr/xattr_writer.c000066400000000000000000000055731461472204600227170ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/xattr/xattr_writer.h000066400000000000000000000023111461472204600227070ustar00rootroot00000000000000/* 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 "util/str_table.h" #include "util/rbtree.h" #include "util/array.h" #include "util/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.3.1/lib/sqfs/xattr/xattr_writer_flush.c000066400000000000000000000171271461472204600241160ustar00rootroot00000000000000/* 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.3.1/lib/sqfs/xattr/xattr_writer_record.c000066400000000000000000000060611461472204600242460ustar00rootroot00000000000000/* 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.3.1/lib/tar/000077500000000000000000000000001461472204600164535ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/tar/Makemodule.am000066400000000000000000000010101461472204600210450ustar00rootroot00000000000000libtar_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_old.c libtar_a_SOURCES += 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/tar.h include/tar/format.h libtar_a_CFLAGS = $(AM_CFLAGS) libtar_a_CPPFLAGS = $(AM_CPPFLAGS) noinst_LIBRARIES += libtar.a squashfs-tools-ng-1.3.1/lib/tar/checksum.c000066400000000000000000000022371461472204600204250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * checksum.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar/format.h" #include 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.3.1/lib/tar/cleanup.c000066400000000000000000000012721461472204600202500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * cleanup.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" #include #include 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.3.1/lib/tar/internal.h000066400000000000000000000016141461472204600204420ustar00rootroot00000000000000/* 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/tar.h" #include "tar/format.h" #include "util/util.h" 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, }; 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); 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.3.1/lib/tar/number.c000066400000000000000000000026111461472204600201070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * number.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar/format.h" #include #include 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; } static 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); } squashfs-tools-ng-1.3.1/lib/tar/padd_file.c000066400000000000000000000012561461472204600205320ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * padd_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar/tar.h" #include "tar/format.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.3.1/lib/tar/pax_header.c000066400000000000000000000206121461472204600207200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * pax_header.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" #include #include #include static 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; } static void urldecode(char *str) { unsigned char *out = (unsigned char *)str; char *in = str; while (*in != '\0') { sqfs_u8 x = *(in++); if (x == '%' && isxdigit(in[0]) && isxdigit(in[1])) { hex_decode(in, 2, &x, 1); in += 2; } *(out++) = x; } *out = '\0'; } static int pax_uid(tar_header_decoded_t *out, sqfs_u64 id) { out->uid = id; return 0; } static int pax_gid(tar_header_decoded_t *out, sqfs_u64 id) { out->gid = id; return 0; } static int pax_size(tar_header_decoded_t *out, sqfs_u64 size) { out->record_size = size; return 0; } static int pax_mtime(tar_header_decoded_t *out, sqfs_s64 mtime) { out->mtime = mtime; return 0; } static int pax_rsize(tar_header_decoded_t *out, sqfs_u64 size) { out->actual_size = size; return 0; } static int pax_path(tar_header_decoded_t *out, char *path) { free(out->name); out->name = path; return 0; } static int pax_slink(tar_header_decoded_t *out, char *path) { free(out->link_target); out->link_target = path; return 0; } static int pax_sparse_map(tar_header_decoded_t *out, const char *line) { sparse_map_t *last = NULL, *list = NULL, *ent = NULL; free_sparse_list(out->sparse); out->sparse = 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++) == ','); out->sparse = list; return 0; 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 -1; } static int pax_xattr_schily(tar_header_decoded_t *out, tar_xattr_t *xattr) { xattr->next = out->xattr; out->xattr = xattr; return 0; } static int pax_xattr_libarchive(tar_header_decoded_t *out, tar_xattr_t *xattr) { int ret; ret = base64_decode((const char *)xattr->value, xattr->value_len, xattr->value, &xattr->value_len); if (ret) return -1; urldecode(xattr->key); xattr->value[xattr->value_len] = '\0'; xattr->next = out->xattr; out->xattr = xattr; return 0; } enum { PAX_TYPE_SINT = 0, PAX_TYPE_UINT, PAX_TYPE_STRING, PAX_TYPE_CONST_STRING, PAX_TYPE_PREFIXED_XATTR, PAX_TYPE_IGNORE, }; static const struct pax_handler_t { const char *name; int flag; int type; union { int (*sint)(tar_header_decoded_t *out, sqfs_s64 sval); int (*uint)(tar_header_decoded_t *out, sqfs_u64 uval); int (*str)(tar_header_decoded_t *out, char *str); int (*cstr)(tar_header_decoded_t *out, const char *str); int (*xattr)(tar_header_decoded_t *out, tar_xattr_t *xattr); } cb; } pax_fields[] = { { "uid", PAX_UID, PAX_TYPE_UINT, { .uint = pax_uid } }, { "gid", PAX_GID, PAX_TYPE_UINT, { .uint = pax_gid } }, { "path", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, { "size", PAX_SIZE, PAX_TYPE_UINT, { .uint = pax_size } }, { "linkpath", PAX_SLINK_TARGET, PAX_TYPE_STRING, { .str = pax_slink } }, { "mtime", PAX_MTIME, PAX_TYPE_SINT, { .sint = pax_mtime } }, { "GNU.sparse.name", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, { "GNU.sparse.size", PAX_SPARSE_SIZE, PAX_TYPE_UINT, {.uint = pax_rsize} }, { "GNU.sparse.realsize", PAX_SPARSE_SIZE, PAX_TYPE_UINT, {.uint = pax_rsize} }, { "GNU.sparse.major", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, { .str = NULL } }, { "GNU.sparse.minor", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, { .str = NULL }}, { "SCHILY.xattr", 0, PAX_TYPE_PREFIXED_XATTR, { .xattr = pax_xattr_schily } }, { "LIBARCHIVE.xattr", 0, PAX_TYPE_PREFIXED_XATTR, { .xattr = pax_xattr_libarchive } }, { "GNU.sparse.map", 0, PAX_TYPE_CONST_STRING, { .cstr = pax_sparse_map } }, }; static const struct pax_handler_t *find_handler(const char *key) { size_t i, fieldlen; for (i = 0; i < sizeof(pax_fields) / sizeof(pax_fields[0]); ++i) { if (pax_fields[i].type == PAX_TYPE_PREFIXED_XATTR) { fieldlen = strlen(pax_fields[i].name); if (strncmp(key, pax_fields[i].name, fieldlen)) continue; if (key[fieldlen] != '.') continue; return pax_fields + i; } if (!strcmp(key, pax_fields[i].name)) return pax_fields + i; } return NULL; } static tar_xattr_t *mkxattr(const char *key, const char *value, size_t valuelen) { size_t keylen = strlen(key); tar_xattr_t *xattr; xattr = calloc(1, sizeof(*xattr) + keylen + 1 + valuelen + 1); if (xattr == NULL) return NULL; xattr->key = xattr->data; memcpy(xattr->key, key, keylen); xattr->key[keylen] = '\0'; xattr->value = (sqfs_u8 *)xattr->key + keylen + 1; memcpy(xattr->value, value, valuelen); xattr->value[valuelen] = '\0'; xattr->value_len = valuelen; return xattr; } static int apply_handler(tar_header_decoded_t *out, const struct pax_handler_t *field, const char *key, const char *value, size_t valuelen) { tar_xattr_t *xattr; sqfs_s64 s64val; sqfs_u64 uval; char *copy; switch (field->type) { case PAX_TYPE_SINT: if (value[0] == '-') { if (pax_read_decimal(value + 1, &uval)) return -1; s64val = -((sqfs_s64)uval); } else { if (pax_read_decimal(value, &uval)) return -1; s64val = (sqfs_s64)uval; } return field->cb.sint(out, s64val); case PAX_TYPE_UINT: if (pax_read_decimal(value, &uval)) return -1; return field->cb.uint(out, uval); case PAX_TYPE_CONST_STRING: return field->cb.cstr(out, value); case PAX_TYPE_STRING: copy = strdup(value); if (copy == NULL) { perror("processing pax header"); return -1; } if (field->cb.str(out, copy)) { free(copy); return -1; } break; case PAX_TYPE_PREFIXED_XATTR: xattr = mkxattr(key + strlen(field->name) + 1, value, valuelen); if (xattr == NULL) { perror("reading pax xattr field"); return -1; } if (field->cb.xattr(out, xattr)) { free(xattr); return -1; } break; default: break; } return 0; } 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 offset = 0, num_bytes = 0; const struct pax_handler_t *field; 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; key = ptr; while (*ptr != '\0' && *ptr != '=') ++ptr; if (ptr == key || *ptr != '=') goto fail_malformed; *(ptr++) = '\0'; value = ptr; field = find_handler(key); if (field != NULL) { if (apply_handler(out, field, key, value, len - (value - line) - 1)) { goto fail; } *set_by_pax |= field->flag; } else if (!strcmp(key, "GNU.sparse.offset")) { if (pax_read_decimal(value, &offset)) goto fail; } else if (!strcmp(key, "GNU.sparse.numbytes")) { if (pax_read_decimal(value, &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; } } } 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.3.1/lib/tar/read_header.c000066400000000000000000000155341461472204600210520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_header.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" #include #include 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->uid = field; } if (!(set_by_pax & PAX_GID)) { if (read_number(hdr->gid, sizeof(hdr->gid), &field)) return -1; out->gid = field; } if (!(set_by_pax & PAX_DEV_MAJ)) { if (read_number(hdr->devmajor, sizeof(hdr->devmajor), &field)) return -1; out->devno = makedev(field, minor(out->devno)); } if (!(set_by_pax & PAX_DEV_MIN)) { if (read_number(hdr->devminor, sizeof(hdr->devminor), &field)) return -1; out->devno = makedev(major(out->devno), 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->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->mode |= S_IFREG; break; case TAR_TYPE_LINK: out->is_hard_link = true; break; case TAR_TYPE_SLINK: out->mode = S_IFLNK | 0777; break; case TAR_TYPE_CHARDEV: out->mode |= S_IFCHR; break; case TAR_TYPE_BLOCKDEV: out->mode |= S_IFBLK; break; case TAR_TYPE_DIR: out->mode |= S_IFDIR; break; case TAR_TYPE_FIFO: out->mode |= S_IFIFO; break; default: out->unknown_record = true; break; } 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->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.3.1/lib/tar/read_sparse_map_new.c000066400000000000000000000041441461472204600226200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_sparse_map_new.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" #include #include #include 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.3.1/lib/tar/read_sparse_map_old.c000066400000000000000000000037551461472204600226140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * read_sparse_map_old.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" #include #include 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.3.1/lib/tar/record_to_memory.c000066400000000000000000000013171461472204600221710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * record_to_memory.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar/tar.h" #include "internal.h" #include 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.3.1/lib/tar/write_header.c000066400000000000000000000153151461472204600212660ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * write_header.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "internal.h" #include 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.3.1/lib/util/000077500000000000000000000000001461472204600166425ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/lib/util/Makemodule.am000066400000000000000000000026621461472204600212520ustar00rootroot00000000000000libutil_a_SOURCES = include/util/util.h include/util/str_table.h libutil_a_SOURCES += include/util/hash_table.h include/util/test.h libutil_a_SOURCES += lib/util/str_table.c lib/util/alloc.c libutil_a_SOURCES += lib/util/rbtree.c include/util/rbtree.h libutil_a_SOURCES += lib/util/array.c include/util/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/util/threadpool.h libutil_a_SOURCES += include/util/w32threadwrap.h libutil_a_SOURCES += lib/util/threadpool_serial.c libutil_a_SOURCES += lib/util/is_memory_zero.c libutil_a_SOURCES += lib/util/mkdir_p.c libutil_a_SOURCES += lib/util/canonicalize_name.c libutil_a_SOURCES += lib/util/filename_sane.c libutil_a_SOURCES += lib/util/source_date_epoch.c libutil_a_SOURCES += lib/util/file_cmp.c libutil_a_SOURCES += lib/util/hex_decode.c libutil_a_SOURCES += lib/util/base64_decode.c libutil_a_SOURCES += lib/util/fix_win32_filename.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/util/mempool.h endif noinst_LIBRARIES += libutil.a squashfs-tools-ng-1.3.1/lib/util/alloc.c000066400000000000000000000011721461472204600201010ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * alloc.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/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.3.1/lib/util/array.c000066400000000000000000000041471461472204600201320ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * array.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "util/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.3.1/lib/util/base64_decode.c000066400000000000000000000034641461472204600214040ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * base64_decode.c * * Copyright (C) 2022 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/test.h" #include static int base64_digit(int c) { if (isupper(c)) return c - 'A'; if (islower(c)) return c - 'a' + 26; if (isdigit(c)) return c - '0' + 52; if (c == '+') return 62; if (c == '/' || c == '-') return 63; return -1; } int base64_decode(const char *in, size_t in_len, sqfs_u8 *out, size_t *out_len) { int i1, i2, i3, i4; size_t count = 0; while (in_len >= 4) { i1 = base64_digit(*(in++)); i2 = base64_digit(*(in++)); i3 = *(in++); i4 = *(in++); in_len -= 4; if (i1 < 0 || i2 < 0 || count >= *out_len) goto fail; out[count++] = (i1 << 2) | (i2 >> 4); if (i3 == '=' || i3 == '_') { if ((i4 != '=' && i4 != '_') || in_len > 0) goto fail; break; } i3 = base64_digit(i3); if (i3 < 0 || count >= *out_len) goto fail; out[count++] = ((i2 & 0x0F) << 4) | (i3 >> 2); if (i4 == '=' || i4 == '_') { if (in_len > 0) goto fail; break; } i4 = base64_digit(i4); if (i4 < 0 || count >= *out_len) goto fail; out[count++] = ((i3 & 0x3) << 6) | i4; } /* libarchive has this bizarre bastardization of truncated base64 */ if (in_len > 0) { if (in_len == 1) goto fail; i1 = base64_digit(*(in++)); i2 = base64_digit(*(in++)); in_len -= 2; if (i1 < 0 || i2 < 0 || count >= *out_len) goto fail; out[count++] = (i1 << 2) | (i2 >> 4); if (in_len > 0) { i3 = *(in++); --in_len; if (i3 != '=' && i3 != '_') { i3 = base64_digit(i3); if (i3 < 0 || count >= *out_len) goto fail; out[count++] = ((i2 & 0x0F) << 4) | (i3 >> 2); } } } *out_len = count; return 0; fail: *out_len = 0; return -1; } squashfs-tools-ng-1.3.1/lib/util/canonicalize_name.c000066400000000000000000000017451461472204600224540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * canonicalize_name.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.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.3.1/lib/util/fast_urem_by_const.h000066400000000000000000000056531461472204600227110ustar00rootroot00000000000000/* * 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 defined(__SIZEOF_INT128__) && __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.3.1/lib/util/file_cmp.c000066400000000000000000000014541461472204600205700ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * file_cmp.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "sqfs/io.h" #include int check_file_range_equal(sqfs_file_t *file, void *scratch, size_t scratch_sz, sqfs_u64 loc_a, sqfs_u64 loc_b, sqfs_u64 size) { sqfs_u8 *ptr_a = scratch, *ptr_b = ptr_a + scratch_sz / 2; int ret; while (size > 0) { size_t diff = scratch_sz / 2; diff = (sqfs_u64)diff > size ? size : diff; ret = file->read_at(file, loc_a, ptr_a, diff); if (ret != 0) return ret; ret = file->read_at(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; } squashfs-tools-ng-1.3.1/lib/util/filename_sane.c000066400000000000000000000006371461472204600216020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * filename_sane.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include bool is_filename_sane(const char *name) { if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) return false; while (*name != '\0') { if (*name == '/') return false; ++name; } return true; } squashfs-tools-ng-1.3.1/lib/util/fix_win32_filename.c000066400000000000000000000064721461472204600224670ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * fix_win32_filename.c * * Copyright (C) 2024 David Oberhollenzer */ #include "util/util.h" #include #include #ifdef _MSC_VER #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif typedef struct { size_t used; size_t available; char buffer[]; } buffer_t; static buffer_t *buffer_append(buffer_t *buf, const char *data, size_t count) { size_t bufspace, needed; if (buf == NULL) { buf = calloc(1, sizeof(*buf) + 128); if (buf == NULL) return NULL; buf->used = 1; buf->available = 128; buf->buffer[0] = '\0'; } bufspace = buf->available; needed = buf->used + count; while (bufspace < needed) bufspace += 128; if (bufspace != buf->available) { void *new_buf = realloc(buf, sizeof(*buf) + bufspace); if (new_buf == NULL) { free(buf); return NULL; } buf = new_buf; buf->available = bufspace; } buf->used -= 1; memcpy(buf->buffer + buf->used, data, count); buf->used += count; buf->buffer[buf->used++] = '\0'; return buf; } 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 buffer_t *handle_component(buffer_t *buf, const char *comp, size_t len) { for (size_t i = 0; i < sizeof(bad_names) / sizeof(bad_names[0]); ++i) { size_t badlen = strlen(bad_names[i]); if (badlen > len || strncasecmp(comp, bad_names[i], badlen)) continue; while (badlen < len && comp[badlen] == '_') ++badlen; if (badlen == len) { buf = buffer_append(buf, comp, len); if (buf != NULL) buf = buffer_append(buf, "_", 1); return buf; } } while (len > 0) { sqfs_u8 value, rep[3]; size_t i = 0; for (i = 0; i < len; ++i) { if (comp[i] < 0x20 || comp[i] == 0x7F) break; if (comp[i] == '<' || comp[i] == '>' || comp[i] == ':') break; if (comp[i] == '|' || comp[i] == '?' || comp[i] == '*') break; if (comp[i] == '\\' || comp[i] == '\"') break; } if (i > 0) { buf = buffer_append(buf, comp, i); if (buf == NULL || i == len) break; } value = comp[i++]; comp += i; len -= i; rep[0] = 0xEF; rep[1] = 0x80 | ((value >> 6) & 0x3f); rep[2] = 0x80 | ( value & 0x3f); buf = buffer_append(buf, (const char *)rep, 3); if (buf == NULL) break; } return buf; } static buffer_t *handle_name(buffer_t *buf, const char *name, size_t len) { char *sep; while ((sep = memchr(name, '.', len)) != NULL) { buf = handle_component(buf, name, sep - name); if (buf == NULL) return NULL; buf = buffer_append(buf, ".", 1); if (buf == NULL) return NULL; len -= sep - name + 1; name = sep + 1; } return handle_component(buf, name, len); } char *fix_win32_filename(const char *path) { buffer_t *buf = NULL; char *sep, *out; size_t len; while ((sep = strchr(path, '/')) != NULL) { buf = handle_name(buf, path, sep - path); if (buf == NULL) return NULL; buf = buffer_append(buf, "/", 1); if (buf == NULL) return NULL; path = sep + 1; } buf = handle_name(buf, path, strlen(path)); if (buf == NULL) return NULL; len = buf->used; memmove(buf, buf->buffer, len); out = realloc(buf, len); if (out == NULL) out = (char *)buf; return out; } squashfs-tools-ng-1.3.1/lib/util/hash_table.c000066400000000000000000000314251461472204600211050ustar00rootroot00000000000000/* * 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 "util/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.3.1/lib/util/hex_decode.c000066400000000000000000000011741461472204600211000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * hex_decode.h * * Copyright (C) 2022 David Oberhollenzer */ #include "util/util.h" #include static sqfs_u8 xdigit(int in) { if (isupper(in)) return in - 'A' + 10; if (islower(in)) return in - 'a' + 10; return in - '0'; } int hex_decode(const char *in, size_t in_sz, sqfs_u8 *out, size_t out_sz) { while (out_sz > 0 && in_sz >= 2 && isxdigit(in[0]) && isxdigit(in[1])) { sqfs_u8 hi = xdigit(*(in++)); sqfs_u8 lo = xdigit(*(in++)); *(out++) = (hi << 4) | lo; in_sz -= 2; --out_sz; } return (in_sz > 0) ? -1 : 0; } squashfs-tools-ng-1.3.1/lib/util/is_memory_zero.c000066400000000000000000000016161461472204600220540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * is_memory_zero.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util/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.3.1/lib/util/mempool.c000066400000000000000000000101661461472204600204620ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * mempool.c * * Copyright (C) 2021 David Oberhollenzer */ #include "util/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; retry_pool: 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; } if (i == mem->bitmap_count) { it->obj_free = 0; goto retry_pool; } for (j = 0; j < (sizeof(it->bitmap[i]) * CHAR_BIT); ++j) { if (!(it->bitmap[i] & (1UL << j))) break; } if (j == (sizeof(it->bitmap[i]) * CHAR_BIT)) { it->obj_free = 0; goto retry_pool; } 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); it->obj_free += 1; } squashfs-tools-ng-1.3.1/lib/util/mkdir_p.c000066400000000000000000000054331461472204600204400ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * mkdir_p.c * * Copyright (C) 2019 David Oberhollenzer */ #include "util/util.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 = done ? end : (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.3.1/lib/util/rbtree.c000066400000000000000000000122601461472204600202720ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * rbtree.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "sqfs/error.h" #include "util/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.3.1/lib/util/source_date_epoch.c000066400000000000000000000014721461472204600224650ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * source_date_epoch.c * * Copyright (C) 2019 David Oberhollenzer */ #include "util/util.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.3.1/lib/util/str_table.c000066400000000000000000000073311461472204600207710ustar00rootroot00000000000000/* 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 "util/str_table.h" #include "util/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.3.1/lib/util/threadpool.c000066400000000000000000000174201461472204600211530ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool.c * * Copyright (C) 2021 David Oberhollenzer */ #include "util/threadpool.h" #include "util/util.h" #include #include #if defined(_WIN32) || defined(__WINDOWS__) #include "util/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.3.1/lib/util/threadpool_serial.c000066400000000000000000000056421461472204600225150ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool_serial.c * * Copyright (C) 2021 David Oberhollenzer */ #include "util/threadpool.h" #include "util/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.3.1/lib/util/xxhash.c000066400000000000000000000067471461472204600203270ustar00rootroot00000000000000/* * 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/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.3.1/licenses/000077500000000000000000000000001461472204600167245ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/licenses/0BSD.txt000066400000000000000000000011371461472204600201570ustar00rootroot00000000000000Permission 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.3.1/licenses/GPLv2.txt000066400000000000000000000432541461472204600203670ustar00rootroot00000000000000 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.3.1/licenses/GPLv3.txt000066400000000000000000001045151461472204600203660ustar00rootroot00000000000000 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.3.1/licenses/LGPLv3.txt000066400000000000000000000167441461472204600205100ustar00rootroot00000000000000 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.3.1/licenses/LZ4.txt000066400000000000000000000024371461472204600201040ustar00rootroot00000000000000LZ4 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.3.1/licenses/hash_table.txt000066400000000000000000000022371461472204600215630ustar00rootroot00000000000000Copyright © 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.3.1/licenses/musl.txt000066400000000000000000000140741461472204600204530ustar00rootroot00000000000000musl 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.3.1/licenses/xxhash.txt000066400000000000000000000025751461472204600210010ustar00rootroot00000000000000xxHash - 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.3.1/licenses/xz.txt000066400000000000000000000014001461472204600201210ustar00rootroot00000000000000liblzma 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.3.1/licenses/zstd.txt000066400000000000000000000027251461472204600204570ustar00rootroot00000000000000Copyright (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.3.1/m4/000077500000000000000000000000001461472204600154375ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/m4/ax_prog_doxygen.m4000066400000000000000000000500221461472204600210740ustar00rootroot00000000000000# =========================================================================== # 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.3.1/m4/ax_pthread.m4000066400000000000000000000540341461472204600200260ustar00rootroot00000000000000# =========================================================================== # 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 and PTHREAD_CXX to any special C compiler that is # needed for multi-threaded programs (defaults to the value of CC # respectively CXX otherwise). (This is necessary on e.g. AIX to use the # special cc_r/CC_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # CXX="$PTHREAD_CXX" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, 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. # Copyright (c) 2019 Marc Stevens # # 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 31 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on 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"]) AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) 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 with a "," contain both # C compiler flags (before ",") and linker flags (after ","). Other items # starting with a "-" are C compiler flags, and remaining items are # library names, except for "none" which indicates that we try without # any flags at all, and "pthread-config" which is a program returning # the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -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,-lpthread pthread $ax_pthread_flags" ;; esac # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) # Note that for GCC and Clang -pthread generally implies -lpthread, # except when -nostdlib is passed. # This is problematic using libtool to build C++ shared libraries with pthread: # [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 # [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 # [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 # To solve this, first try -pthread together with -lpthread for GCC AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) # Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first AS_IF([test "x$ax_pthread_clang" = "xyes"], [ax_pthread_flags="-pthread,-lpthread -pthread"]) # 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)"]) 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]) ;; *,*) PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $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 *some_global = NULL; static void routine(void *a) { /* To avoid any unused-parameter or unused-but-set-parameter warning. */ some_global = a; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); 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 # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then 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"]) AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) ], [ AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) ] ) ]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) AC_SUBST([PTHREAD_CXX]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then 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.3.1/m4/compiler.m4000066400000000000000000000030331461472204600175120ustar00rootroot00000000000000dnl 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.3.1/m4/zstd.m4000066400000000000000000000012531461472204600166660ustar00rootroot00000000000000AC_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.3.1/packages/000077500000000000000000000000001461472204600166755ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/packages/APKBUILD000066400000000000000000000017021461472204600200130ustar00rootroot00000000000000# 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.3.1/packages/Dockerfile000066400000000000000000000026341461472204600206740ustar00rootroot00000000000000# 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.3.1/packages/PKGBUILD000066400000000000000000000030351461472204600200220ustar00rootroot00000000000000# 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.3.1/packages/README.md000066400000000000000000000057161461472204600201650ustar00rootroot00000000000000# 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.3.1/packages/build000077500000000000000000000026431461472204600177270ustar00rootroot00000000000000#!/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.3.1/packages/build-all000077500000000000000000000007431461472204600204740ustar00rootroot00000000000000#!/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.3.1/packages/build-helper000077500000000000000000000065731461472204600212120ustar00rootroot00000000000000#!/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.3.1/packages/debian/000077500000000000000000000000001461472204600201175ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/packages/debian/changelog000066400000000000000000000055211461472204600217740ustar00rootroot00000000000000squashfs-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.3.1/packages/debian/clean000066400000000000000000000000131461472204600211160ustar00rootroot00000000000000config.log squashfs-tools-ng-1.3.1/packages/debian/control000066400000000000000000000102051461472204600215200ustar00rootroot00000000000000Source: 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.3.1/packages/debian/copyright000066400000000000000000000045711461472204600220610ustar00rootroot00000000000000Format: 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.3.1/packages/debian/libsquashfs-dev.install000066400000000000000000000001231461472204600246030ustar00rootroot00000000000000usr/include/ usr/lib/*/libsquashfs.a usr/lib/*/libsquashfs.so usr/lib/*/pkgconfig/ squashfs-tools-ng-1.3.1/packages/debian/libsquashfs1.install000066400000000000000000000000331461472204600241100ustar00rootroot00000000000000usr/lib/*/libsquashfs.so.* squashfs-tools-ng-1.3.1/packages/debian/rules000077500000000000000000000004161461472204600212000ustar00rootroot00000000000000#!/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.3.1/packages/debian/source/000077500000000000000000000000001461472204600214175ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/packages/debian/source/format000066400000000000000000000000141461472204600226250ustar00rootroot000000000000003.0 (quilt) squashfs-tools-ng-1.3.1/packages/debian/squashfs-tools-ng.install000066400000000000000000000000301461472204600250750ustar00rootroot00000000000000usr/bin/ usr/share/man/ squashfs-tools-ng-1.3.1/packages/debian/upstream/000077500000000000000000000000001461472204600217575ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/packages/debian/upstream/metadata000066400000000000000000000004231461472204600234610ustar00rootroot00000000000000--- 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.3.1/packages/debian/watch000066400000000000000000000001631461472204600211500ustar00rootroot00000000000000version=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.3.1/packages/squashfs-tools-ng.spec000066400000000000000000000105331461472204600231500ustar00rootroot00000000000000## 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.3.1/scripts/000077500000000000000000000000001461472204600166065ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/scripts/coverity.sh000077500000000000000000000020571461472204600210150ustar00rootroot00000000000000#!/bin/sh COVERITY_PATH="$HOME/Downloads/cov-analysis-linux64-2023.6.2" 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" if [ $# -eq 1 ]; then case "$1" in --32bit) ./autogen.sh ./configure CFLAGS="-m32" CXXFLAGS="-m32" LDFLAGS="-m32" DESCRIPTION="$DESCRIPTION-32bit" ;; *) echo "Unknown option `$1`" >&2 exit 1 ;; esac else ./autogen.sh ./configure fi 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.3.1/scripts/lib_syms_sane.sh000077500000000000000000000014061461472204600217750ustar00rootroot00000000000000#!/bin/bash SPC="[[:space:]]\+" HEXNUM="[0-9a-fA-F]\+" NUM="[0-9]\+" WINE_PATTERN="^${SPC}${HEXNUM}${SPC}${NUM}${SPC}" ELF_PATTERN="^${SPC}${NUM}:${SPC}${HEXNUM}${SPC}${NUM}${SPC}" symbols_from_file() { case "$1" in *.dll) winedump -j export "$1" | grep "$WINE_PATTERN" | \ sed "s/$WINE_PATTERN//g" ;; *) readelf -TW -s "$1" | grep "$ELF_PATTERN" | grep -v "UND" | \ sed "s/$ELF_PATTERN//g" | grep -o "${NUM}${SPC}.\+$" | \ sed "s/^${NUM}${SPC}//g" esac } symbols_from_file "$1" | grep -q "^sqfs_.*" if [ "$?" != "0" ]; then symbols_from_file "$1" echo "ERROR: No symbols with sqfs_ prefix found!" exit 1 fi symbols_from_file "$1" | grep -v "^sqfs_" if [ "$?" != "1" ]; then echo "ERROR: Symbols without sqfs_ prefix found!" exit 1 fi exit 0 squashfs-tools-ng-1.3.1/scripts/mksrcrelease.sh000077500000000000000000000064771461472204600216430ustar00rootroot00000000000000#!/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" changed=$(askyesno "Have any public interfaces been changed") added=$(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.3.1/scripts/mkwinbins.sh000077500000000000000000000233741461472204600211570ustar00rootroot00000000000000#!/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" ;; *) tar -xf "$PKG_TAR" ;; esac } } ################################## get zlib ################################## PKG_DIR="zlib-1.2.12" PKG_TAR="${PKG_DIR}.tar.xz" PKG_HASH="7db46b8d7726232a621befaab4a1c870f00a90805511c0e0090441dac57def18" download mkdir -p "$W32_DIR/bin" "$W32_DIR/include" "$W32_DIR/lib/pkgconfig" mkdir -p "$W64_DIR/bin" "$W64_DIR/include" "$W64_DIR/lib/pkgconfig" cp "$PKG_DIR/zlib.h" "$PKG_DIR/zconf.h" "$W32_DIR/include" cp "$PKG_DIR/zlib.h" "$PKG_DIR/zconf.h" "$W64_DIR/include" pushd "$PKG_DIR" obj="adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o" obj="$obj gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o" obj="$obj uncompr.o zutil.o" for outfile in $obj; do infile="$(basename $outfile .o).c" ${W32_PREFIX}-gcc -O3 -c "$infile" -o "$outfile" done ${W32_PREFIX}-windres --define GCC_WINDRES -o zlibrc.o win32/zlib1.rc ${W32_PREFIX}-gcc -shared -Wl,--out-implib,libz.dll.a -o zlib1.dll \ win32/zlib.def $obj zlibrc.o ${W32_PREFIX}-strip zlib1.dll ${W32_PREFIX}-ar rcs libz.a $obj rm *.o mv zlib1.dll "$W32_DIR/bin" mv libz.a libz.dll.a "$W32_DIR/lib" cat > "$W32_DIR/lib/pkgconfig/zlib.pc" <<_EOF prefix=$W32_DIR libdir=$W32_DIR/lib sharedlibdir=$W32_DIR/bin includedir=$W32_DIR/include Name: zlib Description: zlib compression library Version: 1.2.12 Libs: -L$W32_DIR/lib -L$W32_DIR/bin -lz Cflags: -I$W32_DIR/include _EOF for outfile in $obj; do infile="$(basename $outfile .o).c" ${W64_PREFIX}-gcc -O3 -c "$infile" -o "$outfile" done ${W64_PREFIX}-windres --define GCC_WINDRES -o zlibrc.o win32/zlib1.rc ${W64_PREFIX}-gcc -shared -Wl,--out-implib,libz.dll.a -o zlib1.dll \ win32/zlib.def $obj zlibrc.o ${W64_PREFIX}-strip zlib1.dll ${W64_PREFIX}-ar rcs libz.a $obj rm *.o mv zlib1.dll "$W64_DIR/bin" mv libz.a libz.dll.a "$W64_DIR/lib" cat > "$W64_DIR/lib/pkgconfig/zlib.pc" <<_EOF prefix=$W64_DIR libdir=$W64_DIR/lib sharedlibdir=$W64_DIR/bin includedir=$W64_DIR/include Name: zlib Description: zlib compression library Version: 1.2.12 Libs: -L$W64_DIR/lib -L$W64_DIR/bin -lz Cflags: -I$W64_DIR/include _EOF popd ################################### 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.5.2-win32" PKG_TAR="${PKG_DIR}.zip" PKG_HASH="d0a5361401607f2f85706989fbc69ebb760c34d2337e72573a303433898c3196" 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.5.2 Libs: -L$W32_DIR/lib -lzstd Cflags: -I$W32_DIR/include _EOF PKG_DIR="zstd-v1.5.2-win64" PKG_TAR="${PKG_DIR}.zip" PKG_HASH="2faf3b9061b731f8d37c5b3bb4a6f08be89af43f62bdd93f784a85af7d7c4f5b" 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.5.2 Libs: -L$W64_DIR/lib -lzstd Cflags: -I$W64_DIR/include _EOF ################################## get lz4 ################################## PKG_DIR="lz4-1.9.4" PKG_TAR="${PKG_DIR}.tar.gz" PKG_HASH="0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b" download pushd "$PKG_DIR/lib" make TARGET_OS=Windows CC=${W32_PREFIX}-gcc WINDRES=${W32_PREFIX}-windres mv dll/*.lib "$W32_DIR/lib" mv dll/*.dll "$W32_DIR/bin" cp *.h "$W32_DIR/include" rm *.o *.a make TARGET_OS=Windows CC=${W64_PREFIX}-gcc WINDRES=${W64_PREFIX}-windres mv dll/*.lib "$W64_DIR/lib" mv dll/*.dll "$W64_DIR/bin" cp *.h "$W64_DIR/include" rm *.o *.a popd cat > "$W32_DIR/lib/pkgconfig/liblz4.pc" <<_EOF prefix=$W32_DIR libdir=$W32_DIR/lib includedir=$W32_DIR/include Name: lz4 Description: fast lossless compression algorithm library URL: https://lz4.github.io/lz4/ Version: 1.9.4 Libs: -L$W32_DIR/lib -llz4 Cflags: -I$W32_DIR/include _EOF cat > "$W64_DIR/lib/pkgconfig/liblz4.pc" <<_EOF prefix=$W64_DIR libdir=$W64_DIR/lib includedir=$W64_DIR/include Name: lz4 Description: fast lossless compression algorithm library URL: https://lz4.github.io/lz4/ Version: 1.9.4 Libs: -L$W64_DIR/lib -llz4 Cflags: -I$W64_DIR/include _EOF ################################ build 32 bit ################################ export PKG_CONFIG_PATH="" export PKG_CONFIG_LIBDIR="$W32_DIR/lib/pkgconfig" export PKG_CONFIG_SYSROOT_DIR="$W32_DIR" ./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" cp "$W32_DIR/bin/"*.dll . make 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" make clean make make install-strip ################################ build 64 bit ################################ export PKG_CONFIG_PATH="" export PKG_CONFIG_LIBDIR="$W64_DIR/lib/pkgconfig" export PKG_CONFIG_SYSROOT_DIR="$W64_DIR" ./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" make clean cp "$W64_DIR/bin/"*.dll . make 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" make clean make 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.3.1/tests/000077500000000000000000000000001461472204600162615ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/000077500000000000000000000000001461472204600206105ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/Makemodule.am000066400000000000000000000051341461472204600232150ustar00rootroot00000000000000GENDATADIR=$(top_srcdir)/tests/gensquashfs test_filemap_xattr_SOURCES = tests/gensquashfs/filemap_xattr.c \ bin/gensquashfs/filemap_xattr.c \ bin/gensquashfs/mkfs.h test_filemap_xattr_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs test_filemap_xattr_CPPFLAGS += -DTESTPATH=$(GENDATADIR)/xattr1.txt test_filemap_xattr_LDADD = libsquashfs.la libfstree.a libutil.a test_filemap_xattr_LDADD += libio.a libcompat.a test_fstree_from_file_SOURCES = tests/gensquashfs/fstree_from_file.c \ bin/gensquashfs/fstree_from_file.c \ bin/gensquashfs/fstree_from_dir.c \ bin/gensquashfs/mkfs.h test_fstree_from_file_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs test_fstree_from_file_CPPFLAGS += -DTESTPATH=$(GENDATADIR)/fstree1.txt test_fstree_from_file_LDADD = libfstree.a libio.a libutil.a libcompat.a test_fstree_glob1_SOURCES = tests/gensquashfs/fstree_glob1.c \ bin/gensquashfs/fstree_from_file.c \ bin/gensquashfs/fstree_from_dir.c \ bin/gensquashfs/mkfs.h test_fstree_glob1_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs test_fstree_glob1_CPPFLAGS += -DTESTPATH=$(GENDATADIR) test_fstree_glob1_LDADD = libfstree.a libio.a libutil.a libcompat.a test_fstree_from_dir_SOURCES = tests/gensquashfs/fstree_from_dir.c \ bin/gensquashfs/fstree_from_dir.c \ bin/gensquashfs/mkfs.h test_fstree_from_dir_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs test_fstree_from_dir_CPPFLAGS += -DTESTPATH=$(GENDATADIR)/testdir test_fstree_from_dir_LDADD = libfstree.a libutil.a libcompat.a test_sort_file_SOURCES = tests/gensquashfs/sort_file.c \ bin/gensquashfs/fstree_from_file.c \ bin/gensquashfs/fstree_from_dir.c \ bin/gensquashfs/sort_by_file.c \ bin/gensquashfs/mkfs.h test_sort_file_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs test_sort_file_LDADD = libfstree.a libio.a libutil.a libcompat.a fstree_fuzz_SOURCES = tests/gensquashfs/fstree_fuzz.c \ bin/gensquashfs/fstree_from_file.c \ bin/gensquashfs/fstree_from_dir.c \ bin/gensquashfs/mkfs.h fstree_fuzz_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs fstree_fuzz_LDADD = libfstree.a libio.a libutil.a libcompat.a GENSQUASHFS_TESTS = \ test_filemap_xattr test_fstree_from_file test_fstree_from_dir \ test_fstree_glob1 test_sort_file if BUILD_TOOLS noinst_PROGRAMS += fstree_fuzz check_PROGRAMS += $(GENSQUASHFS_TESTS) TESTS += $(GENSQUASHFS_TESTS) endif EXTRA_DIST += $(GENDATADIR)/xattr1.txt $(GENDATADIR)/fstree1.txt EXTRA_DIST += $(GENDATADIR)/fstree_glob1.txt $(GENDATADIR)/fstree_glob2.txt EXTRA_DIST += $(GENDATADIR)/fstree_glob3.txt EXTRA_DIST += $(GENDATADIR)/testdir squashfs-tools-ng-1.3.1/tests/gensquashfs/filemap_xattr.c000066400000000000000000000045201461472204600236140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * filemap_xattr.c * * Copyright (C) 2022 David Oberhollenzer */ #include "config.h" #include "util/test.h" #include "mkfs.h" static const char *dev_selinux = "system_u:object_r:device_t:s0"; static const char *zero_selinux = "system_u:object_r:zero_device_t:s0"; static const char *rfkill_selinux = "system_u:object_r:wireless_device_t:s0"; static const sqfs_u8 rfkill_acl[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x06, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x06, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff }; int main(int argc, char **argv) { struct XattrMapPattern *pat; struct XattrMapEntry *ent; struct XattrMap *map; int ret; (void)argc; (void)argv; map = xattr_open_map_file(TEST_PATH); TEST_NOT_NULL(map); /* the third pattern */ pat = map->patterns; TEST_NOT_NULL(pat); TEST_STR_EQUAL(pat->path, "dev/rfkill"); ent = pat->entries; TEST_NOT_NULL(ent); TEST_STR_EQUAL(ent->key, "system.posix_acl_access"); TEST_EQUAL_UI(ent->value_len, sizeof(rfkill_acl)); ret = memcmp(ent->value, rfkill_acl, ent->value_len); TEST_EQUAL_I(ret, 0); ent = ent->next; TEST_NOT_NULL(ent); TEST_STR_EQUAL(ent->key, "security.selinux"); TEST_EQUAL_UI(ent->value_len, strlen(rfkill_selinux)); ret = memcmp(ent->value, rfkill_selinux, ent->value_len); TEST_EQUAL_I(ret, 0); ent = ent->next; TEST_NULL(ent); /* the second pattern */ pat = pat->next; TEST_NOT_NULL(pat); TEST_STR_EQUAL(pat->path, "dev/zero"); ent = pat->entries; TEST_NOT_NULL(ent); TEST_STR_EQUAL(ent->key, "security.selinux"); TEST_EQUAL_UI(ent->value_len, strlen(zero_selinux)); ret = memcmp(ent->value, zero_selinux, ent->value_len); TEST_EQUAL_I(ret, 0); ent = ent->next; TEST_NULL(ent); /* the first pattern */ pat = pat->next; TEST_NOT_NULL(pat); TEST_STR_EQUAL(pat->path, "dev"); ent = pat->entries; TEST_NOT_NULL(ent); TEST_STR_EQUAL(ent->key, "security.selinux"); TEST_EQUAL_UI(ent->value_len, strlen(dev_selinux)); ret = memcmp(ent->value, dev_selinux, ent->value_len); TEST_EQUAL_I(ret, 0); ent = ent->next; TEST_NULL(ent); /* no more patterns */ pat = pat->next; TEST_NULL(pat); xattr_close_map_file(map); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/gensquashfs/fstree1.txt000066400000000000000000000003771461472204600227310ustar00rootroot00000000000000# 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.3.1/tests/gensquashfs/fstree_from_dir.c000066400000000000000000000075641461472204600241410ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/test.h" #include "mkfs.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, "dira"); 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, "file_a0"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "file_a1"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "file_a2"); 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, "dirb"); 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, "file_b0"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "file_b1"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "file_b2"); 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, "dirc"); 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, "file_c0"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "file_c1"); TEST_ASSERT(S_ISREG(m->mode)); TEST_ASSERT(m->parent == n); m = m->next; TEST_NOT_NULL(m); TEST_STR_EQUAL(m->name, "file_c2"); 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(int argc, char **argv) { struct stat sb; tree_node_t *n; fstree_t fs; (void)argc; (void)argv; /* 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.3.1/tests/gensquashfs/fstree_from_file.c000066400000000000000000000047121461472204600242720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_from_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/test.h" #include "mkfs.h" int main(int argc, char **argv) { tree_node_t *n; fstree_t fs; (void)argc; (void)argv; 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.3.1/tests/gensquashfs/fstree_fuzz.c000066400000000000000000000010731461472204600233230ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_fuzz.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "mkfs.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.3.1/tests/gensquashfs/fstree_glob1.c000066400000000000000000000124311461472204600233310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_glob1.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util/test.h" #include "mkfs.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(int argc, char **argv) { fstree_t fs; int ret; (void)argc; (void)argv; /* 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.3.1/tests/gensquashfs/fstree_glob1.txt000066400000000000000000000001131461472204600237200ustar00rootroot00000000000000dir /tarcorpus 0755 0 0 glob /tarcorpus 0755 0 0 -type d -- ../libtar/data squashfs-tools-ng-1.3.1/tests/gensquashfs/fstree_glob2.txt000066400000000000000000000002151461472204600237240ustar00rootroot00000000000000dir /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.3.1/tests/gensquashfs/fstree_glob3.txt000066400000000000000000000001431461472204600237250ustar00rootroot00000000000000glob / 0755 0 0 -type d ../libtar/data glob / 0644 0 0 -type f -name "*gnu*.tar" -- ../libtar/data squashfs-tools-ng-1.3.1/tests/gensquashfs/sort_file.c000066400000000000000000000077371461472204600227600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * sort_file.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "sqfs/block.h" #include "util/test.h" #include "util/util.h" #include "mkfs.h" static const char *listing = "dir /bin 0755 0 0\n" "dir /lib 0755 0 0\n" "dir /usr 0755 0 0\n" "dir /usr/share 0755 0 0\n" "\n" "file /bin/chown 0755 0 0\n" "file /bin/ls 0755 0 0\n" "file /bin/chmod 0755 0 0\n" "file /bin/dir 0755 0 0\n" "file /bin/cp 0755 0 0\n" "file /bin/dd 0755 0 0\n" "file /bin/ln 0755 0 0\n" "file /bin/mkdir 0755 0 0\n" "file /bin/mknod 0755 0 0\n" "\n" "file /lib/libssl.so 0755 0 0\n" "file /lib/libfoobar.so 0755 0 0\n" "file /lib/libwhatever.so 0755 0 0\n" "\n" "file /usr/share/bla.txt 0644 0 0\n"; static const char *sort_file = "# Blockwise reverse the order of the /bin files\n" " 10 [glob] /bin/mk*\n" " 20 [glob] /bin/ch*\n" " 30 [glob] /bin/d*\n" " 40 /bin/cp\n" " 50 [glob] /bin/*\n" "\n" "# Make this file appear first\n" " -10000 [dont_compress,dont_fragment,align] /usr/share/bla.txt"; static const char *initial_order[] = { "bin/chmod", "bin/chown", "bin/cp", "bin/dd", "bin/dir", "bin/ln", "bin/ls", "bin/mkdir", "bin/mknod", "lib/libfoobar.so", "lib/libssl.so", "lib/libwhatever.so", "usr/share/bla.txt", }; static const char *after_sort_order[] = { "usr/share/bla.txt", "lib/libfoobar.so", "lib/libssl.so", "lib/libwhatever.so", "bin/mkdir", "bin/mknod", "bin/chmod", "bin/chown", "bin/dd", "bin/dir", "bin/cp", "bin/ln", "bin/ls", }; static sqfs_s64 priorities[] = { -10000, 0, 0, 0, 10, 10, 20, 20, 30, 30, 40, 50, 50, }; static int flags[] = { SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN | SQFS_BLK_DONT_FRAGMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /*****************************************************************************/ static sqfs_u8 temp_buffer[2048]; static const char *input_file = NULL; static void destroy_noop(sqfs_object_t *obj) { (void)obj; } static int memfile_load(istream_t *strm) { strcpy((char *)temp_buffer, input_file); strm->eof = true; strm->buffer_used = strlen(input_file); return 0; } static const char *get_filename(istream_t *strm) { (void)strm; return "memstream"; } static istream_t memstream = { .base = { .destroy = destroy_noop, }, .buffer_used = 0, .buffer_offset = 0, .eof = false, .buffer = temp_buffer, .precache = memfile_load, .get_filename = get_filename, }; /*****************************************************************************/ int main(int argc, char **argv) { file_info_t *fi; fstree_t fs; size_t i; (void)argc; (void)argv; input_file = listing; memstream.buffer_used = 0; memstream.buffer_offset = 0; memstream.eof = false; TEST_ASSERT(fstree_init(&fs, NULL) == 0); TEST_ASSERT(fstree_from_file_stream(&fs, &memstream, NULL) == 0); fstree_post_process(&fs); for (i = 0, fi = fs.files; fi != NULL; fi = fi->next, ++i) { tree_node_t *n = container_of(fi, tree_node_t, data.file); char *path = fstree_get_path(n); int ret; TEST_NOT_NULL(path); ret = canonicalize_name(path); TEST_EQUAL_I(ret, 0); TEST_STR_EQUAL(initial_order[i], path); free(path); TEST_EQUAL_I(fi->priority, 0); TEST_EQUAL_I(fi->flags, 0); } TEST_EQUAL_UI(i, sizeof(initial_order) / sizeof(initial_order[0])); input_file = sort_file; memstream.buffer_used = 0; memstream.buffer_offset = 0; memstream.eof = false; TEST_ASSERT(fstree_sort_files(&fs, &memstream) == 0); for (i = 0, fi = fs.files; fi != NULL; fi = fi->next, ++i) { tree_node_t *n = container_of(fi, tree_node_t, data.file); char *path = fstree_get_path(n); int ret; TEST_NOT_NULL(path); ret = canonicalize_name(path); TEST_EQUAL_I(ret, 0); TEST_STR_EQUAL(after_sort_order[i], path); free(path); TEST_EQUAL_I(fi->priority, priorities[i]); TEST_EQUAL_I(fi->flags, flags[i]); } TEST_EQUAL_UI(i, sizeof(after_sort_order) / sizeof(after_sort_order[0])); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/000077500000000000000000000000001461472204600222665ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dira/000077500000000000000000000000001461472204600232055ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dira/file_a0000066400000000000000000000000001461472204600244150ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dira/file_a1000066400000000000000000000000001461472204600244160ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dira/file_a2000066400000000000000000000000001461472204600244170ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirb/000077500000000000000000000000001461472204600232065ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirb/file_b0000066400000000000000000000000001461472204600244170ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirb/file_b1000066400000000000000000000000001461472204600244200ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirb/file_b2000066400000000000000000000000001461472204600244210ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirc/000077500000000000000000000000001461472204600232075ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirc/file_c0000066400000000000000000000000001461472204600244210ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirc/file_c1000066400000000000000000000000001461472204600244220ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/testdir/dirc/file_c2000066400000000000000000000000001461472204600244230ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/gensquashfs/xattr1.txt000066400000000000000000000004531461472204600225760ustar00rootroot00000000000000# file: dev/ security.selinux="system_u:object_r:device_t:s0" # file: dev/zero security.selinux="system_u:object_r:zero_device_t:s0" # file: dev/rfkill security.selinux="system_u:object_r:wireless_device_t:s0" system.posix_acl_access=0sAgAAAAEABgD/////AgAGAOgDAAAEAAYA/////xAABgD/////IAAEAP////8= squashfs-tools-ng-1.3.1/tests/libfstree/000077500000000000000000000000001461472204600202405ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libfstree/Makemodule.am000066400000000000000000000026121461472204600226430ustar00rootroot00000000000000FSTDATADIR=$(top_srcdir)/tests/libfstree test_mknode_simple_SOURCES = tests/libfstree/mknode_simple.c test_mknode_simple_LDADD = libfstree.a libcompat.a test_mknode_slink_SOURCES = tests/libfstree/mknode_slink.c test_mknode_slink_LDADD = libfstree.a libcompat.a test_mknode_reg_SOURCES = tests/libfstree/mknode_reg.c test_mknode_reg_LDADD = libfstree.a libcompat.a test_mknode_dir_SOURCES = tests/libfstree/mknode_dir.c test_mknode_dir_LDADD = libfstree.a libcompat.a test_gen_inode_numbers_SOURCES = tests/libfstree/gen_inode_numbers.c test_gen_inode_numbers_LDADD = libfstree.a libutil.a libcompat.a test_add_by_path_SOURCES = tests/libfstree/add_by_path.c test_add_by_path_LDADD = libfstree.a libutil.a libcompat.a test_get_path_SOURCES = tests/libfstree/get_path.c test_get_path_LDADD = libfstree.a libutil.a libcompat.a test_fstree_sort_SOURCES = tests/libfstree/fstree_sort.c test_fstree_sort_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/lib/fstree test_fstree_sort_LDADD = libfstree.a libio.a libutil.a libcompat.a test_fstree_init_SOURCES = tests/libfstree/fstree_init.c test_fstree_init_LDADD = libfstree.a libio.a libutil.a libcompat.a FSTREE_TESTS = \ 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_init if BUILD_TOOLS check_PROGRAMS += $(FSTREE_TESTS) TESTS += $(FSTREE_TESTS) endif squashfs-tools-ng-1.3.1/tests/libfstree/add_by_path.c000066400000000000000000000070701461472204600226460ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * add_by_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *a, *b; struct stat sb; fstree_t fs; char *opts; (void)argc; (void)argv; 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.3.1/tests/libfstree/fstree_init.c000066400000000000000000000022631461472204600227220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_init.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" #include "util/util.h" int main(int argc, char **argv) { fstree_t fs; char *str; (void)argc; (void)argv; 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.3.1/tests/libfstree/fstree_sort.c000066400000000000000000000040061461472204600227430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fstree_sort.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *a, *b, *c, *d; struct stat sb; fstree_t fs; int ret; (void)argc; (void)argv; memset(&sb, 0, sizeof(sb)); sb.st_mode = S_IFBLK | 0600; sb.st_rdev = 1337; /* in order */ ret = fstree_init(&fs, NULL); TEST_EQUAL_I(ret, 0); a = fstree_mknode(fs.root, "a", 1, NULL, &sb); TEST_NOT_NULL(a); TEST_ASSERT(fs.root->data.dir.children == a); TEST_NULL(a->next); b = fstree_mknode(fs.root, "b", 1, NULL, &sb); TEST_NOT_NULL(a); TEST_ASSERT(fs.root->data.dir.children == a); TEST_ASSERT(a->next == b); TEST_NULL(b->next); c = fstree_mknode(fs.root, "c", 1, NULL, &sb); TEST_NOT_NULL(c); TEST_ASSERT(fs.root->data.dir.children == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_NULL(c->next); d = fstree_mknode(fs.root, "d", 1, NULL, &sb); TEST_NOT_NULL(d); TEST_ASSERT(fs.root->data.dir.children == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); fstree_cleanup(&fs); /* out-of-order */ ret = fstree_init(&fs, NULL); TEST_EQUAL_I(ret, 0); d = fstree_mknode(fs.root, "d", 1, NULL, &sb); TEST_NOT_NULL(d); TEST_ASSERT(fs.root->data.dir.children == d); TEST_NULL(d->next); c = fstree_mknode(fs.root, "c", 1, NULL, &sb); TEST_NOT_NULL(c); TEST_ASSERT(fs.root->data.dir.children == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); b = fstree_mknode(fs.root, "b", 1, NULL, &sb); TEST_NOT_NULL(b); TEST_ASSERT(fs.root->data.dir.children == b); TEST_ASSERT(b->next == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); a = fstree_mknode(fs.root, "a", 1, NULL, &sb); TEST_NOT_NULL(a); TEST_ASSERT(fs.root->data.dir.children == a); TEST_ASSERT(a->next == b); TEST_ASSERT(b->next == c); TEST_ASSERT(c->next == d); TEST_NULL(d->next); fstree_cleanup(&fs); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libfstree/gen_inode_numbers.c000066400000000000000000000040541461472204600240710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * gen_inode_table.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/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(int argc, char **argv) { tree_node_t *a, *b, *c; fstree_t fs; (void)argc; (void)argv; // 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.3.1/tests/libfstree/get_path.c000066400000000000000000000023041461472204600221760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_path.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *a, *b, *c, *d; struct stat sb; fstree_t fs; char *str; (void)argc; (void)argv; 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.3.1/tests/libfstree/mknode_dir.c000066400000000000000000000027701461472204600225250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_dir.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *root, *a, *b; struct stat sb; fstree_t fs; (void)argc; (void)argv; 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 == a); TEST_ASSERT(a->next == b); TEST_EQUAL_UI(root->link_count, 4); TEST_NULL(b->next); TEST_NULL(root->parent); TEST_NULL(root->next); free(root); free(a); free(b); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libfstree/mknode_reg.c000066400000000000000000000020141461472204600225130ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_reg.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *node; struct stat sb; fstree_t fs; (void)argc; (void)argv; 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.3.1/tests/libfstree/mknode_simple.c000066400000000000000000000050551461472204600232370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_simple.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *node; struct stat sb; fstree_t fs; (void)argc; (void)argv; 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.3.1/tests/libfstree/mknode_slink.c000066400000000000000000000027471461472204600230730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * mknode_slink.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "fstree.h" #include "util/test.h" int main(int argc, char **argv) { tree_node_t *node; struct stat sb; fstree_t fs; (void)argc; (void)argv; 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.3.1/tests/libio/000077500000000000000000000000001461472204600173575ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libio/Makemodule.am000066400000000000000000000044361461472204600217700ustar00rootroot00000000000000test_get_line_SOURCES = tests/libio/get_line.c test_get_line_LDADD = libio.a libcompat.a test_get_line_CPPFLAGS = $(AM_CPPFLAGS) test_get_line_CPPFLAGS += -DTESTFILE=$(top_srcdir)/tests/libio/get_line.txt test_xfrm_bzip2_SOURCES = tests/libio/uncompress.c test_xfrm_bzip2_LDADD = libio.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/libio/uncompress.c test_xfrm_bzip22_LDADD = libio.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/libio/uncompress.c test_xfrm_xz_LDADD = libio.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/libio/uncompress.c test_xfrm_xz2_LDADD = libio.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/libio/uncompress.c test_xfrm_gzip_LDADD = libio.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/libio/uncompress.c test_xfrm_zstd_LDADD = libio.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/libio/uncompress.c test_xfrm_zstd2_LDADD = libio.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 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/libio/get_line.txt squashfs-tools-ng-1.3.1/tests/libio/get_line.c000066400000000000000000000061311461472204600213120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_line.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "util/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(int argc, char **argv) { (void)argc; (void)argv; 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.3.1/tests/libio/get_line.txt000066400000000000000000000001021461472204600216770ustar00rootroot00000000000000 The quick brown fox jumps over the lazy dog squashfs-tools-ng-1.3.1/tests/libio/uncompress.c000066400000000000000000000430231461472204600217230ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * uncompress.c * * Copyright (C) 2021 David Oberhollenzer */ #include "io/istream.h" #include "io/xfrm.h" #include "util/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 IO_COMPRESSOR_BZIP2 #elif defined(TEST_XZ) || defined(TEST_XZ2) #define COMP_NAME "xz" #define COMP_ID IO_COMPRESSOR_XZ #elif defined(TEST_GZIP) #define COMP_NAME "gzip" #define COMP_ID IO_COMPRESSOR_GZIP #elif defined(TEST_ZSTD) || defined(TEST_ZSTD2) #define COMP_NAME "zstd" #define COMP_ID IO_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(int argc, char **argv) { char buffer[2 * (sizeof(orig) / sizeof(orig[0]))]; const char *name; istream_t *xfrm; size_t orig_sz; int ret; (void)argc; (void)argv; /* XXX: null terminator not included in the compressed blob */ orig_sz = (sizeof(orig) / sizeof(orig[0])) - 1; /* generic API test */ TEST_ASSERT(io_compressor_exists(COMP_ID)); name = io_compressor_name_from_id(COMP_ID); TEST_STR_EQUAL(name, COMP_NAME); ret = io_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.3.1/tests/libsqfs/000077500000000000000000000000001461472204600177245ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libsqfs/Makemodule.am000066400000000000000000000013301461472204600223230ustar00rootroot00000000000000test_abi_SOURCES = tests/libsqfs/abi.c test_abi_LDADD = libsquashfs.la libcompat.a test_table_SOURCES = tests/libsqfs/table.c test_table_LDADD = libsquashfs.la libcompat.a test_xattr_writer_SOURCES = tests/libsqfs/xattr_writer.c test_xattr_writer_LDADD = libsquashfs.la libcompat.a xattr_benchmark_SOURCES = tests/libsqfs/xattr_benchmark.c xattr_benchmark_LDADD = libcommon.a libsquashfs.la libcompat.a test_get_node_path_SOURCES = tests/libsqfs/get_node_path.c test_get_node_path_LDADD = libcommon.a libsquashfs.la libcompat.a LIBSQFS_TESTS = \ test_abi test_table test_xattr_writer test_get_node_path if BUILD_TOOLS noinst_PROGRAMS += xattr_benchmark endif check_PROGRAMS += $(LIBSQFS_TESTS) TESTS += $(LIBSQFS_TESTS) squashfs-tools-ng-1.3.1/tests/libsqfs/abi.c000066400000000000000000000130261461472204600206250ustar00rootroot00000000000000/* 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 "util/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))); if (__alignof__(sqfs_compressor_config_t) == __alignof__(sqfs_u32)) { TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, opt), (3 * sizeof(sqfs_u32))); } else if (__alignof__(sqfs_compressor_config_t) == __alignof__(sqfs_u64)) { 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; size_t off; 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)); if (__alignof__(stats) == __alignof__(sqfs_u32)) { TEST_ASSERT(sizeof(stats) >= (sizeof(sqfs_u32) + 7 * sizeof(sqfs_u64))); } else if (__alignof__(stats) == __alignof__(sqfs_u64)) { TEST_ASSERT(sizeof(stats) >= (8 * sizeof(sqfs_u64))); } TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, size), 0); if (sizeof(size_t) < sizeof(sqfs_u64) && (__alignof__(sqfs_block_processor_stats_t) == __alignof__(sqfs_u64))) { off = sizeof(sqfs_u64); } else { off = sizeof(stats.size); } TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, input_bytes_read), off); off += sizeof(sqfs_u64); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, output_bytes_generated), off); off += sizeof(sqfs_u64); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, data_block_count), off); off += sizeof(sqfs_u64); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, frag_block_count), off); off += sizeof(sqfs_u64); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, sparse_block_count), off); off += sizeof(sqfs_u64); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, total_frag_count), off); off += sizeof(sqfs_u64); TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, actual_frag_count), off); } 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(int argc, char **argv) { (void)argc; (void)argv; test_compressor_opt_struct(); test_compressor_names(); test_blockproc_stats(); test_blockproc_desc(); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libsqfs/get_node_path.c000066400000000000000000000054511461472204600226750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * get_node_path.c * * Copyright (C) 2022 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "util/test.h" #include "sqfs/dir_reader.h" #include "sqfs/error.h" int main(int argc, char **argv) { sqfs_tree_node_t *n0, *n1, *n2; char *str; int ret; (void)argc; (void)argv; n0 = calloc(1, sizeof(*n0) + 16); TEST_NOT_NULL(n0); n1 = calloc(1, sizeof(*n1) + 16); TEST_NOT_NULL(n1); n2 = calloc(1, sizeof(*n2) + 16); TEST_NOT_NULL(n2); /* no parent -> must return "/" */ ret = sqfs_tree_node_get_path(n0, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/"); sqfs_free(str); /* hiearchy levels */ n1->parent = n0; n0->children = n1; strcpy((char *)n1->name, "bar"); n2->parent = n1; n1->children = n2; strcpy((char *)n2->name, "baz"); ret = sqfs_tree_node_get_path(n1, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/bar"); sqfs_free(str); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/bar/baz"); sqfs_free(str); /* root node must not have a name */ strcpy((char *)n0->name, "foo"); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, SQFS_ERROR_ARG_INVALID); TEST_NULL(str); n0->name[0] = '\0'; ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/bar/baz"); sqfs_free(str); /* non-root nodes must have names */ n1->name[0] = '\0'; ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED); TEST_NULL(str); n1->name[0] = 'b'; ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/bar/baz"); sqfs_free(str); /* some names are illegal */ strcpy((char *)n1->name, ".."); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED); TEST_NULL(str); strcpy((char *)n1->name, "."); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED); TEST_NULL(str); strcpy((char *)n1->name, "a/b"); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED); TEST_NULL(str); strcpy((char *)n1->name, "bar"); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/bar/baz"); sqfs_free(str); /* link loops must be detected */ n0->parent = n2; strcpy((char *)n0->name, "foo"); ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, SQFS_ERROR_LINK_LOOP); TEST_NULL(str); n0->parent = NULL; n0->name[0] = '\0'; ret = sqfs_tree_node_get_path(n2, &str); TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(str); TEST_STR_EQUAL(str, "/bar/baz"); sqfs_free(str); /* cleanup */ free(n0); free(n1); free(n2); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libsqfs/table.c000066400000000000000000000107431461472204600211640ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * table.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "util/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(int argc, char **argv) { sqfs_u64 start, value, locations[4], *copy; sqfs_u16 hdr; size_t i; int ret; (void)argc; (void)argv; /* 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.3.1/tests/libsqfs/xattr_benchmark.c000066400000000000000000000052221461472204600232450ustar00rootroot00000000000000/* 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.3.1/tests/libsqfs/xattr_writer.c000066400000000000000000000210231461472204600226240ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xattr_writer.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "compat.h" #include "util/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(int argc, char **argv) { 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; (void)argc; (void)argv; /* 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.3.1/tests/libtar/000077500000000000000000000000001461472204600175365ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/Makemodule.am000066400000000000000000000206531461472204600221460ustar00rootroot00000000000000TARDATADIR=$(top_srcdir)/tests/libtar/data test_tar_gnu0_SOURCES = tests/libtar/tar_simple.c test_tar_gnu0_LDADD = libtar.a libio.a libutil.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 test_tar_gnu1_LDADD = libtar.a libio.a libutil.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 test_tar_gnu2_LDADD = libtar.a libio.a libutil.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 test_tar_gnu3_LDADD = libtar.a libio.a libutil.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 test_tar_gnu4_LDADD = libtar.a libio.a libutil.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 test_tar_gnu5_LDADD = libtar.a libio.a libutil.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 test_tar_gnu6_LDADD = libtar.a libio.a libutil.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 test_tar_pax0_LDADD = libtar.a libio.a libutil.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 test_tar_pax1_LDADD = libtar.a libio.a libutil.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 test_tar_pax2_LDADD = libtar.a libio.a libutil.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 test_tar_pax3_LDADD = libtar.a libio.a libutil.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 test_tar_pax4_LDADD = libtar.a libio.a libutil.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 test_tar_pax5_LDADD = libtar.a libio.a libutil.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 test_tar_ustar0_LDADD = libtar.a libio.a libutil.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 test_tar_ustar1_LDADD = libtar.a libio.a libutil.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 test_tar_ustar2_LDADD = libtar.a libio.a libutil.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 test_tar_ustar3_LDADD = libtar.a libio.a libutil.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 test_tar_ustar4_LDADD = libtar.a libio.a libutil.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 test_tar_ustar5_LDADD = libtar.a libio.a libutil.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 test_tar_ustar6_LDADD = libtar.a libio.a libutil.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 test_tar_target_filled_LDADD = libtar.a libio.a libutil.a libcompat.a test_tar_target_filled_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu_SOURCES = tests/libtar/tar_sparse_gnu.c test_tar_sparse_gnu_LDADD = libtar.a libio.a libutil.a libcompat.a test_tar_sparse_gnu_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR) test_tar_sparse_gnu0_SOURCES = tests/libtar/tar_sparse.c test_tar_sparse_gnu0_LDADD = libtar.a libio.a libutil.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 test_tar_sparse_gnu1_LDADD = libtar.a libio.a libutil.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 test_tar_sparse_gnu2_LDADD = libtar.a libio.a libutil.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 test_tar_sparse_gnu3_LDADD = libtar.a libio.a libutil.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 test_tar_xattr_bsd_LDADD = libtar.a libio.a libutil.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 test_tar_xattr_schily_LDADD = libtar.a libio.a libutil.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 test_tar_xattr_schily_bin_LDADD = libtar.a libio.a libutil.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 libio.a libutil.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.3.1/tests/libtar/data/000077500000000000000000000000001461472204600204475ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/CREDITS000066400000000000000000000023021461472204600214640ustar00rootroot00000000000000The 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.3.1/tests/libtar/data/file-size/000077500000000000000000000000001461472204600223365ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/file-size/12-digit.tar000066400000000000000000000040001461472204600243600ustar00rootroot00000000000000big-file.bin000644 001750 001750 10000000000013375730126 014134 0ustar00mgornymgorny000000 000000 squashfs-tools-ng-1.3.1/tests/libtar/data/file-size/gnu.tar000066400000000000000000000040001461472204600236310ustar00rootroot00000000000000big-file.bin000064400017500001750€13375730126012015 0ustar mgornymgornysquashfs-tools-ng-1.3.1/tests/libtar/data/file-size/pax.tar000066400000000000000000000040001461472204600236300ustar00rootroot00000000000000./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.3.1/tests/libtar/data/format-acceptance/000077500000000000000000000000001461472204600240235ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/format-acceptance/gnu-g.tar000066400000000000000000000040001461472204600255420ustar00rootroot00000000000000input.txt0000644000175000017500000000000513375560044014561 0ustar mgornymgorny1337556176213375561750test squashfs-tools-ng-1.3.1/tests/libtar/data/format-acceptance/gnu.tar000066400000000000000000000040001461472204600253160ustar00rootroot00000000000000input.txt0000644000175000017500000000000513375560044012370 0ustar mgornymgornytest squashfs-tools-ng-1.3.1/tests/libtar/data/format-acceptance/link_filled.tar000066400000000000000000000240001461472204600270030ustar00rootroot0000000000000020_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.3.1/tests/libtar/data/format-acceptance/pax.tar000066400000000000000000000060001461472204600253170ustar00rootroot00000000000000PaxHeader/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.3.1/tests/libtar/data/format-acceptance/ustar-pre-posix.tar000066400000000000000000000040001461472204600276070ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 13375560044 013610 0ustar mgornymgorny000000 000000 test squashfs-tools-ng-1.3.1/tests/libtar/data/format-acceptance/ustar.tar000066400000000000000000000040001461472204600256630ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 13375560044 013650 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.3.1/tests/libtar/data/format-acceptance/v7.tar000066400000000000000000000040001461472204600250610ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 13375560044 006461 test squashfs-tools-ng-1.3.1/tests/libtar/data/large-mtime/000077500000000000000000000000001461472204600226525ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/large-mtime/12-digit.tar000066400000000000000000000040001461472204600246740ustar00rootroot00000000000000input.txt000644 001750 001750 00000000005 100000000000013623 0ustar00mgornymgorny000000 000000 test squashfs-tools-ng-1.3.1/tests/libtar/data/large-mtime/gnu.tar000066400000000000000000000040001461472204600241450ustar00rootroot00000000000000input.txt00006440001750000175000000000005€011504 0ustar mgornymgornytest squashfs-tools-ng-1.3.1/tests/libtar/data/large-mtime/pax.tar000066400000000000000000000060001461472204600241460ustar00rootroot00000000000000./PaxHeaders.11530/input.txt0000644000000000000000000000012077777777777012714 xustar0020 mtime=8589934592 30 atime=1543015522.291866334 30 ctime=1543015033.979919105 input.txt0000644000175000017500000000000500000000000013622 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.3.1/tests/libtar/data/long-paths/000077500000000000000000000000001461472204600225235ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/long-paths/gnu.tar000066400000000000000000000060001461472204600240200ustar00rootroot00000000000000././@LongLink0000000000000000000000000000024500000000000011707 Lustar rootwheel012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/input.txt012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789/01234560000644000175000017500000000000513375567346022655 0ustar mgornymgornytest squashfs-tools-ng-1.3.1/tests/libtar/data/long-paths/pax.tar000066400000000000000000000060001461472204600240170ustar00rootroot00000000000000012345678901234567890123456789/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.3.1/tests/libtar/data/long-paths/ustar.tar000066400000000000000000000040001461472204600243630ustar00rootroot00000000000000012345678901234567890123456789/012345678901234567890123456789/input.txt000644 001750 001750 00000000005 13375567346 033463 0ustar00mgornymgorny000000 000000 012345678901234567890123456789/012345678901234567890123456789/012345678901234567890123456789test squashfs-tools-ng-1.3.1/tests/libtar/data/negative-mtime/000077500000000000000000000000001461472204600233625ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/negative-mtime/gnu.tar000066400000000000000000000240001461472204600246570ustar00rootroot00000000000000input.txt00006440001750000175000000000005ÿÿÿÿÿÿÿÿí/úp016500 0ustar mgornymgornytest squashfs-tools-ng-1.3.1/tests/libtar/data/negative-mtime/pax.tar000066400000000000000000000240001461472204600246560ustar00rootroot00000000000000./PaxHeaders.12320/input.txt0000644000000000000000000000010600000000000012601 xustar0020 mtime=-315622800 20 atime=-315622800 30 ctime=1543015908.675050405 input.txt0000644000175000017500000000000500000000000013622 0ustar00mgornymgorny00000000000000test squashfs-tools-ng-1.3.1/tests/libtar/data/sparse-files/000077500000000000000000000000001461472204600230445ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/sparse-files/gnu-small.tar000066400000000000000000000230001461472204600254460ustar00rootroot00000000000000input.bin0000644000175000017500000002000013376224003021541 Sustar mgornymgorny00000000000000000100000000100000000000010000000020000000000000000000002000000test test squashfs-tools-ng-1.3.1/tests/libtar/data/sparse-files/gnu.tar000066400000000000000000001040001461472204600243400ustar00rootroot00000000000000input.bin0000644000175000017500000010000013376223472023616 Sustar mgornymgorny00000000000000000100000000100000000000010000000020000000000001000000003000000000000100000001000000000004000000000000100000000500000000000010000000060000000000001000000007000000000000100000001000000000000000000test test test test test test test test squashfs-tools-ng-1.3.1/tests/libtar/data/sparse-files/pax-gnu0-0.tar000066400000000000000000001060001461472204600253450ustar00rootroot00000000000000./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.3.1/tests/libtar/data/sparse-files/pax-gnu0-1.tar000066400000000000000000001050001461472204600253450ustar00rootroot00000000000000./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.3.1/tests/libtar/data/sparse-files/pax-gnu1-0.tar000066400000000000000000001060001461472204600253460ustar00rootroot00000000000000./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.3.1/tests/libtar/data/user-group-largenum/000077500000000000000000000000001461472204600243675ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/user-group-largenum/8-digit.tar000066400000000000000000000040001461472204600263360ustar00rootroot00000000000000input.txt000644 400000004000000000000000005 13376036700 011334 0ustar00000000 000000 test squashfs-tools-ng-1.3.1/tests/libtar/data/user-group-largenum/gnu.tar000066400000000000000000000040001461472204600256620ustar00rootroot00000000000000input.txt0000644€€€€0000000000513376036700007404 0ustar test squashfs-tools-ng-1.3.1/tests/libtar/data/user-group-largenum/pax.tar000066400000000000000000000060001461472204600256630ustar00rootroot00000000000000./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.3.1/tests/libtar/data/xattr/000077500000000000000000000000001461472204600216115ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libtar/data/xattr/acl.tar000066400000000000000000000060001461472204600230540ustar00rootroot00000000000000PaxHeader/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.3.1/tests/libtar/data/xattr/xattr-libarchive.tar000066400000000000000000000060001461472204600255650ustar00rootroot00000000000000PaxHeader/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.3.1/tests/libtar/data/xattr/xattr-schily-binary.tar000066400000000000000000000060001461472204600262320ustar00rootroot00000000000000./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.3.1/tests/libtar/data/xattr/xattr-schily.tar000066400000000000000000000060001461472204600247500ustar00rootroot00000000000000./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.3.1/tests/libtar/tar_big_file.c000066400000000000000000000014301461472204600223060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_big_file.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "tar/tar.h" #include "io/file.h" #include "util/test.h" int main(int argc, char **argv) { tar_header_decoded_t hdr; istream_t *fp; (void)argc; (void)argv; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.uid, 01750); TEST_EQUAL_UI(hdr.gid, 01750); TEST_EQUAL_UI(hdr.actual_size, 8589934592); 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.3.1/tests/libtar/tar_fuzz.c000066400000000000000000000014201461472204600215430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_fuzz.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/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.record_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.3.1/tests/libtar/tar_simple.c000066400000000000000000000025661461472204600220520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_simple.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/tar.h" #include "util/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(int argc, char **argv) { tar_header_decoded_t hdr; char buffer[6]; sqfs_s64 ts; istream_t *fp; (void)argc; (void)argv; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.uid, TESTUID); TEST_EQUAL_UI(hdr.gid, TESTGID); TEST_EQUAL_UI(hdr.actual_size, 5); ts = TESTTS; 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.3.1/tests/libtar/tar_sparse.c000066400000000000000000000040161461472204600220460ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_sparse.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/tar.h" #include "util/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.mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.uid, 01750); TEST_EQUAL_UI(hdr.gid, 01750); 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(int argc, char **argv) { (void)argc; (void)argv; test_case_sparse( STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE) ); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libtar/tar_sparse_gnu.c000066400000000000000000000023171461472204600227210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_sparse_gnu.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/tar.h" #include "util/test.h" int main(int argc, char **argv) { tar_header_decoded_t hdr; sparse_map_t *sparse; istream_t *fp; (void)argc; (void)argv; 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.mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.uid, 01750); TEST_EQUAL_UI(hdr.gid, 01750); 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.3.1/tests/libtar/tar_target_filled.c000066400000000000000000000067261461472204600233700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_target_filled.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/tar.h" #include "util/test.h" int main(int argc, char **argv) { tar_header_decoded_t hdr; char buffer[16]; istream_t *fp; (void)argc; (void)argv; 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.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.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.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.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.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.actual_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.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.actual_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.mode, S_IFDIR | 0777); TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/"); clear_header(&hdr); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.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.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.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.3.1/tests/libtar/tar_xattr.c000066400000000000000000000021261461472204600217130ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_xattr.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/tar.h" #include "util/test.h" int main(int argc, char **argv) { tar_header_decoded_t hdr; char buffer[6]; istream_t *fp; (void)argc; (void)argv; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.uid, 01750); TEST_EQUAL_UI(hdr.gid, 01750); TEST_EQUAL_UI(hdr.actual_size, 5); 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.3.1/tests/libtar/tar_xattr_bin.c000066400000000000000000000024201461472204600225400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * tar_xattr_bin.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "io/file.h" #include "tar/tar.h" #include "util/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(int argc, char **argv) { tar_header_decoded_t hdr; char buffer[6]; istream_t *fp; (void)argc; (void)argv; fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE)); TEST_NOT_NULL(fp); TEST_ASSERT(read_header(fp, &hdr) == 0); TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644); TEST_EQUAL_UI(hdr.uid, 01750); TEST_EQUAL_UI(hdr.gid, 01750); TEST_EQUAL_UI(hdr.actual_size, 5); 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.3.1/tests/libutil/000077500000000000000000000000001461472204600177255ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/libutil/Makemodule.am000066400000000000000000000033711461472204600223330ustar00rootroot00000000000000test_str_table_SOURCES = tests/libutil/str_table.c test_str_table_LDADD = libutil.a libio.a libcompat.a test_str_table_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(top_srcdir)/tests/libutil test_rbtree_SOURCES = tests/libutil/rbtree.c 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_CPPFLAGS = $(AM_CPPFLAGS) test_threadpool_LDADD = libutil.a libcompat.a $(PTHREAD_LIBS) if HAVE_PTHREAD test_threadpool_CPPFLAGS += -DHAVE_PTHREAD endif test_ismemzero_SOURCES = tests/libutil/is_memory_zero.c test_ismemzero_LDADD = libutil.a libcompat.a test_canonicalize_name_SOURCES = tests/libutil/canonicalize_name.c test_canonicalize_name_LDADD = libutil.a libcompat.a test_filename_sane_SOURCES = tests/libutil/filename_sane.c test_filename_sane_SOURCES += lib/util/filename_sane.c test_filename_sane_LDADD = libcompat.a libutil.a test_sdate_epoch_SOURCES = tests/libutil/epoch.c test_sdate_epoch_LDADD = libutil.a libcompat.a test_hex_decode_SOURCES = tests/libutil/hex_decode.c test_hex_decode_LDADD = libutil.a libcompat.a test_base64_decode_SOURCES = tests/libutil/base64_decode.c test_base64_decode_LDADD = libutil.a libcompat.a test_fix_win32_filename_SOURCES = tests/libutil/fix_win32_filename.c test_fix_win32_filename_LDADD = libutil.a libcompat.a LIBUTIL_TESTS = \ test_str_table test_rbtree test_xxhash test_threadpool test_ismemzero \ test_canonicalize_name test_filename_sane test_fix_win32_filename \ test_sdate_epoch test_hex_decode test_base64_decode check_PROGRAMS += $(LIBUTIL_TESTS) TESTS += $(LIBUTIL_TESTS) EXTRA_DIST += $(top_srcdir)/tests/libutil/words.txt squashfs-tools-ng-1.3.1/tests/libutil/base64_decode.c000066400000000000000000000031721461472204600224630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * base64_decode.c * * Copyright (C) 2022 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/test.h" static const struct { int result; const char *in; const char *out; } test_vec[] = { { 0, "", "" }, { 0, "Zg", "f" }, { 0, "Zg==", "f" }, { 0, "Zm8=", "fo" }, { 0, "Zm9v", "foo" }, { 0, "Zm9vYg==", "foob" }, { 0, "Zm9vYmE=", "fooba" }, { 0, "Zm9vYmFy", "foobar" }, { 0, "TGV0J3MgYWxsIGxvdmUgTGFpbiEK", "Let's all love Lain!\n" }, { -1, "Zg==X", "XX" }, }; int main(int argc, char **argv) { sqfs_u8 buffer[256]; size_t i, j; (void)argc; (void)argv; for (i = 0; i < sizeof(test_vec) / sizeof(test_vec[0]); ++i) { const size_t in_len = strlen(test_vec[i].in); const size_t out_len = strlen(test_vec[i].out); size_t real_out; int ret; /* initialize the buffer */ for (j = 0; j < sizeof(buffer); ++j) { buffer[j] = (j % 2) ? 0xAA : 0x55; } /* convert */ real_out = sizeof(buffer); ret = base64_decode(test_vec[i].in, in_len, buffer, &real_out); /* make sure pattern is un-touched after expected offset */ j = (in_len / 4) * 3; if (in_len % 4) j += 3; for (; j < sizeof(buffer); ++j) { TEST_ASSERT(buffer[j] == ((j % 2) ? 0xAA : 0x55)); } /* check result */ if (test_vec[i].result == 0) { TEST_ASSERT(ret == 0); TEST_EQUAL_UI(real_out, out_len); ret = memcmp(buffer, test_vec[i].out, out_len); TEST_ASSERT(ret == 0); } else { TEST_ASSERT(ret != 0); TEST_EQUAL_UI(real_out, 0); } fprintf(stderr, "CASE %lu OK\n", (unsigned long)i); } return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libutil/canonicalize_name.c000066400000000000000000000033641461472204600235360ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * canonicalize_name.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/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(int argc, char **argv) { char buffer[512]; size_t i; (void)argc; (void)argv; 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.3.1/tests/libutil/epoch.c000066400000000000000000000026231461472204600211720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * epoch.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/test.h" #if defined(_WIN32) || defined(__WINDOWS__) static void setenv(const char *key, const char *value, int overwrite) { char buffer[128]; (void)overwrite; snprintf(buffer, sizeof(buffer) - 1, "%s=%s", key, value); buffer[sizeof(buffer) - 1] = '\0'; _putenv(buffer); } static void unsetenv(const char *key) { setenv(key, "", 0); } #endif int main(int argc, char **argv) { sqfs_u32 ts; (void)argc; (void)argv; unsetenv("SOURCE_DATE_EPOCH"); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 0); setenv("SOURCE_DATE_EPOCH", "1337", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 1337); setenv("SOURCE_DATE_EPOCH", "0xCAFE", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 0); setenv("SOURCE_DATE_EPOCH", "foobar", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 0); setenv("SOURCE_DATE_EPOCH", "-12", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 0); setenv("SOURCE_DATE_EPOCH", "12", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 12); setenv("SOURCE_DATE_EPOCH", "4294967295", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 0xFFFFFFFF); setenv("SOURCE_DATE_EPOCH", "4294967296", 1); ts = get_source_date_epoch(); TEST_EQUAL_UI(ts, 0); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libutil/filename_sane.c000066400000000000000000000014701461472204600226610ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * filename_sane.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/test.h" static const char *must_work[] = { "foobar", "test.txt", NULL, }; static const char *must_not_work[] = { ".", "..", "/foo", "foo/", "foo/bar", NULL, }; int main(int argc, char **argv) { size_t i; (void)argc; (void)argv; for (i = 0; must_work[i] != NULL; ++i) { if (!is_filename_sane(must_work[i])) { fprintf(stderr, "%s was rejected!\n", must_work[i]); return EXIT_FAILURE; } } for (i = 0; must_not_work[i] != NULL; ++i) { if (is_filename_sane(must_not_work[i])) { fprintf(stderr, "%s was accepted!\n", must_not_work[i]); return EXIT_FAILURE; } } return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libutil/fix_win32_filename.c000066400000000000000000000025531461472204600235460ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * fix_win32_filename.c * * Copyright (C) 2024 David Oberhollenzer */ #include "config.h" #include "util/test.h" #include "util/util.h" static const struct { const char *path; const char *result; } test_data[] = { { "foo", "foo" }, { "foo/bar", "foo/bar" }, { "foo/bar.txt", "foo/bar.txt" }, { "COM1", "COM1_" }, { "COM1.txt", "COM1_.txt" }, { "foo.aux", "foo.aux_" }, { "foo/bar/test.LPT1/bla", "foo/bar/test.LPT1_/bla" }, { "C:\\/foo/COM1.bla/bar", "C\xEF\x80\xBA\xEF\x81\x9c/foo/COM1_.bla/bar" }, { "com1", "com1_" }, { "COM1_", "COM1__" }, { "COM1__", "COM1___" }, { "COM1___", "COM1____" }, }; int main(int argc, char **argv) { (void)argc; (void)argv; for (size_t i = 0; i < sizeof(test_data) / sizeof(test_data[0]); ++i) { char *result = fix_win32_filename(test_data[i].path); size_t out_len = strlen(test_data[i].result); if (result == NULL) { fprintf(stderr, "OOM for test case %u (%s)?\n", (unsigned int)i, test_data[i].path); return EXIT_FAILURE; } if (out_len != strlen(result) || memcmp(result, test_data[i].result, out_len) != 0) { fprintf(stderr, "Mismatch for %s -> %s, got %s instead!\n", test_data[i].path, test_data[i].result, result); free(result); return EXIT_FAILURE; } free(result); } return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libutil/hex_decode.c000066400000000000000000000027311461472204600221630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * hex_decode.c * * Copyright (C) 2022 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/test.h" static const struct { int result; const char *in; const char *out; } test_vec[] = { { 0, "", NULL }, { -1, "A", NULL }, { 0, "AA", "\xAA" }, { 0, "0A", "\x0A" }, { 0, "A0", "\xA0" }, { -1, "A0B", NULL }, { 0, "A0BC", "\xA0\xBC" }, { 0, "0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF" }, { 0, "0123456789abcdef", "\x01\x23\x45\x67\x89\xAB\xCD\xEF" }, { -1, "0123456789ABCDEFGH", NULL }, { -1, "0123456789abcdefgh", NULL }, }; int main(int argc, char **argv) { sqfs_u8 buffer[256]; size_t i, j; (void)argc; (void)argv; for (i = 0; i < sizeof(test_vec) / sizeof(test_vec[0]); ++i) { size_t in_len = strlen(test_vec[i].in); size_t out_len = in_len / 2; int ret; /* initialize the buffer */ for (j = 0; j < sizeof(buffer); ++j) { buffer[j] = (j % 2) ? 0xAA : 0x55; } /* convert */ ret = hex_decode(test_vec[i].in, in_len, buffer, sizeof(buffer)); /* make sure pattern is un-touched after expected offset */ for (j = out_len; j < sizeof(buffer); ++j) { TEST_ASSERT(buffer[j] == ((j % 2) ? 0xAA : 0x55)); } /* check result */ if (test_vec[i].result == 0) { TEST_ASSERT(ret == 0); ret = memcmp(buffer, test_vec[i].out, out_len); TEST_ASSERT(ret == 0); } else { TEST_ASSERT(ret != 0); } } return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libutil/is_memory_zero.c000066400000000000000000000012011461472204600231250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * is_memory_zero.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util/test.h" #include "util/util.h" int main(int argc, char **argv) { unsigned char temp[1024]; size_t i, j; (void)argc; (void)argv; 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.3.1/tests/libutil/rbtree.c000066400000000000000000000117351461472204600213630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * rbtree.c * * Copyright (C) 2020 David Oberhollenzer */ #include "config.h" #include "util/rbtree.h" #include "util/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(int argc, char **argv) { size_t count, blkdepth, mind, maxd; sqfs_s32 key, key2; rbtree_t rb, copy; rbtree_node_t *n; sqfs_u64 value; int ret; (void)argc; (void)argv; 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.3.1/tests/libutil/str_table.c000066400000000000000000000030271461472204600220520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * str_table.c * * Copyright (C) 2019 David Oberhollenzer */ #include "config.h" #include "util/str_table.h" #include "io/file.h" #include "compat.h" #include "util/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(int argc, char **argv) { str_table_t table; size_t i, j, idx; const char *str; (void)argc; (void)argv; 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.3.1/tests/libutil/threadpool.c000066400000000000000000000057001461472204600222340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * threadpool.c * * Copyright (C) 2021 David Oberhollenzer */ #include "config.h" #include "util/threadpool.h" #include "util/test.h" #if defined(_WIN32) || defined(__WINDOWS__) #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include static CRITICAL_SECTION mutex; static unsigned int ticket; static void ticket_init(void) { InitializeCriticalSection(&mutex); ticket = 0; } static void ticket_cleanup(void) { DeleteCriticalSection(&mutex); ticket = 0; } static void ticket_wait(unsigned int value) { for (;;) { EnterCriticalSection(&mutex); if (value == ticket) { ticket += 1; LeaveCriticalSection(&mutex); break; } LeaveCriticalSection(&mutex); SwitchToThread(); } } #elif defined(HAVE_PTHREAD) #include static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static unsigned int ticket; static void ticket_init(void) { ticket = 0; } static void ticket_cleanup(void) { } static void ticket_wait(unsigned int value) { for (;;) { pthread_mutex_lock(&mutex); if (value == ticket) { ticket += 1; pthread_mutex_unlock(&mutex); break; } pthread_mutex_unlock(&mutex); sched_yield(); } } #else static void ticket_init(void) { } static void ticket_cleanup(void) { } static void ticket_wait(unsigned int value) { (void)value; } #endif static int worker(void *user, void *work_item) { unsigned int value = *((unsigned int *)work_item); (void)user; ticket_wait(value); *((unsigned int *)work_item) = 42; return 0; } static int worker_serial(void *user, void *work_item) { (void)user; *((unsigned int *)work_item) = 42; return 0; } static void test_case(thread_pool_t *pool) { unsigned int values[10]; unsigned int *ptr; size_t i, count; int ret; /* must return a sane value */ count = pool->get_worker_count(pool); TEST_ASSERT(count >= 1); /* dequeue on empty pool MUST NOT lock up */ ptr = pool->dequeue(pool); TEST_NULL(ptr); /* submit work items in reverse order */ ticket_init(); for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { values[i] = (sizeof(values) / sizeof(values[0]) - 1) - i; ret = pool->submit(pool, values + i); TEST_EQUAL_I(ret, 0); } /* items must dequeue in the same order */ 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); } ticket_cleanup(); /* queue now empty */ ptr = pool->dequeue(pool); TEST_NULL(ptr); } int main(int argc, char **argv) { thread_pool_t *pool; (void)argc; (void)argv; /* test the actual parallel implementation */ pool = thread_pool_create(10, worker); TEST_NOT_NULL(pool); test_case(pool); pool->destroy(pool); /* repeate the test with the serial reference implementation */ pool = thread_pool_create_serial(worker_serial); TEST_NOT_NULL(pool); test_case(pool); pool->destroy(pool); return EXIT_SUCCESS; } squashfs-tools-ng-1.3.1/tests/libutil/words.txt000066400000000000000000000145721461472204600216350ustar00rootroot00000000000000a 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.3.1/tests/libutil/xxhash.c000066400000000000000000000030061461472204600213730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * xxhash.c * * Copyright (C) 2020 David Oberhollenzer */ #include "config.h" #include "util/util.h" #include "util/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(int argc, char **argv) { sqfs_u32 hash; size_t i; (void)argc; (void)argv; 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.3.1/tests/rdsquashfs/000077500000000000000000000000001461472204600204445ustar00rootroot00000000000000squashfs-tools-ng-1.3.1/tests/rdsquashfs/Makemodule.am000066400000000000000000000002651461472204600230510ustar00rootroot00000000000000if WINDOWS else check_SCRIPTS += tests/rdsquashfs/pathtraversal.sh TESTS += tests/rdsquashfs/pathtraversal.sh endif EXTRA_DIST += $(top_srcdir)/tests/rdsquashfs/pathtraversal.sqfs squashfs-tools-ng-1.3.1/tests/rdsquashfs/pathtraversal.sh.in000066400000000000000000000004171461472204600242670ustar00rootroot00000000000000#!/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.3.1/tests/rdsquashfs/pathtraversal.sqfs000066400000000000000000000100001461472204600242110ustar00rootroot00000000000000hsqs P€ÿÿÿÿÿÿÿÿ™ÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿThis file has been created through a path traversal bug. BxÚcbXÂÈ Fÿß‚ÞÂÕ°@if VbV0û?\žjH~In!½Èò¬HêTäÀl¤¶ v.xÚc`€F(ÍÄÀÉž_’œ‘¨WRQeb0ÍÄàâ”TÎa¸Ú€€