pax_global_header00006660000000000000000000000064145003460550014514gustar00rootroot0000000000000052 comment=3f4dd2928ab362f8b20eab2be864d8e622472df5 squashfuse-0.5.0/000077500000000000000000000000001450034605500137055ustar00rootroot00000000000000squashfuse-0.5.0/.cirrus.yml000066400000000000000000000013341450034605500160160ustar00rootroot00000000000000env: CIRRUS_CLONE_DEPTH: 1 freebsd_task: freebsd_instance: image_family: freebsd-12-3 setup_script: - pkg install -y autoconf automake libtool pkgconf fusefs-libs - pkg install -y lzo2 liblz4 zstd - pkg install -y squashfs-tools coreutils - kldload fuse - sysctl vfs.usermount=1 build_script: - ./autogen.sh - CPPFLAGS="-I/usr/local/include -Werror" LDFLAGS="-L/usr/local/lib" ./configure - make test_script: - make check - diff -u ci/expected-features/all ci/features install_script: - sudo make install always: after_script: - cp /tmp/*.log . || true - for f in *.log tests/*.log ci/features; do echo ::: $f; cat $f; done || true - mksquashfs || true squashfuse-0.5.0/.gitattributes000066400000000000000000000047261450034605500166110ustar00rootroot00000000000000############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain squashfuse-0.5.0/.github/000077500000000000000000000000001450034605500152455ustar00rootroot00000000000000squashfuse-0.5.0/.github/workflows/000077500000000000000000000000001450034605500173025ustar00rootroot00000000000000squashfuse-0.5.0/.github/workflows/ci.yml000066400000000000000000000070771450034605500204330ustar00rootroot00000000000000name: Github Actions CI on: pull_request: schedule: # Every friday - cron: '0 0 * * 5' jobs: unit: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - name: Ubuntu os: ubuntu-latest - name: No FUSE os: ubuntu-latest disable_fuse: "--disable-fuse" check_features: demo - name: Old distro os: ubuntu-20.04 - name: Mac os: macos-latest env: CHECK_FEATURES: ${{ matrix.check_features }} DISABLE_FUSE: ${{ matrix.disable_fuse }} steps: - name: checkout uses: actions/checkout@v2 - name: apt dependencies run: > sudo apt-get install -y automake autoconf libtool pkg-config zlib1g-dev liblzo2-dev liblzma-dev liblz4-dev libzstd-dev fio if: runner.os == 'Linux' - name: apt fuse 2 run: sudo apt-get install -y libfuse-dev fuse if: matrix.os == 'ubuntu-20.04' - name: apt fuse 3 run: sudo apt-get install -y libfuse3-dev fuse3 if: matrix.os == 'ubuntu-latest' - name: homebrew dependencies env: HOMEBREW_NO_AUTO_UPDATE: 1 run: | brew install autoconf automake libtool pkgconfig squashfs coreutils brew install --cask macfuse if: runner.os == 'macOS' - name: build run: | ./autogen.sh CPPFLAGS="-Werror" ./configure $DISABLE_FUSE make -j2 V=1 - name: test run: | make check diff -u ci/expected-features/${CHECK_FEATURES:-all} ci/features if: runner.os != 'macOS' - name: test run: | # On macOS loading the macfuse extension is needed. This # command should load it without rebooting, except System # Integrity Protection disallows it, so the tests get skipped # there. This command came from # https://github.com/actions/runner-images/issues/4731 if sudo kextload /Library/Filesystems/macfuse.fs/Contents/Extensions/$(sw_vers -productVersion|cut -d. -f1)/macfuse.kext; then make check diff -u ci/expected-features/${CHECK_FEATURES:-all} ci/features fi if: runner.os == 'macOS' - name: install run: sudo make install - name: output run: | cp /tmp/*.log . || true mksquashfs > mksquashfs.log || true if: always() - name: upload uses: actions/upload-artifact@v2 with: name: logs ${{ matrix.name }} path: | *.log tests/*.log ci/features if: always() windows: runs-on: windows-2019 steps: - name: install chocolatey dependencies shell: powershell run: choco install -y squashfs - name: setup msbuild uses: microsoft/setup-msbuild@v1.0.2 - name: checkout uses: actions/checkout@v2 - name: generate header shell: bash run: ./gen_swap.sh squashfs_fs.h - name: build working-directory: ./win shell: powershell run: MSBuild.exe squashfuse_ls.vcxproj -p:PlatformToolset=v142 - name: test working-directory: ./win shell: bash run: | set -x mkdir test touch test/foo test/bar test/'iggy blah' mksquashfs test test.squashfs ls test | sort > expected ./Debug/squashfuse_ls.exe test.squashfs | dos2unix | sort > actual diff -u expected actual squashfuse-0.5.0/.github/workflows/release.yml000066400000000000000000000013661450034605500214530ustar00rootroot00000000000000name: release on: release: types: [created] jobs: release_assets: name: release_assets runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'created' steps: - name: Checkout uses: actions/checkout@v2 - name: Install apt dependencies run: | sudo apt-get update sudo apt-get install -y automake autoconf libtool pkg-config \ zlib1g-dev liblzo2-dev liblzma-dev liblz4-dev libzstd-dev fio \ libfuse-dev fuse - name: Build run: | ./autogen.sh ./configure make dist V=1 - name: Release uses: softprops/action-gh-release@v1 with: files: | *.tar.gz squashfuse-0.5.0/.gitignore000066400000000000000000000005151450034605500156760ustar00rootroot00000000000000/*.inc squashfuse squashfuse_ll squashfuse_ls squashfuse_extract *.dSYM *.o *.lo *.la *.tar.gz .libs .deps Makefile Makefile.in aclocal.m4 autom4te.cache build-aux /config.* configure libtool stamp-h1 *.pc win/Debug win/Release *.sdf *.opensdf *.suo *.vcxproj.user ci/features tests/lib.sh tests/ll-smoke.sh tests/umount-test.sh squashfuse-0.5.0/CONFIGURATION000066400000000000000000000016301450034605500156370ustar00rootroot00000000000000These are the most useful options to ./configure: --disable-high-level Disable the `squashfuse' program, which uses the regular FUSE API --disable-low-level Disable the `squashfuse_ll' program, which uses the low-level API --disable-fuse Disable both the above --disable-demo Disable the `squashfuse_ls' program, a demo of libsquashfuse --with-fuse=PREFIX Look for FUSE in this prefix directory --with-fuse-include=DIR Look for FUSE headers here (default: PREFIX/include/fuse) --with-fuse-lib=DIR Look for FUSE libraries here (default: PREFIX/lib) --with-fuse-soname=NAME Look for a FUSE library named libNAME (default: fuse) --with-zlib=PREFIX Look for this compression library in this prefix directory --with-xz=PREFIX --with-lzo=PREFIX --with-lz4=PREFIX --with-zstd=PREFIX More options are available in `./configure --help' squashfuse-0.5.0/LICENSE000066400000000000000000000030701450034605500147120ustar00rootroot00000000000000The squashfuse distribution as a whole is copyright Dave Vasilevsky and is subject to the copyright notice reproduced at the bottom of this file. The file squashfs_fs.h is copyright Phillip Lougher and, with his permission, subject to the same license. Copyright (c) 2012 Dave Vasilevsky Phillip Lougher All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. squashfuse-0.5.0/Makefile.am000066400000000000000000000114471450034605500157500ustar00rootroot00000000000000COMPRESSION_LIBS = $(ZLIB_LIBS) $(XZ_LIBS) $(LZO_LIBS) $(LZ4_LIBS) $(ZSTD_LIBS) ACLOCAL_AMFLAGS = -I m4 --install # Suppress AppleDouble if MAKE_EXPORT export COPYFILE_DISABLE = 1 endif dist_man_MANS = EXTRA_DIST = gen_swap.sh autogen.sh LICENSE CONFIGURATION PLATFORMS NEWS win bin_PROGRAMS = noinst_PROGRAMS = lib_LTLIBRARIES = noinst_LTLIBRARIES = pkgincludedir = @includedir@/squashfuse pkginclude_HEADERS = squashfuse.h squashfs_fs.h \ cache.h common.h config.h decompress.h dir.h file.h fs.h stack.h table.h \ traverse.h util.h xattr.h pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = squashfuse.pc # Convenience lib to we get static executables noinst_LTLIBRARIES += libsquashfuse_convenience.la libsquashfuse_convenience_la_SOURCES = swap.c cache.c table.c dir.c file.c fs.c \ decompress.c xattr.c hash.c stack.c traverse.c util.c \ nonstd-pread.c nonstd-stat.c cache_mt.c \ squashfs_fs.h common.h nonstd-internal.h nonstd.h swap.h cache.h table.h \ dir.h file.h decompress.h xattr.h squashfuse.h hash.h stack.h traverse.h \ util.h fs.h libsquashfuse_convenience_la_CPPFLAGS = $(ZLIB_CPPFLAGS) $(XZ_CPPFLAGS) $(LZO_CPPFLAGS) \ $(LZ4_CPPFLAGS) $(ZSTD_CPPFLAGS) $(FUSE_CPPFLAGS) libsquashfuse_convenience_la_LIBADD = $(COMPRESSION_LIBS) $(FUSE_LIBS) # Main library: libsquashfuse lib_LTLIBRARIES += libsquashfuse.la libsquashfuse_la_SOURCES = libsquashfuse_la_CPPFLAGS = $(ZLIB_CPPFLAGS) $(XZ_CPPFLAGS) $(LZO_CPPFLAGS) \ $(LZ4_CPPFLAGS) $(ZSTD_CPPFLAGS) $(FUSE_CPPFLAGS) libsquashfuse_la_LIBADD = libsquashfuse_convenience.la if SQ_WANT_FUSE # Helper for FUSE clients: libfuseprivate libfuseprivate_la_SOURCES = fuseprivate.c nonstd-makedev.c nonstd-enoattr.c \ fuseprivate.h stat.h stat.c libfuseprivate_la_CPPFLAGS = $(FUSE_CPPFLAGS) libfuseprivate_la_LIBADD = $(COMPRESSION_LIBS) $(FUSE_LIBS) noinst_LTLIBRARIES += libfuseprivate.la endif # High-level squashfuse if SQ_WANT_HIGHLEVEL bin_PROGRAMS += squashfuse squashfuse_SOURCES = hl.c squashfuse_CPPFLAGS = $(ZLIB_CPPFLAGS) $(XZ_CPPFLAGS) $(LZO_CPPFLAGS) \ $(LZ4_CPPFLAGS) $(ZSTD_CPPFLAGS) $(FUSE_CPPFLAGS) squashfuse_LDADD = libsquashfuse_convenience.la libfuseprivate.la $(COMPRESSION_LIBS) $(FUSE_LIBS) dist_man_MANS += squashfuse.1 endif # Low-level squashfuse_ll, if supported if SQ_WANT_LOWLEVEL # convenience lib so we can link squashfuse_ll statically noinst_LTLIBRARIES += libsquashfuse_ll_convenience.la libsquashfuse_ll_convenience_la_SOURCES = ll.c ll_inode.c nonstd-daemon.c libsquashfuse_ll_convenience_la_CPPFLAGS = $(ZLIB_CPPFLAGS) $(XZ_CPPFLAGS) $(LZO_CPPFLAGS) \ $(LZ4_CPPFLAGS) $(ZSTD_CPPFLAGS) $(FUSE_CPPFLAGS) libsquashfuse_ll_convenience_la_LIBADD = libsquashfuse_convenience.la libfuseprivate.la # squashfuse_ll library we will install lib_LTLIBRARIES += libsquashfuse_ll.la libsquashfuse_ll_la_SOURCES = libsquashfuse_ll_la_CPPFLAGS = $(ZLIB_CPPFLAGS) $(XZ_CPPFLAGS) $(LZO_CPPFLAGS) \ $(LZ4_CPPFLAGS) $(ZSTD_CPPFLAGS) $(FUSE_CPPFLAGS) libsquashfuse_ll_la_LIBADD = libsquashfuse_ll_convenience.la $(COMPRESSION_LIBS) $(FUSE_LIBS) # squashfuse_ll binary that's statically linked against internal libs bin_PROGRAMS += squashfuse_ll squashfuse_ll_SOURCES = ll_main.c squashfuse_ll_CPPFLAGS = $(ZLIB_CPPFLAGS) $(XZ_CPPFLAGS) $(LZO_CPPFLAGS) \ $(LZ4_CPPFLAGS) $(ZSTD_CPPFLAGS) $(FUSE_CPPFLAGS) squashfuse_ll_LDADD = libsquashfuse_ll_convenience.la $(COMPRESSION_LIBS) $(FUSE_LIBS) dist_man_MANS += squashfuse_ll.1 pkgconfig_DATA += squashfuse_ll.pc pkginclude_HEADERS += ll.h endif if SQ_WANT_DEMO # Sample program squashfuse_ls noinst_PROGRAMS += squashfuse_ls squashfuse_ls_SOURCES = ls.c squashfuse_ls_LDADD = libsquashfuse.la $(COMPRESSION_LIBS) # Sample program squashfuse_extract noinst_PROGRAMS += squashfuse_extract squashfuse_extract_CPPFLAGS = $(FUSE_CPPFLAGS) squashfuse_extract_SOURCES = extract.c stat.h stat.c nonstd-makedev.c nonstd-symlink.c squashfuse_extract_LDADD = libsquashfuse.la $(COMPRESSION_LIBS) \ $(FUSE_LIBS) endif TESTS = if SQ_FUSE_TESTS TESTS += tests/ll-smoke.sh TESTS += tests/notify_test.sh if MULTITHREADED # I know this test looks backwards, but the default smoke test is multithreaded # when threading is enabled. So we additionally run a singlethreaded test in # that case. TESTS += tests/ll-smoke-singlethreaded.sh endif if SIGTERM_HANDLER TESTS += tests/umount-test.sh endif check_PROGRAMS = cachetest endiantest cachetest_SOURCES=tests/cachetest.c cachetest_LDADD=libsquashfuse.la $(COMPRESSION_LIBS) endiantest_SOURCES = tests/endiantest.c TESTS += cachetest endiantest endif if SQ_DEMO_TESTS TESTS += tests/ls.sh endif tests/ll-smoke.sh tests/ls.sh: tests/lib.sh # Handle generation of swap include files CLEANFILES = swap.h.inc swap.c.inc EXTRA_DIST += swap.h.inc swap.c.inc $(libsquashfuse_convenience_la_OBJECTS): swap.h.inc swap.h.inc swap.c.inc: gen_swap.sh squashfs_fs.h Makefile SED="$(SED)" $(srcdir)/gen_swap.sh $(srcdir)/squashfs_fs.h squashfuse-0.5.0/NEWS000066400000000000000000000046211450034605500144070ustar00rootroot000000000000000.5.0 - 13 September 2023 * Add `--notify_fd` option 0.4.0 - 30 July 2023 * Restore the library api from 0.2.0, adding `_with_subdir` to functions that have the new subdir functionality. 0.3.0 - 27 July 2023 * Add `--subdir` option for mounting a subdirectory * Enable multithreading in squashfuse_ll by default 0.2.0 - 30 June 2023 * Fix bug that swapped the "trusted" and "security" extended attribute prefixes, often resulting in "No data available" errors * Add multithreading support to squashfuse_ll with configure option * Improve SIGTERM handling to do lazy unmount * Add "-o uid" and "-o gid" options to squashfuse_ll, similar to the corresponding FUSE library for high-level options * Add support for LZMA legacy images * Add squashfuse_ll man page and reconcile help messages with man pages * Fix code to work with c99 0.1.105 - 27 June 2022 * Use optimized linux byteswap macros if available * Fix "No such file or directory" when launched with empty fd 0 * Negative cache failed lookups. This saves a FUSE operation when repeatedly looking up non-existent files. * Split squashfuse_ll into a lib and executable * Remove redundant #if in ll header * Add CI for NetBSD * Move CI away from Travis * Update FreeBSD CI job to build on 12.3 0.1.104 - 27 March 2021 * Various bug fixes, new platform support * Support libfuse version 3 * MacOS idle timeout support 0.1.103 - 4 April 2018 * Fix crash bug when underlying IO fails * Fix scanf format to be C99 compliant 0.1.102 - 28 February 2018 * new feature -o timeout=SECONDS for squashfuse_ll to automatically unmount a squashfuse mount if it has not been accessed in a certain amount of time 0.1.101 - 9 December 2017 * zstd support * minor bug fixes 0.1.100 - 27 May 2016 * minor bugfixes around symlink handling * performance improvements * support for offset mounting * variety of other fixes since 0.1.99 0.1.99 - unreleased * Support for LZ4 compression. * Faster directory listing. * Squashfuse now uses the high-level FUSE API. The old low-level squashfuse is renamed to squashfuse_ll. * Squashfuse core now builds as a library. Demo program is included. * New API for simple recursive traversal of a squashfs filesystem. * Changed API for directories, uses less memory and exposes fewer internals. * Windows support for the core library and demo. squashfuse-0.5.0/PLATFORMS000066400000000000000000000075051450034605500152060ustar00rootroot00000000000000Overview -------- The following operating systems are known to be supported: * Linux * Mac OS X * FreeBSD * NetBSD * OpenIndiana * Android These don't seem to work yet: * Windows * Solaris * QNX * Minix * Haiku * Hurd These don't have FUSE: * iOS * OpenBSD * DragonFly BSD - NetBSD's `puffs' is in HEAD * Commercial UNIX: AIX, HP-UX, etc Details ------- * Linux + Versions: Kernel 2.4.20 or later, most distros since ~2004 + Tested on: - Debian / squeeze 6.0 / i386, ppc - Ubuntu / Trusty 14.04 / amd64 - Ubuntu / Precise 12.04 / amd64 - Ubuntu / Natty 11.10 / amd64 - Ubuntu / Lucid 10.04 / amd64 - CentOS / 3.9 / i386 / FUSE 2.5 + Requirements: CONFIG_FUSE_FS kernel option + Packages: - apt-get Build: gcc make libc-dev libfuse-dev libz-dev liblzo2-dev liblzma-dev liblz4-dev libattr1-dev pkg-config Runtime: libfuse2 zlib1g liblzo2-2 liblzma2 * Mac OS X + Versions: Mac OS X 10.4 and later + Tested on: - 10.9 Lion / x86_64 / OSXFUSE - 10.7 Lion / x86_64 / Tuxera FUSE, fuse4x, OSXFUSE - 10.6 Snow Leopard / x86_64 / MacFUSE, fuse4x - 10.5 Leopard / ppc / MacFUSE - 10.4 Tiger / i386 / MacFUSE + Requirements - Command-line developer tools - OSXFUSE, fuse4x or MacFUSE: http://osxfuse.github.com/, http://fuse4x.org/, http://code.google.com/p/macfuse/ + Packages: - Not necessary just to build a basic zlib-supporting squashfuse. - All package managers require the full Xcode for FUSE, not just command-line tools. - MacPorts port install lzo2 liblzma zlib lz4 osxfuse - Fink fink install automake1.11 libtool2 fink install lzo2 liblzma5 osxfuse-dev osxfuse-shlibs pkgconfig - Homebrew brew install lzo xz lz4 osxfuse # follow instructions from `brew info osxfuse' + Configure: If using a package manager, use --with-lzo=/wherever and/or --with-lz4=/wherever + Notes: - OS X uses a different naming scheme for extended attributes, so they may not be very useful - LZ4 often builds a dynamic liblz4 with a bad install_name on OS X. Either link statically, or use install_name_tool to correct it. * FreeBSD + Versions: FreeBSD 6.0 and later + Tested on: FreeBSD 9.0 / amd64 + Packages: From ports or packages: fusefs-libs fusefs-kmod lzo2 lz4 + Notes: FreeBSD FUSE doesn't support extended attributes * NetBSD + Versions: NetBSD 6.0 and later + Tested on: NetBSD 6.0 BETA / i386 + Requirements: puffs enabled in kernel (present by default) + Packages: From pkgsrc: libfuse libperfuse lzo + Configure: ./configure --with-fuse=/usr/pkg --with-lzo=/usr/pkg + Notes: - Mounting seems to require root - NetBSD FUSE doesn't support extended attributes - perfused crashes mysteriously on some archives * OpenIndiana + Tested on: OpenIndiana 151a / i386 + Requirements: C compiler: gcc or Sun Studio 12.3 work + Packages: From `Spec Files Extra` repo: fusefs libfuse lzo xz + Installation: Must ensure device is where libfuse expects it: ln -s "/devices/pseudo/fuse@0:fuse" /dev/fuse + Notes: - Mounting requires root - OpenIndiana FUSE doesn't support extended attributes * Android + Tested on: Android emulator / 2.3.3 / armel + Requirements: - CONFIG_FUSE_FS enabled in kernel. Most Android kernels have this disabled, so the kernel must be rebuilt. - fuse-android: https://github.com/seth-hg/fuse-android + Configure: - Cross-compile from Linux by passing --host flag to configure - Use CPPFLAGS/LDFLAGS/LIBS that fuse-android used for fusexmp - May need `-nodefaultlibs' in LDFLAGS, since configure likes linking and compiling together in a single gcc invokation, and Android's build system doesn't. squashfuse-0.5.0/README000066400000000000000000000176751450034605500146050ustar00rootroot00000000000000 squashfuse - Mount SquashFS archives using FUSE https://github.com/vasi/squashfuse Squashfuse lets you mount SquashFS archives in user-space. It supports almost all features of the SquashFS format, yet is still fast and memory-efficient. So that everyone can use it, squashfuse supports many different operating systems and is available under a permissing license. SquashFS is an efficiently compressed, read-only storage format. Support for it has been built into the Linux kernel since 2009. It is very common on Live CDs and embedded Linux distributions. Quick start: $ ./configure && make $ ./squashfuse foo.squashfs mountpoint 1. Table of contents ==================== 0. Introduction 1. Table of contents 2. Getting started - System requirements - Getting the source - Building - Usage 3. About squashfuse - Is squashfuse for you? - What's included? - Features - Known bugs 4. References - Licensing - Acknowledgements - Links 2. Getting started ================== 2a. System requirements ----------------------- To build and use squashfuse, you must be able to use the terminal of your operating system. Runtime requirements: - FUSE 2.5 or later - At least one of the following compression libraries - zlib - lzo2 - xz (aka. liblzma) - lz4 - zstd - (optional) libattr, for better extended attribute support on Linux Build requirements: - A C compiler - make (any variant) - sed - (optional) pkg-config, for detection of dependencies To build from the development repository, rather than a tarball, you'll need: - autoconf 2.60 or later - automake 1.11 or later - libtool 2 Known fully-supported platforms: - Linux - Mac OS X - FreeBSD - NetBSD - OpenIndiana - Android - other platforms may work too! For a precise list of packages you will need on your OS, and other platform notes, please see the file `PLATFORMS'. 2b. Getting the source ---------------------- The squashfuse distribution can be downloaded from SourceForge: https://sourceforge.net/projects/squashfuse/files/ The development repository uses git, at GitHub: http://github.com/vasi/squashfuse 2c. Installation ---------------- Squashfuse is built with the usual `configure && make'. If you need more detailed instructions: 0. Ensure you're at your terminal, in the directory containing this README. 1. (if needed) If the file `configure' is already present, skip this step. Otherwise, run `./autogen.sh' to generate one. 2. Run `./configure' to set up the build. You can find useful configuration options in the file `CONFIGURATION', or by running `./configure --help'. If configure fails, check that you really have all the requirements installed. You may also want to check the `PLATFORMS' file to see if there are any special notes for your operating system. 3. Run `make' to build `squashfuse'. 4. (optional) If you want to use squashfuse in this directory, that's ok. But if you'd rather install it, run `make install'. If you need root privileges, `sudo make install' may work. 2d. Example: Ubuntu ------------------- For example on Ubuntu 16.04: sudo apt-get -y install git autoconf libtool make gcc libtool libfuse-dev liblzma-dev libtoolize --force aclocal autoheader automake --force-missing --add-missing autoconf ./configure --with-xz=/usr/lib/ make 2e. Usage --------- You'll need a SquashFS archive to use squashfuse. If you don't already have one, you can create one using the `mksquashfs' utility from the squashfs-tools project. To create a SquashFS archive: $ mksquashfs DIRECTORY ARCHIVE To mount a SquashFS archive with squashfuse: $ squashfuse ARCHIVE MOUNTPOINT To unmount when you're done: $ umount MOUNTPOINT # On Mac/BSD $ fusermount -u MOUNTPOINT # On Linux For more options, see the man page squashfuse(1). 3. About squashfuse =================== 3a. Is squashfuse for you? -------------------------- Squashfuse is a great option if you have a SquashFS archive, and: - You're not running Linux, or - You don't have root access, or - You're too concerned about security to use root, or - You find it inconvenient to elevate privileges, or - SquashFS is not built into your kernel, but FUSE is, or - You want to hack on the SquashFS format without risking kernel panics. Squashfuse is probably not the right tool for the job, if: - You don't have FUSE. More and more systems have FUSE, but some don't. Squashfuse requires it. - You have a very old SquashFS archive. Neither squashfuse nor the Linux kernel support SquashFS versions less than 4.0. Use `unsquashfs' from the squashfs-tools project. - You want to create or modify a SquashFS archive. Neither squashfuse nor the Linux kernel support write access, use `mksquashfs' from squashfs-tools. - You want to extract an entire SquashFS archive. If you don't want to mount anything, it's more efficient and convenient to just use unsquashfs. - You want your root filesystem `/' to be SquashFS. This isn't well-tested, though it may be possible. - You're highly concerned about bugs. The SquashFS kernel module has seen much more testing than squashfuse. If you don't yet use SquashFS, consider starting, now that squashfuse exists. For many uses, the chief drawbacks of SquashFS were requiring Linux and root access, but squashfuse has that covered. - Use SquashFS for archival and backup, instead of tar. It offers faster creation (multi-core), and browsing without unpacking. - Use SquashFS instead of zip. It has better compression, and faster directory lookup. - Use SquashFS instead of compressed disk images like DMG, uzip or Partimage. It has better compression and portability. 3b. What's included? -------------------- Squashfuse currently comprises three programs: * squashfuse Allows you to mount a squashfs filesystem. * squashfuse_ll Like `squashfuse', but implemented using the low-level FUSE API. It's a tiny bit faster, but less portable. * squashfuse_ls Lists all the files in a squashfs archive. A demonstration of using the squashfuse core in the absence of FUSE. 3c. Features ------------ Squashfuse supports the following SquashFS features: - zlib, LZO, LZMA2, LZ4 and zstd decompression - Fast, indexed directory lookup - Fast, indexed seeking within files - Caching of decompressed blocks - De-duplicated files - Sparse files - Extended attributes - Files larger than 4GB Squashfuse is missing the following features: - LZMA1 compression (deprecated) - Support for SquashFS versions less than 4.0 - Multi-core decompression 3c. Known bugs -------------- - On 32-bit systems with a large inode cache, when mounting a large SquashFS archive created with the "-no-exports" option, squashfuse_ll may use a large amount of memory. This is due to a bug in the FUSE API, where ino_t is shrunk to 32-bits. 4. References ============= 4a. Licensing ------------- Squashfuse is copyright (c) 2012-2014 Dave Vasilevsky Squashfuse is distributed under the 2-clause BSD license. See the file LICENSE for details. 4b. Acknowledgements -------------------- Thanks to: * Phillip Lougher, for designing the SquashFS format, and implementing support in the kernel. Also for providing permission to use and distribute squashfs_fs.h under a BSD-style license. * Maël Kerbiriou, for implementing LZ4 support. 4c. Links --------- * SquashFS - SquashFS home page, includeing squashfs-tools: http://squashfs.sourceforge.net/ - squashfs-tools for non-Linux: https://github.com/vasi/squashfs-tools * FUSE - FUSE home page: http://fuse.sourceforge.net/ - OSXFUSE (FUSE for Macs): http://osxfuse.github.io/ * Other implementations of the SquashFS format - Linux kernel: https://github.com/torvalds/linux/tree/master/fs/squashfs - 7-zip / p7zip: http://www.7-zip.org/ - GRUB 2 bootloader: http://www.gnu.org/software/grub/ squashfuse-0.5.0/TODO000066400000000000000000000041461450034605500144020ustar00rootroot00000000000000For 0.1 Submit squashfs-tools patches Build system Autotools versioning Currently autoconf 2.68, automake 1.11 tested. Use AC_PREREQ? Test older versions libsquashfuse Separate out a library with a proper API Try connecting it to File Roller, The Unarchiver, etc Support win32api? Debug builds: configure option? Mac builds Support specifying SDK, arch, universal builds Why don't builds on Lion with 10.4 SDK work? Test suite? Fully process squashfs_fs.h direct from kernel Remove [0] lines for Windows Add __le## for many OSes Remove function macros Redefine INVALID_BLK etc... Features Design a good API for libsquashfuse Use better error codes, error reporting, sanity checks Support/emulate non-namespaced xattrs, for OS X Write a clone of unsquashfs using libsquashfuse? squashfs 3 and earlier support? LZMA1 compression? Performance Multi-threading Caches, blockidx aren't thread safe Use a thread pool? Or one thread per request? Read metadata (eg: UIDs) in parallel? How do we get FUSE to do multi-threading? On some OSes it just doesn't Byte swapping Use platform-specific optimizations (eg: libkern/OSByteOrder.h) Also arch-specific Block index: Create incrementally, as needed? Speculative read-ahead? Caching and threading strategy delegation? eg: Small caches for low-memory; huge caches for complete extraction Profile for optimization opportunities Misc mksquashfs is slow, write a faster version? Platform support Known bad QNX 6.5 (qfuse) fuse_session_add_chan() crashes High-level crashes too Windows (fuse4win/dokan) Dokan sample bluescreens, fuse4win sample crashes CBFS? Totally different API, expensive Pismo File Mount? Explorer Namespace Extension? FUSE-NT? 7-zip can browse squashfs archives, just not in explorer Linux kernel < 2.4.20 Requires fuse < 2.4.0. Maybe high-level? LUFS? No low-level fuse Solaris 11 (uvfs/fuse): High-level sort-of works Minix 3.2 (puffs/refuse): High-level won't work Haiku (userlandfs): High-level won't work, userlandfs hard to build Hurd (libfuse): High-level works fine, version 2.5 squashfuse-0.5.0/autogen.sh000077500000000000000000000007341450034605500157120ustar00rootroot00000000000000#!/bin/sh if autoreconf --version > /dev/null 2>&1; then : ; else echo "Missing autoconf" exit 1 fi if aclocal --version > /dev/null 2>&1; then : ; else echo "Missing automake" exit 1 fi if libtoolize --version > /dev/null 2>&1; then : ; else if glibtoolize --version > /dev/null 2>&1; then : ; else echo "Missing libtool" exit 1 fi fi if pkg-config --version > /dev/null 2>&1; then : ; else echo "Missing pkg-config" exit 1 fi exec autoreconf -i squashfuse-0.5.0/cache.c000066400000000000000000000074711450034605500151250ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #ifndef SQFS_MULTITHREADED #include "cache.h" #include "fs.h" #include #include typedef struct sqfs_cache_internal { uint8_t *buf; sqfs_cache_dispose dispose; size_t size, count; size_t next; /* next block to evict */ } sqfs_cache_internal; typedef struct { int valid; sqfs_cache_idx idx; } sqfs_cache_entry_hdr; sqfs_err sqfs_cache_init(sqfs_cache *cache, size_t size, size_t count, sqfs_cache_dispose dispose) { sqfs_cache_internal *c = malloc(sizeof(sqfs_cache_internal)); if (!c) { return SQFS_ERR; } c->size = size + sizeof(sqfs_cache_entry_hdr); c->count = count; c->dispose = dispose; c->next = 0; c->buf = calloc(count, c->size); if (c->buf) { *cache = c; return SQFS_OK; } sqfs_cache_destroy(&c); return SQFS_ERR; } static sqfs_cache_entry_hdr *sqfs_cache_entry_header( sqfs_cache_internal* cache, size_t i) { return (sqfs_cache_entry_hdr *)(cache->buf + i * cache->size); } static void* sqfs_cache_entry(sqfs_cache_internal* cache, size_t i) { return (void *)(sqfs_cache_entry_header(cache, i) + 1); } void sqfs_cache_destroy(sqfs_cache *cache) { if (cache && *cache) { sqfs_cache_internal *c = *cache; if (c->buf) { size_t i; for (i = 0; i < c->count; ++i) { sqfs_cache_entry_hdr *hdr = sqfs_cache_entry_header(c, i); if (hdr->valid) { c->dispose((void *)(hdr + 1)); } } } free(c->buf); free(c); *cache = NULL; } } void *sqfs_cache_get(sqfs_cache *cache, sqfs_cache_idx idx) { size_t i; sqfs_cache_internal *c = *cache; sqfs_cache_entry_hdr *hdr; for (i = 0; i < c->count; ++i) { hdr = sqfs_cache_entry_header(c, i); if (hdr->idx == idx) { assert(hdr->valid); return sqfs_cache_entry(c, i); } } /* No existing entry; free one if necessary, allocate a new one. */ i = (c->next++); c->next %= c->count; hdr = sqfs_cache_entry_header(c, i); if (hdr->valid) { /* evict */ c->dispose((void *)(hdr + 1)); hdr->valid = 0; } hdr->idx = idx; return (void *)(hdr + 1); } int sqfs_cache_entry_valid(const sqfs_cache *cache, const void *e) { sqfs_cache_entry_hdr *hdr = ((sqfs_cache_entry_hdr *)e) - 1; return hdr->valid; } void sqfs_cache_entry_mark_valid(sqfs_cache *cache, void *e) { sqfs_cache_entry_hdr *hdr = ((sqfs_cache_entry_hdr *)e) - 1; assert(hdr->valid == 0); hdr->valid = 1; } void sqfs_cache_put(const sqfs_cache *cache, const void *e) { // nada, we have no locking in single-threaded implementation. } #endif /* SQFS_MULTITHREADED */ squashfuse-0.5.0/cache.h000066400000000000000000000054411450034605500151250ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_CACHE_H #define SQFS_CACHE_H #include "common.h" /* Really simplistic cache * - Linear search * - Linear eviction * - No thread safety * - Misses are caller's responsibility */ typedef uint64_t sqfs_cache_idx; typedef void (*sqfs_cache_dispose)(void* data); struct sqfs_cache_internal; typedef struct sqfs_cache_internal *sqfs_cache; sqfs_err sqfs_cache_init(sqfs_cache *cache, size_t size, size_t count, sqfs_cache_dispose dispose); void sqfs_cache_destroy(sqfs_cache *cache); /* Get an entry for the given index. * * This will always succeed (evicting if necessary). The caller must then * call sqfs_cache_entry_valid() to determine if the entry is valid. If not * valid, the entry is newly allocated and the caller is responsible for * initializing it and then calling sqfs_cache_entry_mark_valid(). * * This call may block in multithreaded case. * * In multithreaded case, the cache is locked on return (no entries can * be added or removed). Caller must call sqfs_cache_put() when it is safe * to evict the returned cache entry. */ void *sqfs_cache_get(sqfs_cache *cache, sqfs_cache_idx idx); /* inform cache it is now safe to evict this entry. */ void sqfs_cache_put(const sqfs_cache *cache, const void *e); /* Determine if cache entry contains valid contents. */ int sqfs_cache_entry_valid(const sqfs_cache *cache, const void *e); /* Mark cache entry as containing valid contents. */ void sqfs_cache_entry_mark_valid(sqfs_cache *cache, void *e); #endif squashfuse-0.5.0/cache_mt.c000066400000000000000000000101651450034605500156170ustar00rootroot00000000000000#include "config.h" #ifdef SQFS_MULTITHREADED /* Thread-safe cache implementation. * * Simple implementation: basic hash table, each individual entry is * protected by a mutex, any collision is handled by eviction. */ #include "cache.h" #include "fs.h" #include #include #include typedef struct sqfs_cache_internal { uint8_t *buf; sqfs_cache_dispose dispose; size_t entry_size, count; } sqfs_cache_internal; typedef struct { enum { EMPTY, FULL } state; sqfs_cache_idx idx; pthread_mutex_t lock; } sqfs_cache_entry_hdr; // MurmurHash64A performance-optimized for hash of uint64_t keys const static uint64_t kMurmur2Seed = 4193360111ul; static uint64_t MurmurRehash64A(uint64_t key) { const uint64_t m = 0xc6a4a7935bd1e995; const int r = 47; uint64_t h = (uint64_t)kMurmur2Seed ^ (sizeof(uint64_t) * m); key *= m; key ^= key >> r; key *= m; h ^= key; h *= m; h ^= h >> r; h *= m; h ^= h >> r; return h; } static sqfs_cache_entry_hdr *sqfs_cache_entry_header( sqfs_cache_internal* cache, size_t i) { assert(i < cache->count); return (sqfs_cache_entry_hdr *)(cache->buf + i * cache->entry_size); } sqfs_err sqfs_cache_init(sqfs_cache *cache, size_t entry_size, size_t count, sqfs_cache_dispose dispose) { size_t i; pthread_mutexattr_t attr; sqfs_cache_internal *c = malloc(sizeof(sqfs_cache_internal)); if (!c) { return SQFS_ERR; } c->entry_size = entry_size + sizeof(sqfs_cache_entry_hdr); c->count = count; c->dispose = dispose; pthread_mutexattr_init(&attr); #if defined(_GNU_SOURCE) && !defined(NDEBUG) pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); #endif c->buf = calloc(c->count, c->entry_size); if (!c->buf) { goto err_out; } for (i = 0; i < c->count; ++i) { sqfs_cache_entry_hdr *hdr = sqfs_cache_entry_header(c, i); hdr->state = EMPTY; if (pthread_mutex_init(&hdr->lock, &attr)) { goto err_out; } } pthread_mutexattr_destroy(&attr); *cache = c; return SQFS_OK; err_out: sqfs_cache_destroy(&c); return SQFS_ERR; } void sqfs_cache_destroy(sqfs_cache *cache) { if (cache && *cache) { sqfs_cache_internal *c = *cache; if (c->buf) { size_t i; for (i = 0; i < c->count; ++i) { sqfs_cache_entry_hdr *hdr = sqfs_cache_entry_header(c, i); if (hdr->state == FULL) { c->dispose((void *)(hdr + 1)); } if (pthread_mutex_destroy(&hdr->lock)) { assert(0); } } } free(c->buf); free(c); *cache = NULL; } } void *sqfs_cache_get(sqfs_cache *cache, sqfs_cache_idx idx) { sqfs_cache_internal *c = *cache; sqfs_cache_entry_hdr *hdr; void *entry; uint64_t key = MurmurRehash64A(idx) % c->count; hdr = sqfs_cache_entry_header(c, key); if (pthread_mutex_lock(&hdr->lock)) { assert(0); } /* matching unlock is in sqfs_cache_put() */ entry = (void *)(hdr + 1); if (hdr->state == EMPTY) { hdr->idx = idx; return entry; } /* There's a valid entry: it's either a cache hit or a collision. */ assert(hdr->state == FULL); if (hdr->idx == idx) { return entry; } /* Collision. */ c->dispose((void *)(hdr + 1)); hdr->state = EMPTY; hdr->idx = idx; return entry; } int sqfs_cache_entry_valid(const sqfs_cache *cache, const void *e) { sqfs_cache_entry_hdr *hdr = ((sqfs_cache_entry_hdr *)e) - 1; return hdr->state == FULL; } void sqfs_cache_entry_mark_valid(sqfs_cache *cache, void *e) { sqfs_cache_entry_hdr *hdr = ((sqfs_cache_entry_hdr *)e) - 1; assert(hdr->state == EMPTY); hdr->state = FULL; } void sqfs_cache_put(const sqfs_cache *cache, const void *e) { sqfs_cache_entry_hdr *hdr = ((sqfs_cache_entry_hdr *)e) - 1; if (pthread_mutex_unlock(&hdr->lock)) { assert(0); } } #endif /* SQFS_MULTITHREADED */ squashfuse-0.5.0/ci/000077500000000000000000000000001450034605500143005ustar00rootroot00000000000000squashfuse-0.5.0/ci/expected-features/000077500000000000000000000000001450034605500177155ustar00rootroot00000000000000squashfuse-0.5.0/ci/expected-features/all000066400000000000000000000001361450034605500204100ustar00rootroot00000000000000Decompressors: ZLIB XZ LZO LZ4 ZSTD High-level: yes Low-level: yes Demo: yes Tests: FUSE demo squashfuse-0.5.0/ci/expected-features/demo000066400000000000000000000001271450034605500205640ustar00rootroot00000000000000Decompressors: ZLIB XZ LZO LZ4 ZSTD High-level: no Low-level: no Demo: yes Tests: demo squashfuse-0.5.0/ci/features.in000066400000000000000000000001701450034605500164440ustar00rootroot00000000000000Decompressors:@sq_decompressors@ High-level: @sq_high_level@ Low-level: @sq_low_level@ Demo: @sq_demo@ Tests:@sq_tests@ squashfuse-0.5.0/ci/mksquashfs.sh000077500000000000000000000007061450034605500170270ustar00rootroot00000000000000#!/bin/sh set -ev cd .. curl -L -o squashfs-tools.tar.gz https://github.com/plougher/squashfs-tools/archive/4.4.tar.gz tar xf squashfs-tools.tar.gz cd squashfs-tools* if [ "$TRAVIS_OS_NAME" = osx ]; then curl -L "https://github.com/plougher/squashfs-tools/pull/69.patch?full_index=1" | patch -p1 fi cd squashfs-tools if [ "$TRAVIS_OS_NAME" = osx ]; then make else make XZ_SUPPORT=1 LZO_SUPPORT=1 LZ4_SUPPORT=1 ZSTD_SUPPORT=1 fi sudo make install squashfuse-0.5.0/ci/netbsd.sh000077500000000000000000000013741450034605500161230ustar00rootroot00000000000000#!/bin/sh set -ex # Set PATH, PKG_PATH, etc . /root/.profile # Install packages pkg_add autoconf automake libtool pkg-config fuse \ lz4 lzo zstd \ squashfs coreutils # For tests # Enter our source dir, and print nice data on exit cd /squashfuse cleanup() { if [ "$?" != 0 ]; then cat config.log || true cat tests/*.log || true mksquashfs || true fi } trap "cleanup" EXIT # Build ./autogen.sh LDFLAGS='-L/usr/pkg/lib' CPPFLAGS='-Werror -I/usr/pkg/include' ./configure make # Test # Ensure buffers are big enough that perfuse doesn't yield warnings sysctl -w kern.sbmax=3000000 # Run on actual disk, /tmp may not be large mkdir /root/tmp TMPDIR=/root/tmp make check diff -u ci/expected-features/all ci/features # Test install make install squashfuse-0.5.0/common.h000066400000000000000000000054621450034605500153550ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_COMMON_H #define SQFS_COMMON_H #include "config.h" #include #include #include #ifdef _WIN32 # include # include # define atomic_inc_relaxed(ptr) \ _InterlockedIncrement(ptr) # define atomic_dec_acqrel(ptr) \ _InterlockedDecrement(ptr) #else typedef mode_t sqfs_mode_t; typedef uid_t sqfs_id_t; typedef off_t sqfs_off_t; typedef int sqfs_fd_t; # define atomic_inc_relaxed(ptr) \ __atomic_add_fetch(&block->refcount, 1, __ATOMIC_RELAXED) # define atomic_dec_acqrel(ptr) \ __atomic_sub_fetch(&block->refcount, 1, __ATOMIC_ACQ_REL) #endif typedef enum { SQFS_OK, SQFS_ERR, SQFS_BADFORMAT, /* unsupported file format */ SQFS_BADVERSION, /* unsupported squashfs version */ SQFS_BADCOMP, /* unsupported compression method */ SQFS_UNSUP /* unsupported feature */ } sqfs_err; #define SQFS_INODE_ID_BYTES 6 typedef uint64_t sqfs_inode_id; typedef uint32_t sqfs_inode_num; typedef struct sqfs sqfs; typedef struct sqfs_inode sqfs_inode; typedef struct { size_t size; void *data; long refcount; } sqfs_block; typedef struct { sqfs_off_t block; size_t offset; } sqfs_md_cursor; /* Increment the refcount on the block. */ static inline void sqfs_block_ref(sqfs_block *block) { atomic_inc_relaxed(&block->refcount); } /* decrement the refcount on the block, return non-zero if we held the last * reference. */ static inline int sqfs_block_deref(sqfs_block *block) { return atomic_dec_acqrel(&block->refcount) == 0; } #endif squashfuse-0.5.0/configure.ac000066400000000000000000000103701450034605500161740ustar00rootroot00000000000000AC_INIT([squashfuse], [0.5.0], [dave@vasilevsky.ca]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) AH_TOP([ #ifndef SQFS_CONFIG_H #define SQFS_CONFIG_H ]) AH_BOTTOM([#endif]) AC_CANONICAL_BUILD AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([foreign -Wall subdir-objects]) AC_USE_SYSTEM_EXTENSIONS AM_SILENT_RULES(yes) AM_PROG_AR LT_INIT PKG_PROG_PKG_CONFIG PKG_INSTALLDIR # Compiler AC_PROG_AWK AC_PROG_SED AC_PROG_CPP AC_SYS_LARGEFILE AM_PROG_CC_C_O SQ_PROG_CC_WALL # Non-POSIX declarations SQ_CHECK_DECL_MAKEDEV SQ_CHECK_DECL_PREAD SQ_CHECK_DECL_S_IFSOCK SQ_CHECK_DECL_ENOATTR([:]) SQ_CHECK_DECL_SYMLINK # Decompression SQ_CHECK_DECOMPRESS([ZLIB],[z],[uncompress],[zlib.h],,[gzip]) SQ_CHECK_DECOMPRESS([XZ],[lzma],[lzma_stream_buffer_decode],[lzma.h],[liblzma],[xz]) SQ_CHECK_DECOMPRESS([LZO],[lzo2],[lzo1x_decompress_safe],[lzo/lzo1x.h],,[lzo]) SQ_CHECK_DECOMPRESS([LZ4],[lz4],[LZ4_decompress_safe],[lz4.h],,[lz4]) SQ_CHECK_DECOMPRESS([ZSTD],[zstd],[ZSTD_decompress],[zstd.h],,[zstd]) AS_IF([test "x$sq_decompressors" = x], [AC_MSG_FAILURE([At least one decompression library must exist])]) # FUSE SQ_FUSE_API AS_IF([test "x$sq_fuse_found" = xyes],[ SQ_FUSE_API_LOWLEVEL SQ_FUSE_API_VERSION SQ_FUSE_API_XATTR_POSITION ]) SQ_FUSE_RESULT # Misc SQ_CHECK_PROG_MAKE_EXPORT AC_CHECK_TYPE([__le16],[ AC_DEFINE([HAVE_LINUX_TYPES_LE16],1, [Define if defines the type __le16]) ],,[#include ]) AC_CHECK_HEADERS([asm/byteorder.h]) AC_CHECK_HEADERS([endian.h machine/endian.h], [break]) AC_C_INLINE # Other options AC_ARG_ENABLE([demo], AS_HELP_STRING([--disable-demo], [disable library demo]),, [sq_demo=yes]) AM_CONDITIONAL([SQ_WANT_DEMO], [test "x$sq_demo" = xyes]) # The 'make check' tests are only known to work on linux. AC_CHECK_PROGS([sq_fusermount],[fusermount3 fusermount],[no]) AC_CHECK_PROG([sq_mksquashfs],[mksquashfs],[yes],[no]) AC_CHECK_PROGS([sq_md5sum],[md5sum gmd5sum],[no]) AS_CASE(["${build_os}__$sq_low_level$sq_fusermount$sq_mksquashfs$sq_md5sum"], [linux*__yesfusermount*yes*md5sum],[sq_fuse_tests=yes], [darwin*__yes*yes*md5sum],[sq_fuse_tests=yes], [freebsd*__yes*yes*md5sum],[sq_fuse_tests=yes], [netbsd*__yes*yes*md5sum],[sq_fuse_tests=yes], [sq_fuse_tests=no]) AS_IF([test "x$sq_fuse_tests" = xyes],[sq_tests="$sq_tests FUSE"]) AS_IF([test "x$sq_mksquashfs$sq_demo" = xyesyes],[ sq_demo_tests=yes sq_tests="$sq_tests demo" ]) AM_CONDITIONAL([SQ_FUSE_TESTS], [test "x$sq_fuse_tests" = xyes]) AM_CONDITIONAL([SQ_DEMO_TESTS], [test "x$sq_demo_tests" = xyes]) AS_IF([test "x$sq_tests" = x], [sq_tests=" none"]) AC_SUBST([sq_mksquashfs_compressors]) AC_CONFIG_FILES([tests/ll-smoke.sh],[chmod +x tests/ll-smoke.sh]) AC_CONFIG_FILES([tests/umount-test.sh],[chmod +x tests/umount-test.sh]) AS_IF([test "x$sq_high_level$sq_low_level$sq_demo" = xnonono], AC_MSG_FAILURE([Nothing left to build])) AC_ARG_ENABLE([multithreading], AS_HELP_STRING([--disable-multithreading], [disable multi-threaded low-level FUSE driver]),, [enable_multithreading="yes"]) AS_IF([test x$enable_multithreading = xyes], [ AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required for multithreaded build])) AC_DEFINE(SQFS_MULTITHREADED, 1, [Enable multi-threaded low-level FUSE driver]) ]) AM_CONDITIONAL([MULTITHREADED], [test x$enable_multithreading = xyes]) AC_ARG_ENABLE([sigterm-handler], AS_HELP_STRING([--enable-sigterm-handler], [enable lazy umount on SIGTERM in low-level FUSE driver]), [ AC_CHECK_HEADER([linux/version.h], , [], AC_MSG_ERROR([linux host required for sigterm-handler.])) AC_DEFINE(SQFS_SIGTERM_HANDLER, 1, [Enable lazy umount on SIGTERM in low-level FUSE driver]) ]) AM_CONDITIONAL([SIGTERM_HANDLER], [test x$enable_sigterm_handler = xyes]) AC_SUBST([sq_decompressors]) AC_SUBST([sq_high_level]) AC_SUBST([sq_low_level]) AC_SUBST([sq_demo]) AC_SUBST([sq_tests]) AC_CONFIG_FILES([Makefile squashfuse.pc squashfuse_ll.pc tests/lib.sh ci/features]) AC_OUTPUT AS_ECHO() AS_ECHO(["Compression support ....... :$sq_decompressors"]) AS_ECHO(["High-level FUSE driver .... : $sq_high_level"]) AS_ECHO(["Low-level FUSE driver ..... : $sq_low_level"]) AS_ECHO(["Demo program .............. : $sq_demo"]) AS_ECHO(["Tests ..................... :$sq_tests"]) AS_ECHO() squashfuse-0.5.0/decompress.c000066400000000000000000000140441450034605500162200ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "decompress.h" #include "squashfs_fs.h" #include "swap.h" #include #if _WIN32 #include "win_decompress.c.inc" #endif #ifdef HAVE_ZLIB_H #include static sqfs_err sqfs_decompressor_zlib(void *in, size_t insz, void *out, size_t *outsz) { uLongf zout = *outsz; int zerr = uncompress((Bytef*)out, &zout, in, insz); if (zerr != Z_OK) return SQFS_ERR; *outsz = zout; return SQFS_OK; } #define CAN_DECOMPRESS_ZLIB 1 #endif #ifdef HAVE_LZMA_H #include static sqfs_err sqfs_decompressor_xz(void *in, size_t insz, void *out, size_t *outsz) { /* FIXME: Save stream state, to minimize setup time? */ uint64_t memlimit = UINT64_MAX; size_t inpos = 0, outpos = 0; lzma_ret err = lzma_stream_buffer_decode(&memlimit, 0, NULL, in, &inpos, insz, out, &outpos, *outsz); if (err != LZMA_OK) return SQFS_ERR; *outsz = outpos; return SQFS_OK; } #define CAN_DECOMPRESS_XZ 1 // The following implementation was adapted from squashfs-tools // https://github.com/plougher/squashfs-tools.git #define LZMA_PROPS_SIZE 5 #define LZMA_UNCOMP_SIZE 8 #define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE) #define MEMLIMIT (32 * 1024 * 1024) static sqfs_err sqfs_decompressor_lzma(void *in, size_t insz, void *out, size_t *outsz) { lzma_stream strm = LZMA_STREAM_INIT; uint32_t uncompressed_size = 0, res; unsigned char lzma_header[LZMA_HEADER_SIZE]; res = lzma_alone_decoder(&strm, MEMLIMIT); if (res != LZMA_OK) { lzma_end(&strm); return SQFS_ERR; } memcpy(lzma_header, in, LZMA_HEADER_SIZE); uncompressed_size = *((uint32_t*)(lzma_header + LZMA_PROPS_SIZE)); sqfs_swapin32(&uncompressed_size); if (uncompressed_size > *outsz) { lzma_end(&strm); return SQFS_ERR; } memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE); strm.next_out = out; strm.avail_out = *outsz; strm.next_in = lzma_header; strm.avail_in = LZMA_HEADER_SIZE; res = lzma_code(&strm, LZMA_RUN); if (res != LZMA_OK || strm.avail_in != 0) { lzma_end(&strm); return SQFS_ERR; } strm.next_in = (uint8_t *)in + LZMA_HEADER_SIZE; strm.avail_in = insz - LZMA_HEADER_SIZE; res = lzma_code(&strm, LZMA_FINISH); lzma_end(&strm); if (res == LZMA_STREAM_END || (res == LZMA_OK && strm.total_out >= uncompressed_size && strm.avail_in == 0)) { *outsz = uncompressed_size; return SQFS_OK; } return SQFS_ERR; } #define CAN_DECOMPRESS_LZMA 1 #endif #ifdef HAVE_LZO_LZO1X_H #include static sqfs_err sqfs_decompressor_lzo(void *in, size_t insz, void *out, size_t *outsz) { lzo_uint lzout = *outsz; int err = lzo1x_decompress_safe(in, insz, out, &lzout, NULL); if (err != LZO_E_OK) return SQFS_ERR; *outsz = lzout; return SQFS_OK; } #define CAN_DECOMPRESS_LZO 1 #endif #ifdef HAVE_LZ4_H #include static sqfs_err sqfs_decompressor_lz4(void *in, size_t insz, void *out, size_t *outsz) { int lz4out = LZ4_decompress_safe (in, out, insz, *outsz); if (lz4out < 0) return SQFS_ERR; *outsz = lz4out; return SQFS_OK; } #define CAN_DECOMPRESS_LZ4 1 #endif #ifdef HAVE_ZSTD_H #include static sqfs_err sqfs_decompressor_zstd(void *in, size_t insz, void *out, size_t *outsz) { const size_t zstdout = ZSTD_decompress(out, *outsz, in, insz); if (ZSTD_isError(zstdout)) return SQFS_ERR; *outsz = zstdout; return SQFS_OK; } #define CAN_DECOMPRESS_ZSTD 1 #endif sqfs_decompressor sqfs_decompressor_get(sqfs_compression_type type) { switch (type) { #ifdef CAN_DECOMPRESS_ZLIB case ZLIB_COMPRESSION: return &sqfs_decompressor_zlib; #endif #ifdef CAN_DECOMPRESS_LZMA case LZMA_COMPRESSION: return &sqfs_decompressor_lzma; #endif #ifdef CAN_DECOMPRESS_LZO case LZO_COMPRESSION: return &sqfs_decompressor_lzo; #endif #ifdef CAN_DECOMPRESS_XZ case XZ_COMPRESSION: return &sqfs_decompressor_xz; #endif #ifdef CAN_DECOMPRESS_LZ4 case LZ4_COMPRESSION: return &sqfs_decompressor_lz4; #endif #ifdef CAN_DECOMPRESS_ZSTD case ZSTD_COMPRESSION: return &sqfs_decompressor_zstd; #endif default: return NULL; } } static char *const sqfs_compression_names[SQFS_COMP_MAX] = { NULL, "zlib", "lzma", "lzo", "xz", "lz4", "zstd", }; char *sqfs_compression_name(sqfs_compression_type type) { if (type < 0 || type >= SQFS_COMP_MAX) return NULL; return sqfs_compression_names[type]; } void sqfs_compression_supported(sqfs_compression_type *types) { size_t i = 0; memset(types, SQFS_COMP_UNKNOWN, SQFS_COMP_MAX * sizeof(*types)); #ifdef CAN_DECOMPRESS_ZLIB types[i++] = ZLIB_COMPRESSION; #endif #ifdef CAN_DECOMPRESS_LZMA types[i++] = LZMA_COMPRESSION; #endif #ifdef CAN_DECOMPRESS_LZO types[i++] = LZO_COMPRESSION; #endif #ifdef CAN_DECOMPRESS_XZ types[i++] = XZ_COMPRESSION; #endif #ifdef CAN_DECOMPRESS_LZ4 types[i++] = LZ4_COMPRESSION; #endif #ifdef CAN_DECOMPRESS_ZSTD types[i++] = ZSTD_COMPRESSION; #endif } squashfuse-0.5.0/decompress.h000066400000000000000000000034751450034605500162330ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_DECOMPRESS_H #define SQFS_DECOMPRESS_H #include "common.h" #define SQFS_COMP_UNKNOWN 0 #define SQFS_COMP_MAX 16 typedef int sqfs_compression_type; char *sqfs_compression_name(sqfs_compression_type type); /* put supported compression types into an array */ void sqfs_compression_supported(sqfs_compression_type *types); typedef sqfs_err (*sqfs_decompressor)(void *in, size_t insz, void *out, size_t *outsz); sqfs_decompressor sqfs_decompressor_get(sqfs_compression_type type); #endif squashfuse-0.5.0/dir.c000066400000000000000000000206011450034605500146260ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "dir.h" #include "fs.h" #include "swap.h" #include #include #include /* Read some directory metadata, updating the dir structure as necessary */ static sqfs_err sqfs_dir_md_read(sqfs *fs, sqfs_dir *dir, void *buf, size_t size); /* Fast forwards to a directory header. */ typedef sqfs_err sqfs_dir_header_f(sqfs *fs, sqfs_md_cursor *cur, struct squashfs_dir_index *index, bool *stop, void *arg); static sqfs_err sqfs_dir_ff_header(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir, sqfs_dir_header_f func, void *arg); /* Fast forward a directory to the given offset. Return error if it doesn't exist. */ static sqfs_err sqfs_dir_ff_offset(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir, sqfs_off_t offset); static sqfs_err sqfs_dir_md_read(sqfs *fs, sqfs_dir *dir, void *buf, size_t size) { dir->offset += size; return sqfs_md_read(fs, &dir->cur, buf, size); } sqfs_err sqfs_dir_open(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir, off_t offset) { if (!S_ISDIR(inode->base.mode)) return SQFS_ERR; memset(dir, 0, sizeof(*dir)); dir->cur.block = inode->xtra.dir.start_block + fs->sb.directory_table_start; dir->cur.offset = inode->xtra.dir.offset; dir->offset = 0; dir->total = inode->xtra.dir.dir_size <= 3 ? 0 : inode->xtra.dir.dir_size - 3; if (offset) { /* Fast forward to the given offset */ sqfs_err err = sqfs_dir_ff_offset(fs, inode, dir, offset); if (err) return err; } return SQFS_OK; } void sqfs_dentry_init(sqfs_dir_entry *entry, char *namebuf) { entry->name = namebuf; } sqfs_off_t sqfs_dentry_offset(sqfs_dir_entry *entry) { return entry->offset; } sqfs_off_t sqfs_dentry_next_offset(sqfs_dir_entry *entry) { return entry->next_offset; } int sqfs_dentry_type(sqfs_dir_entry *entry) { return entry->type; } sqfs_mode_t sqfs_dentry_mode(sqfs_dir_entry *entry) { return sqfs_mode(sqfs_dentry_type(entry)); } sqfs_inode_id sqfs_dentry_inode(sqfs_dir_entry *entry) { return entry->inode; } sqfs_inode_num sqfs_dentry_inode_num(sqfs_dir_entry *entry) { return entry->inode_number; } size_t sqfs_dentry_name_size(sqfs_dir_entry *entry) { return entry->name_size; } const char *sqfs_dentry_name(sqfs_dir_entry *entry) { if (!entry->name) return NULL; entry->name[sqfs_dentry_name_size(entry)] = '\0'; return entry->name; } bool sqfs_dentry_is_dir(sqfs_dir_entry *entry) { return S_ISDIR(sqfs_dentry_mode(entry)); } bool sqfs_dir_next(sqfs *fs, sqfs_dir *dir, sqfs_dir_entry *entry, sqfs_err *err) { struct squashfs_dir_entry e; *err = SQFS_OK; entry->offset = dir->offset; while (dir->header.count == 0) { if (dir->offset >= dir->total) return false; if ((*err = sqfs_dir_md_read(fs, dir, &dir->header, sizeof(dir->header)))) return false; sqfs_swapin_dir_header(&dir->header); ++(dir->header.count); /* biased by one */ } if ((*err = sqfs_dir_md_read(fs, dir, &e, sizeof(e)))) return false; sqfs_swapin_dir_entry(&e); --(dir->header.count); entry->type = e.type; entry->name_size = e.size + 1; entry->inode = ((uint64_t)dir->header.start_block << 16) + e.offset; /* e.inode_number is signed */ entry->inode_number = dir->header.inode_number + (int16_t)e.inode_number; *err = sqfs_dir_md_read(fs, dir, entry->name, sqfs_dentry_name_size(entry)); if (*err) return false; entry->next_offset = dir->offset; return true; } static sqfs_err sqfs_dir_ff_header(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir, sqfs_dir_header_f func, void *arg) { struct squashfs_dir_index idx; sqfs_md_cursor cur = inode->next; size_t count = inode->xtra.dir.idx_count; if (count == 0) return SQFS_OK; while (count--) { sqfs_err err; bool stop = 0; if ((err = sqfs_md_read(fs, &cur, &idx, sizeof(idx)))) return err; sqfs_swapin_dir_index(&idx); if ((err = func(fs, &cur, &idx, &stop, arg))) return err; if (stop) break; dir->cur.block = idx.start_block + fs->sb.directory_table_start; dir->offset = idx.index; } dir->cur.offset = (dir->cur.offset + dir->offset) % SQUASHFS_METADATA_SIZE; return SQFS_OK; } /* Helper for sqfs_dir_ff_offset */ static sqfs_err sqfs_dir_ff_offset_f(sqfs *fs, sqfs_md_cursor *cur, struct squashfs_dir_index *index, bool *stop, void *arg) { sqfs_off_t offset = *(sqfs_off_t*)arg; if (index->index >= offset) { *stop = true; return SQFS_OK; } return sqfs_md_read(fs, cur, NULL, index->size + 1); /* skip name */ } static sqfs_err sqfs_dir_ff_offset(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir, sqfs_off_t offset) { sqfs_err err; sqfs_dir_entry entry; err = sqfs_dir_ff_header(fs, inode, dir, sqfs_dir_ff_offset_f, &offset); if (err) return err; sqfs_dentry_init(&entry, NULL); while (dir->offset < offset && sqfs_dir_next(fs, dir, &entry, &err)) ; /* pass */ if (err) return err; return dir->offset == offset ? SQFS_OK : SQFS_ERR; } /* Helper for sqfs_dir_lookup */ typedef struct { const char *cmp; size_t cmplen; char *name; } sqfs_dir_ff_name_t; static sqfs_err sqfs_dir_ff_name_f(sqfs *fs, sqfs_md_cursor *cur, struct squashfs_dir_index *index, bool *stop, void *arg) { sqfs_err err; sqfs_dir_ff_name_t *args = (sqfs_dir_ff_name_t*)arg; size_t name_size = index->size + 1; if ((err = sqfs_md_read(fs, cur, args->name, name_size))) return err; args->name[name_size] = '\0'; int order = strncmp(args->name, args->cmp, args->cmplen); if (order > 0 || (order == 0 && name_size > args->cmplen)) *stop = true; return SQFS_OK; } sqfs_err sqfs_dir_lookup(sqfs *fs, sqfs_inode *inode, const char *name, size_t namelen, sqfs_dir_entry *entry, bool *found) { sqfs_err err; sqfs_dir dir; sqfs_dir_ff_name_t arg; *found = false; if ((err = sqfs_dir_open(fs, inode, &dir, 0))) return err; /* Fast forward to header */ arg.cmp = name; arg.cmplen = namelen; arg.name = entry->name; if ((err = sqfs_dir_ff_header(fs, inode, &dir, sqfs_dir_ff_name_f, &arg))) return err; /* Iterate to find the right entry */ while (sqfs_dir_next(fs, &dir, entry, &err)) { int order = strncmp(sqfs_dentry_name(entry), name, namelen); if (order == 0 && sqfs_dentry_name_size(entry) == namelen) *found = true; if (order >= 0) break; } return err; } sqfs_err sqfs_lookup_path_with_id(sqfs *fs, sqfs_inode *inode, const char *path, bool *found, sqfs_inode_id *id) { sqfs_err err; sqfs_name buf; sqfs_dir_entry entry; *found = false; sqfs_dentry_init(&entry, buf); while (*path) { const char *name; size_t size; bool dfound; /* Find next path component */ while (*path == '/') /* skip leading slashes */ ++path; name = path; while (*path && *path != '/') ++path; size = path - name; if (size == 0) /* we're done */ break; if ((err = sqfs_dir_lookup(fs, inode, name, size, &entry, &dfound))) return err; if (!dfound) return SQFS_OK; /* not found */ if (id) *id = sqfs_dentry_inode(&entry); if ((err = sqfs_inode_get(fs, inode, sqfs_dentry_inode(&entry)))) return err; } *found = true; return SQFS_OK; } sqfs_err sqfs_lookup_path(sqfs *fs, sqfs_inode *inode, const char *path, bool *found) { return sqfs_lookup_path_with_id(fs, inode, path, found, NULL); }; squashfuse-0.5.0/dir.h000066400000000000000000000073651450034605500146470ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_DIR_H #define SQFS_DIR_H #include "common.h" #include "squashfs_fs.h" typedef struct { sqfs_md_cursor cur; sqfs_off_t offset, total; struct squashfs_dir_header header; } sqfs_dir; typedef struct { sqfs_inode_id inode; sqfs_inode_num inode_number; int type; char *name; size_t name_size; sqfs_off_t offset, next_offset; } sqfs_dir_entry; typedef char sqfs_name[SQUASHFS_NAME_LEN + 1]; /* Begin a directory traversal, initializing the dir structure. If offset is non-zero, fast-forward to that offset in the directory. */ sqfs_err sqfs_dir_open(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir, off_t offset); /* Initialize a dir_entry structure before use. 'namebuf' should be a character buffer of enough size to hold any name, see sqfs_name. It may also be NULL, in which case no names will be placed into this dir_entry. */ void sqfs_dentry_init(sqfs_dir_entry *entry, char *namebuf); /* Get the next directory entry, filling in the dir_entry. Returns false when out of entries, or on error. */ bool sqfs_dir_next(sqfs *fs, sqfs_dir *dir, sqfs_dir_entry *entry, sqfs_err *err); /* Lookup an entry in a directory inode. The dir_entry must have been initialized with a buffer. */ sqfs_err sqfs_dir_lookup(sqfs *fs, sqfs_inode *inode, const char *name, size_t namelen, sqfs_dir_entry *entry, bool *found); /* Lookup a complete path, and replace *inode with the results. Uses / (slash) as the directory separator. */ sqfs_err sqfs_lookup_path(sqfs *fs, sqfs_inode *inode, const char *path, bool *found); /* Similar to sqfs_lookup_path, additionaly replaces *id with sqfs_inode_id of inode */ sqfs_err sqfs_lookup_path_with_id(sqfs *fs, sqfs_inode *inode, const char *path, bool *found, sqfs_inode_id *id); /* Accessors on sqfs_dir_entry */ sqfs_off_t sqfs_dentry_offset (sqfs_dir_entry *entry); sqfs_off_t sqfs_dentry_next_offset (sqfs_dir_entry *entry); int sqfs_dentry_type (sqfs_dir_entry *entry); sqfs_mode_t sqfs_dentry_mode (sqfs_dir_entry *entry); sqfs_inode_id sqfs_dentry_inode (sqfs_dir_entry *entry); sqfs_inode_num sqfs_dentry_inode_num (sqfs_dir_entry *entry); size_t sqfs_dentry_name_size (sqfs_dir_entry *entry); bool sqfs_dentry_is_dir (sqfs_dir_entry *entry); /* Yields the name of this directory entry, or NULL if the dir_entry structure was initialized without a name buffer. Name will be nul-terminated. */ const char * sqfs_dentry_name (sqfs_dir_entry *entry); #endif squashfuse-0.5.0/extract.c000066400000000000000000000132311450034605500155230ustar00rootroot00000000000000#include "nonstd.h" #include "squashfs_fs.h" #include "squashfuse.h" #include "stat.h" #include #include #include #include #include #include #define PROGNAME "squashfuse_extract" #define ERR_MISC (1) #define ERR_USAGE (2) #define ERR_OPEN (3) static void usage() { fprintf(stderr, "Usage: %s ARCHIVE PATH_TO_EXTRACT\n", PROGNAME); fprintf(stderr, " %s ARCHIVE -a\n", PROGNAME); exit(ERR_USAGE); } static void die(const char *msg) { fprintf(stderr, "%s\n", msg); exit(ERR_MISC); } static bool starts_with(const char *pre, const char *str) { size_t lenpre = strlen(pre), lenstr = strlen(str); return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0; } int main(int argc, char *argv[]) { sqfs_err err = SQFS_OK; sqfs_traverse trv; sqfs fs; char *image; char *path_to_extract; char *prefix; char prefixed_path_to_extract[1024]; struct stat st; prefix = "squashfs-root/"; if (access(prefix, F_OK ) == -1 ) { if (mkdir(prefix, 0777) == -1) { perror("mkdir error"); exit(EXIT_FAILURE); } } if (argc != 3) usage(); image = argv[1]; path_to_extract = argv[2]; if ((err = sqfs_open_image(&fs, image, 0))) exit(ERR_OPEN); if ((err = sqfs_traverse_open(&trv, &fs, sqfs_inode_root(&fs)))) die("sqfs_traverse_open error"); while (sqfs_traverse_next(&trv, &err)) { if (!trv.dir_end) { if ((starts_with(path_to_extract, trv.path) != 0) || (strcmp("-a", path_to_extract) == 0)){ fprintf(stderr, "trv.path: %s\n", trv.path); fprintf(stderr, "sqfs_inode_id: %llu\n", (unsigned long long)trv.entry.inode); sqfs_inode inode; if (sqfs_inode_get(&fs, &inode, trv.entry.inode)) die("sqfs_inode_get error"); fprintf(stderr, "inode.base.inode_type: %i\n", inode.base.inode_type); fprintf(stderr, "inode.xtra.reg.file_size: %llu\n", (unsigned long long)inode.xtra.reg.file_size); strcpy(prefixed_path_to_extract, ""); strcat(strcat(prefixed_path_to_extract, prefix), trv.path); if (inode.base.inode_type == SQUASHFS_DIR_TYPE){ fprintf(stderr, "inode.xtra.dir.parent_inode: %ui\n", inode.xtra.dir.parent_inode); fprintf(stderr, "mkdir: %s/\n", prefixed_path_to_extract); if (access(prefixed_path_to_extract, F_OK ) == -1 ) { if (mkdir(prefixed_path_to_extract, 0777) == -1) { perror("mkdir error"); exit(1); } } } else if (inode.base.inode_type == SQUASHFS_REG_TYPE){ fprintf(stderr, "Extract to: %s\n", prefixed_path_to_extract); if (sqfs_stat(&fs, &inode, &st) != 0) die("sqfs_stat error"); printf("Permissions: "); printf( (S_ISDIR(st.st_mode)) ? "d" : "-"); printf( (st.st_mode & S_IRUSR) ? "r" : "-"); printf( (st.st_mode & S_IWUSR) ? "w" : "-"); printf( (st.st_mode & S_IXUSR) ? "x" : "-"); printf( (st.st_mode & S_IRGRP) ? "r" : "-"); printf( (st.st_mode & S_IWGRP) ? "w" : "-"); printf( (st.st_mode & S_IXGRP) ? "x" : "-"); printf( (st.st_mode & S_IROTH) ? "r" : "-"); printf( (st.st_mode & S_IWOTH) ? "w" : "-"); printf( (st.st_mode & S_IXOTH) ? "x" : "-"); printf("\n"); // Read the file in chunks off_t bytes_already_read = 0; sqfs_off_t bytes_at_a_time = 64*1024; FILE * f; f = fopen (prefixed_path_to_extract, "w+"); if (f == NULL) die("fopen error"); while (bytes_already_read < inode.xtra.reg.file_size) { char buf[bytes_at_a_time]; if (sqfs_read_range(&fs, &inode, (sqfs_off_t) bytes_already_read, &bytes_at_a_time, buf)) die("sqfs_read_range error"); // fwrite(buf, 1, bytes_at_a_time, stdout); fwrite(buf, 1, bytes_at_a_time, f); bytes_already_read = bytes_already_read + bytes_at_a_time; } fclose(f); chmod (prefixed_path_to_extract, st.st_mode); } else if (inode.base.inode_type == SQUASHFS_SYMLINK_TYPE){ size_t size = strlen(trv.path)+1; char buf[size]; int ret = sqfs_readlink(&fs, &inode, buf, &size); if (ret != 0) die("sqfs_readlink error"); fprintf(stderr, "Symlink: %s to %s \n", prefixed_path_to_extract, buf); unlink(prefixed_path_to_extract); ret = sqfs_symlink(buf, prefixed_path_to_extract); if (ret != 0) die("symlink error"); } else { fprintf(stderr, "TODO: Implement inode.base.inode_type %i\n", inode.base.inode_type); } fprintf(stderr, "\n"); } } } if (err) die("sqfs_traverse_next error"); sqfs_traverse_close(&trv); sqfs_fd_close(fs.fd); return 0; } squashfuse-0.5.0/file.c000066400000000000000000000215441450034605500147760ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "file.h" #include "fs.h" #include "swap.h" #include "table.h" #include #include #include sqfs_err sqfs_frag_entry(sqfs *fs, struct squashfs_fragment_entry *frag, uint32_t idx) { sqfs_err err = SQFS_OK; if (idx == SQUASHFS_INVALID_FRAG) return SQFS_ERR; err = sqfs_table_get(&fs->frag_table, fs, idx, frag); sqfs_swapin_fragment_entry(frag); return err; } sqfs_err sqfs_frag_block(sqfs *fs, sqfs_inode *inode, size_t *offset, size_t *size, sqfs_block **block) { struct squashfs_fragment_entry frag; sqfs_err err = SQFS_OK; if (!S_ISREG(inode->base.mode)) return SQFS_ERR; err = sqfs_frag_entry(fs, &frag, inode->xtra.reg.frag_idx); if (err) return err; err = sqfs_data_cache(fs, &fs->frag_cache, frag.start_block, frag.size, block); if (err) return SQFS_ERR; *offset = inode->xtra.reg.frag_off; *size = inode->xtra.reg.file_size % fs->sb.block_size; return SQFS_OK; } size_t sqfs_blocklist_count(sqfs *fs, sqfs_inode *inode) { uint64_t size = inode->xtra.reg.file_size; size_t block = fs->sb.block_size; if (inode->xtra.reg.frag_idx == SQUASHFS_INVALID_FRAG) { return sqfs_divceil(size, block); } else { return (size_t)(size / block); } } void sqfs_blocklist_init(sqfs *fs, sqfs_inode *inode, sqfs_blocklist *bl) { bl->fs = fs; bl->remain = sqfs_blocklist_count(fs, inode); bl->cur = inode->next; bl->started = false; bl->pos = 0; bl->block = inode->xtra.reg.start_block; bl->input_size = 0; } sqfs_err sqfs_blocklist_next(sqfs_blocklist *bl) { sqfs_err err = SQFS_OK; bool compressed; if (bl->remain == 0) return SQFS_ERR; --(bl->remain); err = sqfs_md_read(bl->fs, &bl->cur, &bl->header, sizeof(bl->header)); if (err) return err; sqfs_swapin32(&bl->header); bl->block += bl->input_size; sqfs_data_header(bl->header, &compressed, &bl->input_size); if (bl->started) bl->pos += bl->fs->sb.block_size; bl->started = true; return SQFS_OK; } sqfs_err sqfs_read_range(sqfs *fs, sqfs_inode *inode, sqfs_off_t start, sqfs_off_t *size, void *buf) { sqfs_err err = SQFS_OK; sqfs_off_t file_size; size_t block_size; sqfs_blocklist bl; size_t read_off; char *buf_orig; if (!S_ISREG(inode->base.mode)) return SQFS_ERR; file_size = inode->xtra.reg.file_size; block_size = fs->sb.block_size; if (*size < 0 || start > file_size) return SQFS_ERR; if (start == file_size) { *size = 0; return SQFS_OK; } err = sqfs_blockidx_blocklist(fs, inode, &bl, start); if (err) return err; read_off = start % block_size; buf_orig = buf; while (*size > 0) { sqfs_block *block = NULL; size_t data_off, data_size; size_t take; bool fragment = (bl.remain == 0); if (fragment) { /* fragment */ if (inode->xtra.reg.frag_idx == SQUASHFS_INVALID_FRAG) break; err = sqfs_frag_block(fs, inode, &data_off, &data_size, &block); if (err) return err; } else { if ((err = sqfs_blocklist_next(&bl))) return err; if (bl.pos + block_size <= start) continue; data_off = 0; if (bl.input_size == 0) { /* Hole! */ data_size = (size_t)(file_size - bl.pos); if (data_size > block_size) data_size = block_size; } else { err = sqfs_data_cache(fs, &fs->data_cache, bl.block, bl.header, &block); if (err) return err; data_size = block->size; } } take = data_size - read_off; if (take > *size) take = (size_t)(*size); if (block) { memcpy(buf, (char*)block->data + data_off + read_off, take); sqfs_block_dispose(block); } else { memset(buf, 0, take); } read_off = 0; *size -= take; buf = (char*)buf + take; if (fragment) break; } *size = (char*)buf - buf_orig; return *size ? SQFS_OK : SQFS_ERR; } /* To read block N of a M-block file, we have to read N blocksizes from the, metadata. This is a lot of work for large files! So for those files, we use an index to speed it up. The M blocksizes are split between M / SQUASHFS_METADATA_SIZE MD-blocks. For each of these blocks, we maintain in the index the location of the MD-block, and the location of the data block corresponding to the start of that MD-block. Then to read block N, we just calculate which metadata block index ("metablock") we want, and get that block-index entry. Then we only need to read that one MD-block to seek within the file. */ /* Is a file worth indexing? */ static bool sqfs_blockidx_indexable(sqfs *fs, sqfs_inode *inode) { size_t blocks = sqfs_blocklist_count(fs, inode); size_t md_size = blocks * sizeof(sqfs_blocklist_entry); return md_size >= SQUASHFS_METADATA_SIZE; } static void sqfs_blockidx_dispose(void *data) { free(*(sqfs_blockidx_entry**)data); } sqfs_err sqfs_blockidx_init(sqfs_cache *cache) { return sqfs_cache_init(cache, sizeof(sqfs_blockidx_entry**), SQUASHFS_META_SLOTS, &sqfs_blockidx_dispose); } sqfs_err sqfs_blockidx_add(sqfs *fs, sqfs_inode *inode, sqfs_blockidx_entry **out, sqfs_blockidx_entry **cachep) { size_t blocks; /* Number of blocks in the file */ size_t md_size; /* Amount of metadata necessary to hold the blocksizes */ size_t count; /* Number of block-index entries necessary */ sqfs_blockidx_entry *blockidx; sqfs_blocklist bl; size_t i = 0; bool first = true; *out = NULL; blocks = sqfs_blocklist_count(fs, inode); md_size = blocks * sizeof(sqfs_blocklist_entry); count = (inode->next.offset + md_size - 1) / SQUASHFS_METADATA_SIZE; blockidx = malloc(count * sizeof(sqfs_blockidx_entry)); if (!blockidx) return SQFS_ERR; sqfs_blocklist_init(fs, inode, &bl); while (bl.remain && i < count) { sqfs_err err = SQFS_OK; /* If the MD cursor offset is small, we found a new MD-block. * Skip the first MD-block, because we already know where it is: * inode->next.offset */ if (bl.cur.offset < sizeof(sqfs_blocklist_entry) && !first) { blockidx[i].data_block = bl.block + bl.input_size; blockidx[i++].md_block = (uint32_t)(bl.cur.block - fs->sb.inode_table_start); } first = false; err = sqfs_blocklist_next(&bl); if (err) { free(blockidx); return SQFS_ERR; } } *out = *cachep = blockidx; return SQFS_OK; } sqfs_err sqfs_blockidx_blocklist(sqfs *fs, sqfs_inode *inode, sqfs_blocklist *bl, sqfs_off_t start) { size_t block, metablock, skipped; sqfs_blockidx_entry *blockidx, **bp; sqfs_cache_idx idx; sqfs_blocklist_init(fs, inode, bl); block = (size_t)(start / fs->sb.block_size); if (block > bl->remain) { /* fragment */ bl->remain = 0; return SQFS_OK; } /* How many MD-blocks do we want to skip? */ metablock = (bl->cur.offset + block * sizeof(sqfs_blocklist_entry)) / SQUASHFS_METADATA_SIZE; if (metablock == 0) return SQFS_OK; /* no skip needed, don't want an index */ if (!sqfs_blockidx_indexable(fs, inode)) return SQFS_OK; /* too small to index */ /* Get the index, creating it if necessary */ idx = inode->base.inode_number + 1; /* zero means invalid index */ bp = sqfs_cache_get(&fs->blockidx, idx); if (sqfs_cache_entry_valid(&fs->blockidx, bp)) { blockidx = *bp; } else { sqfs_err err = sqfs_blockidx_add(fs, inode, &blockidx, bp); if (err) { sqfs_cache_put(&fs->blockidx, bp); return err; } sqfs_cache_entry_mark_valid(&fs->blockidx, bp); } skipped = (metablock * SQUASHFS_METADATA_SIZE / sizeof(sqfs_blocklist_entry)) - (bl->cur.offset / sizeof(sqfs_blocklist_entry)); blockidx += metablock - 1; bl->cur.block = blockidx->md_block + fs->sb.inode_table_start; bl->cur.offset %= sizeof(sqfs_blocklist_entry); bl->remain -= skipped; bl->pos = (uint64_t)skipped * fs->sb.block_size; bl->block = blockidx->data_block; sqfs_cache_put(&fs->blockidx, bp); return SQFS_OK; } squashfuse-0.5.0/file.h000066400000000000000000000054441450034605500150040ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_FILE_H #define SQFS_FILE_H #include "common.h" #include "squashfs_fs.h" #include "cache.h" sqfs_err sqfs_frag_entry(sqfs *fs, struct squashfs_fragment_entry *frag, uint32_t idx); sqfs_err sqfs_frag_block(sqfs *fs, sqfs_inode *inode, size_t *offset, size_t *size, sqfs_block **block); typedef uint32_t sqfs_blocklist_entry; typedef struct { sqfs *fs; size_t remain; /* How many blocks left in the file? */ sqfs_md_cursor cur; /* Points to next blocksize in MD */ bool started; uint64_t pos; uint64_t block; /* Points to next data block location */ sqfs_blocklist_entry header; /* Packed blocksize data */ uint32_t input_size; /* Extracted size of this block */ } sqfs_blocklist; size_t sqfs_blocklist_count(sqfs *fs, sqfs_inode *inode); void sqfs_blocklist_init(sqfs *fs, sqfs_inode *inode, sqfs_blocklist *bl); sqfs_err sqfs_blocklist_next(sqfs_blocklist *bl); sqfs_err sqfs_read_range(sqfs *fs, sqfs_inode *inode, sqfs_off_t start, sqfs_off_t *size, void *buf); /*** Block index for skipping to the middle of large files ***/ typedef struct { uint64_t data_block; /* A data block where the file continues */ uint32_t md_block; /* A metadata block with blocksizes that continue from data_block */ } sqfs_blockidx_entry; sqfs_err sqfs_blockidx_init(sqfs_cache *cache); /* Get a blocklist fast-forwarded to the correct location */ sqfs_err sqfs_blockidx_blocklist(sqfs *fs, sqfs_inode *inode, sqfs_blocklist *bl, sqfs_off_t start); #endif squashfuse-0.5.0/fs.c000066400000000000000000000336401450034605500144670ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fs.h" #include "file.h" #include "dir.h" #include "nonstd.h" #include "swap.h" #include "xattr.h" #include #include #include #ifdef SQFS_MULTITHREADED # define DATA_CACHED_BLKS 48 # define FRAG_CACHED_BLKS 48 #else # define DATA_CACHED_BLKS 1 # define FRAG_CACHED_BLKS 3 #endif void sqfs_version_supported(int *min_major, int *min_minor, int *max_major, int *max_minor) { *min_major = *max_major = SQUASHFS_MAJOR; *min_minor = 0; *max_minor = SQUASHFS_MINOR; } void sqfs_version(sqfs *fs, int *major, int *minor) { *major = fs->sb.s_major; *minor = fs->sb.s_minor; } sqfs_compression_type sqfs_compression(sqfs *fs) { return fs->sb.compression; } sqfs_err sqfs_init_with_subdir(sqfs *fs, sqfs_fd_t fd, size_t offset, const char *subdir) { sqfs_err err = SQFS_OK; memset(fs, 0, sizeof(*fs)); fs->fd = fd; fs->offset = offset; if (sqfs_pread(fd, &fs->sb, sizeof(fs->sb), fs->offset) != sizeof(fs->sb)) return SQFS_BADFORMAT; sqfs_swapin_super_block(&fs->sb); if (fs->sb.s_magic != SQUASHFS_MAGIC) { if (fs->sb.s_magic != SQFS_MAGIC_SWAP) return SQFS_BADFORMAT; sqfs_swap16(&fs->sb.s_major); sqfs_swap16(&fs->sb.s_minor); } if (fs->sb.s_major != SQUASHFS_MAJOR || fs->sb.s_minor > SQUASHFS_MINOR) return SQFS_BADVERSION; if (!(fs->decompressor = sqfs_decompressor_get(fs->sb.compression))) return SQFS_BADCOMP; err = sqfs_table_init(&fs->id_table, fd, fs->sb.id_table_start + fs->offset, sizeof(uint32_t), fs->sb.no_ids); err |= sqfs_table_init(&fs->frag_table, fd, fs->sb.fragment_table_start + fs->offset, sizeof(struct squashfs_fragment_entry), fs->sb.fragments); if (sqfs_export_ok(fs)) { err |= sqfs_table_init(&fs->export_table, fd, fs->sb.lookup_table_start + fs->offset, sizeof(uint64_t), fs->sb.inodes); } err |= sqfs_xattr_init(fs); err |= sqfs_block_cache_init(&fs->md_cache, SQUASHFS_CACHED_BLKS); err |= sqfs_block_cache_init(&fs->data_cache, DATA_CACHED_BLKS); err |= sqfs_block_cache_init(&fs->frag_cache, FRAG_CACHED_BLKS); err |= sqfs_blockidx_init(&fs->blockidx); if (subdir && subdir[0] != '\0') { sqfs_inode root; err |= sqfs_inode_get(fs, &root, sqfs_inode_root(fs)); sqfs_inode_id new_root; bool found = false; err |= sqfs_lookup_path_with_id(fs, &root, subdir, &found, &new_root); if (!found) { sqfs_destroy(fs); return SQFS_ERR; } fs->sb.root_inode = new_root; } if (err) { sqfs_destroy(fs); return SQFS_ERR; } return SQFS_OK; } sqfs_err sqfs_init(sqfs *fs, sqfs_fd_t fd, size_t offset) { return sqfs_init_with_subdir(fs, fd, offset, NULL); } void sqfs_destroy(sqfs *fs) { sqfs_table_destroy(&fs->id_table); sqfs_table_destroy(&fs->frag_table); if (sqfs_export_ok(fs)) sqfs_table_destroy(&fs->export_table); sqfs_cache_destroy(&fs->md_cache); sqfs_cache_destroy(&fs->data_cache); sqfs_cache_destroy(&fs->frag_cache); sqfs_cache_destroy(&fs->blockidx); } void sqfs_md_header(uint16_t hdr, bool *compressed, uint16_t *size) { *compressed = !(hdr & SQUASHFS_COMPRESSED_BIT); *size = hdr & ~SQUASHFS_COMPRESSED_BIT; if (!*size) *size = SQUASHFS_COMPRESSED_BIT; } void sqfs_data_header(uint32_t hdr, bool *compressed, uint32_t *size) { *compressed = !(hdr & SQUASHFS_COMPRESSED_BIT_BLOCK); *size = hdr & ~SQUASHFS_COMPRESSED_BIT_BLOCK; } sqfs_err sqfs_block_read(sqfs *fs, sqfs_off_t pos, bool compressed, uint32_t size, size_t outsize, sqfs_block **block) { sqfs_err err = SQFS_ERR; if (!(*block = malloc(sizeof(**block)))) return SQFS_ERR; /* start with refcount one, so dispose on failure path works as expected. */ (*block)->refcount = 1; if (!((*block)->data = malloc(size))) goto error; if (sqfs_pread(fs->fd, (*block)->data, size, pos + fs->offset) != size) goto error; if (compressed) { char *decomp = malloc(outsize); if (!decomp) goto error; err = fs->decompressor((*block)->data, size, decomp, &outsize); if (err) { free(decomp); goto error; } free((*block)->data); (*block)->data = decomp; (*block)->size = outsize; } else { (*block)->size = size; } return SQFS_OK; error: sqfs_block_dispose(*block); *block = NULL; return err; } sqfs_err sqfs_md_block_read(sqfs *fs, sqfs_off_t pos, size_t *data_size, sqfs_block **block) { sqfs_err err = SQFS_OK; uint16_t hdr; bool compressed; uint16_t size; *data_size = 0; if (sqfs_pread(fs->fd, &hdr, sizeof(hdr), pos + fs->offset) != sizeof(hdr)) return SQFS_ERR; pos += sizeof(hdr); *data_size += sizeof(hdr); sqfs_swapin16(&hdr); sqfs_md_header(hdr, &compressed, &size); err = sqfs_block_read(fs, pos, compressed, size, SQUASHFS_METADATA_SIZE, block); *data_size += size; return err; } sqfs_err sqfs_data_block_read(sqfs *fs, sqfs_off_t pos, uint32_t hdr, sqfs_block **block) { bool compressed; uint32_t size; sqfs_data_header(hdr, &compressed, &size); return sqfs_block_read(fs, pos, compressed, size, fs->sb.block_size, block); } sqfs_err sqfs_md_cache(sqfs *fs, sqfs_off_t *pos, sqfs_block **block) { sqfs_block_cache_entry *entry = sqfs_cache_get(&fs->md_cache, *pos); if (!sqfs_cache_entry_valid(&fs->md_cache, entry)) { sqfs_err err = SQFS_OK; /* fprintf(stderr, "MD BLOCK: %12llx\n", (long long)*pos); */ err = sqfs_md_block_read(fs, *pos, &entry->data_size, &entry->block); if (err) { sqfs_cache_put(&fs->md_cache, entry); return err; } sqfs_cache_entry_mark_valid(&fs->md_cache, entry); } /* block is created with refcount 1, which accounts for presence in the * cache (will be decremented on eviction). * * We increment it here as a convienience for the caller, who will * obviously want one. Therefore all callers must eventually call deref * by means of calling sqfs_block_dispose(). */ *block = entry->block; *pos += entry->data_size; sqfs_block_ref(entry->block); /* it is now safe to evict the entry from the cache, we have a * reference to the block so eviction will not destroy it. */ sqfs_cache_put(&fs->md_cache, entry); return SQFS_OK; } sqfs_err sqfs_data_cache(sqfs *fs, sqfs_cache *cache, sqfs_off_t pos, uint32_t hdr, sqfs_block **block) { sqfs_block_cache_entry *entry = sqfs_cache_get(cache, pos); if (!sqfs_cache_entry_valid(cache, entry)) { sqfs_err err = SQFS_OK; err = sqfs_data_block_read(fs, pos, hdr, &entry->block); if (err) { sqfs_cache_put(cache, entry); return err; } sqfs_cache_entry_mark_valid(cache, entry); } /* block is created with refcount 1, which accounts for presence in the * cache (will be decremented on eviction). * * We increment it here as a convenience for the caller, who will * obviously want one. Therefore all callers must eventually call deref * by means of calling sqfs_block_dispose(). */ *block = entry->block; sqfs_block_ref(*block); /* it is now safe to evict the entry from the cache, we have a * reference to the block so eviction will not destroy it. */ sqfs_cache_put(cache, entry); return SQFS_OK; } void sqfs_block_dispose(sqfs_block *block) { if (sqfs_block_deref(block)) { free(block->data); free(block); } } static void sqfs_block_cache_dispose(void *data) { sqfs_block_cache_entry *entry = (sqfs_block_cache_entry*)data; sqfs_block_dispose(entry->block); } sqfs_err sqfs_block_cache_init(sqfs_cache *cache, size_t count) { return sqfs_cache_init(cache, sizeof(sqfs_block_cache_entry), count, &sqfs_block_cache_dispose); } void sqfs_md_cursor_inode(sqfs_md_cursor *cur, sqfs_inode_id id, sqfs_off_t base) { cur->block = (id >> 16) + base; cur->offset = id & 0xffff; } sqfs_err sqfs_md_read(sqfs *fs, sqfs_md_cursor *cur, void *buf, size_t size) { sqfs_off_t pos = cur->block; while (size > 0) { sqfs_block *block; size_t take; sqfs_err err = sqfs_md_cache(fs, &pos, &block); if (err) return err; take = block->size - cur->offset; if (take > size) take = size; if (buf) memcpy(buf, (char*)block->data + cur->offset, take); if (buf) buf = (char*)buf + take; size -= take; cur->offset += take; if (cur->offset == block->size) { cur->block = pos; cur->offset = 0; } sqfs_block_dispose(block); } return SQFS_OK; } size_t sqfs_divceil(uint64_t total, size_t group) { size_t q = (size_t)(total / group); if (total % group) q += 1; return q; } sqfs_err sqfs_id_get(sqfs *fs, uint16_t idx, sqfs_id_t *id) { uint32_t rid; sqfs_err err = sqfs_table_get(&fs->id_table, fs, idx, &rid); if (err) return err; sqfs_swapin32(&rid); *id = (sqfs_id_t)rid; return SQFS_OK; } sqfs_err sqfs_readlink(sqfs *fs, sqfs_inode *inode, char *buf, size_t *size) { size_t want; sqfs_md_cursor cur; sqfs_err err = SQFS_OK; if (!S_ISLNK(inode->base.mode)) return SQFS_ERR; want = inode->xtra.symlink_size; if (!buf) { *size = want + 1; return SQFS_OK; } if (want > *size - 1) want = *size - 1; cur = inode->next; err = sqfs_md_read(fs, &cur, buf, want); buf[want] = '\0'; return err; } int sqfs_export_ok(sqfs *fs) { return fs->sb.lookup_table_start != SQUASHFS_INVALID_BLK; } sqfs_err sqfs_export_inode(sqfs *fs, sqfs_inode_num n, sqfs_inode_id *i) { uint64_t r; sqfs_err err = SQFS_OK; if (!sqfs_export_ok(fs)) return SQFS_UNSUP; err = sqfs_table_get(&fs->export_table, fs, n - 1, &r); if (err) return err; sqfs_swapin64(&r); *i = r; return SQFS_OK; } sqfs_inode_id sqfs_inode_root(sqfs *fs) { return fs->sb.root_inode; } /* Turn the internal format of a device number to our system's dev_t * It looks like rdev is just what the Linux kernel uses: 20 bit minor, * split in two around a 12 bit major */ static void sqfs_decode_dev(sqfs_inode *i, uint32_t rdev) { i->xtra.dev.major = (rdev >> 8) & 0xfff; i->xtra.dev.minor = (rdev & 0xff) | ((rdev >> 12) & 0xfff00); } #define INODE_TYPE(_type) \ struct squashfs_##_type##_inode x; \ err = sqfs_md_read(fs, &inode->next, &x, sizeof(x)); \ if (err) return err; \ sqfs_swapin_##_type##_inode(&x) sqfs_err sqfs_inode_get(sqfs *fs, sqfs_inode *inode, sqfs_inode_id id) { sqfs_md_cursor cur; sqfs_err err = SQFS_OK; memset(inode, 0, sizeof(*inode)); inode->xattr = SQUASHFS_INVALID_XATTR; sqfs_md_cursor_inode(&cur, id, fs->sb.inode_table_start); inode->next = cur; err = sqfs_md_read(fs, &cur, &inode->base, sizeof(inode->base)); if (err) return err; sqfs_swapin_base_inode(&inode->base); inode->base.mode |= sqfs_mode(inode->base.inode_type); switch (inode->base.inode_type) { case SQUASHFS_REG_TYPE: { INODE_TYPE(reg); inode->nlink = 1; inode->xtra.reg.start_block = x.start_block; inode->xtra.reg.file_size = x.file_size; inode->xtra.reg.frag_idx = x.fragment; inode->xtra.reg.frag_off = x.offset; break; } case SQUASHFS_LREG_TYPE: { INODE_TYPE(lreg); inode->nlink = x.nlink; inode->xtra.reg.start_block = x.start_block; inode->xtra.reg.file_size = x.file_size; inode->xtra.reg.frag_idx = x.fragment; inode->xtra.reg.frag_off = x.offset; inode->xattr = x.xattr; break; } case SQUASHFS_DIR_TYPE: { INODE_TYPE(dir); inode->nlink = x.nlink; inode->xtra.dir.start_block = x.start_block; inode->xtra.dir.offset = x.offset; inode->xtra.dir.dir_size = x.file_size; inode->xtra.dir.idx_count = 0; inode->xtra.dir.parent_inode = x.parent_inode; break; } case SQUASHFS_LDIR_TYPE: { INODE_TYPE(ldir); inode->nlink = x.nlink; inode->xtra.dir.start_block = x.start_block; inode->xtra.dir.offset = x.offset; inode->xtra.dir.dir_size = x.file_size; inode->xtra.dir.idx_count = x.i_count; inode->xtra.dir.parent_inode = x.parent_inode; inode->xattr = x.xattr; break; } case SQUASHFS_SYMLINK_TYPE: case SQUASHFS_LSYMLINK_TYPE: { INODE_TYPE(symlink); inode->nlink = x.nlink; inode->xtra.symlink_size = x.symlink_size; if (inode->base.inode_type == SQUASHFS_LSYMLINK_TYPE) { /* skip symlink target */ cur = inode->next; err = sqfs_md_read(fs, &cur, NULL, inode->xtra.symlink_size); if (err) return err; err = sqfs_md_read(fs, &cur, &inode->xattr, sizeof(inode->xattr)); if (err) return err; sqfs_swapin32(&inode->xattr); } break; } case SQUASHFS_BLKDEV_TYPE: case SQUASHFS_CHRDEV_TYPE: { INODE_TYPE(dev); inode->nlink = x.nlink; sqfs_decode_dev(inode, x.rdev); break; } case SQUASHFS_LBLKDEV_TYPE: case SQUASHFS_LCHRDEV_TYPE: { INODE_TYPE(ldev); inode->nlink = x.nlink; sqfs_decode_dev(inode, x.rdev); inode->xattr = x.xattr; break; } case SQUASHFS_SOCKET_TYPE: case SQUASHFS_FIFO_TYPE: { INODE_TYPE(ipc); inode->nlink = x.nlink; break; } case SQUASHFS_LSOCKET_TYPE: case SQUASHFS_LFIFO_TYPE: { INODE_TYPE(lipc); inode->nlink = x.nlink; inode->xattr = x.xattr; break; } default: return SQFS_ERR; } return SQFS_OK; } #undef INODE_TYPE squashfuse-0.5.0/fs.h000066400000000000000000000107621450034605500144740ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_FS_H #define SQFS_FS_H #include "common.h" #include "squashfs_fs.h" #include "cache.h" #include "decompress.h" #include "table.h" struct sqfs { sqfs_fd_t fd; size_t offset; int uid; int gid; struct squashfs_super_block sb; const char *notify_pipe; sqfs_table id_table; sqfs_table frag_table; sqfs_table export_table; sqfs_cache md_cache; sqfs_cache data_cache; sqfs_cache frag_cache; sqfs_cache blockidx; sqfs_decompressor decompressor; struct squashfs_xattr_id_table xattr_info; sqfs_table xattr_table; }; typedef uint32_t sqfs_xattr_idx; struct sqfs_inode { struct squashfs_base_inode base; int nlink; sqfs_xattr_idx xattr; sqfs_md_cursor next; union { struct { int major, minor; } dev; size_t symlink_size; struct { uint64_t start_block; uint64_t file_size; uint32_t frag_idx; uint32_t frag_off; } reg; struct { uint32_t start_block; uint16_t offset; uint32_t dir_size; uint16_t idx_count; uint32_t parent_inode; } dir; } xtra; }; void sqfs_version_supported(int *min_major, int *min_minor, int *max_major, int *max_minor); /* Number of groups of size 'group' required to hold size 'total' */ size_t sqfs_divceil(uint64_t total, size_t group); sqfs_err sqfs_init(sqfs *fs, sqfs_fd_t fd, size_t offset); sqfs_err sqfs_init_with_subdir(sqfs *fs, sqfs_fd_t fd, size_t offset, const char *subdir); void sqfs_destroy(sqfs *fs); /* Ok to call these even on incompletely constructed filesystems */ void sqfs_version(sqfs *fs, int *major, int *minor); sqfs_compression_type sqfs_compression(sqfs *fs); void sqfs_md_header(uint16_t hdr, bool *compressed, uint16_t *size); void sqfs_data_header(uint32_t hdr, bool *compressed, uint32_t *size); typedef struct { sqfs_block *block; size_t data_size; } sqfs_block_cache_entry; sqfs_err sqfs_block_cache_init(sqfs_cache *cache, size_t count); sqfs_err sqfs_block_read(sqfs *fs, sqfs_off_t pos, bool compressed, uint32_t size, size_t outsize, sqfs_block **block); void sqfs_block_dispose(sqfs_block *block); sqfs_err sqfs_md_block_read(sqfs *fs, sqfs_off_t pos, size_t *data_size, sqfs_block **block); sqfs_err sqfs_data_block_read(sqfs *fs, sqfs_off_t pos, uint32_t hdr, sqfs_block **block); /* Don't dispose after getting block, it's in the cache */ sqfs_err sqfs_md_cache(sqfs *fs, sqfs_off_t *pos, sqfs_block **block); sqfs_err sqfs_data_cache(sqfs *fs, sqfs_cache *cache, sqfs_off_t pos, uint32_t hdr, sqfs_block **block); void sqfs_md_cursor_inode(sqfs_md_cursor *cur, sqfs_inode_id id, sqfs_off_t base); sqfs_err sqfs_md_read(sqfs *fs, sqfs_md_cursor *cur, void *buf, size_t size); sqfs_err sqfs_inode_get(sqfs *fs, sqfs_inode *inode, sqfs_inode_id id); sqfs_mode_t sqfs_mode(int inode_type); sqfs_err sqfs_id_get(sqfs *fs, uint16_t idx, sqfs_id_t *id); /* Puts up to *size characters of the link name into buf. Always null- * terminates the buffer. Pass null as buf to have the size returned. */ sqfs_err sqfs_readlink(sqfs *fs, sqfs_inode *inode, char *buf, size_t *size); /* Find inode_id by inode_num */ int sqfs_export_ok(sqfs *fs); sqfs_err sqfs_export_inode(sqfs *fs, sqfs_inode_num n, sqfs_inode_id *i); /* Find the root inode */ sqfs_inode_id sqfs_inode_root(sqfs *fs); #endif squashfuse-0.5.0/fuseprivate.c000066400000000000000000000134161450034605500164130ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fuseprivate.h" #include #include #include #include #include #include #include "nonstd.h" #if HAVE_DECL_FUSE_CMDLINE_HELP #include #endif int sqfs_listxattr(sqfs *fs, sqfs_inode *inode, char *buf, size_t *size) { sqfs_xattr x; size_t count = 0; if (sqfs_xattr_open(fs, inode, &x)) return -EIO; while (x.remain) { size_t n; if (sqfs_xattr_read(&x)) return EIO; n = sqfs_xattr_name_size(&x); count += n + 1; if (buf) { if (count > *size) return ERANGE; if (sqfs_xattr_name(&x, buf, true)) return EIO; buf += n; *buf++ = '\0'; } } *size = count; return 0; } void sqfs_minimal_fuse_usage() { fprintf(stderr, "\nSelection of FUSE options:\n"); fprintf(stderr," -h --help print help\n"); fprintf(stderr," -V --version print version\n"); fprintf(stderr," -d -o debug enable debug output (implies -f)\n"); fprintf(stderr," -f foreground operation\n"); } int sqfs_usage(char *progname, bool fuse_usage, bool ll_usage) { fprintf(stderr, "%s (c) 2012 Dave Vasilevsky\n\n", PACKAGE_STRING); fprintf(stderr, "Usage: %s [options] ARCHIVE MOUNTPOINT\n", progname ? progname : PACKAGE_NAME); fprintf(stderr, "\n%s options:\n", progname); fprintf(stderr, " -o offset=N offset N bytes into ARCHIVE to mount\n"); fprintf(stderr, " -o subdir=PATH mount subdirectory PATH of ARCHIVE\n"); fprintf(stderr, " -o notify_pipe=PATH named pipe that will receive 's' (success)\n" " or 'f' (failure) when the mountpoint is ready\n"); if (ll_usage) { fprintf(stderr, " -o timeout=N idle N seconds for automatic unmount\n"); fprintf(stderr, " -o uid=N set file owner to uid N\n"); fprintf(stderr, " -o gid=N set file group to gid N\n"); } if (fuse_usage) { if (ll_usage) { #ifdef SQFS_MULTITHREADED #if HAVE_DECL_FUSE_CMDLINE_HELP fprintf(stderr, "\nFUSE options:\n"); fuse_cmdline_help(); #else struct fuse_args args = FUSE_ARGS_INIT(0, NULL); fuse_opt_add_arg(&args, ""); /* progname */ fuse_opt_add_arg(&args, "-ho"); fprintf(stderr, "\n"); fuse_parse_cmdline(&args, NULL, NULL, NULL); #endif #else /* Skip the multithreaded options */ sqfs_minimal_fuse_usage(); #endif } else { /* Standard FUSE help includes confusing * multi-threaded options so don't use it */ sqfs_minimal_fuse_usage(); fprintf(stderr," -o allow_other allow access by other users\n"); fprintf(stderr," -o allow_root allow access by the superuser\n"); } } return -2; } int sqfs_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { sqfs_opts *opts = (sqfs_opts*)data; if (key == FUSE_OPT_KEY_NONOPT) { if (opts->mountpoint) { return -1; /* Too many args */ } else if (opts->image) { opts->mountpoint = 1; return 1; } else { opts->image = arg; return 0; } } else if (key == FUSE_OPT_KEY_OPT) { if (strncmp(arg, "-h", 2) == 0 || strncmp(arg, "--h", 3) == 0) return -1; } return 1; /* Keep */ } int sqfs_statfs(sqfs *sq, struct statvfs *st) { struct squashfs_super_block *sb = &sq->sb; st->f_bsize = sb->block_size; st->f_frsize = sb->block_size; st->f_blocks = ((sb->bytes_used - 1) >> sb->block_log) + 1; st->f_bfree = 0; st->f_bavail = 0; st->f_files = sb->inodes; st->f_ffree = 0; st->f_favail = 0; st->f_namemax = SQUASHFS_NAME_LEN; return 0; } void notify_mount_ready(const char *notify_pipe, char status) { struct stat stat_buf = {}; if (stat(notify_pipe, &stat_buf)) { fprintf(stderr, "Cannot stat file \"%s\" (%d)\n", notify_pipe, errno); goto err; } if (!(stat_buf.st_mode & S_IFIFO)) { fprintf(stderr, "\"%s\" is not a pipe\n", notify_pipe); goto err; } sqfs_fd_t pipe_fd = open(notify_pipe, O_WRONLY); if(pipe_fd < 0) { fprintf(stderr, "Open fifo failed \"%s\" (%d)\n", notify_pipe, errno); goto err; } if (write(pipe_fd, &status, 1) < 0) { fprintf(stderr, "Write to fifo failed \"%s\" (%d)\n", notify_pipe, errno); goto err; } close(pipe_fd); err: return; } void notify_mount_ready_async(const char *notify_pipe, char status) { if (!notify_pipe) { return; } pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "Fork Failed"); } else if (pid == 0) { /* child process */ notify_mount_ready(notify_pipe, status); exit(0); } else { /* parent process */ } } squashfuse-0.5.0/fuseprivate.h000066400000000000000000000044271450034605500164220ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_FUSEPRIVATE_H #define SQFS_FUSEPRIVATE_H #include "squashfuse.h" #include #define NOTIFY_SUCCESS 's' #define NOTIFY_FAILURE 'f' /* Common functions for FUSE high- and low-level clients */ /* Populate an xattr list. Return an errno value. */ int sqfs_listxattr(sqfs *fs, sqfs_inode *inode, char *buf, size_t *size); /* Print a usage string */ int sqfs_usage(char *progname, bool fuse_usage, bool ll_usage); /* Parse command-line arguments */ typedef struct { char *progname; const char *image; const char *subdir; int mountpoint; size_t offset; unsigned int idle_timeout_secs; int uid; int gid; const char *notify_pipe; } sqfs_opts; int sqfs_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs); /* Get filesystem super block info */ int sqfs_statfs(sqfs *sq, struct statvfs *st); void notify_mount_ready(const char *notify_pipe, char status); void notify_mount_ready_async(const char *notify_pipe, char status); #endif squashfuse-0.5.0/gen_swap.sh000077500000000000000000000031251450034605500160500ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2012 Dave Vasilevsky # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ${SED:-sed} -n ' /^struct squashfs_/,/^}/{ s/^struct \(squashfs_\([^[:space:]]*\)\).*/void sqfs_swapin_\2(struct \1 *s){/p;t decl s/};/}/p;t s/^[[:space:]]*__le\([[:digit:]]*\)[[:space:]]*\([a-z_]*\);$/sqfs_swapin\1_internal(\&s->\2);/p } d :decl s/{/;/ w swap.h.inc ' < "$1" > swap.c.inc squashfuse-0.5.0/hash.c000066400000000000000000000070161450034605500150000ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "hash.h" #include #include static sqfs_err sqfs_hash_add_internal(sqfs_hash *h, int doubling, sqfs_hash_key k, sqfs_hash_value v) { size_t hash = (k & (h->capacity - 1)); sqfs_hash_bucket *b = malloc(sizeof(sqfs_hash_bucket) + h->value_size); if (!b) return SQFS_ERR; b->key = k; memcpy(&b->value, v, h->value_size); b->next = h->buckets[hash]; h->buckets[hash] = b; ++h->size; return SQFS_OK; } static sqfs_err sqfs_hash_double(sqfs_hash *h) { sqfs_hash_bucket **ob = h->buckets; size_t oc = h->capacity; size_t i; sqfs_err err; if ((err = sqfs_hash_init(h, h->value_size, oc * 2))) return err; for (i = 0; i < oc; ++i) { sqfs_hash_bucket *b = ob[i]; while (b) { sqfs_hash_bucket *n; if (!err) err = sqfs_hash_add_internal(h, 1, b->key, &b->value); n = b->next; free(b); b = n; } } free(ob); return err; } sqfs_err sqfs_hash_init(sqfs_hash *h, size_t vsize, size_t initial) { memset(h, 0, sizeof(*h)); if ((initial & (initial - 1))) /* not power of two? */ return SQFS_ERR; h->buckets = calloc(initial, sizeof(sqfs_hash_bucket*)); if (!h->buckets) return SQFS_ERR; h->capacity = initial; h->size = 0; h->value_size = vsize; return SQFS_OK; } void sqfs_hash_destroy(sqfs_hash *h) { size_t i; for (i = 0; i < h->capacity; ++i) { sqfs_hash_bucket *b = h->buckets[i]; while (b) { sqfs_hash_bucket *n = b->next; free(b); b = n; } } free(h->buckets); } sqfs_hash_value sqfs_hash_get(sqfs_hash *h, sqfs_hash_key k) { size_t hash = (k & (h->capacity - 1)); sqfs_hash_bucket *b = h->buckets[hash]; while (b) { if (b->key == k) return &b->value; b = b->next; } return NULL; } sqfs_err sqfs_hash_add(sqfs_hash *h, sqfs_hash_key k, sqfs_hash_value v) { if (h->size >= h->capacity) { sqfs_err err = sqfs_hash_double(h); if (err) return err; } return sqfs_hash_add_internal(h, 0, k, v); } sqfs_err sqfs_hash_remove(sqfs_hash *h, sqfs_hash_key k) { size_t hash = (k & (h->capacity - 1)); sqfs_hash_bucket **bp = &h->buckets[hash]; while (*bp) { if ((*bp)->key == k) { sqfs_hash_bucket *b = *bp; *bp = b->next; free(b); --h->size; return SQFS_OK; } bp = &(*bp)->next; } return SQFS_OK; } squashfuse-0.5.0/hash.h000066400000000000000000000042611450034605500150040ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_HASH_H #define SQFS_HASH_H #include "common.h" /* Simple hashtable * - Keys are integers * - Values are opaque data * * Implementation * - Hash function is modulus * - Chaining for duplicates * - Sizes are powers of two */ typedef uint32_t sqfs_hash_key; typedef void *sqfs_hash_value; typedef struct sqfs_hash_bucket { struct sqfs_hash_bucket *next; sqfs_hash_key key; char value[1]; /* extended to size */ } sqfs_hash_bucket; typedef struct { size_t value_size; size_t capacity; size_t size; sqfs_hash_bucket **buckets; } sqfs_hash; sqfs_err sqfs_hash_init(sqfs_hash *h, size_t vsize, size_t initial); void sqfs_hash_destroy(sqfs_hash *h); sqfs_hash_value sqfs_hash_get(sqfs_hash *h, sqfs_hash_key k); sqfs_err sqfs_hash_add(sqfs_hash *h, sqfs_hash_key k, sqfs_hash_value v); sqfs_err sqfs_hash_remove(sqfs_hash *h, sqfs_hash_key k); #endif squashfuse-0.5.0/hl.c000066400000000000000000000207661450034605500144670ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "squashfuse.h" #include "fuseprivate.h" #include "stat.h" #include "nonstd.h" #include #include #include #include #include #include typedef struct sqfs_hl sqfs_hl; struct sqfs_hl { sqfs fs; sqfs_inode root; }; static sqfs_err sqfs_hl_lookup(sqfs **fs, sqfs_inode *inode, const char *path) { bool found; sqfs_hl *hl = fuse_get_context()->private_data; *fs = &hl->fs; if (inode) *inode = hl->root; /* copy */ if (path) { sqfs_err err = sqfs_lookup_path(*fs, inode, path, &found); if (err) return err; if (!found) return SQFS_ERR; } return SQFS_OK; } static void sqfs_hl_op_destroy(void *user_data) { sqfs_hl *hl = (sqfs_hl*)user_data; sqfs_destroy(&hl->fs); free(hl); } static void *sqfs_hl_op_init(struct fuse_conn_info *conn #if FUSE_USE_VERSION >= 30 ,struct fuse_config *cfg #endif ) { sqfs_hl *hl = fuse_get_context()->private_data; notify_mount_ready_async(hl->fs.notify_pipe, NOTIFY_SUCCESS); return hl; } static int sqfs_hl_op_getattr(const char *path, struct stat *st #if FUSE_USE_VERSION >= 30 , struct fuse_file_info *fi #endif ) { sqfs *fs; sqfs_inode inode; if (sqfs_hl_lookup(&fs, &inode, path)) return -ENOENT; if (sqfs_stat(fs, &inode, st)) return -ENOENT; return 0; } static int sqfs_hl_op_opendir(const char *path, struct fuse_file_info *fi) { sqfs *fs; sqfs_inode *inode; inode = malloc(sizeof(*inode)); if (!inode) return -ENOMEM; if (sqfs_hl_lookup(&fs, inode, path)) { free(inode); return -ENOENT; } if (!S_ISDIR(inode->base.mode)) { free(inode); return -ENOTDIR; } fi->fh = (intptr_t)inode; return 0; } static int sqfs_hl_op_releasedir(const char *path, struct fuse_file_info *fi) { free((sqfs_inode*)(intptr_t)fi->fh); fi->fh = 0; return 0; } static int sqfs_hl_op_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi #if FUSE_USE_VERSION >= 30 ,enum fuse_readdir_flags flags #endif ) { sqfs_err err; sqfs *fs; sqfs_inode *inode; sqfs_dir dir; sqfs_name namebuf; sqfs_dir_entry entry; struct stat st; sqfs_hl_lookup(&fs, NULL, NULL); inode = (sqfs_inode*)(intptr_t)fi->fh; if (sqfs_dir_open(fs, inode, &dir, offset)) return -EINVAL; memset(&st, 0, sizeof(st)); sqfs_dentry_init(&entry, namebuf); while (sqfs_dir_next(fs, &dir, &entry, &err)) { sqfs_off_t doff = sqfs_dentry_next_offset(&entry); st.st_mode = sqfs_dentry_mode(&entry); if (filler(buf, sqfs_dentry_name(&entry), &st, doff #if FUSE_USE_VERSION >= 30 , 0 #endif )) { return 0; } } if (err) return -EIO; return 0; } static int sqfs_hl_op_open(const char *path, struct fuse_file_info *fi) { sqfs *fs; sqfs_inode *inode; if (fi->flags & (O_WRONLY | O_RDWR)) return -EROFS; inode = malloc(sizeof(*inode)); if (!inode) return -ENOMEM; if (sqfs_hl_lookup(&fs, inode, path)) { free(inode); return -ENOENT; } if (!S_ISREG(inode->base.mode)) { free(inode); return -EISDIR; } fi->fh = (intptr_t)inode; fi->keep_cache = 1; return 0; } static int sqfs_hl_op_create(const char* unused_path, mode_t unused_mode, struct fuse_file_info *unused_fi) { return -EROFS; } static int sqfs_hl_op_release(const char *path, struct fuse_file_info *fi) { free((sqfs_inode*)(intptr_t)fi->fh); fi->fh = 0; return 0; } static int sqfs_hl_op_read(const char *path, char *buf, size_t size, off_t off, struct fuse_file_info *fi) { sqfs *fs; sqfs_hl_lookup(&fs, NULL, NULL); sqfs_inode *inode = (sqfs_inode*)(intptr_t)fi->fh; off_t osize = size; if (sqfs_read_range(fs, inode, off, &osize, buf)) return -EIO; return osize; } static int sqfs_hl_op_readlink(const char *path, char *buf, size_t size) { sqfs *fs; sqfs_inode inode; if (sqfs_hl_lookup(&fs, &inode, path)) return -ENOENT; if (!S_ISLNK(inode.base.mode)) { return -EINVAL; } else if (sqfs_readlink(fs, &inode, buf, &size)) { return -EIO; } return 0; } static int sqfs_hl_op_listxattr(const char *path, char *buf, size_t size) { sqfs *fs; sqfs_inode inode; int ferr; if (sqfs_hl_lookup(&fs, &inode, path)) return -ENOENT; ferr = sqfs_listxattr(fs, &inode, buf, &size); if (ferr) return -ferr; return size; } static int sqfs_hl_op_getxattr(const char *path, const char *name, char *value, size_t size #ifdef FUSE_XATTR_POSITION , uint32_t position #endif ) { sqfs *fs; sqfs_inode inode; size_t real = size; #ifdef FUSE_XATTR_POSITION if (position != 0) /* We don't support resource forks */ return -EINVAL; #endif if (sqfs_hl_lookup(&fs, &inode, path)) return -ENOENT; if ((sqfs_xattr_lookup(fs, &inode, name, value, &real))) return -EIO; if (real == 0) return -sqfs_enoattr(); if (size != 0 && size < real) return -ERANGE; return real; } static int sqfs_hl_op_statfs(const char *path, struct statvfs *st) { sqfs_hl *hl = fuse_get_context()->private_data; return sqfs_statfs(&hl->fs, st); } static sqfs_hl *sqfs_hl_open(const char *path, size_t offset, const char *subdir) { sqfs_hl *hl; hl = malloc(sizeof(*hl)); if (!hl) { perror("Can't allocate memory"); } else { memset(hl, 0, sizeof(*hl)); if (sqfs_open_image_with_subdir(&hl->fs, path, offset, subdir) == SQFS_OK) { if (sqfs_inode_get(&hl->fs, &hl->root, sqfs_inode_root(&hl->fs))) fprintf(stderr, "Can't find the root of this filesystem!\n"); else return hl; sqfs_destroy(&hl->fs); } free(hl); } return NULL; } int main(int argc, char *argv[]) { struct fuse_args args; sqfs_opts opts; sqfs_hl *hl; int ret; struct fuse_opt fuse_opts[] = { {"offset=%zu", offsetof(sqfs_opts, offset), 0}, {"subdir=%s", offsetof(sqfs_opts, subdir), 0}, {"notify_pipe=%s", offsetof(sqfs_opts, notify_pipe), 0}, FUSE_OPT_END }; struct fuse_operations sqfs_hl_ops; memset(&sqfs_hl_ops, 0, sizeof(sqfs_hl_ops)); sqfs_hl_ops.init = sqfs_hl_op_init; sqfs_hl_ops.destroy = sqfs_hl_op_destroy; sqfs_hl_ops.getattr = sqfs_hl_op_getattr; sqfs_hl_ops.opendir = sqfs_hl_op_opendir; sqfs_hl_ops.releasedir = sqfs_hl_op_releasedir; sqfs_hl_ops.readdir = sqfs_hl_op_readdir; sqfs_hl_ops.open = sqfs_hl_op_open; sqfs_hl_ops.create = sqfs_hl_op_create; sqfs_hl_ops.release = sqfs_hl_op_release; sqfs_hl_ops.read = sqfs_hl_op_read; sqfs_hl_ops.readlink = sqfs_hl_op_readlink; sqfs_hl_ops.listxattr = sqfs_hl_op_listxattr; sqfs_hl_ops.getxattr = sqfs_hl_op_getxattr; sqfs_hl_ops.statfs = sqfs_hl_op_statfs; args.argc = argc; args.argv = argv; args.allocated = 0; opts.progname = argv[0]; opts.image = NULL; opts.subdir = NULL; opts.mountpoint = 0; opts.offset = 0; opts.notify_pipe = NULL; if (fuse_opt_parse(&args, &opts, fuse_opts, sqfs_opt_proc) == -1) { ret = sqfs_usage(argv[0], true, false); goto out; } if (!opts.image) { ret = sqfs_usage(argv[0], true, false); goto out; } hl = sqfs_hl_open(opts.image, opts.offset, opts.subdir); if (!hl) { ret = -1; goto out; } hl->fs.notify_pipe = opts.notify_pipe; fuse_opt_add_arg(&args, "-s"); /* single threaded */ ret = fuse_main(args.argc, args.argv, &sqfs_hl_ops, hl); out: if (ret) { if (opts.notify_pipe) { notify_mount_ready(opts.notify_pipe, NOTIFY_FAILURE); } } fuse_opt_free_args(&args); return ret; } squashfuse-0.5.0/ll.c000066400000000000000000000346241450034605500144710ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ll.h" #include "fuseprivate.h" #include "stat.h" #include "nonstd.h" #include #include #include #include #include #include #include #include static const double SQFS_TIMEOUT = DBL_MAX; /* See comment near alarm_tick for details of how idle timeouts are managed. */ /* timeout, in seconds, after which we will automatically unmount */ static unsigned int idle_timeout_secs = 0; /* last access timestamp */ static time_t last_access = 0; /* count of files and directories currently open. drecement after * last_access for correctness. */ static sig_atomic_t open_refcount = 0; /* same as lib/fuse_signals.c */ static struct fuse_session *fuse_instance = NULL; static void update_access_time(void) { #ifdef SQFS_MULTITHREADED /* We only need to track access time if we have an idle timeout, * don't bother with expensive operations if idle_timeout is 0. */ if (idle_timeout_secs) { time_t now = time(NULL); __atomic_store_n(&last_access, now, __ATOMIC_RELEASE); } #else last_access = time(NULL); #endif } static void update_open_refcount(int delta) { #ifdef SQFS_MULTITHREADED __atomic_fetch_add(&open_refcount, delta, __ATOMIC_RELEASE); #else open_refcount += delta; #endif } static inline time_t get_access_time(void) { #ifdef SQFS_MULTITHREADED return __atomic_load_n(&last_access, __ATOMIC_ACQUIRE); #else return last_access; #endif } static inline sig_atomic_t get_open_refcount(void) { #ifdef SQFS_MULTITHREADED return __atomic_load_n(&open_refcount, __ATOMIC_ACQUIRE); #else return open_refcount; #endif } void sqfs_ll_op_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { sqfs_ll_i lli; struct stat st; update_access_time(); if (sqfs_ll_iget(req, &lli, ino)) return; if (sqfs_stat(&lli.ll->fs, &lli.inode, &st)) { fuse_reply_err(req, ENOENT); } else { st.st_ino = ino; fuse_reply_attr(req, &st, SQFS_TIMEOUT); } } void sqfs_ll_op_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { sqfs_ll_i *lli; update_access_time(); fi->fh = (intptr_t)NULL; lli = malloc(sizeof(*lli)); if (!lli) { fuse_reply_err(req, ENOMEM); return; } if (sqfs_ll_iget(req, lli, ino) == SQFS_OK) { if (!S_ISDIR(lli->inode.base.mode)) { fuse_reply_err(req, ENOTDIR); } else { fi->fh = (intptr_t)lli; update_open_refcount(1); fuse_reply_open(req, fi); return; } } free(lli); } void sqfs_ll_op_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { update_access_time(); fuse_reply_err(req, EROFS); } void sqfs_ll_op_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { update_access_time(); update_open_refcount(-1); free((sqfs_ll_i*)(intptr_t)fi->fh); fuse_reply_err(req, 0); /* yes, this is necessary */ } size_t sqfs_ll_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *st, off_t off) { #if HAVE_DECL_FUSE_ADD_DIRENTRY return fuse_add_direntry(req, buf, bufsize, name, st, off); #else size_t esize = fuse_dirent_size(strlen(name)); if (bufsize >= esize) fuse_add_dirent(buf, name, st, off); return esize; #endif } void sqfs_ll_op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { sqfs_err sqerr; sqfs_dir dir; sqfs_name namebuf; sqfs_dir_entry entry; size_t esize; struct stat st; char *buf = NULL, *bufpos = NULL; sqfs_ll_i *lli = (sqfs_ll_i*)(intptr_t)fi->fh; int err = 0; update_access_time(); if (sqfs_dir_open(&lli->ll->fs, &lli->inode, &dir, off)) err = EINVAL; if (!err && !(bufpos = buf = malloc(size))) err = ENOMEM; if (!err) { memset(&st, 0, sizeof(st)); sqfs_dentry_init(&entry, namebuf); while (sqfs_dir_next(&lli->ll->fs, &dir, &entry, &sqerr)) { st.st_ino = lli->ll->ino_fuse_num(lli->ll, &entry); st.st_mode = sqfs_dentry_mode(&entry); esize = sqfs_ll_add_direntry(req, bufpos, size, sqfs_dentry_name(&entry), &st, sqfs_dentry_next_offset(&entry)); if (esize > size) break; bufpos += esize; size -= esize; } if (sqerr) err = EIO; } if (err) fuse_reply_err(req, err); else fuse_reply_buf(req, buf, bufpos - buf); free(buf); } void sqfs_ll_op_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { sqfs_ll_i lli; sqfs_err sqerr; sqfs_name namebuf; sqfs_dir_entry entry; bool found; sqfs_inode inode; update_access_time(); if (sqfs_ll_iget(req, &lli, parent)) return; if (!S_ISDIR(lli.inode.base.mode)) { fuse_reply_err(req, ENOTDIR); return; } sqfs_dentry_init(&entry, namebuf); sqerr = sqfs_dir_lookup(&lli.ll->fs, &lli.inode, name, strlen(name), &entry, &found); if (sqerr) { fuse_reply_err(req, EIO); return; } if (!found) { /* Returning with zero inode indicates not found with * timeout, i.e. future lookups of this name will not generate * fuse requests. */ struct fuse_entry_param fentry; memset(&fentry, 0, sizeof(fentry)); fentry.attr_timeout = fentry.entry_timeout = SQFS_TIMEOUT; fentry.ino = 0; fuse_reply_entry(req, &fentry); return; } if (sqfs_inode_get(&lli.ll->fs, &inode, sqfs_dentry_inode(&entry))) { fuse_reply_err(req, ENOENT); } else { struct fuse_entry_param fentry; memset(&fentry, 0, sizeof(fentry)); if (sqfs_stat(&lli.ll->fs, &inode, &fentry.attr)) { fuse_reply_err(req, EIO); } else { fentry.attr_timeout = fentry.entry_timeout = SQFS_TIMEOUT; fentry.ino = lli.ll->ino_register(lli.ll, &entry); fentry.attr.st_ino = fentry.ino; fuse_reply_entry(req, &fentry); } } } void sqfs_ll_op_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { sqfs_inode *inode; sqfs_ll *ll; update_access_time(); if (fi->flags & (O_WRONLY | O_RDWR)) { fuse_reply_err(req, EROFS); return; } inode = malloc(sizeof(sqfs_inode)); if (!inode) { fuse_reply_err(req, ENOMEM); return; } ll = fuse_req_userdata(req); if (sqfs_ll_inode(ll, inode, ino)) { fuse_reply_err(req, ENOENT); } else if (!S_ISREG(inode->base.mode)) { fuse_reply_err(req, EISDIR); } else { fi->fh = (intptr_t)inode; fi->keep_cache = 1; update_open_refcount(1); fuse_reply_open(req, fi); return; } free(inode); } void sqfs_ll_op_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { free((sqfs_inode*)(intptr_t)fi->fh); fi->fh = 0; update_access_time(); update_open_refcount(-1); fuse_reply_err(req, 0); } void sqfs_ll_op_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { sqfs_ll *ll = fuse_req_userdata(req); sqfs_inode *inode = (sqfs_inode*)(intptr_t)fi->fh; sqfs_err err = SQFS_OK; off_t osize; char *buf = malloc(size); if (!buf) { fuse_reply_err(req, ENOMEM); return; } update_access_time(); osize = size; err = sqfs_read_range(&ll->fs, inode, off, &osize, buf); if (err) { fuse_reply_err(req, EIO); } else if (osize == 0) { /* EOF */ fuse_reply_buf(req, NULL, 0); } else { fuse_reply_buf(req, buf, osize); } free(buf); } void sqfs_ll_op_readlink(fuse_req_t req, fuse_ino_t ino) { char *dst; size_t size; sqfs_ll_i lli; update_access_time(); if (sqfs_ll_iget(req, &lli, ino)) return; if (!S_ISLNK(lli.inode.base.mode)) { fuse_reply_err(req, EINVAL); } else if (sqfs_readlink(&lli.ll->fs, &lli.inode, NULL, &size)) { fuse_reply_err(req, EIO); } else if (!(dst = malloc(size + 1))) { fuse_reply_err(req, ENOMEM); } else if (sqfs_readlink(&lli.ll->fs, &lli.inode, dst, &size)) { fuse_reply_err(req, EIO); free(dst); } else { fuse_reply_readlink(req, dst); free(dst); } } void sqfs_ll_op_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { sqfs_ll_i lli; char *buf; int ferr; update_access_time(); if (sqfs_ll_iget(req, &lli, ino)) return; buf = NULL; if (size && !(buf = malloc(size))) { fuse_reply_err(req, ENOMEM); return; } ferr = sqfs_listxattr(&lli.ll->fs, &lli.inode, buf, &size); if (ferr) { fuse_reply_err(req, ferr); } else if (buf) { fuse_reply_buf(req, buf, size); } else { fuse_reply_xattr(req, size); } free(buf); } void sqfs_ll_op_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size #ifdef FUSE_XATTR_POSITION , uint32_t position #endif ) { sqfs_ll_i lli; char *buf = NULL; size_t real = size; #ifdef FUSE_XATTR_POSITION if (position != 0) { /* We don't support resource forks */ fuse_reply_err(req, EINVAL); return; } #endif update_access_time(); if (sqfs_ll_iget(req, &lli, ino)) return; if (!(buf = malloc(size))) fuse_reply_err(req, ENOMEM); else if (sqfs_xattr_lookup(&lli.ll->fs, &lli.inode, name, buf, &real)) fuse_reply_err(req, EIO); else if (real == 0) fuse_reply_err(req, sqfs_enoattr()); else if (size == 0) fuse_reply_xattr(req, real); else if (size < real) fuse_reply_err(req, ERANGE); else fuse_reply_buf(req, buf, real); free(buf); } void sqfs_ll_op_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup) { sqfs_ll_i lli; update_access_time(); sqfs_ll_iget(req, &lli, SQFS_FUSE_INODE_NONE); lli.ll->ino_forget(lli.ll, ino, nlookup); fuse_reply_none(req); } void stfs_ll_op_statfs(fuse_req_t req, fuse_ino_t ino) { sqfs_ll *ll; struct statvfs st; int err; ll = fuse_req_userdata(req); err = sqfs_statfs(&ll->fs, &st); if (err == 0) { fuse_reply_statfs(req, &st); } else { fuse_reply_err(req, err); } } void sqfs_ll_op_init(void *userdata, struct fuse_conn_info *conn) { sqfs_ll *ll = userdata; notify_mount_ready_async(ll->fs.notify_pipe, NOTIFY_SUCCESS); } /* Helpers to abstract out FUSE 2.5 vs 3.0+ differences */ #if FUSE_USE_VERSION >= 30 sqfs_err sqfs_ll_mount( sqfs_ll_chan *ch, const char *mountpoint, struct fuse_args *args, struct fuse_lowlevel_ops *ops, size_t ops_size, void *userdata) { ch->session = fuse_session_new(args, ops, ops_size, userdata); if (!ch->session) { return SQFS_ERR; } if (fuse_session_mount(ch->session, mountpoint)) { fuse_session_destroy(ch->session); ch->session = NULL; return SQFS_ERR; } return SQFS_OK; } void sqfs_ll_unmount(sqfs_ll_chan *ch, const char *mountpoint) { fuse_session_unmount(ch->session); fuse_session_destroy(ch->session); ch->session = NULL; } #else /* FUSE_USE_VERSION >= 30 */ void sqfs_ll_unmount(sqfs_ll_chan *ch, const char *mountpoint) { if (ch->session) { #if HAVE_DECL_FUSE_SESSION_REMOVE_CHAN fuse_session_remove_chan(ch->ch); #endif fuse_session_destroy(ch->session); } #ifdef HAVE_NEW_FUSE_UNMOUNT fuse_unmount(mountpoint, ch->ch); #else close(ch->fd); fuse_unmount(mountpoint); #endif } sqfs_err sqfs_ll_mount( sqfs_ll_chan *ch, const char *mountpoint, struct fuse_args *args, struct fuse_lowlevel_ops *ops, size_t ops_size, void *userdata) { #ifdef HAVE_NEW_FUSE_UNMOUNT ch->ch = fuse_mount(mountpoint, args); if (!ch->ch) { return SQFS_ERR; } #else ch->fd = fuse_mount(mountpoint, args); if (ch->fd == -1) return SQFS_ERR; ch->ch = fuse_kern_chan_new(ch->fd); if (!ch->ch) { close(ch->fd); return SQFS_ERR; } #endif ch->session = fuse_lowlevel_new(args, ops, sizeof(*ops), userdata); if (!ch->session) { sqfs_ll_unmount(ch, mountpoint); return SQFS_ERR; } fuse_session_add_chan(ch->session, ch->ch); return SQFS_OK; } #endif /* FUSE_USE_VERSION >= 30 */ /* Idle unmount timeout management is based on signal handling from fuse (see set_one_signal_handler and exit_handler in libfuse's lib/fuse_signals.c. When an idle timeout is set, we use a one second alarm to check if no activity has taken place within the idle window, as tracked by last_access. We also maintain an open/opendir refcount so that directories and files can be held open and unaccessed without triggering the idle timeout. */ void alarm_tick(int sig) { if (!fuse_instance || idle_timeout_secs == 0) { return; } if (get_open_refcount() == 0 && time(NULL) - get_access_time() > idle_timeout_secs) { /* Safely shutting down fuse in a cross-platform way is a dark art! But just about any platform should stop on SIGINT, so do that */ kill(getpid(), SIGINT); return; } alarm(1); /* always reset our alarm */ } void setup_idle_timeout(struct fuse_session *se, unsigned int timeout_secs) { idle_timeout_secs = timeout_secs; update_access_time(); struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = alarm_tick; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; fuse_instance = se; if (sigaction(SIGALRM, &sa, NULL) == -1) { perror("fuse: cannot get old signal handler"); return; } alarm(1); } void teardown_idle_timeout() { alarm(0); fuse_instance = NULL; } sqfs_ll *sqfs_ll_open_with_subdir(const char *path, size_t offset, const char *subdir) { sqfs_ll *ll; ll = malloc(sizeof(*ll)); if (!ll) { perror("Can't allocate memory"); } else { memset(ll, 0, sizeof(*ll)); ll->fs.offset = offset; if (sqfs_open_image_with_subdir(&ll->fs, path, offset, subdir) == SQFS_OK) { if (sqfs_ll_init(ll)) fprintf(stderr, "Can't initialize this filesystem!\n"); else return ll; sqfs_destroy(&ll->fs); } free(ll); } return NULL; } sqfs_ll *sqfs_ll_open(const char *path, size_t offset) { return sqfs_ll_open_with_subdir(path, offset, NULL); } squashfuse-0.5.0/ll.h000066400000000000000000000111331450034605500144640ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_LL_H #define SQFS_LL_H #include "squashfuse.h" #include typedef struct sqfs_ll sqfs_ll; struct sqfs_ll { sqfs fs; /* Converting inodes between squashfs and fuse */ fuse_ino_t (*ino_fuse)(sqfs_ll *ll, sqfs_inode_id i); sqfs_inode_id (*ino_sqfs)(sqfs_ll *ll, fuse_ino_t i); /* Register a new inode, returning the fuse ID for it */ fuse_ino_t (*ino_register)(sqfs_ll *ll, sqfs_dir_entry *e); void (*ino_forget)(sqfs_ll *ll, fuse_ino_t i, size_t refs); /* Like register, but don't actually remember it */ fuse_ino_t (*ino_fuse_num)(sqfs_ll *ll, sqfs_dir_entry *e); /* Private data, and how to destroy it */ void *ino_data; void (*ino_destroy)(sqfs_ll *ll); }; sqfs_err sqfs_ll_init(sqfs_ll *ll); void sqfs_ll_destroy(sqfs_ll *ll); /* Get an inode from an sqfs_ll */ sqfs_err sqfs_ll_inode(sqfs_ll *ll, sqfs_inode *inode, fuse_ino_t i); /* Convenience function: Get both ll and inode, and handle errors */ #define SQFS_FUSE_INODE_NONE 0 typedef struct { sqfs_ll *ll; sqfs_inode inode; } sqfs_ll_i; sqfs_err sqfs_ll_iget(fuse_req_t req, sqfs_ll_i *lli, fuse_ino_t i); int sqfs_ll_daemonize(int fg); void sqfs_ll_op_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); void sqfs_ll_op_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); void sqfs_ll_op_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi); void sqfs_ll_op_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); size_t sqfs_ll_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *st, off_t off); void sqfs_ll_op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); void sqfs_ll_op_lookup(fuse_req_t req, fuse_ino_t parent, const char *name); void sqfs_ll_op_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); void sqfs_ll_op_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); void sqfs_ll_op_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); void sqfs_ll_op_readlink(fuse_req_t req, fuse_ino_t ino); void sqfs_ll_op_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size); void sqfs_ll_op_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size #ifdef FUSE_XATTR_POSITION , uint32_t position #endif ); void sqfs_ll_op_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); void sqfs_ll_op_init(void *userdata, struct fuse_conn_info *conn); void stfs_ll_op_statfs(fuse_req_t req, fuse_ino_t ino); /* Helpers to abstract out FUSE 2.5 vs 3.0+ differences */ typedef struct { int fd; struct fuse_session *session; #if FUSE_USE_VERSION < 30 struct fuse_chan *ch; #endif } sqfs_ll_chan; sqfs_err sqfs_ll_mount( sqfs_ll_chan *ch, const char *mountpoint, struct fuse_args *args, struct fuse_lowlevel_ops *ops, size_t ops_size, void *userdata); void sqfs_ll_unmount(sqfs_ll_chan *ch, const char *mountpoint); void alarm_tick(int sig); void setup_idle_timeout(struct fuse_session *se, unsigned int timeout_secs); void teardown_idle_timeout(); sqfs_ll *sqfs_ll_open_with_subdir(const char *path, size_t offset, const char *subdir); sqfs_ll *sqfs_ll_open(const char *path, size_t offset); #endif squashfuse-0.5.0/ll_inode.c000066400000000000000000000256041450034605500156450ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ll.h" #include "hash.h" #include "nonstd.h" #include #include #include /* We have three kinds of unique identifiers for inodes: 1. sqfs_inode_id - Points directly to the on-disk location of the inode data. - A 48-bit integer (32-bit block id, and 16 bit offset within a block). - Not assigned sequentially, two IDs will differ by at least 20. - The root inode may have any value of sqfs_inode_id. - You CAN easily get the inode data from an sqfs_inode_id. - You CANNOT get the sqfs_inode_id from the inode data. 2. sqfs_inode_number - Arbitrary identifier for inodes, assigned by mksquashfs. - A 32-bit integer. - Assigned sequentially, starting at zero. - The root inode generally has the value zero. - You CANNOT find the inode data directly from an sqfs_inode_number. - You CAN find the sqfs_inode_number from the inode data. - You CAN lookup sqfs_inode_number -> sqfs_inode_id IFF the squashfs archive has the optional export table enabled. 3. fuse_ino_t - Arbitrary identifier for inodes, assigned by the FUSE driver. - Has the same width as 'long', either 32- or 64-bit. - The value zero is reserved to indicate a non-existent entry. - The value one is reserved to indicate the root inode. To implement a low-level filesystem, we must: - Generate a fuse_ino_t when telling FUSE about a new inode. - Find the inode data when FUSE asks us about a fuse_ino_t. So we need a bidirectional mapping between fuse_ino_t and sqfs_inode_id. We use several different strategies, depending on the bitness of the system, and whether or not the squashfs archive has an export table: 1. On 64-bit systems: fuse_ino_t is big enough to hold the sqfs_inode_id. We can just make minor adjustments for the reserved values of fuse_ino_t. 2. On 32-bit systems, with export table: fuse_ino_t can hold the sqfs_inode_number, with adjustments for reserved values. We can use the export table to lookup the sqfs_inode_id from the sqfs_inode_number. 3. On 32-bit systems, no export: fuse_ino_t again holds sqfs_inode_number. But we have to maintain our own mapping of sqfs_inode_number -> sqfs_inode_id, which can use quite a lot of memory. */ /***** INODE CONVERSION FOR 64-BIT INODES **** * * sqfs(root) maps to FUSE_ROOT_ID == 1 * sqfs(0) maps to 2 * * Both 1 and 2 are guaranteed not to be used by sqfs, due to inode size */ static fuse_ino_t sqfs_ll_ino64_fuse(sqfs_ll *ll, sqfs_inode_id i) { if (i == sqfs_inode_root(&ll->fs)) { return FUSE_ROOT_ID; } else if (i == 0) { return 2; } else { return i; } } static sqfs_inode_id sqfs_ll_ino64_sqfs(sqfs_ll *ll, fuse_ino_t i) { if (i == FUSE_ROOT_ID) { return sqfs_inode_root(&ll->fs); } else if (i == 2) { return 0; } else { return i; } } static fuse_ino_t sqfs_ll_ino64_fuse_num(sqfs_ll *ll, sqfs_dir_entry *e) { return sqfs_ll_ino64_fuse(ll, sqfs_dentry_inode(e)); } static sqfs_err sqfs_ll_ino64_init(sqfs_ll *ll) { ll->ino_fuse = sqfs_ll_ino64_fuse; ll->ino_sqfs = sqfs_ll_ino64_sqfs; ll->ino_fuse_num = sqfs_ll_ino64_fuse_num; return SQFS_OK; } /***** INODE CONVERSION FOR 32-BIT INODES **** * * We maintain a cache of sqfs_inode_num => sqfs_inode_id. * We go the other direction by fetching inodes. * * Mapping: sqfs_inode_num <=> fuse_ino_t * Most num(N) maps to N + 1 * num(root) maps to FUSE_ROOT_ID == 1 * num(0) maps to num(root) + 1 * * FIXME: * - Theoretically this could overflow if a filesystem uses all 2 ** 32 inodes, * since fuse inode zero is unavailable. */ #define SQFS_ICACHE_INITIAL 32 #define FUSE_INODE_NONE 0 #define SQFS_INODE_NONE 1 typedef struct { sqfs_inode_num root; sqfs_hash icache; } sqfs_ll_inode_map; /* Pack tightly to save memory */ typedef struct { uint32_t refcount; uint32_t ino_hi; uint16_t ino_lo; } sqfs_ll_inode_entry; #define IE_INODE(ie) (((uint64_t)(ie)->ino_hi << 16) | (ie)->ino_lo) #define INODE_HI(i) ((i) >> 16) #define INODE_LO(i) ((i) & 0xFFFF) static fuse_ino_t sqfs_ll_ino32_num2fuse(sqfs_ll *ll, sqfs_inode_num n) { sqfs_ll_inode_map *map = ll->ino_data; if (n == map->root) { return FUSE_ROOT_ID; } else if (n == 0) { return map->root + 1; } else { return n + 1; } } static fuse_ino_t sqfs_ll_ino32_fuse2num(sqfs_ll *ll, fuse_ino_t i) { sqfs_ll_inode_map *map = ll->ino_data; if (i == FUSE_ROOT_ID) { return map->root; } else if (i == map->root + 1) { return 0; } else { return i - 1; } } static fuse_ino_t sqfs_ll_ino32_fuse(sqfs_ll *ll, sqfs_inode_id i) { sqfs_inode inode; if (sqfs_inode_get(&ll->fs, &inode, i)) return FUSE_INODE_NONE; /* We shouldn't get here! */ return sqfs_ll_ino32_num2fuse(ll, inode.base.inode_number); } static sqfs_inode_id sqfs_ll_ino32_sqfs(sqfs_ll *ll, fuse_ino_t i) { sqfs_ll_inode_map *map; sqfs_inode_num n; sqfs_ll_inode_entry *ie; if (i == FUSE_ROOT_ID) return sqfs_inode_root(&ll->fs); map = ll->ino_data; n = sqfs_ll_ino32_fuse2num(ll, i); ie = sqfs_hash_get(&map->icache, n); return ie ? IE_INODE(ie) : SQFS_INODE_NONE; } static fuse_ino_t sqfs_ll_ino32_fuse_num(sqfs_ll *ll, sqfs_dir_entry *e) { return sqfs_ll_ino32_num2fuse(ll, sqfs_dentry_inode_num(e)); } static fuse_ino_t sqfs_ll_ino32_register(sqfs_ll *ll, sqfs_dir_entry *e) { sqfs_ll_inode_map *map = ll->ino_data; sqfs_ll_inode_entry *ie = sqfs_hash_get(&map->icache, sqfs_dentry_inode_num(e)); if (ie) { ++ie->refcount; } else { sqfs_err err = SQFS_OK; sqfs_inode_id i = sqfs_dentry_inode(e); sqfs_ll_inode_entry nie; nie.ino_hi = INODE_HI(i); nie.ino_lo = INODE_LO(i); nie.refcount = 1; err = sqfs_hash_add(&map->icache, sqfs_dentry_inode_num(e), &nie); if (err) return FUSE_INODE_NONE; } return sqfs_ll_ino32_fuse_num(ll, e); } static void sqfs_ll_ino32_forget(sqfs_ll *ll, fuse_ino_t i, size_t refs) { sqfs_ll_inode_map *map = ll->ino_data; sqfs_inode_num n = sqfs_ll_ino32_fuse2num(ll, i); sqfs_ll_inode_entry *ie = sqfs_hash_get(&map->icache, n); if (!ie) return; if (ie->refcount > refs) { ie->refcount -= refs; } else { sqfs_hash_remove(&map->icache, n); } } static void sqfs_ll_ino32_destroy(sqfs_ll *ll) { sqfs_ll_inode_map *map = ll->ino_data; sqfs_hash_destroy(&map->icache); free(map); } static sqfs_err sqfs_ll_ino32_init(sqfs_ll *ll) { sqfs_inode inode; sqfs_ll_inode_map *map; sqfs_err err = sqfs_inode_get(&ll->fs, &inode, sqfs_inode_root(&ll->fs)); if (err) return err; map = malloc(sizeof(sqfs_ll_inode_map)); map->root = inode.base.inode_number; sqfs_hash_init(&map->icache, sizeof(sqfs_ll_inode_entry), SQFS_ICACHE_INITIAL); ll->ino_fuse = sqfs_ll_ino32_fuse; ll->ino_sqfs = sqfs_ll_ino32_sqfs; ll->ino_fuse_num = sqfs_ll_ino32_fuse_num; ll->ino_register = sqfs_ll_ino32_register; ll->ino_forget = sqfs_ll_ino32_forget; ll->ino_destroy = sqfs_ll_ino32_destroy; ll->ino_data = map; return err; } /***** INODE CONVERSION FOR 32-BIT INODES, EXPORT TABLE AVAILABLE **** * * Same transformation as regular 32-bit, but no caching. */ static sqfs_inode_id sqfs_ll_ino32exp_sqfs(sqfs_ll *ll, fuse_ino_t i) { sqfs_inode_num n; sqfs_inode_id ret; sqfs_err err = SQFS_OK; if (i == FUSE_ROOT_ID) return sqfs_inode_root(&ll->fs); n = sqfs_ll_ino32_fuse2num(ll, i); err = sqfs_export_inode(&ll->fs, n, &ret); return err ? SQFS_INODE_NONE : ret; } static void sqfs_ll_ino32exp_destroy(sqfs_ll *ll) { free(ll->ino_data); } static sqfs_err sqfs_ll_ino32exp_init(sqfs_ll *ll) { sqfs_inode inode; sqfs_ll_inode_map *map; sqfs_err err = sqfs_inode_get(&ll->fs, &inode, sqfs_inode_root(&ll->fs)); if (err) return err; map = malloc(sizeof(sqfs_ll_inode_map)); map->root = inode.base.inode_number; ll->ino_fuse = sqfs_ll_ino32_fuse; ll->ino_sqfs = sqfs_ll_ino32exp_sqfs; ll->ino_fuse_num = sqfs_ll_ino32_fuse_num; ll->ino_destroy = sqfs_ll_ino32exp_destroy; ll->ino_data = map; return err; } static void sqfs_ll_null_forget(sqfs_ll *ll, fuse_ino_t i, size_t refs) { /* pass */ } sqfs_err sqfs_ll_init(sqfs_ll *ll) { sqfs_err err = SQFS_OK; if (sizeof(fuse_ino_t) >= SQFS_INODE_ID_BYTES) { err = sqfs_ll_ino64_init(ll); } else if (sqfs_export_ok(&ll->fs)) { err = sqfs_ll_ino32exp_init(ll); } else { err = sqfs_ll_ino32_init(ll); } if (!ll->ino_register) ll->ino_register = ll->ino_fuse_num; if (!ll->ino_forget) ll->ino_forget = sqfs_ll_null_forget; return err; } void sqfs_ll_destroy(sqfs_ll *ll) { sqfs_destroy(&ll->fs); if (ll->ino_destroy) ll->ino_destroy(ll); } sqfs_err sqfs_ll_inode(sqfs_ll *ll, sqfs_inode *inode, fuse_ino_t i) { return sqfs_inode_get(&ll->fs, inode, ll->ino_sqfs(ll, i)); } sqfs_err sqfs_ll_iget(fuse_req_t req, sqfs_ll_i *lli, fuse_ino_t i) { sqfs_err err = SQFS_OK; lli->ll = fuse_req_userdata(req); if (i != SQFS_FUSE_INODE_NONE) { err = sqfs_ll_inode(lli->ll, &lli->inode, i); if (err) fuse_reply_err(req, ENOENT); } return err; } sqfs_err sqfs_ll_stat(sqfs_ll *ll, sqfs_inode *inode, struct stat *st) { sqfs_err err = SQFS_OK; uid_t id; memset(st, 0, sizeof(*st)); st->st_mode = inode->base.mode; st->st_nlink = inode->nlink; st->st_mtime = st->st_ctime = st->st_atime = inode->base.mtime; if (S_ISREG(st->st_mode)) { /* FIXME: do symlinks, dirs, etc have a size? */ st->st_size = inode->xtra.reg.file_size; st->st_blocks = st->st_size / 512; } else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { st->st_rdev = sqfs_makedev(inode->xtra.dev.major, inode->xtra.dev.minor); } st->st_blksize = ll->fs.sb.block_size; /* seriously? */ err = sqfs_id_get(&ll->fs, inode->base.uid, &id); if (err) return err; st->st_uid = id; err = sqfs_id_get(&ll->fs, inode->base.guid, &id); st->st_gid = id; if (err) return err; return SQFS_OK; } squashfuse-0.5.0/ll_main.c000066400000000000000000000215041450034605500154660ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ll.h" #include "fuseprivate.h" #include "stat.h" #include "nonstd.h" #include #include #include #include #include #include #include #include #if defined(SQFS_SIGTERM_HANDLER) #include #include static bool kernel_version_at_least(unsigned required_major, unsigned required_minor, unsigned required_micro) { struct utsname info; if (uname(&info) >= 0) { unsigned major, minor, micro; if (sscanf(info.release, "%u.%u.%u", &major, &minor, µ) == 3) { return KERNEL_VERSION(major, minor, micro) >= KERNEL_VERSION(required_major, required_minor, required_micro); } } return false; } /* libfuse's default SIGTERM handler (set up in fuse_set_signal_handlers()) * immediately calls fuse_session_exit(), which shuts down the filesystem * even if there are active users. This leads to nastiness if other processes * still depend on the filesystem. * * So: we respond to SIGTERM by starting a lazy unmount. This is done * by exec'ing fusermount3, which works properly for unpriviledged * users (we cannot use umount2() syscall because it is not signal safe; * fork() and exec(), amazingly, are). * * If we fail to start the lazy umount, we signal ourself with SIGINT, * which falls back to the old behavior of exiting ASAP. */ static const char *g_mount_point = NULL; static void sigterm_handler(int signum) { /* Unfortunately, lazy umount of in-use fuse filesystem triggers * kernel bug on kernels < 5.2, Fixed by kernel commit * e8f3bd773d22f488724dffb886a1618da85c2966 in 5.2. */ if (g_mount_point && kernel_version_at_least(5,2,0)) { int pid = fork(); if (pid == 0) { /* child process: disassociate ourself from parent so * we do not become zombie (as parent does not waitpid()). */ pid_t parent = getppid(); setsid(); execl("/bin/fusermount3", "fusermount3", "-u", "-q", "-z", "--", g_mount_point, NULL); execlp("fusermount3", "fusermount3", "-u", "-q", "-z", "--", g_mount_point, NULL); /* if we get here, we can't run fusermount, * kill the original process with a harshness. */ kill(parent, SIGINT); _exit(0); } else if (pid > 0) { /* parent process: nothing to do, murderous child will do us * in one way or another. */ return; } } /* If we get here, we have failed to lazy unmount for whatever reason, * kill ourself more brutally. */ kill(getpid(), SIGINT); } static void set_sigterm_handler(const char *mountpoint) { struct sigaction sa; g_mount_point = mountpoint; memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigterm_handler; sigemptyset(&(sa.sa_mask)); sa.sa_flags = SA_RESTART; if (sigaction(SIGTERM, &sa, NULL) == -1) { perror("sigaction(SIGTERM)"); } } #endif /* SQFS_SIGTERM_HANDLER */ int main(int argc, char *argv[]) { struct fuse_args args; sqfs_opts opts; #if FUSE_USE_VERSION >= 30 struct fuse_cmdline_opts fuse_cmdline_opts = {}; #else struct { char *mountpoint; int mt, foreground; } fuse_cmdline_opts = {}; #endif int err; int sqfs_err; sqfs_ll *ll = NULL; struct fuse_opt fuse_opts[] = { {"offset=%zu", offsetof(sqfs_opts, offset), 0}, {"timeout=%u", offsetof(sqfs_opts, idle_timeout_secs), 0}, {"uid=%d", offsetof(sqfs_opts, uid), 0}, {"gid=%d", offsetof(sqfs_opts, gid), 0}, {"subdir=%s", offsetof(sqfs_opts, subdir), 0}, {"notify_pipe=%s", offsetof(sqfs_opts, notify_pipe), 0}, FUSE_OPT_END }; struct fuse_lowlevel_ops sqfs_ll_ops; memset(&sqfs_ll_ops, 0, sizeof(sqfs_ll_ops)); sqfs_ll_ops.getattr = sqfs_ll_op_getattr; sqfs_ll_ops.opendir = sqfs_ll_op_opendir; sqfs_ll_ops.releasedir = sqfs_ll_op_releasedir; sqfs_ll_ops.readdir = sqfs_ll_op_readdir; sqfs_ll_ops.lookup = sqfs_ll_op_lookup; sqfs_ll_ops.open = sqfs_ll_op_open; sqfs_ll_ops.create = sqfs_ll_op_create; sqfs_ll_ops.release = sqfs_ll_op_release; sqfs_ll_ops.read = sqfs_ll_op_read; sqfs_ll_ops.readlink = sqfs_ll_op_readlink; sqfs_ll_ops.listxattr = sqfs_ll_op_listxattr; sqfs_ll_ops.getxattr = sqfs_ll_op_getxattr; sqfs_ll_ops.forget = sqfs_ll_op_forget; sqfs_ll_ops.statfs = stfs_ll_op_statfs; sqfs_ll_ops.init = sqfs_ll_op_init; /* PARSE ARGS */ args.argc = argc; args.argv = argv; args.allocated = 0; opts.progname = argv[0]; opts.image = NULL; opts.mountpoint = 0; opts.offset = 0; opts.idle_timeout_secs = 0; opts.uid = 0; opts.gid = 0; opts.subdir = NULL; opts.notify_pipe = NULL; if (fuse_opt_parse(&args, &opts, fuse_opts, sqfs_opt_proc) == -1) { err = sqfs_usage(argv[0], true, true); goto out; } #if FUSE_USE_VERSION >= 30 if (fuse_parse_cmdline(&args, &fuse_cmdline_opts) != 0) { #else if (fuse_parse_cmdline(&args, &fuse_cmdline_opts.mountpoint, &fuse_cmdline_opts.mt, &fuse_cmdline_opts.foreground) == -1) { #endif err = sqfs_usage(argv[0], true, true); goto out; } if (fuse_cmdline_opts.mountpoint == NULL) { err = sqfs_usage(argv[0], true, true); goto out; } /* fuse_daemonize() will unconditionally clobber fds 0-2. * * If we get one of these file descriptors in sqfs_ll_open, * we're going to have a bad time. Just make sure that all * these fds are open before opening the image file, that way * we must get a different fd. */ while (true) { int fd = open("/dev/null", O_RDONLY); if (fd == -1) { /* Can't open /dev/null, how bizarre! However, * fuse_deamonize won't clobber fds if it can't * open /dev/null either, so we ought to be OK. */ break; } if (fd > 2) { /* fds 0-2 are now guaranteed to be open. */ close(fd); break; } } /* OPEN FS */ err = !(ll = sqfs_ll_open_with_subdir(opts.image, opts.offset, opts.subdir)); /* STARTUP FUSE */ if (!err) { ll->fs.uid = opts.uid; ll->fs.gid = opts.gid; ll->fs.notify_pipe = opts.notify_pipe; sqfs_ll_chan ch; err = -1; sqfs_err = sqfs_ll_mount( &ch, fuse_cmdline_opts.mountpoint, &args, &sqfs_ll_ops, sizeof(sqfs_ll_ops), ll); if (sqfs_err != SQFS_OK) { goto out; } if (sqfs_ll_daemonize(fuse_cmdline_opts.foreground) != -1) { if (fuse_set_signal_handlers(ch.session) != -1) { #if defined(SQFS_SIGTERM_HANDLER) set_sigterm_handler(fuse_cmdline_opts.mountpoint); #endif if (opts.idle_timeout_secs) { setup_idle_timeout(ch.session, opts.idle_timeout_secs); } #ifdef SQFS_MULTITHREADED # if FUSE_USE_VERSION >= 30 if (!fuse_cmdline_opts.singlethread) { struct fuse_loop_config config; config.clone_fd = 1; config.max_idle_threads = 10; err = fuse_session_loop_mt(ch.session, &config); } # else /* FUSE_USE_VERSION < 30 */ if (fuse_cmdline_opts.mt) { err = fuse_session_loop_mt(ch.session); } # endif /* FUSE_USE_VERSION */ else #endif err = fuse_session_loop(ch.session); teardown_idle_timeout(); fuse_remove_signal_handlers(ch.session); } } sqfs_ll_destroy(ll); sqfs_ll_unmount(&ch, fuse_cmdline_opts.mountpoint); } out: if (err) { if (opts.notify_pipe) { notify_mount_ready(opts.notify_pipe, NOTIFY_FAILURE); } } fuse_opt_free_args(&args); free(ll); free(fuse_cmdline_opts.mountpoint); return -err; } squashfuse-0.5.0/ls.c000066400000000000000000000043511450034605500144720ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "squashfuse.h" #include #include #include #define PROGNAME "squashfuse_ls" #define ERR_MISC (-1) #define ERR_USAGE (-2) #define ERR_OPEN (-3) static void usage() { fprintf(stderr, "%s (c) 2013 Dave Vasilevsky\n\n", PROGNAME); fprintf(stderr, "Usage: %s ARCHIVE\n", PROGNAME); exit(ERR_USAGE); } static void die(const char *msg) { fprintf(stderr, "%s\n", msg); exit(ERR_MISC); } int main(int argc, char *argv[]) { sqfs_err err = SQFS_OK; sqfs_traverse trv; sqfs fs; char *image; if (argc != 2) usage(); image = argv[1]; if ((err = sqfs_open_image(&fs, image, 0))) exit(ERR_OPEN); if ((err = sqfs_traverse_open(&trv, &fs, sqfs_inode_root(&fs)))) die("sqfs_traverse_open error"); while (sqfs_traverse_next(&trv, &err)) { if (!trv.dir_end) { printf("%s\n", trv.path); } } if (err) die("sqfs_traverse_next error"); sqfs_traverse_close(&trv); sqfs_fd_close(fs.fd); return 0; } squashfuse-0.5.0/m4/000077500000000000000000000000001450034605500142255ustar00rootroot00000000000000squashfuse-0.5.0/m4/.gitignore000066400000000000000000000000651450034605500162160ustar00rootroot00000000000000# Should these be included? lt*.m4 libtool.m4 pkg.m4 squashfuse-0.5.0/m4/squashfuse.m4000066400000000000000000000067541450034605500166720ustar00rootroot00000000000000# Copyright (c) 2012 Dave Vasilevsky # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SQ_CHECK_PROG_MAKE_EXPORT # # Check if make supports exporting variables. Define the MAKE_EXPORT # conditional on success. AC_DEFUN([SQ_CHECK_PROG_MAKE_EXPORT],[ AC_CACHE_CHECK([if ${MAKE-make} supports export], [sq_cv_prog_make_export],[ sq_cv_prog_make_export=no cat > confmak <<'END' export FOO=1 all: END AS_IF([${MAKE-make} -f confmak >/dev/null 2>/dev/null], [sq_cv_prog_make_export=yes]) rm -f confmak ]) AM_CONDITIONAL([MAKE_EXPORT],[test "x$sq_cv_prog_make_export" = xyes]) ]) # SQ_AROUND(STRING, INSIDE) # # If STRING contains INSIDE, return the part of string surrounding INSIDE. # Otherwise, return the original STRING. AC_DEFUN([SQ_AROUND],[dnl `echo | $AWK '{ i=[[index]](v,o); if(i>0){print substr(v,1,i-1) substr(v,i+length(o))}else{print v} }' v="$1" o="$2"` ]) # SQ_SAVE_FLAGS # SQ_RESTORE_FLAGS # SQ_KEEP_FLAGS(PREFIX,[KEEP]) # # Save and restore compiler flags. If KEEP is given, keep any changes that have # been made. Eg: If saved when LIBS="foo", and restored when LIBS="foo bar", # PREFIX_LIBS would be set to " bar". AC_DEFUN([SQ_SAVE_FLAGS],[ AS_VAR_PUSHDEF([sq_save_idx],m4_incr(m4_ifdef([sq_save_idx],sq_save_idx,0))) m4_foreach_w([sq_flag],[LIBS CPPFLAGS],[ AS_VAR_PUSHDEF([sq_save_]sq_flag,[sq_save_]sq_flag[_]sq_save_idx) AS_VAR_SET([sq_save_]sq_flag,$[]sq_flag) ]) ]) AC_DEFUN([SQ_RESTORE_FLAGS],[ m4_foreach_w([sq_flag],[LIBS CPPFLAGS],[ AS_VAR_PUSHDEF([sq_saved],[sq_save_]sq_flag) sq_flag[]=$sq_saved AS_VAR_POPDEF([sq_save_]sq_flag) AS_VAR_POPDEF([sq_saved]) ]) AS_VAR_POPDEF([sq_save_idx]) ]) AC_DEFUN([SQ_KEEP_FLAGS],[ m4_foreach_w([sq_flag],[LIBS CPPFLAGS],[ AS_VAR_PUSHDEF([sq_saved],[sq_save_]sq_flag) AS_VAR_PUSHDEF([sq_tgt],$1[_]sq_flag) AS_IF([test "x$2" = x],,[ AS_VAR_SET([sq_tgt],SQ_AROUND([$sq_flag],$sq_saved)) ]) AC_SUBST(sq_tgt) AS_VAR_POPDEF([sq_tgt]) ]) SQ_RESTORE_FLAGS ]) # SQ_PKG(NAME, PKG, [IF-FOUND], [IF-NOT-FOUND]) # # Like PKG_CHECK_MODULES, but sets non-prefixed LIBS and CPPFLAGS AC_DEFUN([SQ_PKG],[ AS_VAR_PUSHDEF([sq_pkg],[pkgconfig_]$1) PKG_CHECK_MODULES(sq_pkg,[$2],[ LIBS="$LIBS $[]pkgconfig_[]$1[]_LIBS" # yes, CFLAGS, we want the preprocessor to work CPPFLAGS="$CPPFLAGS $[]pkgconfig_[]$1[]_CFLAGS" $3 ],[$4]) ]) squashfuse-0.5.0/m4/squashfuse_c.m4000066400000000000000000000033441450034605500171640ustar00rootroot00000000000000# Copyright (c) 2012 Dave Vasilevsky # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SQ_PROG_CC_WALL # # Check if -Wall is supported AC_DEFUN([SQ_PROG_CC_WALL],[ AC_CACHE_CHECK([how to enable all compiler warnings], [sq_cv_prog_cc_wall], [ sq_cv_prog_cc_wall=unknown sq_save_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Wall" AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)],[sq_cv_prog_cc_wall="-Wall"]) CFLAGS=$sq_save_CFLAGS ]) AS_IF([test "x$sq_cv_prog_cc_wall" = xunknown],, [AC_SUBST([AM_CFLAGS],["$AM_CFLAGS $sq_cv_prog_cc_wall"])]) ]) squashfuse-0.5.0/m4/squashfuse_decompress.m4000066400000000000000000000050241450034605500211030ustar00rootroot00000000000000# Copyright (c) 2012 Dave Vasilevsky # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SQ_CHECK_DECOMPRESS(NAME, LIBRARY, FUNCTION, HEADER, PKGCONFIG, MKSQUASHFS_NAME) # # Check for a decompression library with the given library name, function and # header. If given pkg-config package name, also look using pkg-config. # # On success modify CPPFLAGS and LIBS and append NAME to sq_decompressors. AC_DEFUN([SQ_CHECK_DECOMPRESS],[ SQ_SAVE_FLAGS sq_want=yes sq_specified=no AC_ARG_WITH(m4_tolower($1), AS_HELP_STRING([--with-]m4_tolower($1)[=DIR], m4_tolower($1)[ prefix directory]),[ AS_IF([test "x$withval" = xno],[ sq_want=no ],[ sq_specified=yes CPPFLAGS="$CPPFLAGS -I$withval/include" LIBS="$LIBS -L$withval/lib" ]) ]) sq_dec_ok= AS_IF([test "x$sq_want" = xyes],[ sq_lib="$2" m4_ifval($5,[AS_IF([test "x$sq_specified" = xno],[ SQ_PKG($1,$5,[sq_lib=],[:]) ])]) sq_dec_ok=yes AC_SEARCH_LIBS($3,[$sq_lib],,[sq_dec_ok=]) AS_IF([test "x$sq_dec_ok" = xyes],[AC_CHECK_HEADERS($4,,[sq_dec_ok=])]) AS_IF([test "x$sq_dec_ok" = xyes],[ sq_decompressors="$sq_decompressors $1" sq_mksquashfs_compressors="$sq_mksquashfs_compressors $6" ],[ AS_IF([test "x$sq_specified" = xyes], [AC_MSG_FAILURE([Asked for ]$1[, but it can't be found])]) ]) ]) SQ_KEEP_FLAGS($1,[$sq_dec_ok]) ]) squashfuse-0.5.0/m4/squashfuse_fuse.m4000066400000000000000000000233651450034605500177110ustar00rootroot00000000000000# Copyright (c) 2012 Dave Vasilevsky # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SQ_TRY_FUSE(LIBS, [IF-FOUND], [IF-NOT-FOUND]) # # Check if FUSE low-level compiles and links correctly. AC_DEFUN([SQ_TRY_FUSE],[ sq_fuse_ok=yes AS_VAR_PUSHDEF([sq_cv_lib],[sq_cv_lib_fuse_""$1""_""$LIBS]) AC_CACHE_CHECK([for FUSE library],[sq_cv_lib],[ for sq_lib in '' $1 do SQ_SAVE_FLAGS AS_IF([test "x$sq_lib" = x],,[LIBS="$LIBS -l$sq_lib"]) AC_LINK_IFELSE([AC_LANG_CALL(,[fuse_get_context])],[ AS_IF([test "x$sq_lib" = x],[sq_lib_out="already present"], [sq_lib_out="-l$sq_lib"]) AS_VAR_SET([sq_cv_lib],[$sq_lib_out]) ]) SQ_RESTORE_FLAGS AS_VAR_SET_IF([sq_cv_lib],[break]) done AS_VAR_SET_IF([sq_cv_lib],,[AS_VAR_SET([sq_cv_lib],[no])]) ]) AS_VAR_IF([sq_cv_lib],[no],[sq_fuse_ok=no]) AS_IF([test "x$sq_fuse_ok" = "xno"],,[ AS_VAR_PUSHDEF([sq_cv_hdr],[sq_cv_header_fuse_""$CPPFLAGS]) AC_CACHE_CHECK([for FUSE header],[sq_cv_hdr],[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include #include ])], [AS_VAR_SET([sq_cv_hdr],[yes])], [AS_VAR_SET([sq_cv_hdr],[no])] ) ]) AS_VAR_IF([sq_cv_hdr],[yes],,[sq_fuse_ok=no]) AS_VAR_POPDEF([sq_cv_hdr]) ]) AS_IF([test "x$sq_fuse_ok" = "xno"],[$3],[ AS_VAR_COPY([sq_lib_res],[sq_cv_lib]) AS_IF([test "x$sq_lib_res" = "xalready present"],, [LIBS="$LIBS $sq_lib_res"]) $2 ]) AS_VAR_POPDEF([sq_cv_lib]) ]) # SQ_TRY_FUSE_DIRS(NAME, INCDIR, LIBDIR, CPPFLAGS, LIBS, # [IF-FOUND], [IF-NOT-FOUND]) # # Check if FUSE is in any of the given directories AC_DEFUN([SQ_TRY_FUSE_DIRS],[ AS_IF([test "x$sq_fuse_found" = xyes],,[ AS_IF([test "x$1" = x],,[AC_MSG_NOTICE([Checking for FUSE in $1])]) SQ_SAVE_FLAGS AS_IF([test "x$2" = x],,[CPPFLAGS="$CPPFLAGS -I$2"]) AS_IF([test "x$3" = x],,[LIBS="$LIBS -L$3"]) CPPFLAGS="$CPPFLAGS $4" SQ_TRY_FUSE($5,[sq_fuse_found=yes],[sq_fuse_found=]) SQ_KEEP_FLAGS([FUSE],[$sq_fuse_found]) AS_IF([test "x$sq_fuse_found" = xyes],$6,$7) ]) ]) # SQ_SEARCH_FUSE_DIRS # # Nobody told us where FUSE is, search some common places. AC_DEFUN([SQ_SEARCH_FUSE_DIRS],[ AS_CASE([$target_os],[darwin*],[ SQ_TRY_FUSE_DIRS([OSXFUSE directories], [/usr/local/include/osxfuse/fuse],[/usr/local/lib], [$sq_fuse_cppflags],[osxfuse $sq_fuse_libs]) ]) SQ_TRY_FUSE_DIRS([default directories],,, [$sq_fuse_cppflags],[$sq_fuse_libs]) SQ_TRY_FUSE_DIRS([/usr],[/usr/include/fuse],[/usr/lib], [$sq_fuse_cppflags],[$sq_fuse_libs]) SQ_TRY_FUSE_DIRS([/usr/local],[/usr/local/include/fuse],[/usr/local/lib], [$sq_fuse_cppflags],[$sq_fuse_libs]) AS_IF([test "x$sq_fuse_found" = xyes],[ sq_cv_lib_fuse_LIBS="$FUSE_LIBS" sq_cv_lib_fuse_CPPFLAGS="$FUSE_CPPFLAGS" ],[sq_cv_lib_fuse_LIBS=no]) ]) # SQ_FIND_FUSE # # Find the FUSE library AC_DEFUN([SQ_FIND_FUSE],[ # FUSE headers usually demand _FILE_OFFSET_BITS=64 sq_fuse_cppflags="-D_FILE_OFFSET_BITS=64" sq_fuse_libs="fuse" AS_CASE([$target_os],[darwin*],[ sq_fuse_cppflags="$sq_fuse_cppflags -D__FreeBSD__=10 -D_DARWIN_USE_64_BIT_INODE" sq_fuse_libs="osxfuse fuse4x fuse_ino64 $sq_fuse_libs" ]) AC_ARG_WITH(fuse-soname, AS_HELP_STRING([--with-fuse-soname=SONAME], [FUSE library name]), [sq_fuse_libs="$withval"]) sq_fuse_found= # Specified in arguments AC_ARG_WITH(fuse, AS_HELP_STRING([--with-fuse=DIR], [FUSE prefix directory]),[ fuse_inc="$withval/include/fuse" fuse_lib="$withval/lib" ]) AC_ARG_WITH(fuse-include, AS_HELP_STRING([--with-fuse-include=DIR], [FUSE header directory]), [fuse_inc=$withval]) AC_ARG_WITH(fuse-lib, AS_HELP_STRING([--with-fuse-lib=DIR], [FUSE library directory]), [fuse_lib=$withval]) AS_IF([test "x$fuse_inc$fuse_lib" = x],,[ SQ_TRY_FUSE_DIRS(,[$fuse_inc],[$fuse_lib],[$sq_fuse_cppflags], [$sq_fuse_libs],, [AC_MSG_FAILURE([Can't find FUSE in specified directories])]) ]) # Use pkgconfig to look for fuse3. AS_IF([test "x$sq_fuse_found" = xyes],,[ SQ_SAVE_FLAGS SQ_PKG([fuse3],[fuse3 >= 3.2], [ AC_DEFINE([FUSE_USE_VERSION], [32], [Version of FUSE API to use]) SQ_TRY_FUSE(,[sq_fuse_found=yes], [AC_MSG_FAILURE([Can't find FUSE with pkgconfig])])], [:]) SQ_KEEP_FLAGS([FUSE],[$sq_fuse_found]) ]) # Use pkgconfig to look for fuse2. AS_IF([test "x$sq_fuse_found" = xyes],,[ AC_DEFINE([FUSE_USE_VERSION], [26], [Version of FUSE API to use]) SQ_SAVE_FLAGS SQ_PKG([fuse],[fuse >= 2.6], [SQ_TRY_FUSE(,[sq_fuse_found=yes], [AC_MSG_FAILURE([Can't find FUSE with pkgconfig])])], [:]) SQ_KEEP_FLAGS([FUSE],[$sq_fuse_found]) ]) # Default search locations AS_IF([test "x$sq_cv_lib_fuse_LIBS" = x],[SQ_SEARCH_FUSE_DIRS],[ AS_IF([test "x$sq_cv_lib_fuse_LIBS" = xno],,[ AC_CACHE_CHECK([FUSE libraries],[sq_cv_lib_fuse_LIBS]) AC_CACHE_CHECK([FUSE preprocessor flags],[sq_cv_lib_fuse_CPPFLAGS]) FUSE_LIBS=$sq_cv_lib_fuse_LIBS FUSE_CPPFLAGS=$sq_cv_lib_fuse_CPPFLAGS sq_fuse_found=yes ]) ]) AS_IF([test "x$sq_fuse_found" = xyes],, [AC_MSG_FAILURE([Can't find FUSE])]) ]) # SQ_FUSE_API # # Check for the high-level FUSE API AC_DEFUN([SQ_FUSE_API],[ AC_ARG_ENABLE([high-level], AS_HELP_STRING([--disable-high-level], [disable high-level FUSE driver]),, [sq_high_level=yes]) AC_ARG_ENABLE([low-level], AS_HELP_STRING([--disable-low-level], [disable low-level FUSE driver]),, [sq_low_level=check]) AC_ARG_ENABLE(fuse, AS_HELP_STRING([--disable-fuse], [disable all FUSE drivers])) AS_IF([test "x$enable_fuse" = xno],[ sq_high_level=no sq_low_level=no ]) AS_IF([test "x$sq_high_level$sq_low_level" = xnono],,[SQ_FIND_FUSE]) ]) # SQ_FUSE_API_LOWLEVEL # # Check if we have the low-level FUSE API available AC_DEFUN([SQ_FUSE_API_LOWLEVEL],[ AS_IF([test "x$sq_low_level" = xno],,[ SQ_SAVE_FLAGS LIBS="$LIBS $FUSE_LIBS" CPPFLAGS="$CPPFLAGS $FUSE_CPPFLAGS" sq_fuse_lowlevel_found=yes AC_CHECK_DECL([fuse_session_loop],,[sq_fuse_lowlevel_found=no], [#include ]) AC_CHECK_FUNC([fuse_session_loop],,[sq_fuse_lowlevel_found=no]) SQ_RESTORE_FLAGS AS_IF([test "x$sq_fuse_lowlevel_found" = xno],[ sq_err="The low-level FUSE API is not available" AS_IF([test "x$sq_low_level" = xyes],[AC_MSG_FAILURE($sq_err)], [AC_MSG_WARN($sq_err)]) ]) sq_low_level="$sq_fuse_lowlevel_found" ]) ]) # SQ_FUSE_RESULT # # Handle the results of FUSE checks AC_DEFUN([SQ_FUSE_RESULT],[ AS_IF([test "x$sq_high_level$sq_low_level" = xnono],[ AC_MSG_WARN([Without any FUSE support, you will not be able to mount squashfs archives]) ]) AM_CONDITIONAL([SQ_WANT_HIGHLEVEL], [test "x$sq_high_level" = xyes]) AM_CONDITIONAL([SQ_WANT_LOWLEVEL], [test "x$sq_low_level" = xyes]) AM_CONDITIONAL([SQ_WANT_FUSE], [test "x$sq_high_level$sq_low_level" != xnono]) ]) # SQ_FUSE_API_VERSION # # Check various things that are different in different versions of FUSE AC_DEFUN([SQ_FUSE_API_VERSION],[ SQ_SAVE_FLAGS LIBS="$LIBS $FUSE_LIBS" CPPFLAGS="$CPPFLAGS $FUSE_CPPFLAGS" AS_IF([test "x$sq_low_level" = xyes],[ AC_CHECK_DECLS([fuse_add_direntry,fuse_add_dirent],[found_dirent=yes],, [#include ]) AS_IF([test "x$found_dirent" = xyes],, [AC_MSG_FAILURE([No way to add directory entries])]) AC_CHECK_DECLS([fuse_daemonize],, [SQ_CHECK_NONSTD(daemon,[#include ],[(void)daemon;])], [#include ]) AC_CHECK_DECLS([fuse_session_remove_chan],,, [#include ]) AC_CACHE_CHECK([for two-argument fuse_unmount], [sq_cv_decl_fuse_unmount_two_arg],[ AC_LINK_IFELSE( [AC_LANG_PROGRAM([ #include #include ], [fuse_unmount(0,0)])], [sq_cv_decl_fuse_unmount_two_arg=yes], [sq_cv_decl_fuse_unmount_two_arg=no]) ]) AS_IF([test "x$sq_cv_decl_fuse_unmount_two_arg" = xyes],[ AC_DEFINE([HAVE_NEW_FUSE_UNMOUNT],1, [Define if we have two-argument fuse_unmount]) ]) AC_CHECK_DECLS([fuse_cmdline_help],,, [#include ]) ]) SQ_RESTORE_FLAGS ]) # SQ_FUSE_API_XATTR_POSITION # # Check for OS X's special flag to getxattr. AC_DEFUN([SQ_FUSE_API_XATTR_POSITION],[ SQ_SAVE_FLAGS LIBS="$LIBS $FUSE_LIBS" CPPFLAGS="$CPPFLAGS $FUSE_CPPFLAGS" AC_CACHE_CHECK([for position argument to FUSE xattr operations], [sq_cv_decl_fuse_xattr_position],[ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ],[ struct fuse_operations ops; ops.getxattr(0, 0, 0, 0, 0); ])], [sq_cv_decl_fuse_xattr_position=yes], [sq_cv_decl_fuse_xattr_position=no]) ]) AS_IF([test "x$sq_cv_decl_fuse_xattr_position" = xyes],[ AC_DEFINE([FUSE_XATTR_POSITION],1, [Define if FUSE xattr operations take a position argument]) ]) SQ_RESTORE_FLAGS ]) squashfuse-0.5.0/m4/squashfuse_posix.m4000066400000000000000000000110571450034605500201040ustar00rootroot00000000000000# Copyright (c) 2012 Dave Vasilevsky # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SQ_CHECK_NONSTD(NAME, PROLOG, SOURCE, [IF_NOT_FOUND]) # # Check what #define we need for non-POSIX features. # If no #define works and IF_NOT_FOUND is not given, exit on failure. AC_DEFUN([SQ_CHECK_NONSTD],[ AH_TEMPLATE(AS_TR_CPP([NONSTD_]$1[_DEF]), [Extra definition needed by non-POSIX ]$1) AS_VAR_PUSHDEF([sq_cache],[sq_cv_decl_nonstd_$1]) sq_msg="definition needed by $1" AS_VAR_SET_IF([sq_cache],[ AC_CACHE_CHECK([$sq_msg], [sq_cache]) ],[ AS_VAR_SET([sq_cache],[unknown]) AC_MSG_NOTICE([checking for $sq_msg]) for sq_def in none _DARWIN_C_SOURCE _NETBSD_SOURCE _XOPEN_SOURCE \ _BSD_SOURCE _GNU_SOURCE _POSIX_C_SOURCE do op=define val=1 AS_IF([test "x$sq_def" = "x_POSIX_C_SOURCE"],[ op=undef val= ]) AS_IF([test "x$sq_def" = "x_XOPEN_SOURCE"],[val=500]) AS_IF([test "x$sq_def" = "xnone"],[ sq_prolog= AC_MSG_CHECKING([if $1 works without changes]) ],[ sq_prolog="#$op $sq_def $val" AC_MSG_CHECKING([if $1 requires changing $sq_def]) ]) AC_LINK_IFELSE([AC_LANG_PROGRAM([ $sq_prolog $2 ],[$3]) ],[ AC_MSG_RESULT([yes]) AS_VAR_SET([sq_cache],[$sq_def]) break ],[AC_MSG_RESULT([no])]) done ]) AS_VAR_IF([sq_cache],[unknown], m4_default($4,[AC_MSG_FAILURE([can't figure out how to use $1])]), [AS_VAR_IF([sq_cache],[none],[],[ AC_DEFINE_UNQUOTED(AS_TR_CPP([NONSTD_$1_DEF]),[CHANGE$sq_cache],[]) ])] ) AS_VAR_POPDEF([sq_cache]) ]) # SQ_CHECK_DECL_MAKEDEV_QNX([IF_NOT_FOUND]) # # Check for QNX-style three argument makedev() AC_DEFUN([SQ_CHECK_DECL_MAKEDEV_QNX],[ AC_CACHE_CHECK([for QNX makedev], [sq_cv_decl_makedev_qnx],[ sq_cv_decl_makedev_qnx=no AC_LINK_IFELSE([AC_LANG_PROGRAM([ #include #include #include ],[makedev(ND_LOCAL_NODE,0,0)])], [sq_cv_decl_makedev_qnx=yes]) ]) AS_IF([test "x$sq_cv_decl_makedev_qnx" = xyes], [AC_DEFINE([QNX_MAKEDEV],[1],[define if makedev() is QNX-style])], [$1]) ]) # Various feature checks # # SQ_CHECK_DECL_MAKEDEV - makedev() macro # SQ_CHECK_DECL_PREAD - pread() in unistd.h # SQ_CHECK_DECL_S_IFSOCK - S_IFSOCK in struct stat mode # SQ_CHECK_DECL_ENOATTR([IF_NOT_FOUND]) - ENOATTR error code # SQ_CHECK_DECL_DAEMON - daemon() in unistd.h # SQ_CHECK_DECL_SYMLINK - symlink() in unistd.h AC_DEFUN([SQ_CHECK_DECL_MAKEDEV],[ SQ_CHECK_DECL_MAKEDEV_QNX([ AC_CHECK_HEADERS([sys/mkdev.h],,,[#include ]) AC_CHECK_HEADERS([sys/sysmacros.h],,,[#include ]) SQ_CHECK_NONSTD(makedev,[ #include #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif ],[makedev(0,0)]) ]) ]) AC_DEFUN([SQ_CHECK_DECL_PREAD], [SQ_CHECK_NONSTD(pread,[#include ],[(void)pread;])]) AC_DEFUN([SQ_CHECK_DECL_S_IFSOCK], [SQ_CHECK_NONSTD(S_IFSOCK,[#include ],[mode_t m = S_IFSOCK;])]) AC_DEFUN([SQ_CHECK_DECL_ENOATTR],[ AC_CHECK_HEADERS([attr/xattr.h],,,[#include ]) SQ_CHECK_NONSTD(ENOATTR,[ #ifdef HAVE_ATTR_XATTR_H #include #include #endif #include ],[int e = ENOATTR;],[$1]) ]) AC_DEFUN([SQ_CHECK_DECL_DAEMON], [SQ_CHECK_NONSTD(daemon,[#include ],[(void)daemon;])]) AC_DEFUN([SQ_CHECK_DECL_SYMLINK], [SQ_CHECK_NONSTD(symlink,[#include ],[(void)symlink;])]) squashfuse-0.5.0/nonstd-daemon.c000066400000000000000000000031141450034605500166160ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #define SQFEATURE NONSTD_DAEMON_DEF #include "nonstd-internal.h" #include #include int sqfs_ll_daemonize(int fg) { #if HAVE_DECL_FUSE_DAEMONIZE return fuse_daemonize(fg); #else return daemon(0,0); #endif } squashfuse-0.5.0/nonstd-enoattr.c000066400000000000000000000031311450034605500170260ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #define SQFEATURE NONSTD_ENOATTR_DEF #include "nonstd-internal.h" #ifdef HAVE_ATTR_XATTR_H #include #include #endif #include #ifndef ENOATTR #define ENOATTR ENODATA #endif int sqfs_enoattr() { return ENOATTR; } squashfuse-0.5.0/nonstd-internal.h000066400000000000000000000035711450034605500172030ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define CHANGE_XOPEN_SOURCE 1 #define CHANGE_DARWIN_C_SOURCE 2 #define CHANGE_BSD_SOURCE 3 #define CHANGE_GNU_SOURCE 4 #define CHANGE_POSIX_C_SOURCE 5 #define CHANGE_NETBSD_SOURCE 6 #if SQFEATURE == CHANGE_XOPEN_SOURCE #define _XOPEN_SOURCE 500 #elif SQFEATURE == CHANGE_NETBSD_SOURCE #define _NETBSD_SOURCE #elif SQFEATURE == CHANGE_DARWIN_C_SOURCE #define _DARWIN_C_SOURCE #elif SQFEATURE == CHANGE_BSD_SOURCE #define _BSD_SOURCE #elif SQFEATURE == CHANGE_GNU_SOURCE #define _GNU_SOURCE #elif SQFEATURE == CHANGE_POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif squashfuse-0.5.0/nonstd-makedev.c000066400000000000000000000034731450034605500167770ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #ifdef QNX_MAKEDEV #include #include #include dev_t sqfs_makedev(int maj, int min) { return makedev(ND_LOCAL_NODE, maj, min); } #else #define SQFEATURE NONSTD_MAKEDEV_DEF #include "nonstd-internal.h" #include #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif dev_t sqfs_makedev(int maj, int min) { return makedev(maj, min); } #endif squashfuse-0.5.0/nonstd-pread.c000066400000000000000000000035711450034605500164550ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #ifdef _WIN32 #include "win32.h" ssize_t sqfs_pread(HANDLE file, void *buf, size_t count, sqfs_off_t off) { DWORD bread; OVERLAPPED ov = { 0 }; ov.Offset = (DWORD)off; ov.OffsetHigh = (DWORD)(off >> 32); if (ReadFile(file, buf, count, &bread, &ov) == FALSE) return -1; return bread; } #else #define SQFEATURE NONSTD_PREAD_DEF #include "nonstd-internal.h" #include #include "common.h" ssize_t sqfs_pread(sqfs_fd_t fd, void *buf, size_t count, sqfs_off_t off) { return pread(fd, buf, count, off); } #endif squashfuse-0.5.0/nonstd-stat.c000066400000000000000000000041421450034605500163300ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #define SQFEATURE NONSTD_S_IFSOCK_DEF #include "nonstd-internal.h" #include #include "common.h" #include "squashfs_fs.h" /* S_IF* are not standard */ sqfs_mode_t sqfs_mode(int inode_type) { switch (inode_type) { case SQUASHFS_DIR_TYPE: case SQUASHFS_LDIR_TYPE: return S_IFDIR; case SQUASHFS_REG_TYPE: case SQUASHFS_LREG_TYPE: return S_IFREG; case SQUASHFS_SYMLINK_TYPE: case SQUASHFS_LSYMLINK_TYPE: return S_IFLNK; case SQUASHFS_BLKDEV_TYPE: case SQUASHFS_LBLKDEV_TYPE: return S_IFBLK; case SQUASHFS_CHRDEV_TYPE: case SQUASHFS_LCHRDEV_TYPE: return S_IFCHR; case SQUASHFS_FIFO_TYPE: case SQUASHFS_LFIFO_TYPE: return S_IFIFO; case SQUASHFS_SOCKET_TYPE: case SQUASHFS_LSOCKET_TYPE: return S_IFSOCK; } return 0; } squashfuse-0.5.0/nonstd-symlink.c000066400000000000000000000030211450034605500170360ustar00rootroot00000000000000/* * Copyright (c) 2021 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #define SQFEATURE NONSTD_SYMLINK_DEF #include "nonstd-internal.h" #include int sqfs_symlink(const char *target, const char *linkpath) { return symlink(target, linkpath); } squashfuse-0.5.0/nonstd.h000066400000000000000000000031651450034605500153700ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_STD_H #define SQFS_STD_H #include "common.h" /* Non-standard functions that we need */ dev_t sqfs_makedev(int maj, int min); ssize_t sqfs_pread(sqfs_fd_t fd, void *buf, size_t count, sqfs_off_t off); int sqfs_enoattr(); int sqfs_symlink(const char *target, const char *linkpath); #endif squashfuse-0.5.0/squashfs_fs.h000066400000000000000000000161711450034605500164110ustar00rootroot00000000000000/* * squashfs_fs.h * * Copyright (c) 2012 Phillip Lougher * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQUASHFS_FS #define SQUASHFS_FS #ifdef HAVE_LINUX_TYPES_LE16 #include #else #include typedef uint16_t __le16; typedef uint32_t __le32; typedef uint64_t __le64; #endif #define SQUASHFS_MAGIC 0x73717368 #define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE #define SQUASHFS_MAJOR 4 #define SQUASHFS_MINOR 0 #define SQUASHFS_START 0 /* size of metadata (inode and directory) blocks */ #define SQUASHFS_METADATA_SIZE 8192 #define SQUASHFS_METADATA_LOG 13 /* default size of data blocks */ #define SQUASHFS_FILE_SIZE 131072 #define SQUASHFS_FILE_LOG 17 #define SQUASHFS_FILE_MAX_SIZE 1048576 #define SQUASHFS_FILE_MAX_LOG 20 /* Max number of uids and gids */ #define SQUASHFS_IDS 65536 /* Max length of filename (not 255) */ #define SQUASHFS_NAME_LEN 256 #define SQUASHFS_INVALID_FRAG (0xffffffffU) #define SQUASHFS_INVALID_XATTR (0xffffffffU) #define SQUASHFS_INVALID_BLK ((int64_t)-1) /* Filesystem flags */ #define SQUASHFS_NOI 0 #define SQUASHFS_NOD 1 #define SQUASHFS_NOF 3 #define SQUASHFS_NO_FRAG 4 #define SQUASHFS_ALWAYS_FRAG 5 #define SQUASHFS_DUPLICATE 6 #define SQUASHFS_EXPORT 7 #define SQUASHFS_COMP_OPT 10 /* Max number of types and file types */ #define SQUASHFS_DIR_TYPE 1 #define SQUASHFS_REG_TYPE 2 #define SQUASHFS_SYMLINK_TYPE 3 #define SQUASHFS_BLKDEV_TYPE 4 #define SQUASHFS_CHRDEV_TYPE 5 #define SQUASHFS_FIFO_TYPE 6 #define SQUASHFS_SOCKET_TYPE 7 #define SQUASHFS_LDIR_TYPE 8 #define SQUASHFS_LREG_TYPE 9 #define SQUASHFS_LSYMLINK_TYPE 10 #define SQUASHFS_LBLKDEV_TYPE 11 #define SQUASHFS_LCHRDEV_TYPE 12 #define SQUASHFS_LFIFO_TYPE 13 #define SQUASHFS_LSOCKET_TYPE 14 /* Xattr types */ #define SQUASHFS_XATTR_USER 0 #define SQUASHFS_XATTR_TRUSTED 1 #define SQUASHFS_XATTR_SECURITY 2 #define SQUASHFS_XATTR_VALUE_OOL 256 #define SQUASHFS_XATTR_PREFIX_MASK 0xff #define SQUASHFS_COMPRESSED_BIT (1 << 15) #define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) /* cached data constants for filesystem */ #ifdef SQFS_MULTITHREADED # define SQUASHFS_CACHED_BLKS 128 #else # define SQUASHFS_CACHED_BLKS 8 #endif #define SQUASHFS_MAX_FILE_SIZE_LOG 64 #define SQUASHFS_MAX_FILE_SIZE (1LL << \ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) /* meta index cache */ #define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) #define SQUASHFS_META_ENTRIES 127 #define SQUASHFS_META_SLOTS 8 /* * definitions for structures on disk */ #define ZLIB_COMPRESSION 1 #define LZMA_COMPRESSION 2 #define LZO_COMPRESSION 3 #define XZ_COMPRESSION 4 #define LZ4_COMPRESSION 5 #define ZSTD_COMPRESSION 6 struct squashfs_super_block { __le32 s_magic; __le32 inodes; __le32 mkfs_time; __le32 block_size; __le32 fragments; __le16 compression; __le16 block_log; __le16 flags; __le16 no_ids; __le16 s_major; __le16 s_minor; __le64 root_inode; __le64 bytes_used; __le64 id_table_start; __le64 xattr_id_table_start; __le64 inode_table_start; __le64 directory_table_start; __le64 fragment_table_start; __le64 lookup_table_start; }; struct squashfs_dir_index { __le32 index; __le32 start_block; __le32 size; }; struct squashfs_base_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; }; struct squashfs_ipc_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 nlink; }; struct squashfs_lipc_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 nlink; __le32 xattr; }; struct squashfs_dev_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 nlink; __le32 rdev; }; struct squashfs_ldev_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 nlink; __le32 rdev; __le32 xattr; }; struct squashfs_symlink_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 nlink; __le32 symlink_size; }; struct squashfs_reg_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 start_block; __le32 fragment; __le32 offset; __le32 file_size; }; struct squashfs_lreg_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le64 start_block; __le64 file_size; __le64 sparse; __le32 nlink; __le32 fragment; __le32 offset; __le32 xattr; }; struct squashfs_dir_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 start_block; __le32 nlink; __le16 file_size; __le16 offset; __le32 parent_inode; }; struct squashfs_ldir_inode { __le16 inode_type; __le16 mode; __le16 uid; __le16 guid; __le32 mtime; __le32 inode_number; __le32 nlink; __le32 file_size; __le32 start_block; __le32 parent_inode; __le16 i_count; __le16 offset; __le32 xattr; }; struct squashfs_dir_entry { __le16 offset; __le16 inode_number; __le16 type; __le16 size; }; struct squashfs_dir_header { __le32 count; __le32 start_block; __le32 inode_number; }; struct squashfs_fragment_entry { __le64 start_block; __le32 size; unsigned int unused; }; struct squashfs_xattr_entry { __le16 type; __le16 size; }; struct squashfs_xattr_val { __le32 vsize; }; struct squashfs_xattr_id { __le64 xattr; __le32 count; __le32 size; }; struct squashfs_xattr_id_table { __le64 xattr_table_start; __le32 xattr_ids; __le32 unused; }; #endif squashfuse-0.5.0/squashfuse.1000066400000000000000000000052101450034605500161540ustar00rootroot00000000000000.\"Copyright (c) 2012 Dave Vasilevsky .\"All rights reserved. .\" .\"Redistribution and use in source and binary forms, with or without .\"modification, are permitted provided that the following conditions .\"are met: .\"1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\"2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\"THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR .\"IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\"OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\"IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, .\"INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\"NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\"THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .Dd March 19, 2012 .Dt squashfuse 1 .Os .Sh NAME .Nm squashfuse .Nd mount a SquashFS archive with FUSE .Sh SYNOPSIS .Nm .Op Fl o Ar options .Ar archive .Ar mountpoint .Sh DESCRIPTION .Nm mounts the SquashFS filesystem .Ar archive on the directory .Ar mountpoint . The filesystem can be unmounted using .Xr umount 8 or .Xr fusermount 8 . The .Nm implementation is single-threaded. .Pp Options supported by the .Xr fuse 8 library are supported. In addition the following options are supported: .Bl -tag -width -indent .It Fl o Cm offset=N offset N bytes into archive to mount .El .Bl -tag -width -indent .It Fl o Cm subdir=PATH mount subdirectory PATH as filesystem root .El .Bl -tag -width -indent .It Fl o Cm notify_pipe=PATH named pipe that will receive 's' (success) or 'f' (failure) when the mountpoint is ready .El .Pp Here is a selection of generally useful FUSE library options: .Bl -tag -width -indent .It Fl h, -help print help .It Fl V, -version show FUSE version .It Fl d, o Cm debug enable debug output (implies .Fl f ) .It Fl f foreground operation .It Fl o Cm allow_other allow access by other users .It Fl o Cm allow_root allow access by the superuser .El .Sh SEE ALSO .Xr fuse 8 , .Xr fusermount 8 , .Xr mount 8 , .Xr umount 8 .Pp .mso www.tmac .URL "http://squashfs.sourceforge.net/" SquashFS .Pp .URL "http://fuse.sourceforge.net/" FUSE squashfuse-0.5.0/squashfuse.h000066400000000000000000000027551450034605500162560ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_SQUASHFUSE_H #define SQFS_SQUASHFUSE_H #include "dir.h" #include "file.h" #include "fs.h" #include "traverse.h" #include "util.h" #include "xattr.h" #endif squashfuse-0.5.0/squashfuse.pc.in000066400000000000000000000003771450034605500170340ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/squashfuse Name: squashfuse Description: squashfuse library to mount squashfs archives Version: @VERSION@ Requires: Libs: -L${libdir} -lsquashfuse Cflags: -I${includedir} squashfuse-0.5.0/squashfuse_ll.1000066400000000000000000000043261450034605500166520ustar00rootroot00000000000000.\"Copyright (c) 2012 Dave Vasilevsky .\"All rights reserved. .\" .\"Redistribution and use in source and binary forms, with or without .\"modification, are permitted provided that the following conditions .\"are met: .\"1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\"2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\"THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR .\"IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\"OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\"IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, .\"INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\"NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\"THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .Dd June 30, 2023 .Dt squashfuse_ll 1 .Os .Sh NAME .Nm squashfuse_ll .Nd mount a SquashFS archive with low-level FUSE .Sh SYNOPSIS .Nm .Op Fl o Ar options .Ar archive .Ar mountpoint .Sh DESCRIPTION .Nm mounts the SquashFS filesystem using FUSE .Ar archive on the directory .Ar mountpoint . It is generally compatible with .Xr squashfuse 1 but it uses the higher performance low-level FUSE interface and it supports multi-threading. .Pp See the .Xr squashfuse 1 man page for options. Note that some of the .Xr fuse 8 library options are only supported on high-level FUSE and so do not apply. These additional options are also supported, including some that are normally supported only in high-level FUSE: .Bl -tag -width -indent .It Fl o Cm timeout=N idle N seconds for automatic unmount .It Fl o Cm uid=N set file owner to uid N .It Fl o Cm gid=N set file group to gid N .El .Sh SEE ALSO .Xr squashfuse 1 , .Xr fuse 8 squashfuse-0.5.0/squashfuse_ll.pc.in000066400000000000000000000004341450034605500175150ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/squashfuse Name: squashfuse_ll Description: squashfuse low-level library to mount squashfs archives Version: @VERSION@ Requires: Libs: -L${libdir} -lsquashfuse -lsquashfuse_ll Cflags: -I${includedir} squashfuse-0.5.0/stack.c000066400000000000000000000064121450034605500151610ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "stack.h" #include /* Ensure a capacity of cap */ static sqfs_err sqfs_stack_capacity(sqfs_stack *s, size_t cap) { char *items; if (cap <= s->capacity) return SQFS_OK; items = realloc(s->items, cap * s->value_size); if (!items) return SQFS_ERR; s->items = items; s->capacity = cap; return SQFS_OK; } /* Calculate the next capacity to use */ #define CAPACITY_DEFAULT 8 #define CAPACITY_RATIO 3 / 2 static size_t sqfs_stack_next_capacity(size_t cap) { size_t n; if (cap == 0) return CAPACITY_DEFAULT; n = cap * CAPACITY_RATIO; if (n <= cap) return cap + 1; return n; } /* Grow by one */ static sqfs_err sqfs_stack_grow(sqfs_stack *s) { if (s->size == s->capacity) { sqfs_err err = sqfs_stack_capacity(s, sqfs_stack_next_capacity(s->capacity)); if (err) return err; } s->size++; return SQFS_OK; } sqfs_err sqfs_stack_create(sqfs_stack *s, size_t vsize, size_t initial, sqfs_stack_free_t freer) { s->value_size = vsize; s->freer = freer; s->items = NULL; s->capacity = s->size = 0; return sqfs_stack_capacity(s, initial); } void sqfs_stack_init(sqfs_stack *s) { s->items = NULL; s->capacity = 0; } void sqfs_stack_destroy(sqfs_stack *s) { while (sqfs_stack_pop(s)) ; /* pass */ free(s->items); sqfs_stack_init(s); } sqfs_err sqfs_stack_push(sqfs_stack *s, void *vout) { sqfs_err err = sqfs_stack_grow(s); if (err) return err; return sqfs_stack_top(s, vout); } bool sqfs_stack_pop(sqfs_stack *s) { void *v = NULL; if (s->size == 0) return false; sqfs_stack_top(s, &v); if (s->freer) s->freer(v); s->size--; return true; } size_t sqfs_stack_size(sqfs_stack *s) { return s->size; } sqfs_err sqfs_stack_at(sqfs_stack *s, size_t i, void *vout) { if (i >= s->size) return SQFS_ERR; *(void**)vout = s->items + i * s->value_size; return SQFS_OK; } sqfs_err sqfs_stack_top(sqfs_stack *s, void *vout) { if (s->size == 0) return SQFS_ERR; return sqfs_stack_at(s, s->size - 1, vout); } squashfuse-0.5.0/stack.h000066400000000000000000000040031450034605500151600ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_STACK_H #define SQFS_STACK_H #include "common.h" typedef void (*sqfs_stack_free_t)(void *v); typedef struct { size_t value_size; size_t size; size_t capacity; char *items; sqfs_stack_free_t freer; } sqfs_stack; /* Ensures the struct is in a safe state */ void sqfs_stack_init(sqfs_stack *s); sqfs_err sqfs_stack_create(sqfs_stack *s, size_t vsize, size_t initial, sqfs_stack_free_t freer); void sqfs_stack_destroy(sqfs_stack *s); sqfs_err sqfs_stack_push(sqfs_stack *s, void *vout); bool sqfs_stack_pop(sqfs_stack *s); size_t sqfs_stack_size(sqfs_stack *s); sqfs_err sqfs_stack_at(sqfs_stack *s, size_t i, void *vout); sqfs_err sqfs_stack_top(sqfs_stack *s, void *vout); #endif squashfuse-0.5.0/stat.c000066400000000000000000000045751450034605500150370ustar00rootroot00000000000000/* * Copyright (c) 2019 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "stat.h" #include "nonstd.h" #include sqfs_err sqfs_stat(sqfs *fs, sqfs_inode *inode, struct stat *st) { sqfs_err err = SQFS_OK; uid_t id; memset(st, 0, sizeof(*st)); st->st_mode = inode->base.mode; st->st_nlink = inode->nlink; st->st_mtime = st->st_ctime = st->st_atime = inode->base.mtime; if (S_ISREG(st->st_mode)) { /* FIXME: do symlinks, dirs, etc have a size? */ st->st_size = inode->xtra.reg.file_size; st->st_blocks = st->st_size / 512; } else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { st->st_rdev = sqfs_makedev(inode->xtra.dev.major, inode->xtra.dev.minor); } else if (S_ISLNK(st->st_mode)) { st->st_size = inode->xtra.symlink_size; } st->st_blksize = fs->sb.block_size; /* seriously? */ if (fs->uid > 0) { st->st_uid = fs->uid; } else { err = sqfs_id_get(fs, inode->base.uid, &id); if (err) return err; st->st_uid = id; } if (fs->gid > 0) { st->st_gid = fs->gid; } else { err = sqfs_id_get(fs, inode->base.guid, &id); st->st_gid = id; if (err) return err; } return SQFS_OK; } squashfuse-0.5.0/stat.h000066400000000000000000000030311450034605500150260ustar00rootroot00000000000000/* * Copyright (c) 2019 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_STAT_H #define SQFS_STAT_H #include "squashfuse.h" #include /* Fill in a stat structure. Does not set st_ino */ sqfs_err sqfs_stat(sqfs *fs, sqfs_inode *inode, struct stat *st); #endif squashfuse-0.5.0/swap.c000066400000000000000000000033671450034605500150340ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "swap.h" #ifndef HAVE_ASM_BYTEORDER_H #define SWAP(BITS) \ void sqfs_swapin##BITS(uint##BITS##_t *v) { \ int i; \ uint8_t *c = (uint8_t*)v; \ uint##BITS##_t r = 0; \ for (i = sizeof(*v) - 1; i >= 0; --i) { \ r <<= 8; \ r += c[i]; \ } \ *v = r; \ } SWAP(16) SWAP(32) SWAP(64) #undef SWAP #endif void sqfs_swap16(uint16_t *n) { *n = (*n >> 8) + (*n << 8); } #include "squashfs_fs.h" #include "swap.c.inc" squashfuse-0.5.0/swap.h000066400000000000000000000042661450034605500150400ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_SWAP_H #define SQFS_SWAP_H #include "common.h" #include "squashfs_fs.h" #ifdef HAVE_ASM_BYTEORDER_H #include #endif #define SQFS_MAGIC_SWAP 0x68737173 void sqfs_swap16(uint16_t *n); #ifdef HAVE_ASM_BYTEORDER_H static inline void sqfs_swapin16(uint16_t *v) { *v = __le16_to_cpu(*v); } static inline void sqfs_swapin32(uint32_t *v) { *v = __le32_to_cpu(*v); } static inline void sqfs_swapin64(uint64_t *v) { *v = __le64_to_cpu(*v); } #else void sqfs_swapin16(uint16_t *v); void sqfs_swapin32(uint32_t *v); void sqfs_swapin64(uint64_t *v); #endif static inline void sqfs_swapin16_internal(__le16 *v) { sqfs_swapin16((uint16_t*)v); } static inline void sqfs_swapin32_internal(__le32 *v) { sqfs_swapin32((uint32_t*)v); } static inline void sqfs_swapin64_internal(__le64 *v) { sqfs_swapin64((uint64_t*)v); } #include "swap.h.inc" #endif squashfuse-0.5.0/table.c000066400000000000000000000050311450034605500151370ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "table.h" #include "fs.h" #include "nonstd.h" #include "squashfs_fs.h" #include "swap.h" #include #include sqfs_err sqfs_table_init(sqfs_table *table, sqfs_fd_t fd, sqfs_off_t start, size_t each, size_t count) { size_t i; size_t nblocks, bread; if (count == 0) return SQFS_OK; nblocks = sqfs_divceil(each * count, SQUASHFS_METADATA_SIZE); bread = nblocks * sizeof(uint64_t); table->each = each; if (!(table->blocks = malloc(bread))) goto err; if (sqfs_pread(fd, table->blocks, bread, start) != bread) goto err; for (i = 0; i < nblocks; ++i) sqfs_swapin64(&table->blocks[i]); return SQFS_OK; err: free(table->blocks); table->blocks = NULL; return SQFS_ERR; } void sqfs_table_destroy(sqfs_table *table) { free(table->blocks); table->blocks = NULL; } sqfs_err sqfs_table_get(sqfs_table *table, sqfs *fs, size_t idx, void *buf) { sqfs_block *block; size_t pos = idx * table->each; size_t bnum = pos / SQUASHFS_METADATA_SIZE, off = pos % SQUASHFS_METADATA_SIZE; sqfs_off_t bpos = table->blocks[bnum]; if (sqfs_md_cache(fs, &bpos, &block)) return SQFS_ERR; memcpy(buf, (char*)(block->data) + off, table->each); sqfs_block_dispose(block); return SQFS_OK; } squashfuse-0.5.0/table.h000066400000000000000000000032561450034605500151530ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_TABLE_H #define SQFS_TABLE_H #include "common.h" typedef struct { size_t each; uint64_t *blocks; } sqfs_table; sqfs_err sqfs_table_init(sqfs_table *table, sqfs_fd_t fd, sqfs_off_t start, size_t each, size_t count); void sqfs_table_destroy(sqfs_table *table); sqfs_err sqfs_table_get(sqfs_table *table, sqfs *fs, size_t idx, void *buf); #endif squashfuse-0.5.0/tests/000077500000000000000000000000001450034605500150475ustar00rootroot00000000000000squashfuse-0.5.0/tests/cachetest.c000066400000000000000000000056231450034605500171640ustar00rootroot00000000000000#include "cache.h" #include typedef struct { int x; int y; } TestStruct; static void TestStructDispose(void *t) { // nada. } #define EXPECT_EQ(exp1, exp2) \ do { if ((exp1) != (exp2)) { \ printf("Test failure: expected " #exp1 " to equal " #exp2 \ " at " __FILE__ ":%d\n", __LINE__); \ ++errors; \ } \ } while (0) #define EXPECT_NE(exp1, exp2) \ do { if ((exp1) == (exp2)) { \ printf("Test failure: expected " #exp1 " to !equal " #exp2 \ " at " __FILE__ ":%d\n", __LINE__); \ ++errors; \ } \ } while (0) int test_cache_miss(void) { int errors = 0; sqfs_cache cache; TestStruct *entry; EXPECT_EQ(sqfs_cache_init(&cache, sizeof(TestStruct), 16, TestStructDispose), SQFS_OK); entry = (TestStruct *)sqfs_cache_get(&cache, 1); EXPECT_EQ(sqfs_cache_entry_valid(&cache, entry), 0); sqfs_cache_put(&cache, entry); sqfs_cache_destroy(&cache); return errors == 0; } int test_mark_valid_and_lookup(void) { int errors = 0; sqfs_cache cache; TestStruct *entry; EXPECT_EQ(sqfs_cache_init(&cache, sizeof(TestStruct), 16, TestStructDispose), SQFS_OK); entry = (TestStruct *)sqfs_cache_get(&cache, 1); entry->x = 666; entry->y = 777; sqfs_cache_entry_mark_valid(&cache, entry); sqfs_cache_put(&cache, entry); EXPECT_NE(sqfs_cache_entry_valid(&cache, entry), 0); entry = (TestStruct *)sqfs_cache_get(&cache, 1); EXPECT_NE(sqfs_cache_entry_valid(&cache, entry), 0); EXPECT_EQ(entry->x, 666); EXPECT_EQ(entry->y, 777); sqfs_cache_put(&cache, entry); sqfs_cache_destroy(&cache); return errors == 0; } int test_two_entries(void) { int errors = 0; sqfs_cache cache; TestStruct *entry1, *entry2; EXPECT_EQ(sqfs_cache_init(&cache, sizeof(TestStruct), 16, TestStructDispose), SQFS_OK); entry1 = (TestStruct *)sqfs_cache_get(&cache, 1); entry1->x = 1; entry1->y = 2; sqfs_cache_entry_mark_valid(&cache, entry1); sqfs_cache_put(&cache, entry1); entry2 = (TestStruct *)sqfs_cache_get(&cache, 666); entry2->x = 3; entry2->y = 4; sqfs_cache_entry_mark_valid(&cache, entry2); sqfs_cache_put(&cache, entry2); entry1 = (TestStruct *)sqfs_cache_get(&cache, 1); sqfs_cache_put(&cache, entry1); entry2 = (TestStruct *)sqfs_cache_get(&cache, 666); sqfs_cache_put(&cache, entry2); EXPECT_NE(sqfs_cache_entry_valid(&cache, entry1), 0); EXPECT_NE(sqfs_cache_entry_valid(&cache, entry2), 0); EXPECT_EQ(entry1->x, 1); EXPECT_EQ(entry1->y, 2); EXPECT_EQ(entry2->x, 3); EXPECT_EQ(entry2->y, 4); sqfs_cache_destroy(&cache); return errors == 0; } int main(void) { return test_cache_miss() && test_mark_valid_and_lookup() && test_two_entries() ? 0 : 1; } squashfuse-0.5.0/tests/endiantest.c000066400000000000000000000014721450034605500173550ustar00rootroot00000000000000/* Compare results of our macros vs. functions provided by * endian.h, if available. If not available, simply skip * tests. */ #if defined(HAVE_ENDIAN_H) #define _DEFAULT_SOURCE #include #elif defined(HAVE_MACHINE_ENDIAN_H) #include #else #define SKIP_ENDIAN_TESTS #endif #ifndef SKIP_ENDIAN_TESTS #include "swap.h" int test16(void) { uint16_t val = htole16(0xbabe); sqfs_swapin16(&val); return val == 0xbabe; } int test32(void) { uint32_t val = htole32(0xc0ffee01); sqfs_swapin32(&val); return val == 0xc0ffee01; } int test64(void) { uint64_t val = htole64(0xf00ddeadbeeff00dUL); sqfs_swapin64(&val); return val == 0xf00ddeadbeeff00dUL; } int main(void) { return test16() && test32() && test64() ? 0 : 1; } #else /* SKIP_ENDIAN_TESTS */ int main(void) { return 0; } #endif squashfuse-0.5.0/tests/lib.sh.in000066400000000000000000000012471450034605500165620ustar00rootroot00000000000000sq_umount() { case @build_os@ in linux*) @sq_fusermount@ -u $1 ;; *) umount $1 ;; esac } sq_is_mountpoint() { mount | grep -q "$1" } find_compressors() { touch "$WORKDIR/comp_input" compressors= for comp in @sq_mksquashfs_compressors@; do if mksquashfs "$WORKDIR/comp_input" "$WORKDIR/comp_output" -comp $comp >/dev/null 2>/dev/null; then compressors="$compressors $comp" fi rm -f "$WORKDIR/comp_output" done if [ -z "$compressors" ]; then echo "No common compressor support between squashfuse and mksquashfs!" exit 1 fi echo "Found compressors:$compressors" } squashfuse-0.5.0/tests/ll-smoke-singlethreaded.sh000077500000000000000000000005141450034605500221110ustar00rootroot00000000000000!/bin/bash # Singlethreaded ll-smoke test. # # When multithreading is enabled at build time, it is the default # behavior of squashfuse_ll, but can be disabled at runtime with # the FUSE '-s' commandline option. # # So we just re-run the normal ll-smoke test with the '-s' option. SFLL_EXTRA_ARGS="-s" $(dirname -- $0)/ll-smoke.sh squashfuse-0.5.0/tests/ll-smoke.sh.in000077500000000000000000000126551450034605500175470ustar00rootroot00000000000000#!/bin/sh . "tests/lib.sh" # Very simple smoke test for squashfuse_ll. Make some random files. # assemble a squashfs image, mount it, compare the files. SFLL=${1:-./squashfuse_ll} # The squashfuse_ll binary. IDLE_TIMEOUT=5 trap cleanup EXIT set -e WORKDIR=$(mktemp -d) cleanup() { set +e # Don't care about errors here. if [ -n "$WORKDIR" ]; then if [ -n "$SQ_SAVE_LOGS" ]; then cp "$WORKDIR/squashfs_ll.log" "$SQ_SAVE_LOGS" || true fi if sq_is_mountpoint "$WORKDIR/mount"; then sq_umount "$WORKDIR/mount" fi rm -rf "$WORKDIR" fi } find_compressors echo "Generating random test files..." mkdir -p "$WORKDIR/source" head -c 64000000 /dev/urandom >"$WORKDIR/source/rand1" head -c 17000 /dev/urandom >"$WORKDIR/source/rand2" head -c 100000000 /dev/urandom >"$WORKDIR/source/rand3" mkdir -p "$WORKDIR/source/subdir" head -c 23200 /dev/urandom > "$WORKDIR/source/subdir/rand4" head -c 87 /dev/zero >"$WORKDIR/source/z1 with spaces" for comp in $compressors; do echo "Building $comp squashfs image..." mksquashfs "$WORKDIR/source" "$WORKDIR/squashfs.image" -comp $comp -no-progress mkdir -p "$WORKDIR/mount" echo "Mounting squashfs image..." FIFO_1=$(mktemp -u) mkfifo "$FIFO_1" $SFLL -f $SFLL_EXTRA_ARGS -o notify_pipe="$FIFO_1" "$WORKDIR/squashfs.image" "$WORKDIR/mount" >"$WORKDIR/squahfs_ll.log" 2>&1 & # Wait for the archive to be mounted. TSAN builds can take some time to mount. STATUS=$(head -c1 "$FIFO_1") if [ "$STATUS" != "s" ]; then echo "Image did not mount successfully" cp "$WORKDIR/squahfs_ll.log" /tmp/squahfs_ll.smoke.log echo "There may be clues in /tmp/squahfs_ll.smoke.log" exit 1 fi FIFO_2=$(mktemp -u) mkfifo "$FIFO_2" $SFLL -f $SFLL_EXTRA_ARGS -o notify_pipe="$FIFO_2" "$WORKDIR/squashfs.image" "$WORKDIR/nonexistent_mount" >"$WORKDIR/squahfs_ll.log" 2>&1 & # This time the mount command should fail because the mountpoint doesn't exist STATUS=$(head -c1 "$FIFO_2") if [ "$STATUS" != "f" ]; then echo "Image mounted successfully when it should have failed" cp "$WORKDIR/squahfs_ll.log" /tmp/squahfs_ll.smoke.log echo "There may be clues in /tmp/squahfs_ll.smoke.log" exit 1 fi if command -v fio >/dev/null; then echo "FIO tests..." fio --filename="$WORKDIR/mount/rand1" --direct=1 --rw=randread --ioengine=libaio --bs=512 --iodepth=16 --numjobs=4 --name=j1 --minimal --output=/dev/null --runtime 30 fio --filename="$WORKDIR/mount/rand2" --rw=randread --ioengine=libaio --bs=4k --iodepth=16 --numjobs=4 --name=j2 --minimal --output=/dev/null --runtime 30 fio --filename="$WORKDIR/mount/rand3" --rw=randread --ioengine=psync --bs=128k --name=j3 --minimal --output=/dev/null --runtime 30 else echo "Consider installing fio for better test coverage." fi echo "Comparing files..." cmp "$WORKDIR/source/rand1" "$WORKDIR/mount/rand1" cmp "$WORKDIR/source/rand2" "$WORKDIR/mount/rand2" cmp "$WORKDIR/source/rand3" "$WORKDIR/mount/rand3" cmp "$WORKDIR/source/subdir/rand4" "$WORKDIR/mount/subdir/rand4" cmp "$WORKDIR/source/z1 with spaces" "$WORKDIR/mount/z1 with spaces" echo "Parallel md5sum..." find "$WORKDIR/mount" -type f -exec @sq_md5sum@ \{\} >>"$WORKDIR/md5sums" \; split -l1 "$WORKDIR/md5sums" "$WORKDIR/sumpiece" echo "$WORKDIR"/sumpiece* | xargs -P4 -n1 @sq_md5sum@ -c echo "Lookup tests..." # Look for non-existent files to exercise failed lookup path. if [ -e "$WORKDIR/mount/bogus" ]; then echo "Bogus existence test" exit 1 fi # Twice so we hit cache path. if [ -e "$WORKDIR/mount/bogus" ]; then echo "Bogus existence test #2" exit 1 fi SRCSZ=$(wc -c < "$WORKDIR/source/rand1") MNTSZ=$(wc -c < "$WORKDIR/mount/rand1") if [ "$SRCSZ" != "$MNTSZ" ]; then echo "Bogus size $MNTSZ != $SRCSZ" exit 1 fi echo "Unmounting..." sq_umount "$WORKDIR/mount" echo "Mounting subdirectory..." $SFLL $SFLL_EXTRA_ARGS -osubdir=subdir "$WORKDIR/squashfs.image" "$WORKDIR/mount" # Wait up to 5 seconds to be mounted. TSAN builds can take some time to mount. for _ in $(seq 5); do if sq_is_mountpoint "$WORKDIR/mount"; then break fi sleep 1 done if ! sq_is_mountpoint "$WORKDIR/mount"; then echo "Image did not mount after 5 seconds." cp "$WORKDIR/squashfs_ll.log" /tmp/squashfs_ll.smoke.log echo "There may be clues in /tmp/squashfs_ll.smoke.log" exit 1 fi echo "Comparing files in subdir..." cmp "$WORKDIR/source/subdir/rand4" "$WORKDIR/mount/rand4" echo "Unmounting..." sq_umount "$WORKDIR/mount" # Only test timeouts once, it takes a long time if [ -z "$did_timeout" ]; then echo "Remounting with idle unmount option..." $SFLL $SFLL_EXTRA_ARGS -otimeout=$IDLE_TIMEOUT "$WORKDIR/squashfs.image" "$WORKDIR/mount" if ! sq_is_mountpoint "$WORKDIR/mount"; then echo "Not mounted?" exit 1 fi echo "Waiting up to $(( IDLE_TIMEOUT + 10 )) seconds for idle unmount..." sleep $(( IDLE_TIMEOUT + 10 )) if sq_is_mountpoint "$WORKDIR/mount"; then echo "FS did not idle unmount in timely way." exit 1 fi did_timeout=yes fi rm -f "$WORKDIR/squashfs.image" done echo "Success." exit 0 squashfuse-0.5.0/tests/ls.sh000077500000000000000000000016771450034605500160370ustar00rootroot00000000000000#!/bin/sh . "tests/lib.sh" # trap cleanup EXIT set -e WORKDIR=$(mktemp -d) cleanup() { set +e # Don't care about errors here. if [ -n "$WORKDIR" ]; then rm -rf "$WORKDIR" fi } find_compressors mkdir -p "$WORKDIR/source" head -c 100 /dev/urandom >"$WORKDIR/source/rand1" head -c 17000 /dev/urandom >"$WORKDIR/source/rand2" head -c 100 /dev/urandom >"$WORKDIR/source/rand3" head -c 87 /dev/zero >"$WORKDIR/source/z1 with spaces" (cd "$WORKDIR/source" && find .) | sed -e 's,^\./,,' | grep -v '^\.$' | sort > "$WORKDIR/files" for comp in $compressors; do echo "Building $comp squashfs image,.," mksquashfs "$WORKDIR/source" "$WORKDIR/squashfs.image" -comp $comp -no-progress ./squashfuse_ls "$WORKDIR/squashfs.image" | sort > "$WORKDIR/ls" if ! diff -u "$WORKDIR/files" "$WORKDIR/ls" ; then echo "Found differing files!" exit 1 fi rm -f "$WORKDIR/squashfs.image" done echo "Success." exit 0 squashfuse-0.5.0/tests/notify_test.sh000077500000000000000000000027521450034605500177630ustar00rootroot00000000000000#!/bin/sh . "tests/lib.sh" # Test the notification pipe mechanism: it should receive 's' on success and 'f' on failure trap cleanup EXIT set -e WORKDIR=$(mktemp -d) cleanup() { set +e # Don't care about errors here. if [ -n "$WORKDIR" ]; then if [ -n "$SQ_SAVE_LOGS" ]; then cp "$WORKDIR/squashfs.log" "$SQ_SAVE_LOGS" || true fi if sq_is_mountpoint "$WORKDIR/mount"; then sq_umount "$WORKDIR/mount" fi rm -rf "$WORKDIR" fi } echo "Generating random test files..." mkdir -p "$WORKDIR/source" mkdir -p "$WORKDIR/mount" head -c 17000 /dev/urandom >"$WORKDIR/source/rand1" echo "Building squashfs image..." mksquashfs "$WORKDIR/source" "$WORKDIR/squashfs.image" -no-progress FIFO=$(mktemp -u) mkfifo "$FIFO" for prog in ./squashfuse ./squashfuse_ll; do echo "Mounting squashfs archive with $prog" # Check that 's' is sent to the notification pipe in case of successful mount $prog -f -o notify_pipe="$FIFO" "$WORKDIR/squashfs.image" "$WORKDIR/mount" >"$WORKDIR/squashfs.log" 2>&1 & STATUS=$(head -c1 "$FIFO") if [ "$STATUS" != "s" ]; then echo "Mounting squashfuse on /tmp/squash failed" exit 1 fi sq_umount "$WORKDIR/mount" # Check that 'f' is sent to the notification pipe in case of failure $prog -f -o notify_pipe="$FIFO" /dev/null "$WORKDIR/mount" >"$WORKDIR/squashfs.log" 2>&1 & STATUS=$(head -c1 "$FIFO") if [ "$STATUS" != "f" ]; then echo "Mountpoint should have failed" exit 1 fi done squashfuse-0.5.0/tests/umount-test.sh.in000077500000000000000000000034711450034605500203240ustar00rootroot00000000000000#!/bin/bash . "tests/lib.sh" SFLL=${1:-./squashfuse_ll} # The squashfuse_ll binary. TIMEOUT=20 case @build_os@ in linux*) ;; *) echo "This test is only enabled on linux hosts." exit 0 ;; esac function cleanup { set +e if [[ -n "$TAIL_PID" ]]; then kill "$TAIL_PID" fi @sq_fusermount@ -u "$MNTDIR" >& /dev/null rm -rf "$WORKDIR" } set -e WORKDIR=$(mktemp -d) MNTDIR="$WORKDIR/mountpoint" mkdir -p "$MNTDIR" mkdir -p "$WORKDIR/source" trap cleanup EXIT # Make a tiny squashfs filesystem. echo "Hello world" >"$WORKDIR/source/hello" mksquashfs "$WORKDIR/source" "$WORKDIR/squashfs.image" -comp zstd -no-progress >& /dev/null # Mount it. $SFLL "$WORKDIR/squashfs.image" "$MNTDIR" SFPID=$(pgrep -f "squashfuse_ll.*$MNTDIR") if ! [[ -d /proc/$SFPID ]]; then echo "squashfuse process missing" exit 1 fi if ! grep -q "$MNTDIR" /proc/mounts; then echo "mount missing." exit 1 fi # background a task to hold a file open from the image. tail -f "${MNTDIR}/hello" >/dev/null & TAIL_PID=$! # SIGTERM the squashfuse process. kill -15 "$SFPID" # Now we expect the mountpoint to disappear due to lazy umount. if ! timeout $TIMEOUT bash -c \ "while grep -q $MNTDIR /proc/mounts; do \ sleep 1; done"; then echo "$MNTDIR did not dismount in response to SIGTERM." exit 1 fi # but the process should remain alive, because of the background task. if ! [[ -d /proc/$SFPID ]]; then echo "squashfuse process missing" exit 1 fi # Now kill the background process. kill $TAIL_PID TAIL_PID= # Now we expect the process to die. if ! timeout $TIMEOUT bash -c \ "while [[ -d /proc/$SFPID ]]; do \ sleep 1; done"; then echo "squashfuse process did not die once filesystem was released." exit 1 fi echo "Success." squashfuse-0.5.0/traverse.c000066400000000000000000000215021450034605500157040ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "traverse.h" #include "fs.h" #include #include #define TRAVERSE_PATH_SEPARATOR "/" /* Default initial capacity of trv.path */ #define TRAVERSE_DEFAULT_PATH_CAP 32 enum { /* These states may be set on entry to sqfs_traverse_next(), with real work to do. */ TRAVERSE_DESCEND, /* Descend into the current entry (a dir) */ TRAVERSE_NAME_REMOVE, /* Remove the name from the end of the stored path */ /* End states */ TRAVERSE_ERROR, TRAVERSE_FINISHED, /* Internal */ TRAVERSE_ASCEND, /* Done with a directory, ascend a level */ TRAVERSE_NAME_ADD, /* Add a name to the end of the stored path */ TRAVERSE_GET_ENTRY /* Get the next entry at the same level */ } sqfs_traverse_state; /* The struct stored in trv.stack */ typedef struct { sqfs_dir dir; size_t name_size; } sqfs_traverse_level; /* Make our structure safe */ static void sqfs_traverse_init(sqfs_traverse *trv); /* Path manipulation functions */ static sqfs_err sqfs_traverse_path_init(sqfs_traverse *trv); static sqfs_err sqfs_traverse_path_add(sqfs_traverse *trv, const char *str, size_t size); static sqfs_err sqfs_traverse_path_add_name(sqfs_traverse *trv); static sqfs_err sqfs_traverse_path_add_sep(sqfs_traverse *trv); static void sqfs_traverse_path_remove(sqfs_traverse *trv, size_t size); static void sqfs_traverse_path_remove_name(sqfs_traverse *trv); static void sqfs_traverse_path_remove_sep(sqfs_traverse *trv); /* Set the size of the last path component */ static void sqfs_traverse_path_set_name_size(sqfs_traverse *trv, size_t size); /* Add nul-terminator */ static void sqfs_traverse_path_terminate(sqfs_traverse *trv); /* Descend into new directories, and ascend back */ static sqfs_err sqfs_traverse_descend_inode(sqfs_traverse *trv, sqfs_inode *inode); static sqfs_err sqfs_traverse_descend(sqfs_traverse *trv, sqfs_inode_id iid); static sqfs_err sqfs_traverse_ascend(sqfs_traverse *trv); static void sqfs_traverse_init(sqfs_traverse *trv) { sqfs_dentry_init(&trv->entry, trv->namebuf); sqfs_stack_init(&trv->stack); trv->state = TRAVERSE_ERROR; trv->path = NULL; } sqfs_err sqfs_traverse_open_inode(sqfs_traverse *trv, sqfs *fs, sqfs_inode *inode) { sqfs_err err; sqfs_traverse_init(trv); if ((err = sqfs_traverse_path_init(trv))) goto error; err = sqfs_stack_create(&trv->stack, sizeof(sqfs_traverse_level), 0, NULL); if (err) goto error; trv->fs = fs; if ((err = sqfs_traverse_descend_inode(trv, inode))) goto error; sqfs_traverse_path_set_name_size(trv, 0); /* The root has no name */ trv->state = TRAVERSE_NAME_REMOVE; return SQFS_OK; error: sqfs_traverse_close(trv); return err; } sqfs_err sqfs_traverse_open(sqfs_traverse *trv, sqfs *fs, sqfs_inode_id iid) { sqfs_err err; sqfs_inode inode; if ((err = sqfs_inode_get(fs, &inode, iid))) return err; return sqfs_traverse_open_inode(trv, fs, &inode); } void sqfs_traverse_close(sqfs_traverse *trv) { sqfs_stack_destroy(&trv->stack); free(trv->path); sqfs_traverse_init(trv); } bool sqfs_traverse_next(sqfs_traverse *trv, sqfs_err *err) { sqfs_traverse_level *level; bool found; *err = SQFS_OK; while (true) { switch (trv->state) { case TRAVERSE_GET_ENTRY: if ((*err = sqfs_stack_top(&trv->stack, &level))) goto error; found = sqfs_dir_next(trv->fs, &level->dir, &trv->entry, err); if (*err) goto error; if (found) trv->state = TRAVERSE_NAME_ADD; else trv->state = TRAVERSE_ASCEND; break; case TRAVERSE_NAME_ADD: if ((*err = sqfs_traverse_path_add_name(trv))) goto error; if (sqfs_dentry_is_dir(&trv->entry)) trv->state = TRAVERSE_DESCEND; else trv->state = TRAVERSE_NAME_REMOVE; trv->dir_end = false; return true; case TRAVERSE_NAME_REMOVE: sqfs_traverse_path_remove_name(trv); trv->state = TRAVERSE_GET_ENTRY; break; case TRAVERSE_DESCEND: *err = sqfs_traverse_descend(trv, sqfs_dentry_inode(&trv->entry)); if (*err) goto error; trv->state = TRAVERSE_GET_ENTRY; break; case TRAVERSE_ASCEND: if ((*err = sqfs_traverse_ascend(trv))) goto error; if (sqfs_stack_size(&trv->stack) > 0) { trv->dir_end = true; trv->state = TRAVERSE_NAME_REMOVE; return true; } trv->state = TRAVERSE_FINISHED; break; case TRAVERSE_FINISHED: return false; case TRAVERSE_ERROR: *err = SQFS_ERR; goto error; } } error: trv->state = TRAVERSE_ERROR; return false; } sqfs_err sqfs_traverse_prune(sqfs_traverse *trv) { trv->state = TRAVERSE_NAME_REMOVE; return SQFS_OK; } static sqfs_err sqfs_traverse_path_init(sqfs_traverse *trv) { trv->path_cap = TRAVERSE_DEFAULT_PATH_CAP; if (!(trv->path = malloc(trv->path_cap))) return SQFS_ERR; trv->path[0] = '\0'; trv->path_size = 1; /* includes nul-terminator */ return SQFS_OK; } static void sqfs_traverse_path_terminate(sqfs_traverse *trv) { trv->path[trv->path_size - 1] = '\0'; } static sqfs_err sqfs_traverse_path_add(sqfs_traverse *trv, const char *str, size_t size) { size_t need = trv->path_size + size; if (need > trv->path_cap) { char *next_path; size_t next_cap = trv->path_cap; while (need > next_cap) next_cap *= 2; if (!(next_path = realloc(trv->path, next_cap))) return SQFS_ERR; trv->path = next_path; trv->path_cap = next_cap; } memcpy(trv->path + trv->path_size - 1, str, size); trv->path_size = need; sqfs_traverse_path_terminate(trv); return SQFS_OK; } static void sqfs_traverse_path_remove(sqfs_traverse *trv, size_t size) { if (trv->path_size > size) trv->path_size -= size; else trv->path_size = 1; /* only nul terminator left */ sqfs_traverse_path_terminate(trv); } static sqfs_err sqfs_traverse_path_add_name(sqfs_traverse *trv) { trv->path_last_size = sqfs_dentry_name_size(&trv->entry); return sqfs_traverse_path_add(trv, sqfs_dentry_name(&trv->entry), trv->path_last_size); } static sqfs_err sqfs_traverse_path_add_sep(sqfs_traverse *trv) { return sqfs_traverse_path_add(trv, TRAVERSE_PATH_SEPARATOR, strlen(TRAVERSE_PATH_SEPARATOR)); } static void sqfs_traverse_path_remove_name(sqfs_traverse *trv) { sqfs_traverse_path_remove(trv, trv->path_last_size); } static void sqfs_traverse_path_remove_sep(sqfs_traverse *trv) { sqfs_traverse_path_remove(trv, strlen(TRAVERSE_PATH_SEPARATOR)); } static void sqfs_traverse_path_set_name_size(sqfs_traverse *trv, size_t size) { trv->path_last_size = size; } static sqfs_err sqfs_traverse_descend_inode(sqfs_traverse *trv, sqfs_inode *inode) { sqfs_err err; sqfs_traverse_level *level; bool initial; initial = (sqfs_stack_size(&trv->stack) == 0); if ((err = sqfs_stack_push(&trv->stack, &level))) return err; if ((err = sqfs_dir_open(trv->fs, inode, &level->dir, 0))) return err; if (initial) { /* Don't add the separator or store the size for the root directory */ level->name_size = 0; } else { level->name_size = sqfs_dentry_name_size(&trv->entry); if ((err = sqfs_traverse_path_add_sep(trv))) return err; } return err; } static sqfs_err sqfs_traverse_descend(sqfs_traverse *trv, sqfs_inode_id iid) { sqfs_err err; sqfs_inode inode; if ((err = sqfs_inode_get(trv->fs, &inode, iid))) return err; return sqfs_traverse_descend_inode(trv, &inode); } static sqfs_err sqfs_traverse_ascend(sqfs_traverse *trv) { sqfs_err err; sqfs_traverse_level *level; if ((err = sqfs_stack_top(&trv->stack, &level))) return err; sqfs_traverse_path_remove_sep(trv); /* safe even if initial */ sqfs_traverse_path_set_name_size(trv, level->name_size); sqfs_stack_pop(&trv->stack); return SQFS_OK; } squashfuse-0.5.0/traverse.h000066400000000000000000000047331450034605500157200ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_TRAVERSE_H #define SQFS_TRAVERSE_H #include "common.h" #include "dir.h" #include "stack.h" typedef struct { bool dir_end; sqfs_dir_entry entry; char *path; /* private */ int state; sqfs *fs; sqfs_name namebuf; sqfs_stack stack; size_t path_size, path_cap; size_t path_last_size; } sqfs_traverse; /* Begin a recursive traversal of a filesystem tree. Every sub-item of the given inode will be traversed in-order, but not this inode itself. */ sqfs_err sqfs_traverse_open(sqfs_traverse *trv, sqfs *fs, sqfs_inode_id iid); sqfs_err sqfs_traverse_open_inode(sqfs_traverse *trv, sqfs *fs, sqfs_inode *inode); /* Clean up at any point during or after a traversal */ void sqfs_traverse_close(sqfs_traverse *trv); /* Get the next item in the traversal. An item may be: - A directory entry, in which case trv->entry will be filled - A marker that a directory is finished, in which case trv->dir_end will be true. Returns false if there are no more items. */ bool sqfs_traverse_next(sqfs_traverse *trv, sqfs_err *err); /* Don't recurse into the directory just returned. */ sqfs_err sqfs_traverse_prune(sqfs_traverse *trv); #endif squashfuse-0.5.0/util.c000066400000000000000000000075311450034605500150340ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "util.h" #include "fs.h" #include #ifdef _WIN32 #include sqfs_err sqfs_fd_open(const char *path, sqfs_fd_t *fd, bool print) { *fd = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (*fd != INVALID_HANDLE_VALUE) return SQFS_OK; // FIXME: Better error handling if (print) fprintf(stderr, "CreateFile error: %d\n", GetLastError()); return SQFS_ERR; } void sqfs_fd_close(sqfs_fd_t fd) { CloseHandle(fd); } #else #include #include sqfs_err sqfs_fd_open(const char *path, sqfs_fd_t *fd, bool print) { *fd = open(path, O_RDONLY); if (*fd != -1) return SQFS_OK; if (print) perror("Can't open squashfs image"); return SQFS_ERR; } void sqfs_fd_close(sqfs_fd_t fd) { close(fd); } #endif /* TODO: WIN32 implementation of open/close */ /* TODO: i18n of error messages */ sqfs_err sqfs_open_image_with_subdir(sqfs *fs, const char *image, size_t offset, const char *subdir) { sqfs_err err; sqfs_fd_t fd; if ((err = sqfs_fd_open(image, &fd, stderr))) return err; err = sqfs_init_with_subdir(fs, fd, offset, subdir); switch (err) { case SQFS_OK: break; case SQFS_BADFORMAT: fprintf(stderr, "This doesn't look like a squashfs image.\n"); break; case SQFS_BADVERSION: { int major, minor, mj1, mn1, mj2, mn2; sqfs_version(fs, &major, &minor); sqfs_version_supported(&mj1, &mn1, &mj2, &mn2); fprintf(stderr, "Squashfs version %d.%d detected, only version", major, minor); if (mj1 == mj2 && mn1 == mn2) fprintf(stderr, " %d.%d", mj1, mn1); else fprintf(stderr, "s %d.%d to %d.%d", mj1, mn1, mj2, mn2); fprintf(stderr, " supported.\n"); break; } case SQFS_BADCOMP: { bool first = true; int i; sqfs_compression_type sup[SQFS_COMP_MAX], comp = sqfs_compression(fs); sqfs_compression_supported(sup); fprintf(stderr, "Squashfs image uses %s compression, this version " "supports only ", sqfs_compression_name(comp)); for (i = 0; i < SQFS_COMP_MAX; ++i) { if (sup[i] == SQFS_COMP_UNKNOWN) continue; if (!first) fprintf(stderr, ", "); fprintf(stderr, "%s", sqfs_compression_name(sup[i])); first = false; } fprintf(stderr, ".\n"); break; } default: fprintf(stderr, "Something went wrong trying to read the squashfs " "image.\n"); } if (err) sqfs_fd_close(fd); return err; } sqfs_err sqfs_open_image(sqfs *fs, const char *image, size_t offset) { return sqfs_open_image_with_subdir(fs, image, offset, NULL); } squashfuse-0.5.0/util.h000066400000000000000000000035651450034605500150440ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_UTIL_H #define SQFS_UTIL_H #include "common.h" #include /* Open a file, and optionally print a message on failure */ sqfs_err sqfs_fd_open(const char *path, sqfs_fd_t *fd, bool print); /* Close a file */ void sqfs_fd_close(sqfs_fd_t fd); /* Open a filesystem with subdir and print errors to stderr. */ sqfs_err sqfs_open_image_with_subdir(sqfs *fs, const char *image, size_t offset, const char *subdir); /* Open a filesystem and print errors to stderr. */ sqfs_err sqfs_open_image(sqfs *fs, const char *image, size_t offset); #endif squashfuse-0.5.0/win/000077500000000000000000000000001450034605500145025ustar00rootroot00000000000000squashfuse-0.5.0/win/config.h000066400000000000000000000000351450034605500161160ustar00rootroot00000000000000/* Dummy file for Windows */ squashfuse-0.5.0/win/squashfuse_ls.sln000066400000000000000000000017211450034605500201060ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2013 for Windows Desktop VisualStudioVersion = 12.0.21005.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "squashfuse_ls", "squashfuse_ls.vcxproj", "{45D30A63-AAB9-4A37-976D-E0DBE1972431}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {45D30A63-AAB9-4A37-976D-E0DBE1972431}.Debug|Win32.ActiveCfg = Debug|Win32 {45D30A63-AAB9-4A37-976D-E0DBE1972431}.Debug|Win32.Build.0 = Debug|Win32 {45D30A63-AAB9-4A37-976D-E0DBE1972431}.Release|Win32.ActiveCfg = Release|Win32 {45D30A63-AAB9-4A37-976D-E0DBE1972431}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal squashfuse-0.5.0/win/squashfuse_ls.vcxproj000066400000000000000000000127661450034605500210200ustar00rootroot00000000000000 Debug Win32 Release Win32 {45D30A63-AAB9-4A37-976D-E0DBE1972431} Win32Proj squashfuse_ls Application true v120 Unicode Application false v120 true Unicode true $(ProjectDir);$(ProjectDir)\..;$(IncludePath) false $(ProjectDir);$(ProjectDir)\..;$(IncludePath) Level3 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) Console true true true squashfuse-0.5.0/win/squashfuse_ls.vcxproj.filters000066400000000000000000000111201450034605500224460ustar00rootroot00000000000000 {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {f4b08648-a690-4a35-a6d4-96d10e7d8d3e} {f19971b9-3994-424b-8a01-0c2c582f4fa0} Common sources Common sources Common sources Common sources Common sources Common sources Common sources Common sources Common sources Common sources Windows sources Common sources Common sources Common sources Common sources Common sources Common headers Common headers Common headers Common headers Common headers Common headers Common headers Common headers Common headers Common headers Common headers Common headers Windows headers Windows headers Common headers Common headers Common headers Common headers Common headers Common headers Common sources Windows sources squashfuse-0.5.0/win/tinfl.c000066400000000000000000000721321450034605500157670ustar00rootroot00000000000000/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c) See "unlicense" statement at the end of this file. Rich Geldreich , last updated May 20, 2011 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers. */ #ifndef TINFL_HEADER_INCLUDED #define TINFL_HEADER_INCLUDED #include typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef unsigned long long mz_uint64; #if defined(_M_IX86) || defined(_M_X64) // Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 // Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. #define MINIZ_LITTLE_ENDIAN 1 #endif #if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) // Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) #define MINIZ_HAS_64BIT_REGISTERS 1 #endif // Works around MSVC's spammy "warning C4127: conditional expression is constant" message. #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. // TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; // High level decompression functions: // tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. // On return: // Function returns a pointer to the decompressed data, or NULL on failure. // *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must free() the returned block when it's no longer needed. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. // Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. // Returns 1 on success or 0 on failure. typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; // Max size of LZ dictionary. #define TINFL_LZ_DICT_SIZE 32768 // Return status. typedef enum { TINFL_STATUS_BAD_PARAM = -3, TINFL_STATUS_ADLER32_MISMATCH = -2, TINFL_STATUS_FAILED = -1, TINFL_STATUS_DONE = 0, TINFL_STATUS_NEEDS_MORE_INPUT = 1, TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; // Initializes the decompressor to its initial state. #define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 // Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. // This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); // Internal/private bits follow. enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #endif // #ifdef TINFL_HEADER_INCLUDED // ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) #ifndef TINFL_HEADER_FILE_ONLY #include // MZ_MALLOC, etc. are only used by the optional high-level helper functions. #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN switch(r->m_state) { case 0: #define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END #define TINFL_CR_FINISH } // TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never // reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. #define TINFL_GET_BYTE(state_index, c) do { \ if (pIn_buf_cur >= pIn_buf_end) { \ for ( ; ; ) { \ if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ if (pIn_buf_cur < pIn_buf_end) { \ c = *pIn_buf_cur++; \ break; \ } \ } else { \ c = 0; \ break; \ } \ } \ } else c = *pIn_buf_cur++; } MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. // It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a // Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the // bit buffer contains >=15 bits (deflate's max. Huffman code size). #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ } while (num_bits < 15); // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read // beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully // decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. // The slow path is only executed at the very end of the input buffer. #define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ int temp; mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); } else { TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); } } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for ( ; ; ) { mz_uint8 *pSrc; for ( ; ; ) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif do { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; } while ((int)(counter -= 3) > 2); if ((int)counter > 0) { pOut_buf_cur[0] = pSrc[0]; if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } // Higher level helper functions. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for ( ; ; ) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for ( ; ; ) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } #endif // #ifndef TINFL_HEADER_FILE_ONLY /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to */ squashfuse-0.5.0/win/win32.h000066400000000000000000000035071450034605500156220ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_WIN32_H #define SQFS_WIN32_H #include #include #include #define S_IFIFO 0010000 #define S_IFBLK 0060000 #define S_IFLNK 0120000 #define S_IFSOCK 0140000 #define S_ISDIR(_m) (((_m) & S_IFMT) == S_IFDIR) #define S_ISREG(_m) (((_m) & S_IFMT) == S_IFREG) #define S_ISLNK(_m) (((_m) & S_IFMT) == S_IFLNK) typedef unsigned short sqfs_mode_t; typedef uint32_t sqfs_id_t; /* Internal uids/gids are 32-bits */ typedef SSIZE_T ssize_t; typedef DWORD64 sqfs_off_t; typedef HANDLE sqfs_fd_t; #endif squashfuse-0.5.0/win/win_decompress.c.inc000066400000000000000000000040711450034605500204410ustar00rootroot00000000000000/* * Copyright (c) 2014 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "decompress.h" #include "squashfuse.h" #include enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); static sqfs_err sqfs_decompressor_zlib(void *in, size_t insz, void *out, size_t *outsz) { size_t bytes = tinfl_decompress_mem_to_mem(out, *outsz, in, insz, TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if (bytes == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED) return SQFS_ERR; *outsz = bytes; return SQFS_OK; } #define CAN_DECOMPRESS_ZLIB 1 squashfuse-0.5.0/xattr.c000066400000000000000000000147711450034605500152250ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "xattr.h" #include "fs.h" #include "nonstd.h" #include "swap.h" #include #include #define SQFS_XATTR_PREFIX_MAX SQUASHFS_XATTR_SECURITY typedef struct { const char *pref; size_t len; } sqfs_prefix; sqfs_prefix sqfs_xattr_prefixes[] = { {"user.", 5}, {"trusted.", 8}, {"security.", 9}, }; typedef enum { CURS_VSIZE = 1, CURS_VAL = 2, CURS_NEXT = 4 } sqfs_xattr_curs; sqfs_err sqfs_xattr_init(sqfs *fs) { sqfs_off_t start = fs->sb.xattr_id_table_start; size_t bread; if (start == SQUASHFS_INVALID_BLK) return SQFS_OK; bread = sqfs_pread(fs->fd, &fs->xattr_info, sizeof(fs->xattr_info), start + fs->offset); if (bread != sizeof(fs->xattr_info)) return SQFS_ERR; sqfs_swapin_xattr_id_table(&fs->xattr_info); return sqfs_table_init(&fs->xattr_table, fs->fd, start + sizeof(fs->xattr_info) + fs->offset, sizeof(struct squashfs_xattr_id), fs->xattr_info.xattr_ids); } sqfs_err sqfs_xattr_open(sqfs *fs, sqfs_inode *inode, sqfs_xattr *x) { sqfs_err err; x->remain = 0; /* assume none exist */ if (fs->xattr_info.xattr_ids == 0 || inode->xattr == SQUASHFS_INVALID_XATTR) return SQFS_OK; err = sqfs_table_get(&fs->xattr_table, fs, inode->xattr, &x->info); if (err) return SQFS_ERR; sqfs_swapin_xattr_id(&x->info); sqfs_md_cursor_inode(&x->c_next, x->info.xattr, fs->xattr_info.xattr_table_start); x->fs = fs; x->remain = x->info.count; x->cursors = CURS_NEXT; return SQFS_OK; } sqfs_err sqfs_xattr_read(sqfs_xattr *x) { sqfs_err err; if (x->remain == 0) return SQFS_ERR; if (!(x->cursors & CURS_NEXT)) { x->ool = false; /* force inline */ if ((err = sqfs_xattr_value(x, NULL))) return err; } x->c_name = x->c_next; if ((err = sqfs_md_read(x->fs, &x->c_name, &x->entry, sizeof(x->entry)))) return err; sqfs_swapin_xattr_entry(&x->entry); x->type = x->entry.type & SQUASHFS_XATTR_PREFIX_MASK; x->ool = x->entry.type & SQUASHFS_XATTR_VALUE_OOL; if (x->type > SQFS_XATTR_PREFIX_MAX) return SQFS_ERR; --(x->remain); x->cursors = 0; return err; } size_t sqfs_xattr_name_size(sqfs_xattr *x) { return x->entry.size + sqfs_xattr_prefixes[x->type].len; } sqfs_err sqfs_xattr_name(sqfs_xattr *x, char *name, bool prefix) { sqfs_err err; if (name && prefix) { sqfs_prefix *p = &sqfs_xattr_prefixes[x->type]; memcpy(name, p->pref, p->len); name += p->len; } x->c_vsize = x->c_name; err = sqfs_md_read(x->fs, &x->c_vsize, name, x->entry.size); if (err) return err; x->cursors |= CURS_VSIZE; return err; } sqfs_err sqfs_xattr_value_size(sqfs_xattr *x, size_t *size) { sqfs_err err; if (!(x->cursors & CURS_VSIZE)) if ((err = sqfs_xattr_name(x, NULL, false))) return err; x->c_val = x->c_vsize; if ((err = sqfs_md_read(x->fs, &x->c_val, &x->val, sizeof(x->val)))) return err; sqfs_swapin_xattr_val(&x->val); if (x->ool) { uint64_t pos; x->c_next = x->c_val; if ((err = sqfs_md_read(x->fs, &x->c_next, &pos, sizeof(pos)))) return err; sqfs_swapin64(&pos); x->cursors |= CURS_NEXT; sqfs_md_cursor_inode(&x->c_val, pos, x->fs->xattr_info.xattr_table_start); if ((err = sqfs_md_read(x->fs, &x->c_val, &x->val, sizeof(x->val)))) return err; sqfs_swapin_xattr_val(&x->val); } if (size) *size = x->val.vsize; x->cursors |= CURS_VAL; return err; } sqfs_err sqfs_xattr_value(sqfs_xattr *x, void *buf) { sqfs_err err; sqfs_md_cursor c; if (!(x->cursors & CURS_VAL)) if ((err = sqfs_xattr_value_size(x, NULL))) return err; c = x->c_val; if ((err = sqfs_md_read(x->fs, &c, buf, x->val.vsize))) return err; if (!x->ool) { x->c_next = c; x->cursors |= CURS_NEXT; } return err; } static sqfs_err sqfs_xattr_find_prefix(const char *name, uint16_t *type) { int i; for (i = 0; i <= SQFS_XATTR_PREFIX_MAX; ++i) { sqfs_prefix *p = &sqfs_xattr_prefixes[i]; if (strncmp(name, p->pref, p->len) == 0) { *type = i; return SQFS_OK; } } return SQFS_ERR; } /* FIXME: Indicate EINVAL, ENOMEM? */ sqfs_err sqfs_xattr_find(sqfs_xattr *x, const char *name, bool *found) { sqfs_err err; char *cmp = NULL; uint16_t type; size_t len; if ((err = sqfs_xattr_find_prefix(name, &type))) { /* Consider an invalid prefix to just be not found, or OS X * Finder complains. */ *found = false; return SQFS_OK; } name += sqfs_xattr_prefixes[type].len; len = strlen(name); if (!(cmp = malloc(len))) return SQFS_ERR; while (x->remain) { if ((err = sqfs_xattr_read(x))) goto done; if (x->type != type && x->entry.size != len) continue; if ((err = sqfs_xattr_name(x, cmp, false))) goto done; if (strncmp(name, cmp, len) == 0) { *found = true; goto done; } } *found = false; done: free(cmp); return err; } sqfs_err sqfs_xattr_lookup(sqfs *fs, sqfs_inode *inode, const char *name, void *buf, size_t *size) { sqfs_err err = SQFS_OK; sqfs_xattr xattr; if ((err = sqfs_xattr_open(fs, inode, &xattr))) return err; bool found = false; if ((err = sqfs_xattr_find(&xattr, name, &found))) return err; if (!found) { *size = 0; return err; } size_t real; if ((err = sqfs_xattr_value_size(&xattr, &real))) return err; if (buf && *size >= real) { if ((err = sqfs_xattr_value(&xattr, buf))) return err; } *size = real; return err; } squashfuse-0.5.0/xattr.h000066400000000000000000000052001450034605500152150ustar00rootroot00000000000000/* * Copyright (c) 2012 Dave Vasilevsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SQFS_XATTR_H #define SQFS_XATTR_H #include "common.h" #include "squashfs_fs.h" /* Initialize xattr handling for this fs */ sqfs_err sqfs_xattr_init(sqfs *fs); /* xattr iterator */ typedef struct { sqfs *fs; int cursors; sqfs_md_cursor c_name, c_vsize, c_val, c_next; size_t remain; struct squashfs_xattr_id info; uint16_t type; bool ool; struct squashfs_xattr_entry entry; struct squashfs_xattr_val val; } sqfs_xattr; /* Get xattr iterator for this inode */ sqfs_err sqfs_xattr_open(sqfs *fs, sqfs_inode *inode, sqfs_xattr *x); /* Get new xattr entry. Call while x->remain > 0 */ sqfs_err sqfs_xattr_read(sqfs_xattr *x); /* Accessors on xattr entry. No null-termination! */ size_t sqfs_xattr_name_size(sqfs_xattr *x); sqfs_err sqfs_xattr_name(sqfs_xattr *x, char *name, bool prefix); sqfs_err sqfs_xattr_value_size(sqfs_xattr *x, size_t *size); /* Yield first 'size' bytes */ sqfs_err sqfs_xattr_value(sqfs_xattr *x, void *buf); /* Find an xattr entry */ sqfs_err sqfs_xattr_find(sqfs_xattr *x, const char *name, bool *found); /* Helper to find an xattr value on an inode. Returns in 'size' the size of the xattr, if found, or zero if not found. Does not touch 'buf' if it's not big enough. */ sqfs_err sqfs_xattr_lookup(sqfs *fs, sqfs_inode *inode, const char *name, void *buf, size_t *size); #endif